Attachments you submit will be routed for moderation. If you have an account, please log in first.

Ticket #39: pyinstaller-trunk-r690-py26-win-tweaks-20090728.patch

File pyinstaller-trunk-r690-py26-win-tweaks-20090728.patch, 60.5 KB (added by openticket, 14 months ago)

- chg: assemblies are now put into their own subfolder, also bindepend.py <filename> will show dependent assemblies

  • application.config

     
     1<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
     2<configuration> 
     3 <windows> 
     4  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> 
     5   <assemblyIdentity  processorArchitecture="X86" name="Microsoft.Windows.mysampleApp" type="win32" version="1.0.0.0"/> 
     6   <publisherPolicy apply="no"/>                      
     7   <dependentAssembly> 
     8    <assemblyIdentity type="win32" processorArchitecture="x86" name="Microsoft.Windows.SampleAssembly" publicKeyToken="0000000000000000"/> 
     9    <bindingRedirect oldVersion="2.0.0.0" newVersion="2.0.1.0"/> 
     10   </dependentAssembly> 
     11  </assemblyBinding> 
     12 </windows> 
     13</configuration> 
     14 No newline at end of file 
  • application.manifest

     
     1<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
     2<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 
     3  <assemblyIdentity type="win32"  
     4                    name="myOrganization.myDivision.mySampleApp"  
     5                    version="6.0.0.0"  
     6                    processorArchitecture="x86"  
     7                    publicKeyToken="0000000000000000" 
     8  /> 
     9  <dependency> 
     10    <dependentAssembly> 
     11      <assemblyIdentity type="win32"  
     12                        name="Proseware.Research.SampleAssembly"  
     13                        version="6.0.0.0"  
     14                        processorArchitecture="X86"  
     15                        publicKeyToken="0000000000000000"  
     16                        language="*" 
     17      /> 
     18    </dependentAssembly> 
     19  </dependency> 
     20</assembly> 
     21 No newline at end of file 
  • apptemplate.manifest

     
     1<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
     2<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 
     3  <assemblyIdentity 
     4    version="0.0.0.0" 
     5    processorArchitecture="x86" 
     6    name="myOrganization.myDivision.mySampleApp" 
     7    type="win32" 
     8  /> 
     9  <description>Description</description> 
     10  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> 
     11    <security> 
     12      <requestedPrivileges> 
     13        <requestedExecutionLevel level="asInvoker" uiAccess="false" /> 
     14      </requestedPrivileges> 
     15    </security> 
     16  </trustInfo> 
     17  <dependency> 
     18    <dependentAssembly> 
     19      <assemblyIdentity 
     20         type="win32" 
     21         name="Microsoft.Windows.Common-Controls" 
     22         version="6.0.0.0" 
     23         processorArchitecture="x86" 
     24         publicKeyToken="6595b64144ccf1df" 
     25         language="*" 
     26       /> 
     27    </dependentAssembly> 
     28  </dependency> 
     29  <dependency> 
     30    <dependentAssembly> 
     31      <assemblyIdentity 
     32         type="win32" 
     33         name="Microsoft.VC90.CRT" 
     34         version="9.0.21022.8" 
     35         processorArchitecture="x86" 
     36         publicKeyToken="1fc8b3b9a1e18e3b" 
     37       /> 
     38    </dependentAssembly> 
     39  </dependency> 
     40</assembly> 
     41 No newline at end of file 
  • assembly.manifest

     
     1<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
     2<assembly xmlns="urn:schemas-microsoft-com:asm.v1"  
     3manifestVersion="1.0"> 
     4    <assemblyIdentity type="win32" name="Microsoft.Tools.SampleAssembly" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="0000000000000000"/> 
     5    <file name="sampleu.dll" hash="3eab067f82504bf271ed38112a4ccdf46094eb5a" hashalg="SHA1"> 
     6        <comClass description="Font Property Page" clsid="{0BE35200-8F91-11CE-9DE3-00AA004BB851}"/> 
     7        <comClass description="Color Property Page" clsid="{0BE35201-8F91-11CE-9DE3-00AA004BB851}"/> 
     8        <comClass description="Picture Property Page" clsid="{0BE35202-8F91-11CE-9DE3-00AA004BB851}"/> 
     9    </file> 
     10    <file name="bar.dll" hash="ac72753e5bb20446d88a48c8f0aaae769a962338" hashalg="SHA1"/> 
     11    <file name="foo.dll" hash="a7312a1f6cfb46433001e0540458de60adcd5ec5" hashalg="SHA1"> 
     12        <comClass description="Registrar Class" clsid="{44EC053A-400F-11D0-9DCD-00A0C90391D3}" progid="ATL.Registrar"/> 
     13    <comInterfaceProxyStub iid="{B6EA2051-048A-11D1-82B9-00C04FB9942E}" name=" IAxWinAmbientDispatch " tlbid="{34EC053A-400F-11D0-9DCD-00A0C90391D3}"/> 
     14        <typelib tlbid="{44EC0535-400F-11D0-9DCD-00A0C90391D3}" version="1.0" helpdir=""/> 
     15    </file> 
     16    <file name="sampledll.dll" hash="ba62960ceb15073d2598379307aad84f3a73dfcb" hashalg="SHA1"/> 
     17<windowClass>ToolbarWindow32</windowClass> 
     18        <windowClass>ComboBoxEx32</windowClass> 
     19        <windowClass>sample_trackbar32</windowClass> 
     20        <windowClass>sample_updown32</windowClass> 
     21</assembly> 
     22 No newline at end of file 
  • bindepend.py

     
    4343iswin = sys.platform[:3] == 'win' 
    4444cygwin = sys.platform == 'cygwin' 
    4545 
     46if iswin: 
     47    import __builtin__ 
     48    __builtin__.__dict__["silent"] = False  # True suppresses all messages from the assembly dependency code 
     49    from manifest import RT_MANIFEST, GetManifestResources, ManifestFromXML 
     50    try: 
     51        import resource 
     52    except ImportError, detail: 
     53        resource = None 
     54 
    4655excludes = { 
    4756    'KERNEL32.DLL':1, 
    4857    'ADVAPI.DLL':1, 
     
    7887    'PSAPI.DLL':1, 
    7988    'MSVCP80.DLL':1, 
    8089    'MSVCR80.DLL':1, 
     90    'MSVCP90.DLL':1, 
     91    'MSVCR90.DLL':1, 
     92    'IERTUTIL.DLL':1, 
     93    'POWRPROF.DLL':1, 
     94    'SHLWAPI.DLL':1, 
     95    'URLMON.DLL':1, 
    8196    # regex excludes 
    8297    # don't include in the bundle the libc and the tls stuff 
    8398    r'^/usr/lib/tls':1, 
     
    87102    r'/usr/lib/libGL.*':1, 
    88103    # 
    89104    '^/System/Library/Frameworks':1, 
     105    # MS assembly excludes 
     106    'Microsoft.Windows.Common-Controls':1, 
    90107} 
    91108 
    92109excludesRe = re.compile('|'.join(excludes.keys()), re.I) 
     
    283300        for lib, npth in selectImports(pth, platform, xtrapath): 
    284301            if seen.get(string.upper(lib),0): 
    285302                continue 
     303            if iswin: 
     304                for ftocnm, fn in selectAssemblies(npth): 
     305                    lTOC.append((ftocnm, fn, 'BINARY')) 
    286306            lTOC.append((lib, npth, 'BINARY')) 
    287307 
    288308    return lTOC 
    289309 
     310def getAssemblies(pth): 
     311    """Return the dependent assemblies of a binary.""" 
     312    # check for manifest file 
     313    manifestnm = pth + ".manifest" 
     314    if os.path.isfile(manifestnm): 
     315        fd = open(manifestnm, "rb") 
     316        res = {RT_MANIFEST: {1: {0: fd.read()}}} 
     317        fd.close() 
     318    elif not resource: 
     319        # resource access unavailable (needs pywin32) 
     320        if not silent: 
     321            print "W:", pth 
     322            print "W: Cannot check for assembly dependencies - resource access" 
     323            print "W: unavailable. To enable resource access, please install" 
     324            print "W: http://sourceforge.net/projects/pywin32/" 
     325        return [] 
     326    else: 
     327        # check the binary for embedded manifest 
     328        res = GetManifestResources(pth) 
     329    rv = [] 
     330    if RT_MANIFEST in res and len(res[RT_MANIFEST]): 
     331        for name in res[RT_MANIFEST]: 
     332            for language in res[RT_MANIFEST][name]: 
     333                # check the manifest for dependent assemblies 
     334                try: 
     335                    manifest = ManifestFromXML(res[RT_MANIFEST][name][language]) 
     336                except Exception, exc: 
     337                    if not silent: 
     338                        print "E: Could not parse manifest resource of" 
     339                        print "E:", pth 
     340                        print "E:", exc 
     341                    pass 
     342                else: 
     343                    if manifest.dependentAssemblies and not silent: 
     344                        print "I: Dependent assemblies of %s:" % pth 
     345                        print "I:", ", ".join([assembly.getid()  
     346                                               for assembly in  
     347                                               manifest.dependentAssemblies]) 
     348                    rv.extend(manifest.dependentAssemblies) 
     349    return rv 
     350     
     351def selectAssemblies(pth): 
     352    """Return a binary's dependent assemblies files that should be included. 
     353 
     354    Return a list of pairs (name, fullpath) 
     355    """ 
     356    rv = [] 
     357    for assembly in getAssemblies(pth): 
     358        if assembly.optional or \ 
     359           excludesRe.search(assembly.name) or \ 
     360           seen.get(assembly.getid().upper(),0): 
     361            if assembly.optional and not ( 
     362               excludesRe.search(assembly.name) or  
     363               seen.get(assembly.getid().upper(),0)) and not silent: 
     364                print "I: Skipping search for optional assembly", assembly.name 
     365            continue 
     366        files = assembly.find_files() 
     367        if files: 
     368            seen[assembly.getid().upper()] = 1 
     369            for fn in files: 
     370                fname, fext = os.path.splitext(fn) 
     371                if fext.lower() == ".manifest": 
     372                    ftocnm = assembly.name + fext 
     373                else: 
     374                    ftocnm = os.path.basename(fn) 
     375                ftocnm, fn = [item.encode(sys.getfilesystemencoding())  
     376                              for item in  
     377                              (os.path.join(assembly.name, ftocnm), fn)] 
     378                if not seen.get(fn.upper(),0): 
     379                    if not silent: 
     380                        print "I: Adding", ftocnm 
     381                    seen[fn.upper()] = 1 
     382                    rv.append((ftocnm, fn)) 
     383                else: 
     384                    #print "I: skipping", ftocnm, "part of assembly", assembly.name, "dependency of", pth 
     385                    pass 
     386        elif not silent: 
     387            print "E: Assembly", assembly.getid(), "not found" 
     388    return rv 
     389 
    290390def selectImports(pth, platform=sys.platform, xtrapath=None): 
    291391    """Return the dependencies of a binary that should be included. 
    292392 
     
    302402    iswin = platform[:3] == 'win' 
    303403    for lib in dlls: 
    304404        if not iswin and not cygwin: 
    305                 # plain win case 
     405            # all other platforms 
    306406            npth = lib 
    307407            dir, lib = os.path.split(lib) 
    308408            if excludes.get(dir,0): 
    309409                continue 
    310410        else: 
    311             # all other platforms 
     411            # plain win case 
    312412            npth = getfullnameof(lib, xtrapath) 
    313413 
    314         # now npth is a candidate lib 
     414        # now npth is a candidate lib if found 
    315415        # check again for excludes but with regex FIXME: split the list 
    316         if excludesRe.search(npth): 
    317             if 'libpython' not in npth and 'Python.framework' not in npth: 
     416        candidatelib = npth if npth else lib 
     417        if excludesRe.search(candidatelib): 
     418            if 'libpython' not in candidatelib and 'Python.framework' not in candidatelib: 
    318419                # skip libs not containing (libpython or Python.framework) 
    319420                #print "I: skipping %20s <- %s" % (npth, pth) 
    320421                continue 
     
    324425 
    325426        if npth: 
    326427            rv.append((lib, npth)) 
    327         else: 
     428        elif not seen.get(lib.upper(),0): 
    328429            print "E: lib not found:", lib, "dependency of", pth 
    329430 
    330431    return rv 
     
    388489    """Forwards to the correct getImports implementation for the platform. 
    389490    """ 
    390491    if platform[:3] == 'win' or platform == 'cygwin': 
    391         return _getImports_pe(pth) 
     492        try: 
     493            return _getImports_pe(pth) 
     494        except Exception, exception: 
     495            # Assemblies can pull in files which aren't necessarily PE,  
     496            # but are still needed by the assembly. Any additional binary  
     497            # dependencies should already have been handled by  
     498            # selectAssemblies in that case, so just warn, return an empty  
     499            # list and continue. 
     500            if not silent: 
     501                print 'I: Cannot get binary dependencies for non-PE file:' 
     502                print 'I:', pth 
     503            return [] 
    392504    elif platform == 'darwin': 
    393505        return _getImports_otool(pth) 
    394506    else: 
     
    471583                      help='Target platform, required for cross-bundling (default: current platform)') 
    472584 
    473585    opts, args = parser.parse_args() 
     586    __builtin__.__dict__["silent"] = True  # Suppress all messages from the assembly dependency code 
    474587    import glob 
    475588    for a in args: 
    476589        for fn in glob.glob(a): 
    477             print fn, getImports(fn, opts.target_platform) 
     590            imports = getImports(fn, opts.target_platform) 
     591            if opts.target_platform == "win32": 
     592                imports.extend([a.getid() for a in getAssemblies(fn)]) 
     593            print fn, imports 
  • Build.py

     
    627627        self.name = kws.get('name',None) 
    628628        self.icon = kws.get('icon',None) 
    629629        self.versrsrc = kws.get('version',None) 
     630        self.manifest = kws.get('manifest',None) 
     631        self.resources = kws.get('resources',[]) 
    630632        self.strip = kws.get('strip',None) 
    631633        self.upx = kws.get('upx',None) 
    632634        self.crypt = kws.get('crypt', 0) 
     
    660662            ('debug',    _check_guts_eq), 
    661663            ('icon',     _check_guts_eq), 
    662664            ('versrsrc', _check_guts_eq), 
     665            ('manifest', _check_guts_eq), 
     666            ('resources', _check_guts_eq), 
    663667            ('strip',    _check_guts_eq), 
    664668            ('upx',      _check_guts_eq), 
    665669            ('crypt',    _check_guts_eq), 
     
    679683        if not data: 
    680684            return True 
    681685 
    682         icon, versrsrc = data[3:5] 
    683         if (icon or versrsrc) and not config['hasRsrcUpdate']: 
     686        icon, versrsrc, manifest, resources = data[3:7] 
     687        if (icon or versrsrc or manifest or resources) and not config['hasRsrcUpdate']: 
    684688            # todo: really ignore :-) 
    685             print "ignoring icon and version resources = platform not capable" 
     689            print "ignoring icon, version, manifest and resources = platform not capable" 
    686690 
    687691        mtm = data[-1] 
    688692        crypt = data[-2] 
     
    722726        exe = os.path.join(HOMEPATH, exe) 
    723727        if target_iswin or cygwin: 
    724728            exe = exe + '.exe' 
    725         if config['hasRsrcUpdate']: 
     729        if config['hasRsrcUpdate'] and (self.icon or self.versrsrc or  
     730                                        self.manifest or self.resources): 
     731            tmpnm = tempfile.mktemp() 
     732            shutil.copy2(exe, tmpnm) 
     733            os.chmod(tmpnm, 0755) 
    726734            if self.icon: 
    727                 tmpnm = tempfile.mktemp() 
    728                 shutil.copy2(exe, tmpnm) 
    729                 os.chmod(tmpnm, 0755) 
    730735                icon.CopyIcons(tmpnm, self.icon) 
    731                 trash.append(tmpnm) 
    732                 exe = tmpnm 
    733736            if self.versrsrc: 
    734                 tmpnm = tempfile.mktemp() 
    735                 shutil.copy2(exe, tmpnm) 
    736                 os.chmod(tmpnm, 0755) 
    737737                versionInfo.SetVersion(tmpnm, self.versrsrc) 
    738                 trash.append(tmpnm) 
    739                 exe = tmpnm 
     738            if self.manifest: 
     739                manifest.UpdateManifestResourcesFromXMLFile(tmpnm, self.manifest, [1]) 
     740            for res in self.resources: 
     741                res = res.split(",") 
     742                for i in range(len(res[1:])): 
     743                    try: 
     744                        res[i + 1] = int(res[i + 1]) 
     745                    except ValueError: 
     746                        pass 
     747                resfile = res[0] 
     748                restype = res[1] if len(res) > 1 else None 
     749                resname = res[2] if len(res) > 2 else None 
     750                reslang = res[3] if len(res) > 3 else None 
     751                try: 
     752                    resource.UpdateResourcesFromResFile(tmpnm, resfile,  
     753                                                        [restype or "*"],  
     754                                                        [resname or "*"],  
     755                                                        [reslang or "*"]) 
     756                except resource.pywintypes.error, exc: 
     757                    if exc.args[0] != resource.ERROR_BAD_EXE_FORMAT: 
     758                        print "E:", str(exc) 
     759                        continue 
     760                    if not restype or not resname: 
     761                        print "E: resource type and/or name not specified" 
     762                        continue 
     763                    if "*" in (restype, resname): 
     764                        print ("E: no wildcards allowed for resource type " 
     765                               "and name when source file does not contain " 
     766                               "resources") 
     767                        continue 
     768                    try: 
     769                        resource.UpdateResourcesFromDataFile(tmpnm,  
     770                                                             resfile,  
     771                                                             restype,  
     772                                                             [resname],  
     773                                                             [reslang or 0]) 
     774                    except resource.pywintypes.error, exc: 
     775                        print "E:", str(exc) 
     776            trash.append(tmpnm) 
     777            exe = tmpnm 
    740778        exe = checkCache(exe, self.strip, self.upx and config['hasUPX']) 
    741779        self.copy(exe, outf) 
    742780        if self.append_pkg: 
     
    749787        os.chmod(self.name, 0755) 
    750788        _save_data(self.out, 
    751789                   (self.name, self.console, self.debug, self.icon, 
    752                     self.versrsrc, self.strip, self.upx, self.crypt, mtime(self.name))) 
     790                    self.versrsrc, self.manifest, self.resources, self.strip, self.upx, self.crypt, mtime(self.name))) 
    753791        for item in trash: 
    754792            os.remove(item) 
    755793        return 1 
     
    773811        os.chmod(self.name, 0755) 
    774812        _save_data(self.out, 
    775813                   (self.name, self.console, self.debug, self.icon, 
    776                     self.versrsrc, self.strip, self.upx, mtime(self.name))) 
     814                    self.versrsrc, self.manifest, self.resources, self.strip, self.upx, mtime(self.name))) 
    777815        return 1 
    778816 
    779817 
     
    11061144 
    11071145def main(specfile, configfilename): 
    11081146    global target_platform, target_iswin, config 
    1109     global icon, versionInfo 
     1147    global icon, versionInfo, resource, manifest 
    11101148 
    11111149    try: 
    11121150        config = _load_data(configfilename) 
     
    11301168        sys.exit(1) 
    11311169 
    11321170    if config['hasRsrcUpdate']: 
    1133         import icon, versionInfo 
     1171        import icon, versionInfo, resource, manifest 
    11341172 
    11351173    if config['hasUPX']: 
    11361174        setupUPXFlags() 
     
    11461184    parser.add_option('-C', '--configfile', 
    11471185                      default=os.path.join(HOMEPATH, 'config.dat'), 
    11481186                      help='Name of generated configfile (default: %default)') 
     1187    parser.add_option('-o', '--buildpath', 
     1188                      default=None, 
     1189                      help='Buildpath') 
    11491190    opts, args = parser.parse_args() 
    11501191    if len(args) != 1: 
    11511192        parser.error('Requires exactly one .spec-file') 
  • Makespec.py

     
    135135 
    136136def main(scripts, configfile=None, name=None, tk=0, freeze=0, console=1, debug=0, 
    137137         strip=0, upx=0, comserver=0, ascii=0, workdir=None, 
    138          pathex=[], version_file=None, icon_file=None, crypt=None): 
     138         pathex=[], version_file=None, icon_file=None, manifest_file=None, resources=[], crypt=None): 
    139139 
    140140    try: 
    141141        config = eval(open(configfile, 'r').read()) 
     
    168168        exe_options = "%s, version='%s'" % (exe_options, quote_win_filepath(version_file)) 
    169169    if icon_file: 
    170170        exe_options = "%s, icon='%s'" % (exe_options, quote_win_filepath(icon_file)) 
     171    if manifest_file: 
     172        exe_options = "%s, manifest='%s'" % (exe_options, quote_win_filepath(manifest_file)) 
     173    if resources: 
     174        for i in range(len(resources)): 
     175            resources[i] = quote_win_filepath(resources[i]) 
     176        exe_options = "%s, resources=%s" % (exe_options, repr(resources)) 
    171177    if not ascii and config['hasUnicode']: 
    172178        scripts.insert(0, os.path.join(HOME, 'support', 'useUnicode.py')) 
    173179    for i in range(len(scripts)): 
     
    278284                 dest="version_file", metavar="FILE", 
    279285                 help="add a version resource from FILE to the exe " 
    280286                      "(Windows only)") 
    281     g.add_option("--icon", type="string", dest="icon_file", 
     287    g.add_option("-i", "--icon", type="string", dest="icon_file", 
    282288                 metavar="FILE.ICO or FILE.EXE,ID", 
    283289                 help="If FILE is an .ico file, add the icon to the final " 
    284290                      "executable. Otherwise, the syntax 'file.exe,id' to " 
    285291                      "extract the icon with the specified id " 
    286292                      "from file.exe and add it to the final executable") 
     293    g.add_option("-m", "--manifest", type="string", 
     294                 dest="manifest_file", metavar="FILE", 
     295                 help="add manifest FILE to the exe " 
     296                      "(Windows only)") 
     297    g.add_option("-r", "--resource", type="string", default=[], dest="resources", 
     298                 metavar="FILE[,TYPE[,NAME[,LANGUAGE]]]", action="append", 
     299                 help="add/update resource of the given type, name and language " 
     300                      "from FILE to the final executable. FILE can be a " 
     301                      "data file or an exe/dll. For data files, atleast " 
     302                      "TYPE and NAME need to be specified, LANGUAGE defaults " 
     303                      "to 0 or may be specified as wildcard * to update all " 
     304                      "resources of the given TYPE and NAME. For exe/dll " 
     305                      "files, all resources from FILE will be added/updated " 
     306                      "to the final executable if TYPE, NAME and LANGUAGE " 
     307                      "are omitted or specified as wildcard *." 
     308                      "Multiple resources are allowed, using this option " 
     309                      "multiple times.") 
    287310 
    288311    opts,args = p.parse_args() 
    289312 
  • manifest.py

     
     1#!/usr/bin/env python 
     2 
     3""" 
     4manifest.py 2009-03 Florian Hoech 
     5 
     6Provides the following functionality: 
     7* Create, parse and write MS Windows Manifest files 
     8* Find files which are part of an assembly, by looking for shared and  
     9  private assemblies 
     10* Update manifest resources in dll/exe files 
     11 
     12Implemented: 
     13* Shared and private assemblies support 
     14* Publisher configuration support (.policy files) 
     15* application configuration support (.config files) 
     16 
     17Not implemented: 
     18* Manifest validation (only very basic sanity checks are currently in place) 
     19* comClass, typelib, comInterfaceProxyStub, windowClass subelements of the  
     20  file element 
     21* comInterfaceExternalProxyStub, windowClass subelements of the assembly  
     22  element 
     23 
     24Reference: 
     25About Isolated Applications and Side-by-side Assemblies 
     26http://msdn.microsoft.com/en-us/library/aa374029(VS.85).aspx 
     27""" 
     28import __builtin__ 
     29if not "silent" in __builtin__.__dict__: 
     30    __builtin__.__dict__["silent"] = False  # True suppresses all messages from the assembly dependency code 
     31try: 
     32    import hashlib 
     33except ImportError, detail: 
     34    hashlib = None 
     35    if not silent: 
     36        print 'I: ... file hash calculation unavailable -', detail 
     37import os.path 
     38from glob import glob 
     39from xml.dom import Node, minidom 
     40from xml.dom.minidom import Document, Element 
     41 
     42Document.aChild = Document.appendChild 
     43Document.cE = Document.createElement 
     44Document.cT = Document.createTextNode 
     45Document.getEById = Document.getElementById 
     46Document.getEByTN = Document.getElementsByTagName 
     47Element.aChild = Element.appendChild 
     48Element.getA = Element.getAttribute 
     49Element.getEByTN = Element.getElementsByTagName 
     50Element.remA = Element.removeAttribute 
     51Element.setA = Element.setAttribute 
     52 
     53try: 
     54    import resource 
     55except ImportError, detail: 
     56    resource = None 
     57    if not silent: 
     58        print "W:", detail 
     59        print "W: Cannot check for assembly dependencies - resource access " 
     60        print "W: unavailable. To enable resource access, please install " 
     61        print "W: http://sourceforge.net/projects/pywin32/" 
     62 
     63RT_MANIFEST = 24 
     64 
     65def getChildElementsByTagName(self, tagName): 
     66    """ Return child elements of type tagName if found, else [] """ 
     67    result = [] 
     68    for child in self.childNodes: 
     69        if isinstance(child, Element): 
     70            if child.tagName == tagName: 
     71                result.append(child) 
     72    return result 
     73 
     74def getFirstChildElementByTagName(self, tagName): 
     75    """ Return the first element of type tagName if found, else None """ 
     76    for child in self.childNodes: 
     77        if isinstance(child, Element): 
     78            if child.tagName == tagName: 
     79                return child 
     80    return None 
     81 
     82Document.getCEByTN = getChildElementsByTagName 
     83Document.getFCEByTN = getFirstChildElementByTagName 
     84Element.getCEByTN = getChildElementsByTagName 
     85Element.getFCEByTN = getFirstChildElementByTagName 
     86 
     87def find_files(files, searchpath): 
     88    rfiles = [] 
     89    for file_ in files: 
     90        fn = file_.find(searchpath) 
     91        if fn: 
     92            rfiles.append(fn) 
     93        else: 
     94            # if any of our files does not exist, 
     95            # the assembly is incomplete 
     96            return [] 
     97    return rfiles 
     98 
     99 
     100class _Dummy: 
     101    pass 
     102 
     103     
     104class File(resource.File if resource else _Dummy): 
     105 
     106    """ A file referenced by an assembly inside a manifest. """ 
     107     
     108    def __init__(self, filename="", hashalg=None, hash=None, comClasses=None,  
     109                 typelibs=None, comInterfaceProxyStubs=None,  
     110                 windowClasses=None): 
     111        if resource: 
     112            resource.File.__init__(self, filename) 
     113        else: 
     114            self.filename = filename 
     115        self.name = os.path.basename(filename) 
     116        self.hashalg = hashalg.upper() if hashalg else None 
     117        if os.path.isfile(filename) and hashalg and hashlib: 
     118            self.calc_hash() 
     119        else: 
     120            self.hash = hash 
     121        self.comClasses = comClasses or [] # TO-DO: implement 
     122        self.typelibs = typelibs or [] # TO-DO: implement 
     123        self.comInterfaceProxyStubs = comInterfaceProxyStubs or [] # TO-DO: implement 
     124        self.windowClasses = windowClasses or [] # TO-DO: implement 
     125 
     126    def calc_hash(self, hashalg=None): 
     127        """ Calculate the hash of the file. Will be called automatically from  
     128        the constructor if the file exists and hashalg is given, but may  
     129        also be called manually e.g. to update the hash if the file has  
     130        changed. """ 
     131        fd = open(self.filename, "rb") 
     132        buf = fd.read() 
     133        fd.close() 
     134        if hashalg: 
     135            self.hashalg = hashalg.upper() 
     136        self.hash = getattr(hashlib, self.hashalg.lower())(buf).hexdigest() 
     137 
     138    def find(self, searchpath): 
     139        if not silent: 
     140            print "I: Searching for file", self.name 
     141        fn = os.path.join(searchpath, self.name) 
     142        if os.path.isfile(fn): 
     143            if not silent: 
     144                print "I: Found file", fn 
     145            return fn 
     146        else: 
     147            if not silent: 
     148                print "W: No such file", fn 
     149            return None 
     150         
     151class InvalidManifestError(Exception): 
     152    pass 
     153 
     154     
     155class Manifest(): 
     156 
     157    def __init__(self, manifestVersion=None, noInheritable=False,  
     158                 noInherit=False, type_=None, name=None, language=None,  
     159                 processorArchitecture=None, version=None,  
     160                 publicKeyToken=None, description=None,  
     161                 requestedExecutionLevel=None, uiAccess=None,  
     162                 dependentAssemblies=None, files=None,  
     163                 comInterfaceExternalProxyStubs=None): 
     164        """ Manifest constructor. 
     165         
     166        To build a basic manifest for your application: 
     167          mf = Manifest(type='win32', name='YourAppName', language='*',  
     168                        processorArchitecture='x86', version=[1, 0, 0, 0]) 
     169         
     170        To write the XML to a manifest file: 
     171          mf.writexml("YourAppName.exe.manifest") 
     172        or 
     173          mf.writeprettyxml("YourAppName.exe.manifest") 
     174        """ 
     175        self.filename = None 
     176        self.optional = None 
     177        self.manifestType = "assembly" 
     178        self.manifestVersion = manifestVersion or [1, 0] 
     179        self.noInheritable = noInheritable 
     180        self.noInherit = noInherit 
     181        self.type = type_ 
     182        self.name = name 
     183        self.language = language 
     184        self.processorArchitecture = processorArchitecture 
     185        self.version = version 
     186        self.publicKeyToken = publicKeyToken 
     187        # public key token 
     188        # "A 16-character hexadecimal string that represents the last 8 bytes  
     189        # of the SHA-1 hash of the public key under which the assembly is  
     190        # signed. The public key used to sign the catalog must be 2048 bits  
     191        # or greater. Required for all shared side-by-side assemblies." 
     192        # http://msdn.microsoft.com/en-us/library/aa375692(VS.85).aspx 
     193        self.applyPublisherPolicy = None 
     194        self.description = None 
     195        self.requestedExecutionLevel = requestedExecutionLevel 
     196        self.uiAccess = uiAccess 
     197        self.dependentAssemblies = dependentAssemblies or [] 
     198        self.bindingRedirects = [] 
     199        self.files = files or [] 
     200        self.comInterfaceExternalProxyStubs = comInterfaceExternalProxyStubs or [] # TO-DO: implement 
     201     
     202    def add_dependent_assembly(self, manifestVersion=None, noInheritable=False,  
     203                 noInherit=False, type_=None, name=None, language=None,  
     204                 processorArchitecture=None, version=None,  
     205                 publicKeyToken=None, description=None,  
     206                 requestedExecutionLevel=None, uiAccess=None,  
     207                 dependentAssemblies=None, files=None,  
     208                 comInterfaceExternalProxyStubs=None): 
     209        """ Shortcut for: 
     210        manifest.dependentAssemblies.append(Manifest(*args, *kwargs)) """ 
     211        self.dependentAssemblies.append(Manifest(manifestVersion,  
     212                                        noInheritable, noInherit, type_, name,  
     213                                        language, processorArchitecture,  
     214                                        version, publicKeyToken, description,  
     215                                        requestedExecutionLevel, uiAccess,  
     216                                        dependentAssemblies, files,  
     217                                        comInterfaceExternalProxyStubs)) 
     218     
     219    def add_file(self, name="", hashalg="", hash="", comClasses=None,  
     220                 typelibs=None, comInterfaceProxyStubs=None,  
     221                 windowClasses=None): 
     222        """ Shortcut for: 
     223        manifest.files.append(File(*args, *kwargs)) """ 
     224        self.files.append(File(name, hashalg, hash, comClasses,  
     225                          typelibs, comInterfaceProxyStubs, windowClasses)) 
     226     
     227    def find_files(self): 
     228        """ Search shared and private assemblies and return a list of all  
     229        related files if found. If any files are not found, return an empty  
     230        list.""" 
     231        if None in (self.processorArchitecture, self.name, self.publicKeyToken,  
     232                    self.version): 
     233                        if not silent: 
     234                            print "W: Assembly metadata incomplete - file search not possible" 
     235                        return [] 
     236        if not silent: 
     237            print "I: Searching for files of assembly %s..." % self.getid() 
     238        files = [] 
     239        # search winsxs 
     240        winsxs = os.path.join(os.getenv('SystemRoot'), 'WinSxS') 
     241        if os.path.isdir(winsxs): 
     242            manifests = os.path.join(winsxs, "Manifests") 
     243            if os.path.isdir(manifests): 
     244                for manifestpth in glob(os.path.join(manifests,  
     245                                                     self.getid() + "_*.manifest")): 
     246                    assemblynm = os.path.basename(os.path.splitext(manifestpth)[0]) 
     247                    if not os.path.isfile(manifestpth): 
     248                        if not silent: 
     249                            print "W: No such file", manifestpth 
     250                        continue 
     251                    if not silent: 
     252                        print "I: Found manifest", manifestpth 
     253                    assemblydir = os.path.join(winsxs, assemblynm) 
     254                    if not os.path.isdir(assemblydir): 
     255                        if not silent: 
     256                            print "W: No such dir", assemblydir 
     257                        continue 
     258                    try: 
     259                        manifest = ManifestFromXMLFile(manifestpth) 
     260                    except Exception, exc: 
     261                        if not silent: 
     262                            print "E: Could not parse manifest", manifestpth 
     263                            print "E:", exc 
     264                        pass 
     265                    else: 
     266                        rfiles = find_files(self.files or manifest.files, assemblydir) 
     267                        if rfiles: 
     268                            files.append(manifestpth) 
     269                            files.extend(rfiles) 
     270                            return files 
     271                    break 
     272            elif not silent: 
     273                print "W: No such dir", manifests 
     274        elif not silent: 
     275            print "W: No such dir", winsxs 
     276        # search for private assemblies 
     277        if not self.filename: 
     278            return [] 
     279        dirnm = os.path.dirname(self.filename) 
     280        # if embedded in a dll the assembly may have the same name as the  
     281        # dll, so we need to make sure we don't search for *.dll.dll 
     282        assemblynm, ext = os.path.splitext(self.name) 
     283        if ext.lower() == ".dll": 
     284            # discard the extension 
     285            pass 
     286        else: 
     287            assemblynm = self.name 
     288        for path in [os.path.join(dirnm, self.language or "*"), 
     289                     os.path.join(dirnm, self.language or "*", assemblynm),  
     290                     dirnm,  
     291                     os.path.join(dirnm, assemblynm)]: 
     292            for ext in (".dll", ".manifest"): 
     293                # private assemblies can have the manifest either as  
     294                # separate file or embedded in a DLL 
     295                manifestpth = os.path.join(path, assemblynm + ext) 
     296                if not os.path.isfile(manifestpth): 
     297                    if not silent: 
     298                        print "W: No such file", manifestpth 
     299                    continue 
     300                if not silent: 
     301                    print "I: Found manifest", manifestpth 
     302                try: 
     303                    if ext == ".dll": 
     304                        manifest = ManifestFromResFile(manifestpth, [1]) 
     305                    else: 
     306                        manifest = ManifestFromXMLFile(manifestpth) 
     307                except Exception, exc: 
     308                    if not silent: 
     309                        print "E: Could not parse manifest", manifestpth 
     310                        print "E:", exc 
     311                    pass 
     312                else: 
     313                    rfiles = find_files(self.files or manifest.files, path) 
     314                    if not rfiles: 
     315                        return [] 
     316                    files.append(manifestpth) 
     317                    files.extend(rfiles) 
     318                break 
     319            if not os.path.isfile(manifestpth): 
     320                for file_ in self.files: 
     321                    fn = os.path.join(path, file_.name) 
     322                    if os.path.exists(fn): 
     323                        # if any of our files does exist without the manifest  
     324                        # in the same dir, the assembly is incomplete 
     325                        return [] 
     326        return files 
     327 
     328    def getid(self): 
     329        return '%s_%s_%s_%s' % (self.processorArchitecture,  
     330                                self.name,  
     331                                self.publicKeyToken,  
     332                                ".".join([str(i)  
     333                                          for i in  
     334                                          self.version])) 
     335 
     336    def load_dom(self, domtree, initialize=True): 
     337        """ Load manifest from DOM tree. 
     338        If initialize is True (default), reset existing attributes first.""" 
     339        if domtree.nodeType == Node.DOCUMENT_NODE: 
     340            rootElement = domtree.documentElement 
     341        elif domtree.nodeType == Node.ELEMENT_NODE: 
     342            rootElement = domtree 
     343        else: 
     344            raise InvalidManifestError("Invalid root element node type " +  
     345                                       str(rootElement.nodeType) +  
     346                                       " - has to be one of (DOCUMENT_NODE, ELEMENT_NODE)") 
     347        allowed_names = ("assembly", "assemblyBinding", "configuration",  
     348                         "dependentAssembly") 
     349        if rootElement.tagName not in allowed_names: 
     350            raise InvalidManifestError("Invalid root element <" +  
     351                                       rootElement.tagName +  
     352                                       "> - has to be one of " +  
     353                                       repr(allowed_names)) 
     354        # print "I: loading manifest metadata from element <%s>" % \ 
     355              # rootElement.tagName 
     356        if rootElement.tagName == "configuration": 
     357            for windows in rootElement.getCEByTN("windows"): 
     358                for assemblyBinding in windows.getCEByTN("assemblyBinding"): 
     359                    self.load_dom(assemblyBinding) 
     360        else: 
     361            if initialize: 
     362                self.__init__() 
     363            self.manifestType = rootElement.tagName 
     364            self.manifestVersion = [int(i) for i in (rootElement.getA("manifestVersion") or "1.0").split(".")] 
     365            self.noInheritable = bool(rootElement.getFCEByTN("noInheritable")) 
     366            self.noInherit = bool(rootElement.getFCEByTN("noInherit")) 
     367            for assemblyIdentity in rootElement.getCEByTN("assemblyIdentity"): 
     368                self.type = assemblyIdentity.getA("type") 
     369                self.name = assemblyIdentity.getA("name") 
     370                self.language = assemblyIdentity.getA("language") 
     371                self.processorArchitecture = assemblyIdentity.getA("processorArchitecture") 
     372                self.version = [int(i) for i in (assemblyIdentity.getA("version") or "0.0.0.0").split(".")] 
     373                self.publicKeyToken = assemblyIdentity.getA("publicKeyToken") 
     374            for publisherPolicy in rootElement.getCEByTN("publisherPolicy"): 
     375                self.applyPublisherPolicy = (publisherPolicy.getA("apply") or "").lower() == "yes" 
     376            for description in rootElement.getCEByTN("description"): 
     377                if description.firstChild: 
     378                    self.description = description.firstChild.wholeText 
     379            for trustInfo in rootElement.getCEByTN("trustInfo"): 
     380                for security in trustInfo.getCEByTN("security"): 
     381                    for requestedPrivileges in security.getCEByTN("requestedPrivileges"): 
     382                        for requestedExecutionLevel in requestedPrivileges.getCEByTN("requestedExecutionLevel"): 
     383                            self.requestedExecutionLevel = requestedExecutionLevel.getA("level") 
     384                            self.uiAccess = (requestedExecutionLevel.getA("uiAccess") or "").lower() == "true" 
     385            for dependency in (rootElement.getCEByTN("dependency") if  
     386                               rootElement.tagName != "assemblyBinding" else  
     387                               [rootElement]): 
     388                for dependentAssembly in dependency.getCEByTN("dependentAssembly"): 
     389                    manifest = ManifestFromDOM(dependentAssembly) 
     390                    manifest.optional = (dependency.getA("optional") or "").lower() == "yes" 
     391                    self.dependentAssemblies.append(manifest) 
     392            for bindingRedirect in rootElement.getCEByTN("bindingRedirect"): 
     393                oldVersion = [[int(i) for i in part.split(".")] for part in bindingRedirect.getA("oldVersion").split("-")] 
     394                newVersion = [int(i) for i in bindingRedirect.getA("newVersion").split(".")] 
     395                self.bindingRedirects.append((oldVersion, newVersion)) 
     396            for file_ in rootElement.getCEByTN("file"): 
     397                self.add_file(name=file_.getA("name"), 
     398                              hashalg=file_.getA("hashalg"), 
     399                              hash=file_.getA("hash")) 
     400     
     401    def parse(self, filename_or_file): 
     402        """ Load manifest from file or file object """ 
     403        self.load_dom(minidom.parse(filename_or_file)) 
     404        if isinstance(filename_or_file, (str, unicode)): 
     405            self.filename = filename_or_file 
     406        else: 
     407            self.filename = filename_or_file.filename 
     408     
     409    def parse_string(self, xmlstr): 
     410        """ Load manifest from XML string """ 
     411        self.load_dom(minidom.parseString(xmlstr)) 
     412     
     413    def todom(self): 
     414        """ Return the manifest as DOM tree """ 
     415        doc = Document() 
     416        docE = doc.cE(self.manifestType) 
     417        if self.manifestType == "assemblyBinding": 
     418            cfg = doc.cE("configuration") 
     419            win = doc.cE("windows") 
     420            win.aChild(docE) 
     421            cfg.aChild(win) 
     422            doc.aChild(cfg) 
     423        else: 
     424            doc.aChild(docE) 
     425        if self.manifestType != "dependentAssembly": 
     426            docE.setA("xmlns", "urn:schemas-microsoft-com:asm.v1") 
     427            if self.manifestType != "assemblyBinding": 
     428                docE.setA("manifestVersion",  
     429                          ".".join([str(i) for i in self.manifestVersion])) 
     430        if self.noInheritable: 
     431            docE.aChild(doc.cE("noInheritable")) 
     432        if self.noInherit: 
     433            docE.aChild(doc.cE("noInherit")) 
     434        aId = doc.cE("assemblyIdentity") 
     435        if self.type: 
     436            aId.setAttribute("type", self.type) 
     437        if self.name: 
     438            aId.setAttribute("name", self.name) 
     439        if self.language: 
     440            aId.setAttribute("language", self.language) 
     441        if self.processorArchitecture: 
     442            aId.setAttribute("processorArchitecture",  
     443                             self.processorArchitecture) 
     444        if self.version: 
     445            aId.setAttribute("version",  
     446                             ".".join([str(i) for i in self.version])) 
     447        if self.publicKeyToken: 
     448            aId.setAttribute("publicKeyToken", self.publicKeyToken) 
     449        if aId.hasAttributes(): 
     450            docE.aChild(aId) 
     451        else: 
     452            aId.unlink() 
     453        if self.applyPublisherPolicy != None: 
     454            ppE = doc.cE("publisherPolicy") 
     455            ppE.setA("apply", "yes" if self.applyPublisherPolicy else "no") 
     456            docE.aChild(ppE) 
     457        if self.description: 
     458            descE = doc.cE("description") 
     459            descE.aChild(doc.cT(self.description)) 
     460            docE.aChild(descE) 
     461        if self.requestedExecutionLevel in ("asInvoker", "highestAvailable",  
     462                                            "requireAdministrator"): 
     463            tE = doc.cE("trustInfo") 
     464            tE.setA("xmlns", "urn:schemas-microsoft-com:asm.v3") 
     465            sE = doc.cE("security") 
     466            rpE = doc.cE("requestedPrivileges") 
     467            relE = doc.cE("requestedExecutionLevel") 
     468            relE.setA("level", self.requestedExecutionLevel) 
     469            relE.setA("uiAccess", "true" if self.uiAccess else "false") 
     470            rpE.aChild(relE) 
     471            sE.aChild(rpE) 
     472            tE.aChild(sE) 
     473            docE.aChild(tE) 
     474        if self.dependentAssemblies: 
     475            for assembly in self.dependentAssemblies: 
     476                if self.manifestType != "assemblyBinding": 
     477                    dE = doc.cE("dependency") 
     478                    if assembly.optional: 
     479                        dE.setAttribute("optional", "yes") 
     480                daE = doc.cE("dependentAssembly") 
     481                adom = assembly.todom() 
     482                for child in adom.documentElement.childNodes: 
     483                    daE.aChild(child.cloneNode(False)) 
     484                adom.unlink() 
     485                if self.manifestType != "assemblyBinding": 
     486                    dE.aChild(daE) 
     487                    docE.aChild(dE) 
     488                else: 
     489                    docE.aChild(daE) 
     490        if self.bindingRedirects: 
     491            for bindingRedirect in self.bindingRedirects: 
     492                brE = doc.cE("bindingRedirect") 
     493                brE.setAttribute("oldVersion",  
     494                                 "-".join(".".join(str(i) for i in part) for part in bindingRedirect[0])) 
     495                brE.setAttribute("newVersion",  
     496                                 ".".join(str(i) for i in bindingRedirect[1])) 
     497                docE.aChild(brE) 
     498        if self.files: 
     499            for file_ in self.files: 
     500                fE = doc.cE("file") 
     501                for attr in ("name", "hashalg", "hash"): 
     502                    val = getattr(file_, attr) 
     503                    if val: 
     504                        fE.setA(attr, val) 
     505                docE.aChild(fE) 
     506        return doc 
     507     
     508    def toprettyxml(self, indent="  ", newl=os.linesep, encoding="UTF-8"): 
     509        """ Return the manifest as pretty-printed XML """ 
     510        domtree = self.todom() 
     511        # WARNING: The XML declaration has to follow the order version-encoding-standalone (standalone being optional), 
     512        # otherwise if it is embedded in an exe the exe will fail to launch! ('application configuration incorrect') 
     513        xmlstr = domtree.toprettyxml(indent, newl, encoding).strip(os.linesep).replace('<?xml version="1.0" encoding="UTF-8"?>',  
     514            '<?xml version="1.0" encoding="%s" standalone="yes"?>' % encoding) 
     515        domtree.unlink() 
     516        return xmlstr 
     517     
     518    def toxml(self, encoding="UTF-8"): 
     519        """ Return the manifest as XML """ 
     520        domtree = self.todom() 
     521        # WARNING: The XML declaration has to follow the order version-encoding-standalone (standalone being optional), 
     522        # otherwise if it is embedded in an exe the exe will fail to launch! ('application configuration incorrect') 
     523        xmlstr = domtree.toxml(encoding).replace('<?xml version="1.0" encoding="UTF-8"?>',  
     524            '<?xml version="1.0" encoding="%s" standalone="yes"?>' % encoding) 
     525        domtree.unlink() 
     526        return xmlstr 
     527 
     528    def update_resources(self, dstpath, names=None, languages=None): 
     529        """ Update or add manifest to dll/exe file dstpath, as manifest  
     530        resource """ 
     531        UpdateManifestResourcesFromXML(dstpath, self.toprettyxml(), names,  
     532                                       languages) 
     533     
     534    def writeprettyxml(self, filename_or_file, indent="  ", newl=os.linesep, encoding="UTF-8"): 
     535        """ Write the manifest as XML to a file or file object """ 
     536        if isinstance(filename_or_file, (str, unicode)): 
     537            filename_or_file = open(filename_or_file, "wb") 
     538        xmlstr = self.toprettyxml(indent, newl, encoding) 
     539        filename_or_file.write(xmlstr) 
     540        filename_or_file.close() 
     541     
     542    def writexml(self, filename_or_file, indent="  ", newl=os.linesep, encoding="UTF-8"): 
     543        """ Write the manifest as XML to a file or file object """ 
     544        if isinstance(filename_or_file, (str, unicode)): 
     545            filename_or_file = open(filename_or_file, "wb") 
     546        xmlstr = self.toxml(indent, newl, encoding) 
     547        filename_or_file.write(xmlstr) 
     548        filename_or_file.close() 
     549         
     550def ManifestFromResFile(filename, names=None, languages=None): 
     551    """ Create and return manifest instance from manifest resource in  
     552    dll/exe file """ 
     553    res = GetManifestResources(filename, names, languages) 
     554    if res and res[RT_MANIFEST]: 
     555        while isinstance(res, dict) and res.keys(): 
     556            res = res[res.keys()[0]] 
     557    if isinstance(res, dict): 
     558        raise InvalidManifestError("No manifest resource found in '%s'" %  
     559                                   filename) 
     560    manifest = ManifestFromXML(res) 
     561    manifest.filename = filename 
     562    return manifest 
     563         
     564def ManifestFromDOM(domtree): 
     565    """ Create and return manifest instance from DOM tree """ 
     566    manifest = Manifest() 
     567    manifest.load_dom(domtree) 
     568    return manifest 
     569         
     570def ManifestFromXML(xmlstr): 
     571    """ Create and return manifest instance from XML """ 
     572    manifest = Manifest() 
     573    manifest.parse_string(xmlstr) 
     574    return manifest 
     575         
     576def ManifestFromXMLFile(filename_or_file): 
     577    """ Create and return manifest instance from manifest file """ 
     578    manifest = Manifest() 
     579    manifest.parse(filename_or_file) 
     580    return manifest 
     581 
     582def GetManifestResources(filename, names=None, languages=None): 
     583    """ Get manifest resources from dll/exe file """ 
     584    return resource.GetResources(filename, [RT_MANIFEST], names, languages) 
     585 
     586def UpdateManifestResourcesFromXML(dstpath, xmlstr, names=None,  
     587                                   languages=None): 
     588    """ Update or add manifest XML to dll/exe file dstpath, as manifest  
     589    resource """ 
     590    if not silent: 
     591        print "I: Updating manifest in", dstpath 
     592    name = 1 if dstpath.lower().endswith(".exe") else 2 
     593    resource.UpdateResources(dstpath, xmlstr, RT_MANIFEST, names or [name],  
     594                             languages or [0, "*"]) 
     595 
     596def UpdateManifestResourcesFromXMLFile(dstpath, srcpath, names=None,  
     597                                       languages=None): 
     598    """ Update or add manifest XML from file srcpath to dll/exe file  
     599    dstpath, as manifest resource """ 
     600    if not silent: 
     601        print "I: Updating manifest from", srcpath, "to", dstpath 
     602    name = 1 if dstpath.lower().endswith(".exe") else 2 
     603    resource.UpdateResourcesFromDataFile(dstpath, srcpath, RT_MANIFEST,  
     604                                         names or [name],  
     605                                         languages or [0, "*"]) 
     606 
     607if __name__ == "__main__": 
     608    import sys 
     609     
     610    dstpath = sys.argv[1] 
     611    srcpath = sys.argv[2] 
     612    UpdateManifestResourcesFromXMLFile(dstpath, srcpath) 
     613 No newline at end of file 
  • publisher.policy

     
     1<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
     2<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 
     3   <assemblyIdentity type="win32-policy" publicKeyToken="0000000000000000" name="policy.2.0.Microsoft.Windows.SampleAssembly" version="1.1.0.0" processorArchitecture="x86"/> 
     4   <dependency> 
     5      <dependentAssembly> 
     6         <assemblyIdentity type="win32" name="Microsoft.Windows.SampleAssembly"  processorArchitecture="x86" publicKeyToken="75e377300ab7b886"/> 
     7         <bindingRedirect oldVersion="2.0.0.0" newVersion="2.0.1.0"/> 
     8      </dependentAssembly> 
     9   </dependency> 
     10</assembly> 
     11 No newline at end of file 
  • resource.py

     
     1#!/usr/bin/env python 
     2 
     3import os.path 
     4import pywintypes 
     5import win32api 
     6 
     7LOAD_LIBRARY_AS_DATAFILE = 2 
     8ERROR_BAD_EXE_FORMAT = 193 
     9ERROR_RESOURCE_DATA_NOT_FOUND = 1812 
     10ERROR_RESOURCE_TYPE_NOT_FOUND = 1813 
     11ERROR_RESOURCE_NAME_NOT_FOUND = 1814 
     12ERROR_RESOURCE_LANG_NOT_FOUND = 1815 
     13 
     14class File(): 
     15    def __init__(self, filename): 
     16        self.filename = filename 
     17     
     18    def get_resources(self, types=None, names=None, languages=None): 
     19        """ Get resources. 
     20        types = a list of resource types to search for (None = all) 
     21        names = a list of resource names to search for (None = all) 
     22        languages = a list of resource languages to search for (None = all) 
     23        Return a dict of the form {type_: {name: {language: data}}} which  
     24        might also be empty if no matching resources were found.""" 
     25        return GetResources(self.filename, types, names, languages) 
     26     
     27    def update_resources(self, data, type_, names=None, languages=None): 
     28        """ Update or add resource data. 
     29        type_ = resource type to update 
     30        names = a list of resource names to update (None = all) 
     31        languages = a list of resource languages to update (None = all)""" 
     32        UpdateResources(self.filename, data, type_, names, languages) 
     33     
     34    def update_resources_from_datafile(self, srcpath, type_, names=None,  
     35                                       languages=None): 
     36        """ Update or add resource data from file srcpath. 
     37        type_ = resource type to update 
     38        names = a list of resource names to update (None = all) 
     39        languages = a list of resource languages to update (None = all)""" 
     40        UpdateResourcesFromDataFile(self.filename, srcpath, type_, names,  
     41                                    languages) 
     42     
     43    def update_resources_from_dict(self, res, types=None, names=None,  
     44                                   languages=None): 
     45        """ Update or add resources from resource dict. 
     46        types = a list of resource types to update (None = all) 
     47        names = a list of resource names to update (None = all) 
     48        languages = a list of resource languages to update (None = all)""" 
     49        UpdateResourcesFromDict(self.filename, res, types, names,  
     50                                languages) 
     51     
     52    def update_resources_from_resfile(self, srcpath, types=None, names=None,  
     53                                      languages=None): 
     54        """ Update or add resources from dll/exe file srcpath. 
     55        types = a list of resource types to update (None = all) 
     56        names = a list of resource names to update (None = all) 
     57        languages = a list of resource languages to update (None = all)""" 
     58        UpdateResourcesFromResFile(self.filename, srcpath, types, names,  
     59                                   languages) 
     60 
     61def _GetResources(hsrc, types=None, names=None, languages=None): 
     62    """ Get resources from hsrc. 
     63    types = a list of resource types to search for (None = all) 
     64    names = a list of resource names to search for (None = all) 
     65    languages = a list of resource languages to search for (None = all) 
     66    Return a dict of the form {type_: {name: {language: data}}} which  
     67    might also be empty if no matching resources were found.""" 
     68    res = {} 
     69    try: 
     70        # print "I: Enumerating resource types" 
     71        enum_types = win32api.EnumResourceTypes(hsrc) 
     72        if types and not "*" in types: 
     73            enum_types = filter(lambda type_:  
     74                                type_ in types,  
     75                                enum_types) 
     76        for type_ in enum_types: 
     77            # print "I: Enumerating resources of type", type_ 
     78            enum_names = win32api.EnumResourceNames(hsrc, type_) 
     79            if names and not "*" in names: 
     80                enum_names = filter(lambda name:  
     81                                    name in names,  
     82                                    enum_names) 
     83            for name in enum_names: 
     84                # print "I: Enumerating resources of type", type_, "name", name 
     85                enum_languages = win32api.EnumResourceLanguages(hsrc,  
     86                                                                type_,  
     87                                                                name) 
     88                if languages and not "*" in languages: 
     89                    enum_languages = filter(lambda language:  
     90                                            language in languages,  
     91                                            enum_languages) 
     92                for language in enum_languages: 
     93                    data = win32api.LoadResource(hsrc, type_, name, language) 
     94                    if not type_ in res: 
     95                        res[type_] = {} 
     96                    if not name in res[type_]: 
     97                        res[type_][name] = {} 
     98                    res[type_][name][language] = data 
     99    except pywintypes.error, exception: 
     100        if exception.args[0] in (ERROR_RESOURCE_DATA_NOT_FOUND, 
     101                                 ERROR_RESOURCE_TYPE_NOT_FOUND, 
     102                                 ERROR_RESOURCE_NAME_NOT_FOUND, 
     103                                 ERROR_RESOURCE_LANG_NOT_FOUND): 
     104                                     # print "I:", exception.args[1] + ":", \ 
     105                                         # exception.args[2] 
     106                                     pass 
     107        else: 
     108            raise exception 
     109    return res 
     110 
     111def GetResources(filename, types=None, names=None, languages=None): 
     112    """ Get resources from dll/exe file. 
     113    types = a list of resource types to search for (None = all) 
     114    names = a list of resource names to search for (None = all) 
     115    languages = a list of resource languages to search for (None = all) 
     116    Return a dict of the form {type_: {name: {language: data}}} which  
     117    might also be empty if no matching resources were found.""" 
     118    hsrc = win32api.LoadLibraryEx(filename, 0, LOAD_LIBRARY_AS_DATAFILE) 
     119    res = _GetResources(hsrc, types, names, languages) 
     120    win32api.FreeLibrary(hsrc) 
     121    return res 
     122 
     123def UpdateResources(dstpath, data, type_, names=None, languages=None): 
     124    """ Update or add resource data to dll/exe file dstpath. 
     125    type_ = resource type to update 
     126    names = a list of resource names to update (None = all) 
     127    languages = a list of resource languages to update (None = all)""" 
     128    # look for existing resources 
     129    res = GetResources(dstpath, [type_], names, languages) 
     130    # add type_, names and languages not already present in existing resources 
     131    if not type_ in res and type_ != "*": 
     132        res[type_] = {} 
     133    if names: 
     134        for name in names: 
     135            if not name in res[type_] and name != "*": 
     136                res[type_][name] = [] 
     137                if languages: 
     138                    for language in languages: 
     139                        if not language in res[type_][name] and language != "*": 
     140                            res[type_][name].append(language) 
     141    # add resource to destination, overwriting existing resources 
     142    hdst = win32api.BeginUpdateResource(dstpath, 0) 
     143    for type_ in res: 
     144        for name in res[type_]: 
     145            for language in res[type_][name]: 
     146                print "I: Updating resource type", type_, "name", name, "language", language 
     147                win32api.UpdateResource(hdst, type_, name, data, language) 
     148    win32api.EndUpdateResource(hdst, 0) 
     149 
     150def UpdateResourcesFromDataFile(dstpath, srcpath, type_, names=None,  
     151                                languages=None): 
     152    """ Update or add resource data from file srcpath to dll/exe file  
     153    dstpath. 
     154    type_ = resource type to update 
     155    names = a list of resource names to update (None = all) 
     156    languages = a list of resource languages to update (None = all)""" 
     157    src = open(srcpath, "rb") 
     158    data = src.read() 
     159    src.close() 
     160    UpdateResources(dstpath, data, type_, names, languages) 
     161 
     162def UpdateResourcesFromDict(dstpath, res, types=None, names=None,  
     163                            languages=None): 
     164    """ Update or add resources from resource dict to dll/exe file  
     165    dstpath. 
     166    types = a list of resource types to update (None = all) 
     167    names = a list of resource names to update (None = all) 
     168    languages = a list of resource languages to update (None = all)""" 
     169    for type_ in res: 
     170        if not types or type_ in types: 
     171            for name in res[type_]: 
     172                if not names or name in names: 
     173                    for language in res[type_][name]: 
     174                        if not languages or language in languages: 
     175                            UpdateResources(dstpath,  
     176                                            res[type_][name][language],  
     177                                            [type_], [name], [language]) 
     178 
     179def UpdateResourcesFromResFile(dstpath, srcpath, types=None, names=None,  
     180                               languages=None): 
     181    """ Update or add resources from dll/exe file srcpath to dll/exe file  
     182    dstpath. 
     183    types = a list of resource types to update (None = all) 
     184    names = a list of resource names to update (None = all) 
     185    languages = a list of resource languages to update (None = all)""" 
     186    res = GetResources(srcpath, types, names, languages) 
     187    UpdateResourcesFromDict(dstpath, res) 
     188 No newline at end of file