Ver código fonte

more package nonsense

David Rose 16 anos atrás
pai
commit
d3d91a3de2

+ 19 - 3
direct/src/showutil/FreezeTool.py

@@ -881,8 +881,10 @@ class Freezer:
         false).  The basename is the name of the file to write,
         false).  The basename is the name of the file to write,
         without the extension.
         without the extension.
 
 
-        The return value is the newly-generated filename, including
-        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. """
         
         
         if compileToExe:
         if compileToExe:
             # We must have a __main__ module to make an exe file.
             # We must have a __main__ module to make an exe file.
@@ -895,6 +897,7 @@ class Freezer:
         # Now generate the actual export table.
         # Now generate the actual export table.
         moduleDefs = []
         moduleDefs = []
         moduleList = []
         moduleList = []
+        extras = []
         
         
         for moduleName, mdef in self.getModuleDefs():
         for moduleName, mdef in self.getModuleDefs():
             token = mdef.token
             token = mdef.token
@@ -926,6 +929,19 @@ class Freezer:
                         mangledName = self.mangleName(moduleName)
                         mangledName = self.mangleName(moduleName)
                         moduleDefs.append(self.makeModuleDef(mangledName, code))
                         moduleDefs.append(self.makeModuleDef(mangledName, code))
                         moduleList.append(self.makeModuleListEntry(mangledName, code, moduleName, module))
                         moduleList.append(self.makeModuleListEntry(mangledName, code, moduleName, module))
+                    else:
+
+                        # This is a module with no associated Python
+                        # code.  It must be a compiled file.  Get the
+                        # filename.
+                        filename = getattr(module, '__file__', None)
+                        if filename:
+                            extras.append((moduleName, filename))
+                        else:
+                            # It doesn't even have a filename; it must
+                            # be a built-in module.  No worries about
+                            # this one, then.
+                            pass
 
 
         filename = basename + self.sourceExtension
         filename = basename + self.sourceExtension
 
 
@@ -978,7 +994,7 @@ class Freezer:
             if (os.path.exists(basename + self.objectExtension)):
             if (os.path.exists(basename + self.objectExtension)):
                 os.unlink(basename + self.objectExtension)
                 os.unlink(basename + self.objectExtension)
         
         
-        return target
+        return (target, extras)
 
 
     def compileExe(self, filename, basename):
     def compileExe(self, filename, basename):
         compile = self.compileObj % {
         compile = self.compileObj % {

+ 197 - 53
direct/src/showutil/Packager.py

@@ -67,6 +67,10 @@ class Packager:
         def close(self):
         def close(self):
             """ Writes out the contents of the current package. """
             """ Writes out the contents of the current package. """
 
 
+            if not self.p3dApplication and not self.version:
+                # We must have a version string for packages.
+                self.version = '0.0'
+
             self.packageBasename = self.packageName
             self.packageBasename = self.packageName
             packageDir = self.packageName
             packageDir = self.packageName
             if self.platform:
             if self.platform:
@@ -262,6 +266,15 @@ class Packager:
             if self.displayName:
             if self.displayName:
                 xpackage.SetAttribute('display_name', self.displayName)
                 xpackage.SetAttribute('display_name', self.displayName)
 
 
+            for package in self.requires:
+                xrequires = TiXmlElement('requires')
+                xrequires.SetAttribute('name', package.packageName)
+                if package.platform:
+                    xrequires.SetAttribute('platform', package.platform)
+                if package.version:
+                    xrequires.SetAttribute('version', package.version)
+                xpackage.InsertEndChild(xrequires)
+
             xuncompressedArchive = self.getFileSpec(
             xuncompressedArchive = self.getFileSpec(
                 'uncompressed_archive', self.packageFullpath,
                 'uncompressed_archive', self.packageFullpath,
                 self.packageBasename)
                 self.packageBasename)
@@ -290,6 +303,15 @@ class Packager:
             if self.version:
             if self.version:
                 xpackage.SetAttribute('version', self.version)
                 xpackage.SetAttribute('version', self.version)
 
 
+            for package in self.requires:
+                xrequires = TiXmlElement('requires')
+                xrequires.SetAttribute('name', package.packageName)
+                if package.platform:
+                    xrequires.SetAttribute('platform', package.platform)
+                if package.version:
+                    xrequires.SetAttribute('version', package.version)
+                xpackage.InsertEndChild(xrequires)
+
             for xcomponent in self.components:
             for xcomponent in self.components:
                 xpackage.InsertEndChild(xcomponent)
                 xpackage.InsertEndChild(xcomponent)
 
 
@@ -311,23 +333,33 @@ class Packager:
             self.platform = xpackage.Attribute('platform')
             self.platform = xpackage.Attribute('platform')
             self.version = xpackage.Attribute('version')
             self.version = xpackage.Attribute('version')
 
 
+            self.requires = []
+            xrequires = xpackage.FirstChildElement('requires')
+            while xrequires:
+                packageName = xrequires.Attribute('name')
+                platform = xrequires.Attribute('platform')
+                version = xrequires.Attribute('version')
+                if packageName:
+                    package = self.packager.findPackage(packageName, platform = platform, version = version, requires = self.requires)
+                    if package:
+                        self.requires.append(package)
+                xrequires = xrequires.NextSiblingElement()
+
             self.targetFilenames = {}
             self.targetFilenames = {}
             xcomponent = xpackage.FirstChildElement('component')
             xcomponent = xpackage.FirstChildElement('component')
             while xcomponent:
             while xcomponent:
-                xcomponent = xcomponent.ToElement()
                 name = xcomponent.Attribute('filename')
                 name = xcomponent.Attribute('filename')
                 if name:
                 if name:
                     self.targetFilenames[name] = True
                     self.targetFilenames[name] = True
-                xcomponent = xcomponent.NextSibling()
+                xcomponent = xcomponent.NextSiblingElement()
 
 
             self.moduleNames = {}
             self.moduleNames = {}
             xmodule = xpackage.FirstChildElement('module')
             xmodule = xpackage.FirstChildElement('module')
             while xmodule:
             while xmodule:
-                xmodule = xmodule.ToElement()
                 moduleName = xmodule.Attribute('name')
                 moduleName = xmodule.Attribute('name')
                 if moduleName:
                 if moduleName:
                     self.moduleNames[moduleName] = True
                     self.moduleNames[moduleName] = True
-                xmodule = xmodule.NextSibling()
+                xmodule = xmodule.NextSiblingElement()
 
 
             return True
             return True
 
 
@@ -522,17 +554,17 @@ class Packager:
             self.components.append(xcomponent)
             self.components.append(xcomponent)
 
 
         def requirePackage(self, package):
         def requirePackage(self, package):
-            """ Indicates a dependency on the given package. """
-
-            if package in self.requires:
-                # Already on the list.
-                return
-
-            self.requires.append(package)
-            for filename in package.targetFilenames.keys():
-                self.skipFilenames[filename] = True
-            for moduleName in package.moduleNames.keys():
-                self.skipModules[moduleName] = True
+            """ Indicates a dependency on the given package.  This
+            also implicitly requires all of the package's requirements
+            as well. """
+
+            for p2 in package.requires + [package]:
+                if p2 not in self.requires:
+                    self.requires.append(p2)
+                    for filename in p2.targetFilenames.keys():
+                        self.skipFilenames[filename] = True
+                    for moduleName in p2.moduleNames.keys():
+                        self.skipModules[moduleName] = True
 
 
     def __init__(self):
     def __init__(self):
 
 
@@ -748,12 +780,13 @@ class Packager:
 
 
         return words
         return words
 
 
-    def __getNextLine(self, file):
+    def __getNextLine(self, file, lineNum):
         """ Extracts the next line from the input file, and splits it
         """ Extracts the next line from the input file, and splits it
-        into words.  Returns the list of words, or None at end of
-        file. """
+        into words.  Returns a tuple (lineNum, list), or (lineNum,
+        None) at end of file. """
 
 
         line = file.readline()
         line = file.readline()
+        lineNum += 1
         while line:
         while line:
             line = line.strip()
             line = line.strip()
             if not line:
             if not line:
@@ -765,12 +798,13 @@ class Packager:
                 pass
                 pass
 
 
             else:
             else:
-                return self.__splitLine(line)
+                return (lineNum, self.__splitLine(line))
 
 
             line = file.readline()
             line = file.readline()
+            lineNum += 1
 
 
         # End of file.
         # End of file.
-        return None
+        return (lineNum, None)
 
 
     def readPackageDef(self, packageDef):
     def readPackageDef(self, packageDef):
         """ Reads the lines in the .pdef file named by packageDef and
         """ Reads the lines in the .pdef file named by packageDef and
@@ -781,10 +815,11 @@ class Packager:
 
 
         self.notify.info('Reading %s' % (packageDef))
         self.notify.info('Reading %s' % (packageDef))
         file = open(packageDef.toOsSpecific())
         file = open(packageDef.toOsSpecific())
+        lineNum = 0
 
 
         # Now start parsing the packageDef lines
         # Now start parsing the packageDef lines
         try:
         try:
-            words = self.__getNextLine(file)
+            lineNum, words = self.__getNextLine(file, lineNum)
             while words:
             while words:
                 command = words[0]
                 command = words[0]
                 try:
                 try:
@@ -803,13 +838,13 @@ class Packager:
                     message = '%s command encounted outside of package specification' %(command)
                     message = '%s command encounted outside of package specification' %(command)
                     raise OutsideOfPackageError, message
                     raise OutsideOfPackageError, message
 
 
-                words = self.__getNextLine(file)
+                lineNum, words = self.__getNextLine(file, lineNum)
 
 
         except PackagerError:
         except PackagerError:
             # Append the line number and file name to the exception
             # Append the line number and file name to the exception
             # error message.
             # error message.
             inst = sys.exc_info()[1]
             inst = sys.exc_info()[1]
-            inst.args = (inst.args[0] + ' on line %s of %s' % (lineNum[0], packageDef),)
+            inst.args = (inst.args[0] + ' on line %s of %s' % (lineNum, packageDef),)
             raise
             raise
 
 
         packageList = self.packageList
         packageList = self.packageList
@@ -1053,15 +1088,20 @@ class Packager:
                     raise PackageError, message
                     raise PackageError, message
 
 
         package = self.Package(packageName, self)
         package = self.Package(packageName, self)
+        self.currentPackage = package
+
         package.version = version
         package.version = version
         package.p3dApplication = p3dApplication
         package.p3dApplication = p3dApplication
+
         if package.p3dApplication:
         if package.p3dApplication:
             # Default compression level for an app.
             # Default compression level for an app.
             package.compressionLevel = 6
             package.compressionLevel = 6
+
+            # Every p3dapp requires panda3d.
+            self.require('panda3d')
+            
         package.dryRun = self.dryRun
         package.dryRun = self.dryRun
         
         
-        self.currentPackage = package
-
     def endPackage(self, packageName, p3dApplication = False):
     def endPackage(self, packageName, p3dApplication = False):
         """ Closes a package specification.  This actually generates
         """ Closes a package specification.  This actually generates
         the package file.  The packageName must match the previous
         the package file.  The packageName must match the previous
@@ -1082,40 +1122,55 @@ class Packager:
         package.close()
         package.close()
 
 
         self.packageList.append(package)
         self.packageList.append(package)
-        self.packages[package.packageName] = package
+        self.packages[(package.packageName, package.platform, package.version)] = package
         self.currentPackage = None
         self.currentPackage = None
 
 
-    def findPackage(self, packageName, version = None, searchUrl = None):
+    def findPackage(self, packageName, platform = None, version = None,
+                    requires = None):
         """ Searches for the named package from a previous publish
         """ Searches for the named package from a previous publish
-        operation, either at the indicated URL or along the install
-        search path.
+        operation along the install search path.
+
+        If requires is not None, it is a list of Package objects that
+        are already required.  The new Package object must be
+        compatible with the existing Packages, or an error is
+        returned.  This is also useful for determining the appropriate
+        package version to choose when a version is not specified.
 
 
         Returns the Package object, or None if the package cannot be
         Returns the Package object, or None if the package cannot be
         located. """
         located. """
 
 
+        if not platform:
+            platform = self.platform
+
         # Is it a package we already have resident?
         # Is it a package we already have resident?
-        package = self.packages.get((packageName, version), None)
+        package = self.packages.get((packageName, platform, version), None)
         if package:
         if package:
             return package
             return package
 
 
         # Look on the searchlist.
         # Look on the searchlist.
         for path in self.installSearch:
         for path in self.installSearch:
-            package = self.scanPackageDir(path, packageName, version, self.platform)
-            if package:
-                self.packages[(packageName, version)] = package
-                return package
-            package = self.scanPackageDir(path, packageName, version, None)
+            package = self.__scanPackageDir(path, packageName, platform, version, requires = requires)
+            if not package:
+                package = self.__scanPackageDir(path, packageName, None, version, requires = requires)
+
             if package:
             if package:
-                self.packages[(packageName, version)] = package
+                package = self.packages.setdefault((package.packageName, package.platform, package.version), package)
+                self.packages[(packageName, platform, version)] = package
                 return package
                 return package
                 
                 
         return None
         return None
 
 
-    def scanPackageDir(self, rootDir, packageName, version, platform):
-        """ Scans a directory on disk, looking for _import.xml files
-        that match the indicated packageName and option version.  If a
+    def __scanPackageDir(self, rootDir, packageName, platform, version,
+                         requires = None):
+        """ Scans a directory on disk, looking for *_import.xml files
+        that match the indicated packageName and optional version.  If a
         suitable xml file is found, reads it and returns the assocated
         suitable xml file is found, reads it and returns the assocated
-        Package definition. """
+        Package definition.
+
+        If a version is not specified, and multiple versions are
+        available, the highest-numbered version that matches will be
+        selected.
+        """
 
 
         packageDir = Filename(rootDir, packageName)
         packageDir = Filename(rootDir, packageName)
         basename = packageName
         basename = packageName
@@ -1125,26 +1180,101 @@ class Packager:
             basename += '_%s' % (platform)
             basename += '_%s' % (platform)
 
 
         if version:
         if version:
+            # A specific version package.
             packageDir = Filename(packageDir, version)
             packageDir = Filename(packageDir, version)
             basename += '_%s' % (version)
             basename += '_%s' % (version)
+        else:
+            # Scan all versions.
+            packageDir = Filename(packageDir, '*')
+            basename += '_%s' % ('*')
 
 
         basename += '_import.xml'
         basename += '_import.xml'
         filename = Filename(packageDir, basename)
         filename = Filename(packageDir, basename)
-        if filename.exists():
-            # It exists in the nested directory.
-            package = self.readPackageImportDescFile(filename)
-            if package:
+        filelist = glob.glob(filename.toOsSpecific())
+        if not filelist:
+            # It doesn't exist in the nested directory; try the root
+            # directory.
+            filename = Filename(rootDir, basename)
+            filelist = glob.glob(filename.toOsSpecific())
+
+        self.__sortPackageImportFilelist(filelist)
+        for file in filelist:
+            package = self.__readPackageImportDescFile(Filename.fromOsSpecific(file))
+            if package and self.__packageIsValid(package, requires):
                 return package
                 return package
-        filename = Filename(rootDir, basename)
-        if filename.exists():
-            # It exists in the root directory.
-            package = self.readPackageImportDescFile(filename)
-            if package:
+
+        return None
+
+    def __sortPackageImportFilelist(self, filelist):
+        """ Given a list of *_import.xml filenames, sorts them in
+        reverse order by version, so that the highest-numbered
+        versions appear first in the list. """
+
+        tuples = []
+        for file in filelist:
+            version = file.split('_')[-2]
+            version = self.__makeVersionTuple(version)
+            tuples.append((version, file))
+        tuples.sort(reverse = True)
+
+        return map(lambda t: t[1], tuples)
+
+    def __makeVersionTuple(self, version):
+        """ Converts a version string into a tuple for sorting, by
+        separating out numbers into separate numeric fields, so that
+        version numbers sort numerically where appropriate. """
+
+        words = []
+        p = 0
+        while p < len(version):
+            # Scan to the first digit.
+            w = ''
+            while p < len(version) and version[p] not in string.digits:
+                w += version[p]
+                p += 1
+            words.append(w)
+
+            # Scan to the end of the string of digits.
+            w = ''
+            while p < len(version) and version[p] in string.digits:
+                w += version[p]
+                p += 1
+            words.append(int(w))
+
+        return tuple(words)
+
+    def __packageIsValid(self, package, requires):
+        """ Returns true if the package is valid, meaning it can be
+        imported without conflicts with existing packages already
+        required (such as different versions of panda3d). """
+
+        if not requires:
+            return True
+
+        # Really, we only check the panda3d package for now.  The
+        # other packages will list this as a dependency, and this is
+        # all that matters.
+
+        panda1 = self.__findPackageInList('panda3d', [package] + package.requires)
+        panda2 = self.__findPackageInList('panda3d', requires)
+
+        if not panda1 or not panda2:
+            return True
+
+        if panda1.version == panda2.version:
+            return True
+
+        return False
+
+    def __findPackageInList(self, packageName, list):
+        """ Returns the first package with the indicated name in the list. """
+        for package in list:
+            if package.packageName == packageName:
                 return package
                 return package
 
 
         return None
         return None
 
 
-    def readPackageImportDescFile(self, filename):
+    def __readPackageImportDescFile(self, filename):
         """ Reads the named xml file as a Package, and returns it if
         """ Reads the named xml file as a Package, and returns it if
         valid, or None otherwise. """
         valid, or None otherwise. """
 
 
@@ -1162,15 +1292,18 @@ class Packager:
         named package also.  Files already included in the named
         named package also.  Files already included in the named
         package will be omitted from this one when building it. """
         package will be omitted from this one when building it. """
 
 
+        if not self.currentPackage:
+            raise OutsideOfPackageError
+
         # A special case for the Panda3D package.  We enforce that the
         # A special case for the Panda3D package.  We enforce that the
         # version number matches what we've been compiled with.
         # version number matches what we've been compiled with.
         if packageName == 'panda3d':
         if packageName == 'panda3d':
             if version is None:
             if version is None:
                 version = PandaSystem.getPackageVersionString()
                 version = PandaSystem.getPackageVersionString()
         
         
-        package = self.findPackage(packageName, version = version)
+        package = self.findPackage(packageName, version = version, requires = self.currentPackage.requires)
         if not package:
         if not package:
-            message = "Unknown package %s" % (packageName)
+            message = 'Unknown package %s, version "%s"' % (packageName, version)
             raise PackagerError, message
             raise PackagerError, message
 
 
         self.requirePackage(package)
         self.requirePackage(package)
@@ -1183,6 +1316,9 @@ class Packager:
         named package also.  Files already included in the named
         named package also.  Files already included in the named
         package will be omitted from this one. """
         package will be omitted from this one. """
 
 
+        if not self.currentPackage:
+            raise OutsideOfPackageError
+
         # A special case for the Panda3D package.  We enforce that the
         # A special case for the Panda3D package.  We enforce that the
         # version number matches what we've been compiled with.
         # version number matches what we've been compiled with.
         if package.packageName == 'panda3d':
         if package.packageName == 'panda3d':
@@ -1255,9 +1391,17 @@ class Packager:
                 dirname, basename = filename.rsplit('/', 1)
                 dirname, basename = filename.rsplit('/', 1)
                 dirname += '/'
                 dirname += '/'
 
 
-            basename = freezer.generateCode(basename, compileToExe = compileToExe)
+            basename, extras = freezer.generateCode(basename, compileToExe = compileToExe)
 
 
             package.files.append(self.PackFile(Filename(basename), newName = dirname + basename, deleteTemp = True, extract = True))
             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))
+                
             if not package.platform:
             if not package.platform:
                 package.platform = PandaSystem.getPlatform()
                 package.platform = PandaSystem.getPlatform()
 
 

+ 1 - 1
direct/src/showutil/make_contents.py

@@ -38,7 +38,7 @@ class FileSpec:
 
 
         s = os.stat(pathname)
         s = os.stat(pathname)
         self.size = s.st_size
         self.size = s.st_size
-        self.timestamp = s.st_mtime
+        self.timestamp = int(s.st_mtime)
 
 
         m = md5.new()
         m = md5.new()
         m.update(open(pathname, 'rb').read())
         m.update(open(pathname, 'rb').read())

+ 15 - 5
direct/src/showutil/packp3d.py

@@ -16,7 +16,7 @@ Usage:
 
 
 Options:
 Options:
 
 
-  -r application_root
+  -d application_root
      Specify the root directory of the application source; this is a
      Specify the root directory of the application source; this is a
      directory tree that contains all of your .py files and models.
      directory tree that contains all of your .py files and models.
      If this is omitted, the default is the current directory.
      If this is omitted, the default is the current directory.
@@ -30,6 +30,11 @@ Options:
      (this is preferable to having the module start itself immediately
      (this is preferable to having the module start itself immediately
      upon importing).
      upon importing).
 
 
+  -r package
+     Names an additional package that this application requires at
+     startup time.  The default package is 'panda3d'; you may repeat
+     this option to indicate dependencies on additional packages.
+
   -s search_dir
   -s search_dir
      Additional directories to search for previously-built packages.
      Additional directories to search for previously-built packages.
      This option may be repeated as necessary.
      This option may be repeated as necessary.
@@ -56,18 +61,22 @@ class ArgumentError(StandardError):
     pass
     pass
 
 
 def makePackedApp(args):
 def makePackedApp(args):
-    opts, args = getopt.getopt(args, 'r:m:s:xh')
+    opts, args = getopt.getopt(args, 'd:m:r:s:xh')
 
 
     packager = Packager.Packager()
     packager = Packager.Packager()
 
 
     root = '.'
     root = '.'
     main = None
     main = None
+    requires = []
     versionIndependent = False
     versionIndependent = False
+    
     for option, value in opts:
     for option, value in opts:
-        if option == '-r':
+        if option == '-d':
             root = Filename.fromOsSpecific(value)
             root = Filename.fromOsSpecific(value)
         elif option == '-m':
         elif option == '-m':
             main = value
             main = value
+        elif option == '-r':
+            requires.append(value)
         elif option == '-s':
         elif option == '-s':
             packager.installSearch.append(Filename.fromOsSpecific(value))
             packager.installSearch.append(Filename.fromOsSpecific(value))
         elif option == '-x':
         elif option == '-x':
@@ -110,8 +119,9 @@ def makePackedApp(args):
 
 
     packager.setup()
     packager.setup()
     packager.beginPackage(appBase, p3dApplication = True)
     packager.beginPackage(appBase, p3dApplication = True)
-
-    packager.require('panda3d')
+    for requireName in requires:
+        packager.require(requireName)
+        
     packager.dir(root)
     packager.dir(root)
     packager.mainModule(mainModule)
     packager.mainModule(mainModule)
         
         

+ 16 - 6
direct/src/showutil/ppackage.py

@@ -111,10 +111,20 @@ if not packager.installDir:
 packager.installSearch = [packager.installDir] + packager.installSearch
 packager.installSearch = [packager.installDir] + packager.installSearch
 
 
 packager.setup()
 packager.setup()
-packager.readPackageDef(packageDef)
-
-# Update the contents.xml at the root of the install directory.
-cm = make_contents.ContentsMaker()
-cm.installDir = packager.installDir.toOsSpecific()
-cm.build()
+packages = packager.readPackageDef(packageDef)
+
+# Look to see if we built any true packages, or if all of them were
+# p3d files.
+anyPackages = False
+for package in packages:
+    if not package.p3dApplication:
+        anyPackages = True
+        break
+
+if anyPackages:
+    # If we built any true packages, then update the contents.xml at
+    # the root of the install directory.
+    cm = make_contents.ContentsMaker()
+    cm.installDir = packager.installDir.toOsSpecific()
+    cm.build()