Index: bindepend.py
===================================================================
--- bindepend.py	(revision 690)
+++ bindepend.py	(working copy)
@@ -43,6 +43,16 @@
 iswin = sys.platform[:3] == 'win'
 cygwin = sys.platform == 'cygwin'
 
+silent = False  # True suppresses all informative messages from the dependency code
+
+if iswin:
+    
+    from manifest import RT_MANIFEST, GetManifestResources, ManifestFromXML
+    try:
+        from manifest import resource
+    except ImportError, detail:
+        resource = None
+
 excludes = {
     'KERNEL32.DLL':1,
     'ADVAPI.DLL':1,
@@ -78,6 +88,12 @@
     'PSAPI.DLL':1,
     'MSVCP80.DLL':1,
     'MSVCR80.DLL':1,
+    'MSVCP90.DLL':1,
+    'MSVCR90.DLL':1,
+    'IERTUTIL.DLL':1,
+    'POWRPROF.DLL':1,
+    'SHLWAPI.DLL':1,
+    'URLMON.DLL':1,
     # regex excludes
     # don't include in the bundle the libc and the tls stuff
     r'^/usr/lib/tls':1,
@@ -87,6 +103,8 @@
     r'/usr/lib/libGL.*':1,
     #
     '^/System/Library/Frameworks':1,
+    # MS assembly excludes
+    'Microsoft.Windows.Common-Controls':1,
 }
 
 excludesRe = re.compile('|'.join(excludes.keys()), re.I)
@@ -278,15 +296,111 @@
         fullnm = string.upper(os.path.basename(pth))
         if seen.get(string.upper(nm),0):
             continue
-        #print "I: analyzing", pth
+        if not silent:
+            print "I: Analyzing", pth
         seen[string.upper(nm)] = 1
+        if iswin:
+            for ftocnm, fn in selectAssemblies(pth):
+                lTOC.append((ftocnm, fn, 'BINARY'))
         for lib, npth in selectImports(pth, platform, xtrapath):
-            if seen.get(string.upper(lib),0):
+            if seen.get(string.upper(lib),0) or seen.get(string.upper(npth),0):
                 continue
+            seen[string.upper(npth)] = 1
             lTOC.append((lib, npth, 'BINARY'))
 
     return lTOC
 
