From 8e651c8f8e5ed1fa10ffbba2037cc77967a155a9 Mon Sep 17 00:00:00 2001 From: David Rose Date: Thu, 13 Aug 2009 16:57:35 +0000 Subject: [PATCH] add extension modules --- direct/src/showutil/FreezeTool.py | 44 +++++++--- direct/src/showutil/Packager.py | 141 +++++++++++++++++++++--------- 2 files changed, 133 insertions(+), 52 deletions(-) diff --git a/direct/src/showutil/FreezeTool.py b/direct/src/showutil/FreezeTool.py index c6c7c14e98..f00461a6fc 100644 --- a/direct/src/showutil/FreezeTool.py +++ b/direct/src/showutil/FreezeTool.py @@ -402,6 +402,13 @@ class Freezer: # or dll's; those are always stored with compiled code. self.storePythonSource = False + # This list will be filled in by generateCode() or + # addToMultifile(). It contains a list of all the extension + # modules that were discovered, which have not been added to + # the output. The list is a list of tuples of the form + # (moduleName, filename). + self.extras = [] + # End of public interface. These remaining members should not # be directly manipulated by callers. self.previousModules = {} @@ -837,6 +844,19 @@ class Freezer: if module: # Get the compiled code directly from the module object. code = getattr(module, "__code__", None) + if not code: + # This is a module with no associated Python + # code. It must be an extension module. Get the + # filename. + extensionFilename = getattr(module, '__file__', None) + if extensionFilename: + self.extras.append((moduleName, extensionFilename)) + else: + # It doesn't even have a filename; it must + # be a built-in module. No worries about + # this one, then. + pass + else: # Read the code from the source file and compile it on-the-fly. if sourceFilename and sourceFilename.exists(): @@ -849,7 +869,8 @@ class Freezer: def addToMultifile(self, multifile, compressionLevel = 0): """ After a call to done(), this stores all of the accumulated - python code into the indicated Multifile. """ + python code into the indicated Multifile. Additional + extension modules are listed in self.extras. """ moduleDirs = {} for moduleName, mdef in self.getModuleDefs(): @@ -860,7 +881,8 @@ class Freezer: def writeMultifile(self, mfname): """ After a call to done(), this stores all of the accumulated python code into a Multifile with the indicated filename, - including the extension. """ + including the extension. Additional extension modules are + listed in self.extras.""" self.__replacePaths() @@ -881,10 +903,9 @@ class Freezer: false). The basename is the name of the file to write, without the extension. - The return value is the tuple (filename, extras) where - filename is the newly-generated filename, including the - filename extension, and extras is a list of (moduleName, - filename), for extension modules. """ + The return value is the newly-generated filename, including + the filename extension. Additional extension modules are + listed in self.extras. """ if compileToExe: # We must have a __main__ module to make an exe file. @@ -897,7 +918,6 @@ class Freezer: # Now generate the actual export table. moduleDefs = [] moduleList = [] - extras = [] for moduleName, mdef in self.getModuleDefs(): token = mdef.token @@ -932,11 +952,11 @@ class Freezer: else: # This is a module with no associated Python - # code. It must be a compiled file. Get the + # code. It must be an extension module. Get the # filename. - filename = getattr(module, '__file__', None) - if filename: - extras.append((moduleName, filename)) + extensionFilename = getattr(module, '__file__', None) + if extensionFilename: + self.extras.append((moduleName, extensionFilename)) else: # It doesn't even have a filename; it must # be a built-in module. No worries about @@ -994,7 +1014,7 @@ class Freezer: if (os.path.exists(basename + self.objectExtension)): os.unlink(basename + self.objectExtension) - return (target, extras) + return target def compileExe(self, filename, basename): compile = self.compileObj % { diff --git a/direct/src/showutil/Packager.py b/direct/src/showutil/Packager.py index aed3209e4f..866b4301e5 100644 --- a/direct/src/showutil/Packager.py +++ b/direct/src/showutil/Packager.py @@ -83,6 +83,44 @@ class Packager: for moduleName in self.skipModules.keys(): self.freezer.excludeModule(moduleName) + # First, add the explicit py files. These get turned into + # Python modules. + for file in self.files: + if not file.newName: + file.newName = file.filename + if file.newName in self.skipFilenames: + # Skip this file. + continue + + ext = file.filename.getExtension() + if ext == 'py': + self.addPyFile(file) + + if not self.mainModule and self.p3dApplication: + message = 'No main_module specified for application %s' % (self.packageName) + raise PackagerError, message + if self.mainModule: + if self.mainModule not in self.freezer.modules: + self.freezer.addModule(self.mainModule) + + # Add known module names. + self.moduleNames = {} + for moduleName in self.freezer.getAllModuleNames(): + if moduleName == '__main__': + # Ignore this special case. + continue + + self.moduleNames[moduleName] = True + + xmodule = TiXmlElement('module') + xmodule.SetAttribute('name', moduleName) + self.components.append(xmodule) + + # Pick up any unfrozen Python files. + self.freezer.done() + self.freezer.addToMultifile(self.multifile, self.compressionLevel) + self.addExtensionModules() + # Build up a cross-reference of files we've already # discovered. self.sourceFilenames = {} @@ -104,10 +142,13 @@ class Packager: self.targetFilenames[file.newName] = file processFiles.append(file) + # Now add all the real, non-Python files. This will + # include the extension modules we just discovered above. for file in processFiles: ext = file.filename.getExtension() if ext == 'py': - self.addPyFile(file) + # Already handled, above. + pass elif not self.dryRun: if ext == 'pz': # Strip off an implicit .pz extension. @@ -131,36 +172,18 @@ class Packager: # Any other file. self.addComponent(file) - if not self.mainModule and self.p3dApplication: - message = 'No main_module specified for application %s' % (self.packageName) - raise PackagerError, message - if self.mainModule: - if self.mainModule not in self.freezer.modules: - self.freezer.addModule(self.mainModule) - - # Pick up any unfrozen Python files. - self.freezer.done() - - # Add known module names. - self.moduleNames = {} - for moduleName in self.freezer.getAllModuleNames(): - if moduleName == '__main__': - # Ignore this special case. - continue - - self.moduleNames[moduleName] = True - - xmodule = TiXmlElement('module') - xmodule.SetAttribute('name', moduleName) - self.components.append(xmodule) - # Now that we've processed all of the component files, # (and set our platform if necessary), we can generate the # output filename and write the output files. if not self.p3dApplication and not self.version: - # We must have a version string for packages. + # We must have a version string for packages. Use the + # first versioned string on our require list. self.version = '0.0' + for p2 in self.requires: + if p2.version: + self.version = p2.version + break self.packageBasename = self.packageName packageDir = self.packageName @@ -189,7 +212,6 @@ class Packager: self.packageFullpath.unlink() if not self.dryRun: - self.freezer.addToMultifile(self.multifile, self.compressionLevel) if self.p3dApplication: self.makeP3dInfo() self.multifile.repack() @@ -208,6 +230,27 @@ class Packager: if file.deleteTemp: file.filename.unlink() + def addExtensionModules(self): + """ Adds the extension modules detected by the freezer to + the current list of files. """ + + freezer = self.freezer + if freezer.extras: + if not self.platform: + self.platform = PandaSystem.getPlatform() + + for moduleName, filename in freezer.extras: + filename = Filename.fromOsSpecific(filename) + newName = filename.getBasename() + if '.' in moduleName: + newName = '/'.join(moduleName.split('.')[:-1]) + newName += '/' + filename.getBasename() + # Sometimes the PYTHONPATH has the wrong case in it. + filename.makeTrueCase() + self.files.append(Packager.PackFile(filename, newName = newName, extract = True)) + freezer.extras = [] + + def makeP3dInfo(self): """ Makes the p3d_info.xml file that defines the application startup parameters and such. """ @@ -278,7 +321,7 @@ class Packager: for package in self.requires: xrequires = TiXmlElement('requires') xrequires.SetAttribute('name', package.packageName) - if package.platform: + if self.platform and package.platform: xrequires.SetAttribute('platform', package.platform) if package.version: xrequires.SetAttribute('version', package.version) @@ -315,7 +358,7 @@ class Packager: for package in self.requires: xrequires = TiXmlElement('requires') xrequires.SetAttribute('name', package.packageName) - if package.platform: + if self.platform and package.platform: xrequires.SetAttribute('platform', package.platform) if package.version: xrequires.SetAttribute('version', package.version) @@ -635,10 +678,10 @@ class Packager: self.binaryExtensions = [ 'ttf', 'wav', 'mid' ] # Files that should be extracted to disk. - self.extractExtensions = [ 'dll', 'so', 'dylib', 'exe' ] + self.extractExtensions = [ 'dll', 'pyd', 'so', 'dylib', 'exe' ] # Files that indicate a platform dependency. - self.platformSpecificExtensions = [ 'dll', 'so', 'dylib', 'exe' ] + self.platformSpecificExtensions = [ 'dll', 'pyd', 'so', 'dylib', 'exe' ] # Binary files that are considered uncompressible, and are # copied without compression. @@ -978,6 +1021,24 @@ class Packager: self.module(moduleName, newName = newName) + def parse_exclude_module(self, words): + """ + exclude_module moduleName [forbid=1] + """ + newName = None + + args = self.__parseArgs(words, ['extract']) + + try: + command, moduleName = words + except ValueError: + raise ArgumentError + + forbid = args.get('forbid', None) + if forbid is not None: + forbid = int(forbid) + self.excludeModule(moduleName, forbid = forbid) + def parse_main_module(self, words): """ main_module moduleName @@ -1353,6 +1414,14 @@ class Packager: self.currentPackage.freezer.addModule(moduleName, newName = newName) + def excludeModule(self, moduleName, forbid = False): + """ Marks the indicated Python module as not to be included. """ + + if not self.currentPackage: + raise OutsideOfPackageError + + self.currentPackage.freezer.excludeModule(moduleName, forbid = forbid) + def mainModule(self, moduleName, newName = None): """ Names the indicated module as the "main" module of the application or exe. """ @@ -1400,17 +1469,10 @@ class Packager: dirname, basename = filename.rsplit('/', 1) dirname += '/' - basename, extras = freezer.generateCode(basename, compileToExe = compileToExe) + basename = freezer.generateCode(basename, compileToExe = compileToExe) package.files.append(self.PackFile(Filename(basename), newName = dirname + basename, deleteTemp = True, extract = True)) - for moduleName, filename in extras: - filename = Filename.fromOsSpecific(filename) - newName = filename.getBasename() - if '.' in moduleName: - newName = '/'.join(moduleName.split('.')[:-1]) - newName += '/' + filename.getBasename() - package.files.append(self.PackFile(filename, newName = newName, extract = True)) - + package.addExtensionModules() if not package.platform: package.platform = PandaSystem.getPlatform() @@ -1418,7 +1480,6 @@ class Packager: freezer.reset() package.mainModule = None - def file(self, filename, newNameOrDir = None, extract = None): """ Adds the indicated arbitrary file to the current package.