#
# bulkstamp.py:
#    Stamp versions on all files that can be found in a given tree.
#
# USAGE: python bulkstamp.py <version> <root directory> <descriptions>
#
# Example: python bulkstamp.py 103 ..\win32\Build\ desc.txt
#
# <version> corresponds to the build number. It will be concatenated with
# the major and minor version numbers found in the description file.
#
# Description information is pulled from an input text file with lines of
# the form:
#
#    <basename> <white space> <description>
#
# For example:
#
#    PyWinTypes.dll Common types for Python on Win32
#    etc
#
# The product's name, major, and minor versions are specified as:
#
#    name <white space> <value>
#    major <white space> <value>
#    minor <white space> <value>
#
# The tags are case-sensitive.
#
# Any line beginning with "#" will be ignored. Empty lines are okay.
#

import sys
import os
import verstamp
import fnmatch
import string
import win32api

numStamped = 0

g_patterns = [
  '*.dll',
  '*.pyd',
  '*.exe',
  '*.ocx',
  ]


def walk(arg, dirname, names):
  global numStamped
  vars, debug, descriptions = arg
  for name in names:
    for pat in g_patterns:
      if fnmatch.fnmatch(name, pat):
        # Handle the "_d" thing.
        pathname = os.path.join(dirname, name)
        base, ext = os.path.splitext(name)
        if base[-2:]=='_d':
          name = base[:-2] + ext
        is_dll = ext.lower() != ".exe"
        if descriptions.has_key(os.path.normcase(name)):
          desc = descriptions[os.path.normcase(name)]
          try:
            verstamp.stamp(vars, pathname, desc, is_dll=is_dll)
            numStamped = numStamped + 1
          except win32api.error, (hr, func, desc):
            print "Could not stamp", pathname, "Error", hr, "-", desc
        else:
          print 'WARNING: description not provided for:', name
          # skip branding this - assume already branded or handled elsewhere
#        print "Stamped", pathname

def load_descriptions(fname, vars):
  retvars = {}
  descriptions = { }

  lines = open(fname, 'r').readlines()

  for i in range(len(lines)):
    line = string.strip(lines[i])
    if line != '' and line[0] != '#':
      idx1 = string.find(line, ' ')
      idx2 = string.find(line, '\t')
      if idx1 == -1 or idx2 < idx1:
        idx1 = idx2
      if idx1 == -1:
        print 'ERROR: bad syntax in description file at line %d.' % (i+1)
        sys.exit(1)

      key = line[:idx1]
      val = string.strip(line[idx1:])
      if key in vars:
        retvars[key] = val
      else:
        descriptions[key] = val

  if not retvars.has_key('product'):
    print 'ERROR: description file is missing the product name.'
    sys.exit(1)
  if not retvars.has_key('major'):
    print 'ERROR: description file is missing the major version number.'
    sys.exit(1)
  if not retvars.has_key('minor'):
    print 'ERROR: description file is missing the minor version number.'
    sys.exit(1)

  return retvars, descriptions

def scan(build, root, desc, **custom_vars ):
  global numStamped
  numStamped = 0
  try:
    build = string.atoi(build)
  except ValueError:
    print 'ERROR: build number is not a number: %s' % build
    sys.exit(1)

  debug = 0	### maybe fix this one day

  varList = ['major', 'minor', 'sub', 'company', 'copyright', 'trademarks', 'product']

  vars, descriptions = load_descriptions(desc, varList)
  vars['build'] = build
  vars.update(custom_vars)

  arg = vars, debug, descriptions
  os.path.walk(root, walk, arg)

  print "Stamped %d files." % (numStamped)

if __name__ == '__main__':
  if len(sys.argv) != 4:
    print "ERROR: incorrect invocation. See script's header comments."
    sys.exit(1)

  apply(scan, tuple(sys.argv[1:]))