+def getAssemblies(pth):
+    """Return the dependent assemblies of a binary."""
+    if pth.lower().endswith(".manifest"):
+        return []
+    # check for manifest file
+    manifestnm = pth + ".manifest"
+    if os.path.isfile(manifestnm):
+        fd = open(manifestnm, "rb")
+        res = {RT_MANIFEST: {1: {0: fd.read()}}}
+        fd.close()
+    elif not resource:
+        # resource access unavailable (needs pywin32)
+        return []
+    else:
+        # check the binary for embedded manifest
+        try:
+            res = GetManifestResources(pth)
+        except resource.pywintypes.error, exc:
+            if exc.args[0] == resource.ERROR_BAD_EXE_FORMAT:
+                if not silent:
+                    print 'I: Cannot get manifest resource from non-PE file:'
+                    print 'I:', pth
+                return []
+    rv = []
+    if RT_MANIFEST in res and len(res[RT_MANIFEST]):
+        for name in res[RT_MANIFEST]:
+            for language in res[RT_MANIFEST][name]:
+                # check the manifest for dependent assemblies
+                try:
+                    manifest = ManifestFromXML(res[RT_MANIFEST][name][language])
+                except Exception, exc:
+                    if not silent:
+                        print ("E: Cannot parse manifest resource %s, %s "
+                               "from") % (name, language)
+                        print "E:", pth
+                        print "E:", exc
+                    pass
+                else:
+                    if manifest.dependentAssemblies and not silent:
+                        print "I: Dependent assemblies of %s:" % pth
+                        print "I:", ", ".join([assembly.getid() 
+                                               for assembly in 
+                                               manifest.dependentAssemblies])
+                    rv.extend(manifest.dependentAssemblies)
+    return rv
+    
+def selectAssemblies(pth):
+    """Return a binary's dependent assemblies files that should be included.
+
+    Return a list of pairs (name, fullpath)
+    """
+    rv = []
+    for assembly in getAssemblies(pth):
+        if excludesRe.search(assembly.name):
+            if not silent:
+                print "I: Skipping assembly", assembly.getid()
+            continue
+        if seen.get(assembly.getid().upper(),0):
+            continue
+        elif assembly.optional:
+            if not silent:
+                print "I: Skipping optional assembly", assembly.getid()
+            continue
+        files = assembly.find_files()
+        if files:
+            seen[assembly.getid().upper()] = 1
+            for fn in files:
+                fname, fext = os.path.splitext(fn)
+                if fext.lower() == ".manifest":
+                    ftocnm = assembly.name + fext
+                else:
+                    ftocnm = os.path.basename(fn)
+                nm, ftocnm, fn = [item.encode(sys.getfilesystemencoding()) 
+                                  for item in 
+                                  (ftocnm,
+                                   os.path.join(assembly.name, ftocnm), 
+                                   fn)]
+                if not seen.get(fn.upper(),0):
+                    if not silent:
+                        print "I: Adding", ftocnm
+                    seen[nm.upper()] = 1
+                    seen[fn.upper()] = 1
+                    rv.append((ftocnm, fn))
+                else:
+                    #print "I: skipping", ftocnm, "part of assembly", \
+                    #      assembly.name, "dependency of", pth
+                    pass
+        elif not silent:
+            print "E: Assembly", assembly.getid(), "not found"
+    return rv
+
 def selectImports(pth, platform=sys.platform, xtrapath=None):
     """Return the dependencies of a binary that should be included.
 
@@ -299,31 +413,43 @@
         assert isinstance(xtrapath, list)
         xtrapath = [os.path.dirname(pth)] + xtrapath # make a copy
     dlls = getImports(pth, platform=platform)
-    iswin = platform[:3] == 'win'
     for lib in dlls:
         if not iswin and not cygwin:
-                # plain win case
+            # all other platforms
             npth = lib
             dir, lib = os.path.split(lib)
             if excludes.get(dir,0):
                 continue
         else:
-            # all other platforms
+            # plain win case
             npth = getfullnameof(lib, xtrapath)
 
-        # now npth is a candidate lib
+        # now npth is a candidate lib if found
         # check again for excludes but with regex FIXME: split the list
-        if excludesRe.search(npth):
-            if 'libpython' not in npth and 'Python.framework' not in npth:
+        if npth:
+            candidatelib = npth
+        else:
+            candidatelib = lib
+        if excludesRe.search(candidatelib):
+            if 'libpython' not in candidatelib and \
+               'Python.framework' not in candidatelib:
                 # skip libs not containing (libpython or Python.framework)
-                #print "I: skipping %20s <- %s" % (npth, pth)
+                if not silent and \
+                   not seen.get(string.upper(lib),0) and \
+                   not seen.get(string.upper(npth),0):
+                    print "I: Skipping", lib, "dependency of", \
+                          os.path.basename(pth)
                 continue
             else:
-                #print "I: inserting %20s <- %s" % (npth, pth)
                 pass
-
+        
         if npth:
-            rv.append((lib, npth))
+            if not seen.get(string.upper(lib),0) and \
+               not seen.get(string.upper(npth),0):
+                if not silent:
+                    print "I: Adding", lib, "dependency of", \
+                          os.path.basename(pth)
+                rv.append((lib, npth))
         else:
             print "E: lib not found:", lib, "dependency of", pth
 
@@ -388,7 +514,20 @@
     """Forwards to the correct getImports implementation for the platform.
     """
     if platform[:3] == 'win' or platform == 'cygwin':
-        return _getImports_pe(pth)
+        if pth.lower().endswith(".manifest"):
+            return []
+        try:
+            return _getImports_pe(pth)
+        except Exception, exception:
+            # Assemblies can pull in files which aren't necessarily PE, 
+            # but are still needed by the assembly. Any additional binary 
+            # dependencies should already have been handled by 
+            # selectAssemblies in that case, so just warn, return an empty 
+            # list and continue.
+            if not silent:
+                print 'I: Cannot get binary dependencies for non-PE file:'
+                print 'I:', pth
+            return []
     elif platform == 'darwin':
         return _getImports_otool(pth)
     else:
@@ -471,7 +610,11 @@
                       help='Target platform, required for cross-bundling (default: current platform)')
 
     opts, args = parser.parse_args()
+    silent = True  # Suppress all informative messages from the dependency code
     import glob
     for a in args:
         for fn in glob.glob(a):
-            print fn, getImports(fn, opts.target_platform)
+            imports = getImports(fn, opts.target_platform)
+            if opts.target_platform == "win32":
+                imports.extend([a.getid() for a in getAssemblies(fn)])
+            print fn, imports
Index: Build.py
===================================================================
--- Build.py	(revision 690)
+++ Build.py	(working copy)
@@ -627,6 +627,8 @@
         self.name = kws.get('name',None)
         self.icon = kws.get('icon',None)
         self.versrsrc = kws.get('version',None)
+        self.manifest = kws.get('manifest',None)
+        self.resources = kws.get('resources',[])
         self.strip = kws.get('strip',None)
         self.upx = kws.get('upx',None)
         self.crypt = kws.get('crypt', 0)
@@ -660,6 +662,8 @@
             ('debug',    _check_guts_eq),
             ('icon',     _check_guts_eq),
             ('versrsrc', _check_guts_eq),
+            ('manifest', _check_guts_eq),
+            ('resources', _check_guts_eq),
             ('strip',    _check_guts_eq),
             ('upx',      _check_guts_eq),
             ('crypt',    _check_guts_eq),
@@ -679,10 +683,10 @@
         if not data:
             return True
 
-        icon, versrsrc = data[3:5]
-        if (icon or versrsrc) and not config['hasRsrcUpdate']:
+        icon, versrsrc, manifest, resources = data[3:7]
+        if (icon or versrsrc or manifest or resources) and not config['hasRsrcUpdate']:
             # todo: really ignore :-)
-            print "ignoring icon and version resources = platform not capable"
+            print "ignoring icon, version, manifest and resources = platform not capable"
 
         mtm = data[-1]
         crypt = data[-2]
@@ -722,21 +726,64 @@
         exe = os.path.join(HOMEPATH, exe)
         if target_iswin or cygwin:
             exe = exe + '.exe'
-        if config['hasRsrcUpdate']:
+        if config['hasRsrcUpdate'] and (self.icon or self.versrsrc or 
+                                        self.manifest or self.resources):
+            tmpnm = tempfile.mktemp()
+            shutil.copy2(exe, tmpnm)
+            os.chmod(tmpnm, 0755)
             if self.icon:
-                tmpnm = tempfile.mktemp()
-                shutil.copy2(exe, tmpnm)
-                os.chmod(tmpnm, 0755)
                 icon.CopyIcons(tmpnm, self.icon)
-                trash.append(tmpnm)
-                exe = tmpnm
             if self.versrsrc:
-                tmpnm = tempfile.mktemp()
-                shutil.copy2(exe, tmpnm)
-                os.chmod(tmpnm, 0755)
                 versionInfo.SetVersion(tmpnm, self.versrsrc)
-                trash.append(tmpnm)
-                exe = tmpnm
+            if self.manifest:
+                manifest.UpdateManifestResourcesFromXMLFile(tmpnm, self.manifest, [1])
+            for res in self.resources:
+                res = res.split(",")
+                for i in range(len(res[1:])):
+                    try:
+                        res[i + 1] = int(res[i + 1])
+                    except ValueError:
+                        pass
+                resfile = res[0]
+                if len(res) > 1:
+                    restype = res[1]
+                else:
+                    restype = None
+                if len(res) > 2:
+                    resname = res[2]
+                else:
+                    restype = None
+                if len(res) > 3:
+                    reslang = res[3]
+                else:
+                    restype = None
+                try:
+                    resource.UpdateResourcesFromResFile(tmpnm, resfile, 
+                                                        [restype or "*"], 
+                                                        [resname or "*"], 
+                                                        [reslang or "*"])
+                except resource.pywintypes.error, exc:
+                    if exc.args[0] != resource.ERROR_BAD_EXE_FORMAT:
+                        print "E:", str(exc)
+                        continue
+                    if not restype or not resname:
+                        print "E: resource type and/or name not specified"
+                        continue
+                    if "*" in (restype, resname):
+                        print ("E: no wildcards allowed for resource type "
+                               "and name when source file does not contain "
+                               "resources")
+                        continue
+                    try:
+                        resource.UpdateResourcesFromDataFile(tmpnm, 
+                                                             resfile, 
+                                                             restype, 
+                                                             [resname], 
+                                                             [reslang or 0])
+                    except resource.pywintypes.error, exc:
+                        print "E:", str(exc)
+            trash.append(tmpnm)
+            exe = tmpnm
         exe = checkCache(exe, self.strip, self.upx and config['hasUPX'])
         self.copy(exe, outf)
         if self.append_pkg:
@@ -749,7 +796,7 @@
         os.chmod(self.name, 0755)
         _save_data(self.out,
                    (self.name, self.console, self.debug, self.icon,
-                    self.versrsrc, self.strip, self.upx, self.crypt, mtime(self.name)))
+                    self.versrsrc, self.manifest, self.resources, self.strip, self.upx, self.crypt, mtime(self.name)))
         for item in trash:
             os.remove(item)
         return 1
@@ -773,7 +820,7 @@
         os.chmod(self.name, 0755)
         _save_data(self.out,
                    (self.name, self.console, self.debug, self.icon,
-                    self.versrsrc, self.strip, self.upx, mtime(self.name)))
+                    self.versrsrc, self.manifest, self.resources, self.strip, self.upx, mtime(self.name)))
         return 1
 
 
@@ -1106,7 +1153,7 @@
 
 def main(specfile, configfilename):
     global target_platform, target_iswin, config
-    global icon, versionInfo
+    global icon, versionInfo, resource, manifest
 
     try:
         config = _load_data(configfilename)
@@ -1130,7 +1177,7 @@
         sys.exit(1)
 
     if config['hasRsrcUpdate']:
-        import icon, versionInfo
+        import icon, versionInfo, resource, manifest
 
     if config['hasUPX']:
         setupUPXFlags()
@@ -1146,6 +1193,9 @@
     parser.add_option('-C', '--configfile',
                       default=os.path.join(HOMEPATH, 'config.dat'),
                       help='Name of generated configfile (default: %default)')
+    parser.add_option('-o', '--buildpath',
+                      default=None,
+                      help='Buildpath')
     opts, args = parser.parse_args()
     if len(args) != 1:
         parser.error('Requires exactly one .spec-file')
Index: examples/application.exe.manifest
===================================================================
--- examples/application.exe.manifest	(revision 0)
+++ examples/application.exe.manifest	(revision 0)
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+Application Manifests example
+http://msdn.microsoft.com/en-us/library/aa374191%28VS.85%29.aspx#example
+-->
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+  <assemblyIdentity type="win32" 
+                    name="myOrganization.myDivision.mySampleApp" 
+                    version="6.0.0.0" 
+                    processorArchitecture="x86" 
+                    publicKeyToken="0000000000000000"
+  />
+  <dependency>
+    <dependentAssembly>
+      <assemblyIdentity type="win32" 
+                        name="Proseware.Research.SampleAssembly" 
+                        version="6.0.0.0" 
+                        processorArchitecture="X86" 
+                        publicKeyToken="0000000000000000" 
+                        language="*"
+      />
+    </dependentAssembly>
+  </dependency>
+</assembly>
\ No newline at end of file
Index: examples/application_win2k3.exe.config
===================================================================
--- examples/application_win2k3.exe.config	(revision 0)
+++ examples/application_win2k3.exe.config	(revision 0)
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+Per-application Configuration on Windows Server 2003 example
+http://msdn.microsoft.com/en-us/library/aa375660%28VS.85%29.aspx
+-->
+<configuration>
+ <windows>
+  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
+   <assemblyIdentity  processorArchitecture="X86" name="Microsoft.Windows.mysampleApp" type="win32" version="1.0.0.0"/>
+   <publisherPolicy apply="no"/>
+   <dependentAssembly>
+    <assemblyIdentity type="win32" processorArchitecture="x86" name="Microsoft.Windows.SampleAssembly" publicKeyToken="0000000000000000"/>
+    <bindingRedirect oldVersion="2.0.0.0" newVersion="2.0.1.0"/>
+   </dependentAssembly>
+  </assemblyBinding>
+ </windows>
+</configuration>
\ No newline at end of file
Index: examples/application_winxp.exe.config
===================================================================
--- examples/application_winxp.exe.config	(revision 0)
+++ examples/application_winxp.exe.config	(revision 0)
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+Per-application Configuration on Windows XP example
+http://msdn.microsoft.com/en-us/library/aa375667%28VS.85%29.aspx
+-->
+<configuration>
+  <windows>
+    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
+      <assemblyIdentity 
+          name="Microsoft.Windows.mysampleApp" 
+          processorArchitecture="x86" 
+          version="1.0.0.0" type="win32"/>
+        <dependentAssembly>
+          <assemblyIdentity type="win32" 
+              name="Microsoft.Windows.SampleAssembly" 
+              processorArchitecture="x86" 
+              publicKeyToken="0000000000000000"/>
+          <bindingRedirect 
+              oldVersion="2.0.0.0" 
+              newVersion="2.0.1.0"/>
+        </dependentAssembly>
+    </assemblyBinding>
+   </windows>
+</configuration>
\ No newline at end of file
Index: examples/assembly.manifest
===================================================================
--- examples/assembly.manifest	(revision 0)
+++ examples/assembly.manifest	(revision 0)
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+Assembly Manifests example
+http://msdn.microsoft.com/en-us/library/aa374219%28VS.85%29.aspx#example
+-->
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" 
+manifestVersion="1.0">
+    <assemblyIdentity type="win32" name="Microsoft.Tools.SampleAssembly" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="0000000000000000"/>
+    <file name="sampleu.dll" hash="3eab067f82504bf271ed38112a4ccdf46094eb5a" hashalg="SHA1">
+        <comClass description="Font Property Page" clsid="{0BE35200-8F91-11CE-9DE3-00AA004BB851}"/>
+        <comClass description="Color Property Page" clsid="{0BE35201-8F91-11CE-9DE3-00AA004BB851}"/>
+        <comClass description="Picture Property Page" clsid="{0BE35202-8F91-11CE-9DE3-00AA004BB851}"/>
+    </file>
+    <file name="bar.dll" hash="ac72753e5bb20446d88a48c8f0aaae769a962338" hashalg="SHA1"/>
+    <file name="foo.dll" hash="a7312a1f6cfb46433001e0540458de60adcd5ec5" hashalg="SHA1">
+        <comClass description="Registrar Class" clsid="{44EC053A-400F-11D0-9DCD-00A0C90391D3}" progid="ATL.Registrar"/>
+    <comInterfaceProxyStub iid="{B6EA2051-048A-11D1-82B9-00C04FB9942E}" name=" IAxWinAmbientDispatch " tlbid="{34EC053A-400F-11D0-9DCD-00A0C90391D3}"/>
+        <typelib tlbid="{44EC0535-400F-11D0-9DCD-00A0C90391D3}" version="1.0" helpdir=""/>
+    </file>
+    <file name="sampledll.dll" hash="ba62960ceb15073d2598379307aad84f3a73dfcb" hashalg="SHA1"/>
+<windowClass>ToolbarWindow32</windowClass>
+        <windowClass>ComboBoxEx32</windowClass>
+        <windowClass>sample_trackbar32</windowClass>
+        <windowClass>sample_updown32</windowClass>
+</assembly>
\ No newline at end of file
Index: examples/publisher.policy
===================================================================
--- examples/publisher.policy	(revision 0)
+++ examples/publisher.policy	(revision 0)
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+Publisher Configuration example
+http://msdn.microsoft.com/en-us/library/aa375680%28VS.85%29.aspx
+-->
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+   <assemblyIdentity type="win32-policy" publicKeyToken="0000000000000000" name="policy.2.0.Microsoft.Windows.SampleAssembly" version="1.1.0.0" processorArchitecture="x86"/>
+   <dependency>
+      <dependentAssembly>
+         <assemblyIdentity type="win32" name="Microsoft.Windows.SampleAssembly"  processorArchitecture="x86" publicKeyToken="75e377300ab7b886"/>
+         <bindingRedirect oldVersion="2.0.0.0" newVersion="2.0.1.0"/>
+      </dependentAssembly>
+   </dependency>
+</assembly>
\ No newline at end of file
Index: examples/template.exe.manifest
===================================================================
--- examples/template.exe.manifest	(revision 0)
+++ examples/template.exe.manifest	(revision 0)
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+  <assemblyIdentity
+    type="win32"
+    name="myOrganization.myDivision.mySampleApp"
+    version="0.0.0.0"
+    processorArchitecture="x86"
+  />
+  <description>Description</description>
+  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+    <security>
+      <requestedPrivileges>
+        <requestedExecutionLevel level="asInvoker" uiAccess="false" />
+      </requestedPrivileges>
+    </security>
+  </trustInfo>
+  <dependency>
+    <dependentAssembly>
+      <assemblyIdentity
+         type="win32"
+         name="Microsoft.Windows.Common-Controls"
+         version="6.0.0.0"
+         processorArchitecture="x86"
+         publicKeyToken="6595b64144ccf1df"
+         language="*"
+       />
+    </dependentAssembly>
+  </dependency>
+  <dependency>
+    <dependentAssembly>
+      <assemblyIdentity
+         type="win32"
+         name="Microsoft.VC90.CRT"
+         version="9.0.21022.8"
+         processorArchitecture="x86"
+         publicKeyToken="1fc8b3b9a1e18e3b"
+       />
+    </dependentAssembly>
+  </dependency>
+</assembly>
\ No newline at end of file
Index: Makespec.py
===================================================================
--- Makespec.py	(revision 690)
+++ Makespec.py	(working copy)
@@ -135,7 +135,7 @@
 
 def main(scripts, configfile=None, name=None, tk=0, freeze=0, console=1, debug=0,
          strip=0, upx=0, comserver=0, ascii=0, workdir=None,
-         pathex=[], version_file=None, icon_file=None, crypt=None):
+         pathex=[], version_file=None, icon_file=None, manifest_file=None, resources=[], crypt=None):
 
     try:
         config = eval(open(configfile, 'r').read())
@@ -168,6 +168,12 @@
         exe_options = "%s, version='%s'" % (exe_options, quote_win_filepath(version_file))
     if icon_file:
         exe_options = "%s, icon='%s'" % (exe_options, quote_win_filepath(icon_file))
+    if manifest_file:
+        exe_options = "%s, manifest='%s'" % (exe_options, quote_win_filepath(manifest_file))
+    if resources:
+        for i in range(len(resources)):
+            resources[i] = quote_win_filepath(resources[i])
+        exe_options = "%s, resources=%s" % (exe_options, repr(resources))
     if not ascii and config['hasUnicode']:
         scripts.insert(0, os.path.join(HOME, 'support', 'useUnicode.py'))
     for i in range(len(scripts)):
@@ -278,12 +284,29 @@
                  dest="version_file", metavar="FILE",
                  help="add a version resource from FILE to the exe "
                       "(Windows only)")
-    g.add_option("--icon", type="string", dest="icon_file",
+    g.add_option("-i", "--icon", type="string", dest="icon_file",
                  metavar="FILE.ICO or FILE.EXE,ID",
                  help="If FILE is an .ico file, add the icon to the final "
                       "executable. Otherwise, the syntax 'file.exe,id' to "
                       "extract the icon with the specified id "
                       "from file.exe and add it to the final executable")
+    g.add_option("-m", "--manifest", type="string",
+                 dest="manifest_file", metavar="FILE",
+                 help="add manifest FILE to the exe "
+                      "(Windows only)")
+    g.add_option("-r", "--resource", type="string", default=[], dest="resources",
+                 metavar="FILE[,TYPE[,NAME[,LANGUAGE]]]", action="append",
+                 help="add/update resource of the given type, name and language "
+                      "from FILE to the final executable. FILE can be a "
+                      "data file or an exe/dll. For data files, atleast "
+                      "TYPE and NAME need to be specified, LANGUAGE defaults "
+                      "to 0 or may be specified as wildcard * to update all "
+                      "resources of the given TYPE and NAME. For exe/dll "
+                      "files, all resources from FILE will be added/updated "
+                      "to the final executable if TYPE, NAME and LANGUAGE "
+                      "are omitted or specified as wildcard *."
+                      "Multiple resources are allowed, using this option "
+                      "multiple times.")
 
     opts,args = p.parse_args()
 
Index: manifest.py
===================================================================
--- manifest.py	(revision 0)
+++ manifest.py	(revision 0)
@@ -0,0 +1,932 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2009, Florian Hoech
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301, USA
+
+# DEV NOTES
+#
+# Currently not implemented in the Manifest class:
+# * Validation (only very basic sanity checks are currently in place)
+# * comClass, typelib, comInterfaceProxyStub and windowClass child elements of 
+#   the file element
+# * comInterfaceExternalProxyStub and windowClass child elements of the 
+#   assembly element
+# * Multilanguage User Interface (MUI) support when searching for assembly 
+#   files
+#
+# Isolated Applications and Side-by-side Assemblies:
+# http://msdn.microsoft.com/en-us/library/dd408052%28VS.85%29.aspx
+#
+# Changelog:
+# 2009-07-30  fix: Potential failure in File.calc_hash method if hash
+#                  algorythm not supported
+#             add: Publisher configuration (policy) support when searching for 
+#                  assembly files
+#             fix: Private assemblies are now actually found if present (and no
+#                  shared assembly exists)
+#             add: Python 2.3 compatibility (oldest version supported by 
+#                  pyinstaller)
+#
+# 2009-07-28  chg: Code cleanup, removed a bit of redundancy
+#             add: silent mode (set silent attribute on module)
+#             chg: Do not print messages in silent mode
+#
+# 2009-06-18  chg: Use glob instead of regular expression in Manifest.find_files
+#
+# 2009-05-04  fix: Don't fail if manifest has empty description
+#             fix: Manifests created by the toxml, toprettyxml, writexml or 
+#                  writeprettyxml methods are now correctly recognized by 
+#                  Windows, which expects the XML declaration to be ordered
+#                  version-encoding-standalone (standalone being optional)
+#             add: 'encoding' keyword argument in toxml, toprettyxml, writexml 
+#                  and writeprettyxml methods
+#             chg: UpdateManifestResourcesFromXML and 
+#                  UpdateManifestResourcesFromXMLFile: set resource name 
+#                  depending on file type ie. exe or dll
+#             fix: typo in __main__: UpdateManifestResourcesFromDataFile
+#                  should have been UpdateManifestResourcesFromXMLFile
+#
+# 2009-03-21  First version
+
+"""
+manifest.py
+
+Create, parse and write MS Windows Manifest files.
+Find files which are part of an assembly, by searching shared and 
+private assemblies.
+Update or add manifest resources in Win32 PE files.
+
+Commandline usage:
+manifest.py <dstpath> <xmlpath>
+Updates or adds manifest <xmlpath> as resource in Win32 PE file <dstpath>.
+
+"""
+
+try:
+    import hashlib
+except ImportError, detail:
+    hashlib = None
+    import md5
+    import sha
+import os.path
+from glob import glob
+import re
+from xml.dom import Node, minidom
+from xml.dom.minidom import Document, Element
+
+try:
+    import resource
+except ImportError, detail:
+    resource = None
+    print "W:", detail
+    print "W: Cannot check for assembly dependencies - resource access "
+    print "W: unavailable. To enable resource access, please install "
+    print "W: http://sourceforge.net/projects/pywin32/"
+
+silent = False  # True suppresses all messages
+
+RT_MANIFEST = 24
+
+Document.aChild = Document.appendChild
+Document.cE = Document.createElement
+Document.cT = Document.createTextNode
+Document.getEById = Document.getElementById
+Document.getEByTN = Document.getElementsByTagName
+Element.aChild = Element.appendChild
+Element.getA = Element.getAttribute
+Element.getEByTN = Element.getElementsByTagName
+Element.remA = Element.removeAttribute
+Element.setA = Element.setAttribute
+
+
+def getChildElementsByTagName(self, tagName):
+    """ Return child elements of type tagName if found, else [] """
+    result = []
+    for child in self.childNodes:
+        if isinstance(child, Element):
+            if child.tagName == tagName:
+                result.append(child)
+    return result
+
+
+def getFirstChildElementByTagName(self, tagName):
+    """ Return the first element of type tagName if found, else None """
+    for child in self.childNodes:
+        if isinstance(child, Element):
+            if child.tagName == tagName:
+                return child
+    return None
+
+
+Document.getCEByTN = getChildElementsByTagName
+Document.getFCEByTN = getFirstChildElementByTagName
+Element.getCEByTN = getChildElementsByTagName
+Element.getFCEByTN = getFirstChildElementByTagName
+
+
+class _Hash(object):
+
+    def __init__(self):
+        self.md5 = md5.new
+        self.sha = sha.new
+
+if hashlib is None:
+    hashlib = _Hash()
+
+
+def find_files(files, searchpath):
+    rfiles = []
+    for file_ in files:
+        fn = file_.find(searchpath)
+        if fn:
+            rfiles.append(fn)
+        else:
+            # If any of our files does not exist,
+            # the assembly is incomplete
+            return []
+    return rfiles
+
+
+class _Dummy:
+    pass
+
+
+if resource:
+    _File = resource.File
+else:
+    _File = _Dummy
+
+    
+class File(_File):
+
+    """ A file referenced by an assembly inside a manifest. """
+    
+    def __init__(self, filename="", hashalg=None, hash=None, comClasses=None, 
+                 typelibs=None, comInterfaceProxyStubs=None, 
+                 windowClasses=None):
+        if resource:
+            resource.File.__init__(self, filename)
+        else:
+            self.filename = filename
+        self.name = os.path.basename(filename)
+        if hashalg:
+            self.hashalg = hashalg.upper()
+        else:
+            self.hashalg = None
+        if os.path.isfile(filename) and hashalg and hashlib and \
+           hasattr(hashlib, hashalg.lower()):
+            self.calc_hash()
+        else:
+            self.hash = hash
+        self.comClasses = comClasses or [] # TO-DO: implement
+        self.typelibs = typelibs or [] # TO-DO: implement
+        self.comInterfaceProxyStubs = comInterfaceProxyStubs or [] # TO-DO: implement
+        self.windowClasses = windowClasses or [] # TO-DO: implement
+
+    def calc_hash(self, hashalg=None):
+        """
+        Calculate the hash of the file.
+        
+        Will be called automatically from the constructor if the file exists 
+        and hashalg is given (and supported), but may also be called manually 
+        e.g. to update the hash if the file has changed.
+        
+        """
+        fd = open(self.filename, "rb")
+        buf = fd.read()
+        fd.close()
+        if hashalg:
+            self.hashalg = hashalg.upper()
+        self.hash = getattr(hashlib, self.hashalg.lower())(buf).hexdigest()
+
+    def find(self, searchpath):
+        if not silent:
+            print "I: Searching for file", self.name
+        fn = os.path.join(searchpath, self.name)
+        if os.path.isfile(fn):
+            if not silent:
+                print "I: Found file", fn
+            return fn
+        else:
+            if not silent:
+                print "W: No such file", fn
+            return None
+
+
+class InvalidManifestError(Exception):
+    pass
+
+
+class Manifest(object):
+    
+    # Manifests:
+    # http://msdn.microsoft.com/en-us/library/aa375365%28VS.85%29.aspx
+
+    """
+    Manifest constructor.
+    
+    To build a basic manifest for your application:
+      mf = Manifest(type='win32', name='YourAppName', language='*', 
+                    processorArchitecture='x86', version=[1, 0, 0, 0])
+    
+    To write the XML to a manifest file:
+      mf.writexml("YourAppName.exe.manifest")
+    or
+      mf.writeprettyxml("YourAppName.exe.manifest")
+    
+    """
+
+    def __init__(self, manifestVersion=None, noInheritable=False, 
+                 noInherit=False, type_=None, name=None, language=None, 
+                 processorArchitecture=None, version=None, 
+                 publicKeyToken=None, description=None, 
+                 requestedExecutionLevel=None, uiAccess=None, 
+                 dependentAssemblies=None, files=None, 
+                 comInterfaceExternalProxyStubs=None):
+        self.filename = None
+        self.optional = None
+        self.manifestType = "assembly"
+        self.manifestVersion = manifestVersion or [1, 0]
+        self.noInheritable = noInheritable
+        self.noInherit = noInherit
+        self.type = type_
+        self.name = name
+        self.language = language
+        self.processorArchitecture = processorArchitecture
+        self.version = version
+        self.publicKeyToken = publicKeyToken
+        # publicKeyToken:
+        # A 16-character hexadecimal string that represents the last 8 bytes 
+        # of the SHA-1 hash of the public key under which the assembly is 
+        # signed. The public key used to sign the catalog must be 2048 bits 
+        # or greater. Required for all shared side-by-side assemblies.
+        # http://msdn.microsoft.com/en-us/library/aa375692(VS.85).aspx
+        self.applyPublisherPolicy = None
+        self.description = None
+        self.requestedExecutionLevel = requestedExecutionLevel
+        self.uiAccess = uiAccess
+        self.dependentAssemblies = dependentAssemblies or []
+        self.bindingRedirects = []
+        self.files = files or []
+        self.comInterfaceExternalProxyStubs = comInterfaceExternalProxyStubs or [] # TO-DO: implement
+    
+    def add_dependent_assembly(self, manifestVersion=None, noInheritable=False, 
+                 noInherit=False, type_=None, name=None, language=None, 
+                 processorArchitecture=None, version=None, 
+                 publicKeyToken=None, description=None, 
+                 requestedExecutionLevel=None, uiAccess=None, 
+                 dependentAssemblies=None, files=None, 
+                 comInterfaceExternalProxyStubs=None):
+        """
+        Shortcut for self.dependentAssemblies.append(Manifest(*args, **kwargs))
+        """
+        self.dependentAssemblies.append(Manifest(manifestVersion, 
+                                        noInheritable, noInherit, type_, name, 
+                                        language, processorArchitecture, 
+                                        version, publicKeyToken, description, 
+                                        requestedExecutionLevel, uiAccess, 
+                                        dependentAssemblies, files, 
+                                        comInterfaceExternalProxyStubs))
+        if self.filename:
+            # Enable search for private assembly by assigning bogus filename
+            # (only the directory has to be correct)
+            self.dependentAssemblies[-1].filename = ":".join((self.filename, 
+                                                              name))
+    
+    def add_file(self, name="", hashalg="", hash="", comClasses=None, 
+                 typelibs=None, comInterfaceProxyStubs=None, 
+                 windowClasses=None):
+        """ Shortcut for manifest.files.append """
+        self.files.append(File(name, hashalg, hash, comClasses, 
+                          typelibs, comInterfaceProxyStubs, windowClasses))
+    
+    def find_files(self, ignore_policies=True):
+        """ Search shared and private assemblies and return a list of files.
+        
+        If any files are not found, return an empty list.
+        
+        IMPORTANT NOTE: For the purpose of getting the dependent assembly 
+        files of an executable, the publisher configuration (aka policy)
+        should be ignored (which is the default). Setting ignore_policies=False 
+        is only useful to find out which files are actually loaded at
+        runtime.
+        
+        """
+        # Shared Assemblies:
+        # http://msdn.microsoft.com/en-us/library/aa375996%28VS.85%29.aspx
+        #
+        # Private Assemblies:
+        # http://msdn.microsoft.com/en-us/library/aa375674%28VS.85%29.aspx
+        #
+        # Assembly Searching Sequence:
+        # http://msdn.microsoft.com/en-us/library/aa374224%28VS.85%29.aspx
+        #
+        # WinSxS assemblies override private assemblies by default:
+        # http://msdn.microsoft.com/en-us/library/aa375190%28VS.85%29.aspx
+        #
+        # NOTE:
+        # Multilanguage User Interface (MUI) support not yet implemented
+        version = self.version
+        if not self.getid(version):
+            if not silent:
+                print ("W: File search not possible")
+            return []
+        files = []
+        # Search winsxs
+        winsxs = os.path.join(os.getenv("SystemRoot"), "WinSxS")
+        if os.path.isdir(winsxs):
+            manifests = os.path.join(winsxs, "Manifests")
+            if not ignore_policies:
+                # Publisher Configuration (aka policy)
+                # A publisher configuration file globally redirects 
+                # applications and assemblies having a dependence on one 
+                # version of a side-by-side assembly to use another version of 
+                # the same assembly. This enables applications and assemblies 
+                # to use the updated assembly without having to rebuild all of 
+                # the affected applications.
+                # http://msdn.microsoft.com/en-us/library/aa375680%28VS.85%29.aspx
+                #
+                # Under Windows XP and 2003, policies are stored as 
+                # <version>.policy files inside 
+                # %SystemRoot%\WinSxS\Policies\<name>
+                # Under Vista and later, policies are stored as 
+                # <name>.manifest files inside %SystemRoot%\winsxs\Manifests
+                pcfiles = os.path.join(winsxs, "Policies")
+                if not os.path.isdir(pcfiles):
+                    # Vista or later
+                    pcfiles = manifests
+                if os.path.isdir(pcfiles):
+                    if not silent:
+                        print ("I: Searching for publisher configuration %s..." %
+                               self.getpolicyid())
+                    if pcfiles == manifests:
+                        policyext = ".manifest"
+                    else:
+                        policyext = ""
+                    redirected = False
+                    pat = re.compile("_(?:[a-z]+|[a-z]+-[a-z]+)_[0-9a-f]+$", 
+                                     re.I)
+                    for policypth in glob(os.path.join(pcfiles, 
+                                                       self.getpolicyid() + 
+                                                       "_*" + policyext)):
+                        if (pcfiles == manifests and 
+                            os.path.isfile(policypth)) or \
+                           (pcfiles != manifests and 
+                            os.path.isdir(policypth)):
+                            if pcfiles == manifests:
+                                matched = [policypth]
+                            else:
+                                if not re.search(pat, policypth, re.I):
+                                    continue
+                                matched = glob(os.path.join(policypth, 
+                                                            "*.policy"))
+                            for manifestpth in matched:
+                                if not silent:
+                                    print "I: Found", manifestpth
+                                try:
+                                    policy = ManifestFromXMLFile(manifestpth)
+                                except Exception, exc:
+                                    if not silent:
+                                        print "E: Could not parse file", \
+                                              manifestpth
+                                        print "E:", str(exc)
+                                    pass
+                                else:
+                                    if not silent:
+                                        print ("I: Checking publisher policy "
+                                               "for binding redirects")
+                                    for assembly in policy.dependentAssemblies:
+                                        if assembly.optional:
+                                            continue
+                                        for redirect in \
+                                            assembly.bindingRedirects:
+                                            if not silent:
+                                                old = "-".join([".".join([str(i) 
+                                                                          for i in 
+                                                                          part]) 
+                                                                for part in 
+                                                                redirect[0]])
+                                                new = ".".join([str(i) 
+                                                                for i in
+                                                                redirect[1]])
+                                                print "I: Found redirect " \
+                                                      "for version(s)", old, \
+                                                      "->", new
+                                            if version >= redirect[0][0] and \
+                                               version <= redirect[0][-1] and \
+                                               version != redirect[1]:
+                                                if not silent:
+                                                    print "I: Applying " \
+                                                          "redirect", \
+                                                          ".".join([str(i) 
+                                                                    for i in
+                                                                    version]), \
+                                                          "->", new
+                                                version = redirect[1]
+                                                redirected = True
+                    if not redirected and not silent:
+                        print "I: Publisher configuration not used"
+                elif not silent:
+                    print "W: No such dir", pcfiles
+            if os.path.isdir(manifests):
+                if not silent:
+                    print ("I: Searching for shared assembly %s..." % 
+                           self.getid(version))
+                for manifestpth in glob(os.path.join(manifests, 
+                                                     self.getid(version) + 
+                                                     "_*.manifest")):
+                    if not os.path.isfile(manifestpth):
+                        if not silent:
+                            print "W: Not a file", manifestpth
+                        continue
+                    assemblynm = os.path.basename(
+                        os.path.splitext(manifestpth)[0])
+                    if not silent:
+                        print "I: Found manifest", manifestpth
+                    assemblydir = os.path.join(winsxs, assemblynm)
+                    if not os.path.isdir(assemblydir):
+                        if not silent:
+                            print "W: No such dir", assemblydir
+                        continue
+                    try:
+                        manifest = ManifestFromXMLFile(manifestpth)
+                    except Exception, exc:
+                        if not silent:
+                            print "E: Could not parse manifest", manifestpth
+                            print "E:", exc
+                        pass
+                    else:
+                        rfiles = find_files(self.files or manifest.files, 
+                                            assemblydir)
+                        if rfiles:
+                            files.append(manifestpth)
+                            files.extend(rfiles)
+                            return files
+                        else:
+                            if not silent:
+                                print "W: Shared assembly incomplete"
+                            return []
+                    break
+            elif not silent:
+                print "W: No such dir", manifests
+        elif not silent:
+            print "W: No such dir", winsxs
+        if not silent:
+            print "I: No shared assembly found"
+        # Search for private assemblies
+        if not self.filename:
+            return []
+        dirnm = os.path.dirname(self.filename)
+        # If embedded in a dll the assembly may have the same name as the 
+        # dll, so we need to make sure we don't search for *.dll.dll
+        assemblynm, ext = os.path.splitext(self.name)
+        if ext.lower() == ".dll":
+            # Discard the extension
+            pass
+        else:
+            assemblynm = self.name
+        if not silent:
+            print ("I: Searching for private assembly %s..." % 
+                   self.name)
+        for path in [os.path.join(dirnm, self.language or "*"),
+                     os.path.join(dirnm, self.language or "*", assemblynm), 
+                     dirnm, 
+                     os.path.join(dirnm, assemblynm)]:
+            for ext in (".dll", ".manifest"):
+                # Private assemblies can have the manifest either as 
+                # separate file or embedded in a DLL
+                manifestpth = os.path.join(path, assemblynm + ext)
+                if not os.path.isfile(manifestpth):
+                    # if not silent:
+                        # print "W: No such file", manifestpth
+                    continue
+                if not silent:
+                    print "I: Found manifest", manifestpth
+                try:
+                    if ext == ".dll":
+                        manifest = ManifestFromResFile(manifestpth, [1])
+                    else:
+                        manifest = ManifestFromXMLFile(manifestpth)
+                except Exception, exc:
+                    if not silent:
+                        print "E: Could not parse manifest", manifestpth
+                        print "E:", exc
+                    pass
+                else:
+                    rfiles = find_files(self.files or manifest.files, path)
+                    if not rfiles:
+                        if not silent:
+                            print "W: Private assembly incomplete"
+                        return []
+                    files.append(manifestpth)
+                    files.extend(rfiles)
+                break
+            if not os.path.isfile(manifestpth):
+                for file_ in self.files:
+                    fn = os.path.join(path, file_.name)
+                    if os.path.exists(fn):
+                        # If any of our files does exist without the manifest 
+                        # in the same dir, the assembly is incomplete
+                        if not silent:
+                            print "W: Private assembly incomplete"
+                        return []
+        if not files and not silent:
+            print "W: No private assembly found"
+        return files
+
+    def getid(self, version=None):
+        """
+        Return an identification string which uniquely names a manifest.
+
+        This string is a combination of the manifest's 
+        processorArchitecture, name, publicKeyToken and version.
+        
+        """        
+        if filter(lambda x: not x, [self.processorArchitecture, self.name, 
+                                    self.publicKeyToken, self.version]):
+            if not silent:
+                print "W: Assembly metadata incomplete"
+            return ""
+        return "%s_%s_%s_%s" % (self.processorArchitecture, 
+                                self.name, 
+                                self.publicKeyToken, 
+                                ".".join([str(i) 
+                                          for i in 
+                                          version or self.version]))
+    
+    def getpolicyid(self):
+        """
+        Return an identification string which uniquely names a policy.
+
+        This string is a combination of the manifest's 
+        processorArchitecture, name, publicKeyToken and version.
+        
+        """        
+        if filter(lambda x: not x, [self.processorArchitecture, self.name, 
+                                    self.publicKeyToken, self.version]):
+            if not silent:
+                print "W: Assembly metadata incomplete"
+            return ""
+        return "%s_policy.%s.%s.%s_%s" %  (self.processorArchitecture, 
+                                           self.version[0], 
+                                           self.version[1], 
+                                           self.name, 
+                                           self.publicKeyToken)
+
+    def load_dom(self, domtree, initialize=True):
+        """
+        Load manifest from DOM tree.
+        
+        If initialize is True (default), reset existing attributes first.
+        
+        """
+        if domtree.nodeType == Node.DOCUMENT_NODE:
+            rootElement = domtree.documentElement
+        elif domtree.nodeType == Node.ELEMENT_NODE:
+            rootElement = domtree
+        else:
+            raise InvalidManifestError("Invalid root element node type " + 
+                                       str(rootElement.nodeType) + 
+                                       " - has to be one of (DOCUMENT_NODE, "
+                                       "ELEMENT_NODE)")
+        allowed_names = ("assembly", "assemblyBinding", "configuration", 
+                         "dependentAssembly")
+        if rootElement.tagName not in allowed_names:
+            raise InvalidManifestError("Invalid root element <" + 
+                                       rootElement.tagName + 
+                                       "> - has to be one of " + 
+                                       repr(allowed_names))
+        # print "I: loading manifest metadata from element <%s>" % \
+              # rootElement.tagName
+        if rootElement.tagName == "configuration":
+            for windows in rootElement.getCEByTN("windows"):
+                for assemblyBinding in windows.getCEByTN("assemblyBinding"):
+                    self.load_dom(assemblyBinding)
+        else:
+            if initialize:
+                self.__init__()
+            self.manifestType = rootElement.tagName
+            self.manifestVersion = [int(i) 
+                                    for i in 
+                                    (rootElement.getA("manifestVersion") or 
+                                     "1.0").split(".")]
+            self.noInheritable = bool(rootElement.getFCEByTN("noInheritable"))
+            self.noInherit = bool(rootElement.getFCEByTN("noInherit"))
+            for assemblyIdentity in rootElement.getCEByTN("assemblyIdentity"):
+                self.type = assemblyIdentity.getA("type")
+                self.name = assemblyIdentity.getA("name")
+                self.language = assemblyIdentity.getA("language")
+                self.processorArchitecture = assemblyIdentity.getA(
+                    "processorArchitecture")
+                self.version = [int(i) 
+                                for i in 
+                                (assemblyIdentity.getA("version") or 
+                                 "0.0.0.0").split(".")]
+                self.publicKeyToken = assemblyIdentity.getA("publicKeyToken")
+            for publisherPolicy in rootElement.getCEByTN("publisherPolicy"):
+                self.applyPublisherPolicy = (publisherPolicy.getA("apply") or 
+                                             "").lower() == "yes"
+            for description in rootElement.getCEByTN("description"):
+                if description.firstChild:
+                    self.description = description.firstChild.wholeText
+            for trustInfo in rootElement.getCEByTN("trustInfo"):
+                for security in trustInfo.getCEByTN("security"):
+                    for requestedPrivileges in \
+                        security.getCEByTN("requestedPrivileges"):
+                        for requestedExecutionLevel in \
+                            requestedPrivileges.getCEByTN(
+                                "requestedExecutionLevel"):
+                            self.requestedExecutionLevel = \
+                                requestedExecutionLevel.getA("level")
+                            self.uiAccess = (
+                                requestedExecutionLevel.getA("uiAccess") or 
+                                "").lower() == "true"
+            if rootElement.tagName == "assemblyBinding":
+                dependencies = [rootElement]
+            else:
+                dependencies = rootElement.getCEByTN("dependency")
+            for dependency in dependencies:
+                for dependentAssembly in dependency.getCEByTN(
+                    "dependentAssembly"):
+                    manifest = ManifestFromDOM(dependentAssembly)
+                    manifest.optional = (dependency.getA("optional") or 
+                                         "").lower() == "yes"
+                    self.dependentAssemblies.append(manifest)
+                    if self.filename:
+                        # Enable search for private assembly by assigning bogus 
+                        # filename (only the directory has to be correct)
+                        self.dependentAssemblies[-1].filename = ":".join(
+                            (self.filename, manifest.name))
+            for bindingRedirect in rootElement.getCEByTN("bindingRedirect"):
+                oldVersion = [[int(i) for i in part.split(".")] 
+                              for part in 
+                              bindingRedirect.getA("oldVersion").split("-")]
+                newVersion = [int(i) 
+                              for i in 
+                              bindingRedirect.getA("newVersion").split(".")]
+                self.bindingRedirects.append((oldVersion, newVersion))
+            for file_ in rootElement.getCEByTN("file"):
+                self.add_file(name=file_.getA("name"),
+                              hashalg=file_.getA("hashalg"),
+                              hash=file_.getA("hash"))
+    
+    def parse(self, filename_or_file):
+        """ Load manifest from file or file object """
+        self.__init__()
+        if isinstance(filename_or_file, (str, unicode)):
+            self.filename = filename_or_file
+        else:
+            self.filename = filename_or_file.filename
+        self.load_dom(minidom.parse(filename_or_file), False)
+    
+    def parse_string(self, xmlstr, initialize=True):
+        """ Load manifest from XML string """
+        self.load_dom(minidom.parseString(xmlstr), initialize)
+    
+    def todom(self):
+        """ Return the manifest as DOM tree """
+        doc = Document()
+        docE = doc.cE(self.manifestType)
+        if self.manifestType == "assemblyBinding":
+            cfg = doc.cE("configuration")
+            win = doc.cE("windows")
+            win.aChild(docE)
+            cfg.aChild(win)
+            doc.aChild(cfg)
+        else:
+            doc.aChild(docE)
+        if self.manifestType != "dependentAssembly":
+            docE.setA("xmlns", "urn:schemas-microsoft-com:asm.v1")
+            if self.manifestType != "assemblyBinding":
+                docE.setA("manifestVersion", 
+                          ".".join([str(i) for i in self.manifestVersion]))
+        if self.noInheritable:
+            docE.aChild(doc.cE("noInheritable"))
+        if self.noInherit:
+            docE.aChild(doc.cE("noInherit"))
+        aId = doc.cE("assemblyIdentity")
+        if self.type:
+            aId.setAttribute("type", self.type)
+        if self.name:
+            aId.setAttribute("name", self.name)
+        if self.language:
+            aId.setAttribute("language", self.language)
+        if self.processorArchitecture:
+            aId.setAttribute("processorArchitecture", 
+                             self.processorArchitecture)
+        if self.version:
+            aId.setAttribute("version", 
+                             ".".join([str(i) for i in self.version]))
+        if self.publicKeyToken:
+            aId.setAttribute("publicKeyToken", self.publicKeyToken)
+        if aId.hasAttributes():
+            docE.aChild(aId)
+        else:
+            aId.unlink()
+        if self.applyPublisherPolicy != None:
+            ppE = doc.cE("publisherPolicy")
+            if self.applyPublisherPolicy:
+                ppE.setA("apply", "yes")
+            else:
+                ppE.setA("apply", "no")
+            docE.aChild(ppE)
+        if self.description:
+            descE = doc.cE("description")
+            descE.aChild(doc.cT(self.description))
+            docE.aChild(descE)
+        if self.requestedExecutionLevel in ("asInvoker", "highestAvailable", 
+                                            "requireAdministrator"):
+            tE = doc.cE("trustInfo")
+            tE.setA("xmlns", "urn:schemas-microsoft-com:asm.v3")
+            sE = doc.cE("security")
+            rpE = doc.cE("requestedPrivileges")
+            relE = doc.cE("requestedExecutionLevel")
+            relE.setA("level", self.requestedExecutionLevel)
+            if self.uiAccess:
+                relE.setA("uiAccess", "true")
+            else:
+                relE.setA("uiAccess", "false")
+            rpE.aChild(relE)
+            sE.aChild(rpE)
+            tE.aChild(sE)
+            docE.aChild(tE)
+        if self.dependentAssemblies:
+            for assembly in self.dependentAssemblies:
+                if self.manifestType != "assemblyBinding":
+                    dE = doc.cE("dependency")
+                    if assembly.optional:
+                        dE.setAttribute("optional", "yes")
+                daE = doc.cE("dependentAssembly")
+                adom = assembly.todom()
+                for child in adom.documentElement.childNodes:
+                    daE.aChild(child.cloneNode(False))
+                adom.unlink()
+                if self.manifestType != "assemblyBinding":
+                    dE.aChild(daE)
+                    docE.aChild(dE)
+                else:
+                    docE.aChild(daE)
+        if self.bindingRedirects:
+            for bindingRedirect in self.bindingRedirects:
+                brE = doc.cE("bindingRedirect")
+                brE.setAttribute("oldVersion", 
+                                 "-".join([".".join([str(i) 
+                                                     for i in 
+                                                     part]) 
+                                           for part in 
+                                           bindingRedirect[0]]))
+                brE.setAttribute("newVersion", 
+                                 ".".join([str(i) for i in bindingRedirect[1]]))
+                docE.aChild(brE)
+        if self.files:
+            for file_ in self.files:
+                fE = doc.cE("file")
+                for attr in ("name", "hashalg", "hash"):
+                    val = getattr(file_, attr)
+                    if val:
+                        fE.setA(attr, val)
+                docE.aChild(fE)
+        return doc
+    
+    def toprettyxml(self, indent="  ", newl=os.linesep, encoding="UTF-8"):
+        """ Return the manifest as pretty-printed XML """
+        domtree = self.todom()
+        # WARNING: The XML declaration has to follow the order 
+        # version-encoding-standalone (standalone being optional), otherwise 
+        # if it is embedded in an exe the exe will fail to launch! 
+        # ('application configuration incorrect')
+        xmlstr = domtree.toprettyxml(
+            indent, newl, encoding).strip(os.linesep).replace(
+                '<?xml version="1.0" encoding="UTF-8"?>', 
+                '<?xml version="1.0" encoding="%s" standalone="yes"?>' % 
+                encoding)
+        domtree.unlink()
+        return xmlstr
+    
+    def toxml(self, encoding="UTF-8"):
+        """ Return the manifest as XML """
+        domtree = self.todom()
+        # WARNING: The XML declaration has to follow the order 
+        # version-encoding-standalone (standalone being optional), otherwise 
+        # if it is embedded in an exe the exe will fail to launch! 
+        # ('application configuration incorrect')
+        xmlstr = domtree.toxml(encoding).replace(
+            '<?xml version="1.0" encoding="UTF-8"?>', 
+            '<?xml version="1.0" encoding="%s" standalone="yes"?>' % encoding)
+        domtree.unlink()
+        return xmlstr
+
+    def update_resources(self, dstpath, names=None, languages=None):
+        """ Update or add manifest resource in dll/exe file dstpath """
+        UpdateManifestResourcesFromXML(dstpath, self.toprettyxml(), names, 
+                                       languages)
+    
+    def writeprettyxml(self, filename_or_file, indent="  ", newl=os.linesep, 
+                       encoding="UTF-8"):
+        """ Write the manifest as XML to a file or file object """
+        if isinstance(filename_or_file, (str, unicode)):
+            filename_or_file = open(filename_or_file, "wb")
+        xmlstr = self.toprettyxml(indent, newl, encoding)
+        filename_or_file.write(xmlstr)
+        filename_or_file.close()
+    
+    def writexml(self, filename_or_file, indent="  ", newl=os.linesep, 
+                 encoding="UTF-8"):
+        """ Write the manifest as XML to a file or file object """
+        if isinstance(filename_or_file, (str, unicode)):
+            filename_or_file = open(filename_or_file, "wb")
+        xmlstr = self.toxml(indent, newl, encoding)
+        filename_or_file.write(xmlstr)
+        filename_or_file.close()
+
+
+def ManifestFromResFile(filename, names=None, languages=None):
+    """ Create and return manifest instance from resource in dll/exe file """
+    res = GetManifestResources(filename, names, languages)
+    pth = []
+    if res and res[RT_MANIFEST]:
+        while isinstance(res, dict) and res.keys():
+            key = res.keys()[0]
+            pth.append(str(key))
+            res = res[key]
+    if isinstance(res, dict):
+        raise InvalidManifestError("No manifest resource found in '%s'" % 
+                                   filename)
+    manifest = Manifest()
+    manifest.filename = ":".join([filename] + pth)
+    manifest.parse_string(res, False)
+    return manifest
+
+
+def ManifestFromDOM(domtree):
+    """ Create and return manifest instance from DOM tree """
+    manifest = Manifest()
+    manifest.load_dom(domtree)
+    return manifest
+
+
+def ManifestFromXML(xmlstr):
+    """ Create and return manifest instance from XML """
+    manifest = Manifest()
+    manifest.parse_string(xmlstr)
+    return manifest
+
+
+def ManifestFromXMLFile(filename_or_file):
+    """ Create and return manifest instance from file """
+    manifest = Manifest()
+    manifest.parse(filename_or_file)
+    return manifest
+
+
+def GetManifestResources(filename, names=None, languages=None):
+    """ Get manifest resources from file """
+    return resource.GetResources(filename, [RT_MANIFEST], names, languages)
+
+
+def UpdateManifestResourcesFromXML(dstpath, xmlstr, names=None, 
+                                   languages=None):
+    """ Update or add manifest XML as resource in dstpath """
+    if not silent:
+        print "I: Updating manifest in", dstpath
+    if dstpath.lower().endswith(".exe"):
+        name = 1 
+    else:
+        name = 2
+    resource.UpdateResources(dstpath, xmlstr, RT_MANIFEST, names or [name], 
+                             languages or [0, "*"])
+
+
+def UpdateManifestResourcesFromXMLFile(dstpath, srcpath, names=None, 
+                                       languages=None):
+    """ Update or add manifest XML from srcpath as resource in dstpath """
+    if not silent:
+        print "I: Updating manifest from", srcpath, "in", dstpath
+    if dstpath.lower().endswith(".exe"):
+        name = 1 
+    else:
+        name = 2
+    resource.UpdateResourcesFromDataFile(dstpath, srcpath, RT_MANIFEST, 
+                                         names or [name], 
+                                         languages or [0, "*"])
+
+
+if __name__ == "__main__":
+    import sys
+    
+    dstpath = sys.argv[1]
+    srcpath = sys.argv[2]
+    UpdateManifestResourcesFromXMLFile(dstpath, srcpath)
Index: resource.py
===================================================================
--- resource.py	(revision 0)
+++ resource.py	(revision 0)
@@ -0,0 +1,271 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2009, Florian Hoech
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301, USA
+
+"""
+resource.py
+
+Read and write resources from/to Win32 PE files.
+
+Commandline usage:
+resource.py <dstpath> <srcpath>
+Updates or adds resources from file <srcpath> in file <dstpath>.
+
+2009-03 Florian Hoech
+
+"""
+
+import os.path
+import pywintypes
+import win32api
+
+silent = False  # True suppresses all messages
+
+LOAD_LIBRARY_AS_DATAFILE = 2
+ERROR_BAD_EXE_FORMAT = 193
+ERROR_RESOURCE_DATA_NOT_FOUND = 1812
+ERROR_RESOURCE_TYPE_NOT_FOUND = 1813
+ERROR_RESOURCE_NAME_NOT_FOUND = 1814
+ERROR_RESOURCE_LANG_NOT_FOUND = 1815
+
+
+class File(object):
+
+    """ Win32 PE file class. """
+
+    def __init__(self, filename):
+        self.filename = filename
+    
+    def get_resources(self, types=None, names=None, languages=None):
+        """
+        Get resources.
+        
+        types = a list of resource types to search for (None = all)
+        names = a list of resource names to search for (None = all)
+        languages = a list of resource languages to search for (None = all)
+        Return a dict of the form {type_: {name: {language: data}}} which 
+        might also be empty if no matching resources were found.
+        
+        """
+        return GetResources(self.filename, types, names, languages)
+    
+    def update_resources(self, data, type_, names=None, languages=None):
+        """
+        Update or add resource data.
+        
+        type_ = resource type to update
+        names = a list of resource names to update (None = all)
+        languages = a list of resource languages to update (None = all)
+        
+        """
+        UpdateResources(self.filename, data, type_, names, languages)
+    
+    def update_resources_from_datafile(self, srcpath, type_, names=None, 
+                                       languages=None):
+        """
+        Update or add resource data from file srcpath.
+        
+        type_ = resource type to update
+        names = a list of resource names to update (None = all)
+        languages = a list of resource languages to update (None = all)
+        
+        """
+        UpdateResourcesFromDataFile(self.filename, srcpath, type_, names, 
+                                    languages)
+    
+    def update_resources_from_dict(self, res, types=None, names=None, 
+                                   languages=None):
+        """
+        Update or add resources from resource dict.
+        
+        types = a list of resource types to update (None = all)
+        names = a list of resource names to update (None = all)
+        languages = a list of resource languages to update (None = all)
+        
+        """
+        UpdateResourcesFromDict(self.filename, res, types, names, 
+                                languages)
+    
+    def update_resources_from_resfile(self, srcpath, types=None, names=None, 
+                                      languages=None):
+        """
+        Update or add resources from dll/exe file srcpath.
+        
+        types = a list of resource types to update (None = all)
+        names = a list of resource names to update (None = all)
+        languages = a list of resource languages to update (None = all)
+        
+        """
+        UpdateResourcesFromResFile(self.filename, srcpath, types, names, 
+                                   languages)
+
+
+def _GetResources(hsrc, types=None, names=None, languages=None):
+    """
+    Get resources from hsrc.
+    
+    types = a list of resource types to search for (None = all)
+    names = a list of resource names to search for (None = all)
+    languages = a list of resource languages to search for (None = all)
+    Return a dict of the form {type_: {name: {language: data}}} which 
+    might also be empty if no matching resources were found.
+    
+    """
+    res = {}
+    try:
+        # print "I: Enumerating resource types"
+        enum_types = win32api.EnumResourceTypes(hsrc)
+        if types and not "*" in types:
+            enum_types = filter(lambda type_: 
+                                type_ in types, 
+                                enum_types)
+        for type_ in enum_types:
+            # print "I: Enumerating resources of type", type_
+            enum_names = win32api.EnumResourceNames(hsrc, type_)
+            if names and not "*" in names:
+                enum_names = filter(lambda name: 
+                                    name in names, 
+                                    enum_names)
+            for name in enum_names:
+                # print "I: Enumerating resources of type", type_, "name", name
+                enum_languages = win32api.EnumResourceLanguages(hsrc, 
+                                                                type_, 
+                                                                name)
+                if languages and not "*" in languages:
+                    enum_languages = filter(lambda language: 
+                                            language in languages, 
+                                            enum_languages)
+                for language in enum_languages:
+                    data = win32api.LoadResource(hsrc, type_, name, language)
+                    if not type_ in res:
+                        res[type_] = {}
+                    if not name in res[type_]:
+                        res[type_][name] = {}
+                    res[type_][name][language] = data
+    except pywintypes.error, exception:
+        if exception.args[0] in (ERROR_RESOURCE_DATA_NOT_FOUND,
+                                 ERROR_RESOURCE_TYPE_NOT_FOUND,
+                                 ERROR_RESOURCE_NAME_NOT_FOUND,
+                                 ERROR_RESOURCE_LANG_NOT_FOUND):
+                                     # print "I:", exception.args[1] + ":", \
+                                         # exception.args[2]
+                                     pass
+        else:
+            raise exception
+    return res
+
+
+def GetResources(filename, types=None, names=None, languages=None):
+    """
+    Get resources from dll/exe file.
+    
+    types = a list of resource types to search for (None = all)
+    names = a list of resource names to search for (None = all)
+    languages = a list of resource languages to search for (None = all)
+    Return a dict of the form {type_: {name: {language: data}}} which 
+    might also be empty if no matching resources were found.
+    
+    """
+    hsrc = win32api.LoadLibraryEx(filename, 0, LOAD_LIBRARY_AS_DATAFILE)
+    res = _GetResources(hsrc, types, names, languages)
+    win32api.FreeLibrary(hsrc)
+    return res
+
+
+def UpdateResources(dstpath, data, type_, names=None, languages=None):
+    """
+    Update or add resource data in dll/exe file dstpath.
+    
+    type_ = resource type to update
+    names = a list of resource names to update (None = all)
+    languages = a list of resource languages to update (None = all)
+    
+    """
+    # look for existing resources
+    res = GetResources(dstpath, [type_], names, languages)
+    # add type_, names and languages not already present in existing resources
+    if not type_ in res and type_ != "*":
+        res[type_] = {}
+    if names:
+        for name in names:
+            if not name in res[type_] and name != "*":
+                res[type_][name] = []
+                if languages:
+                    for language in languages:
+                        if not language in res[type_][name] and language != "*":
+                            res[type_][name].append(language)
+    # add resource to destination, overwriting existing resources
+    hdst = win32api.BeginUpdateResource(dstpath, 0)
+    for type_ in res:
+        for name in res[type_]:
+            for language in res[type_][name]:
+                if not silent:
+                    print "I: Updating resource type", type_, "name", name, \
+                          "language", language
+                win32api.UpdateResource(hdst, type_, name, data, language)
+    win32api.EndUpdateResource(hdst, 0)
+
+
+def UpdateResourcesFromDataFile(dstpath, srcpath, type_, names=None, 
+                                languages=None):
+    """
+    Update or add resource data from file srcpath in dll/exe file dstpath.
+    
+    type_ = resource type to update
+    names = a list of resource names to update (None = all)
+    languages = a list of resource languages to update (None = all)
+    
+    """
+    src = open(srcpath, "rb")
+    data = src.read()
+    src.close()
+    UpdateResources(dstpath, data, type_, names, languages)
+
+
+def UpdateResourcesFromDict(dstpath, res, types=None, names=None, 
+                            languages=None):
+    """
+    Update or add resources from resource dict in dll/exe file dstpath.
+    
+    types = a list of resource types to update (None = all)
+    names = a list of resource names to update (None = all)
+    languages = a list of resource languages to update (None = all)
+    
+    """
+    for type_ in res:
+        if not types or type_ in types:
+            for name in res[type_]:
+                if not names or name in names:
+                    for language in res[type_][name]:
+                        if not languages or language in languages:
+                            UpdateResources(dstpath, 
+                                            res[type_][name][language], 
+                                            [type_], [name], [language])
+
+
+def UpdateResourcesFromResFile(dstpath, srcpath, types=None, names=None, 
+                               languages=None):
+    """
+    Update or add resources from dll/exe file srcpath in dll/exe file dstpath.
+    
+    types = a list of resource types to update (None = all)
+    names = a list of resource names to update (None = all)
+    languages = a list of resource languages to update (None = all)
+    
+    """
+    res = GetResources(srcpath, types, names, languages)
+    UpdateResourcesFromDict(dstpath, res)

