tweaks for pirates

This commit is contained in:
David Rose 2009-08-26 23:46:50 +00:00
parent 5f0b8a9a6a
commit 9691c5c403
4 changed files with 212 additions and 77 deletions

View File

@ -424,7 +424,7 @@ class AppRunner(DirectObject):
fn = Filename(f) fn = Filename(f)
if fn.getDirname() == '' and fn.getExtension() == 'prc': if fn.getDirname() == '' and fn.getExtension() == 'prc':
pathname = '%s/%s' % (root, f) pathname = '%s/%s' % (root, f)
data = open(pathname, 'r').read() data = file.open(Filename(pathname), 'r').read()
loadPrcFileData(pathname, data) loadPrcFileData(pathname, data)

View File

@ -35,6 +35,7 @@ class Packager:
def __init__(self, package, filename, def __init__(self, package, filename,
newName = None, deleteTemp = False, newName = None, deleteTemp = False,
explicit = False, compress = None, extract = None, explicit = False, compress = None, extract = None,
text = None, unprocessed = None,
executable = None, platformSpecific = None): executable = None, platformSpecific = None):
assert isinstance(filename, Filename) assert isinstance(filename, Filename)
self.filename = Filename(filename) self.filename = Filename(filename)
@ -43,6 +44,8 @@ class Packager:
self.explicit = explicit self.explicit = explicit
self.compress = compress self.compress = compress
self.extract = extract self.extract = extract
self.text = text
self.unprocessed = unprocessed
self.executable = executable self.executable = executable
self.platformSpecific = platformSpecific self.platformSpecific = platformSpecific
@ -72,6 +75,8 @@ class Packager:
if self.platformSpecific is None: if self.platformSpecific is None:
self.platformSpecific = self.executable or (ext in packager.platformSpecificExtensions) self.platformSpecific = self.executable or (ext in packager.platformSpecificExtensions)
if self.unprocessed is None:
self.unprocessed = self.executable or (ext in packager.unprocessedExtensions)
if self.executable: if self.executable:
# Look up the filename along the system PATH, if necessary. # Look up the filename along the system PATH, if necessary.
@ -211,6 +216,8 @@ class Packager:
def __init__(self, packageName, packager): def __init__(self, packageName, packager):
self.packageName = packageName self.packageName = packageName
self.packager = packager self.packager = packager
self.notify = packager.notify
self.platform = None self.platform = None
self.version = None self.version = None
self.host = None self.host = None
@ -250,11 +257,18 @@ class Packager:
if not self.host: if not self.host:
self.host = self.packager.host self.host = self.packager.host
# Check the version config variable.
version = self.configs.get('version', None)
if version is not None:
self.version = version
del self.configs['version']
# Check the platform_specific config variable. This has # Check the platform_specific config variable. This has
# only three settings: None (unset), True, or False. # only three settings: None (unset), True, or False.
self.platformSpecificConfig = self.configs.get('platform_specific', None) self.platformSpecificConfig = self.configs.get('platform_specific', None)
if self.platformSpecificConfig is not None: if self.platformSpecificConfig is not None:
self.platformSpecificConfig = bool(self.platformSpecificConfig) self.platformSpecificConfig = bool(self.platformSpecificConfig)
del self.configs['platform_specific']
# A special case when building the "panda3d" package. We # A special case when building the "panda3d" package. We
# enforce that the version number matches what we've been # enforce that the version number matches what we've been
@ -265,11 +279,11 @@ class Packager:
if self.version != PandaSystem.getPackageVersionString(): if self.version != PandaSystem.getPackageVersionString():
message = 'mismatched Panda3D version: requested %s, but Panda3D is built as %s' % (self.version, PandaSystem.getPackageVersionString()) message = 'mismatched Panda3D version: requested %s, but Panda3D is built as %s' % (self.version, PandaSystem.getPackageVersionString())
raise PackageError, message raise PackagerError, message
if self.host != PandaSystem.getPackageHostUrl(): if self.host != PandaSystem.getPackageHostUrl():
message = 'mismatched Panda3D host: requested %s, but Panda3D is built as %s' % (self.host, PandaSystem.getPackageHostUrl()) message = 'mismatched Panda3D host: requested %s, but Panda3D is built as %s' % (self.host, PandaSystem.getPackageHostUrl())
raise PackageError, message raise PackagerError, message
if self.p3dApplication: if self.p3dApplication:
# Default compression level for an app. # Default compression level for an app.
@ -330,14 +344,19 @@ class Packager:
# Add the explicit py files that were requested by the # Add the explicit py files that were requested by the
# pdef file. These get turned into Python modules. # pdef file. These get turned into Python modules.
for file in self.files: for file in self.files:
ext = Filename(file.newName).getExtension()
if ext != 'py':
continue
if file.isExcluded(self): if file.isExcluded(self):
# Skip this file. # Skip this file.
continue continue
if file.unprocessed:
# Unprocessed files get dealt with below.
continue
ext = Filename(file.newName).getExtension()
if ext == 'dc':
# Add the modules named implicitly in the dc file.
self.addDcImports(file)
elif ext == 'py':
self.addPyFile(file) self.addPyFile(file)
# Add the main module, if any. # Add the main module, if any.
@ -411,17 +430,24 @@ class Packager:
# discovered above. # discovered above.
for file in self.files: for file in self.files:
ext = Filename(file.newName).getExtension() ext = Filename(file.newName).getExtension()
if ext == 'py': if file.unprocessed:
# Add an unprocessed file verbatim.
self.addComponent(file)
elif ext == 'py':
# Already handled, above. # Already handled, above.
continue pass
elif file.isExcluded(self):
if file.isExcluded(self):
# Skip this file. # Skip this file.
continue pass
elif ext == 'egg' or ext == 'bam':
if ext == 'egg' or ext == 'bam':
# Skip model files this pass. # Skip model files this pass.
pass pass
elif ext == 'dc':
# dc files get a special treatment.
self.addDcFile(file)
elif ext == 'prc':
# So do prc files.
self.addPrcFile(file)
else: else:
# Any other file. # Any other file.
self.addComponent(file) self.addComponent(file)
@ -435,15 +461,16 @@ class Packager:
# because we may add new files that we want to process. # because we may add new files that we want to process.
for file in self.files: for file in self.files:
ext = Filename(file.newName).getExtension() ext = Filename(file.newName).getExtension()
if ext == 'py': if file.unprocessed:
# Already handled, above. # Already handled, above.
continue pass
elif ext == 'py':
if file.isExcluded(self): # Already handled, above.
pass
elif file.isExcluded(self):
# Skip this file. # Skip this file.
continue pass
elif ext == 'egg':
if ext == 'egg':
self.addEggFile(file) self.addEggFile(file)
elif ext == 'bam': elif ext == 'bam':
self.addBamFile(file) self.addBamFile(file)
@ -488,12 +515,7 @@ class Packager:
self.multifile.repack() self.multifile.repack()
self.multifile.close() self.multifile.close()
if self.p3dApplication: if not self.p3dApplication:
# No patches for an application; just move it into place.
multifileFilename.renameTo(self.packageFullpath)
# Make the application file executable.
os.chmod(self.packageFullpath.toOsSpecific(), 0755)
else:
# The "base" package file is the bottom of the patch chain. # The "base" package file is the bottom of the patch chain.
packageBaseFullpath = Filename(self.packageFullpath + '.base') packageBaseFullpath = Filename(self.packageFullpath + '.base')
if not packageBaseFullpath.exists() and \ if not packageBaseFullpath.exists() and \
@ -502,7 +524,14 @@ class Packager:
# It becomes the "base". # It becomes the "base".
self.packageFullpath.renameTo(packageBaseFullpath) self.packageFullpath.renameTo(packageBaseFullpath)
multifileFilename.renameTo(self.packageFullpath) if not multifileFilename.renameTo(self.packageFullpath):
self.notify.error("Cannot move %s to %s" % (multifileFilename, self.packageFullpath))
if self.p3dApplication:
# No patches for an application; just move it into place.
# Make the application file executable.
os.chmod(self.packageFullpath.toOsSpecific(), 0755)
else:
self.compressMultifile() self.compressMultifile()
self.readDescFile() self.readDescFile()
self.writeDescFile() self.writeDescFile()
@ -562,8 +591,8 @@ class Packager:
targetPath.setBinary() targetPath.setBinary()
file.filename.setBinary() file.filename.setBinary()
if not file.filename.copyTo(targetPath): if not file.filename.copyTo(targetPath):
print "Could not copy %s to %s" % ( self.notify.warning("Could not copy %s to %s" % (
file.filename, targetPath) file.filename, targetPath))
# Replace or add the entry in the contents. # Replace or add the entry in the contents.
pe = Packager.PackageEntry() pe = Packager.PackageEntry()
@ -599,7 +628,7 @@ class Packager:
self.sourceFilenames[file.filename] = file self.sourceFilenames[file.filename] = file
if not file.filename.exists(): if file.text is None and not file.filename.exists():
if not file.isExcluded(self): if not file.isExcluded(self):
self.packager.notify.warning("No such file: %s" % (file.filename)) self.packager.notify.warning("No such file: %s" % (file.filename))
return return
@ -644,7 +673,7 @@ class Packager:
filenames = self.__parseDependenciesWindows(tempFile) filenames = self.__parseDependenciesWindows(tempFile)
tempFile.unlink() tempFile.unlink()
if filenames is None: if filenames is None:
print "Unable to determine dependencies from %s" % (file.filename) self.notify.warning("Unable to determine dependencies from %s" % (file.filename))
continue continue
# Attempt to resolve the dependent filename relative # Attempt to resolve the dependent filename relative
@ -727,7 +756,7 @@ class Packager:
filenames = self.__parseDependenciesOSX(tempFile) filenames = self.__parseDependenciesOSX(tempFile)
tempFile.unlink() tempFile.unlink()
if filenames is None: if filenames is None:
print "Unable to determine dependencies from %s" % (file.filename) self.notify.warning("Unable to determine dependencies from %s" % (file.filename))
continue continue
# Attempt to resolve the dependent filename relative # Attempt to resolve the dependent filename relative
@ -799,7 +828,7 @@ class Packager:
filenames = self.__parseDependenciesPosix(tempFile) filenames = self.__parseDependenciesPosix(tempFile)
tempFile.unlink() tempFile.unlink()
if filenames is None: if filenames is None:
print "Unable to determine dependencies from %s" % (file.filename) self.notify.warning("Unable to determine dependencies from %s" % (file.filename))
continue continue
# Attempt to resolve the dependent filename relative # Attempt to resolve the dependent filename relative
@ -1247,11 +1276,126 @@ class Packager:
compress = False) compress = False)
return newName return newName
def addDcFile(self, file):
""" Adds a dc file to the archive. A dc file gets its
internal comments and parameter names stripped out of the
final result automatically. This is as close as we can
come to "compiling" a dc file, since all of the remaining
symbols are meaningful at runtime. """
# First, read in the dc file
dcFile = DCFile()
if not dcFile.read(file.filename):
self.notify.error("Unable to parse %s." % (file.filename))
# And then write it out without the comments and such.
stream = StringStream()
if not dcFile.write(stream, True):
self.notify.error("Unable to write %s." % (file.filename))
file.text = stream.getData()
self.addComponent(file)
def addDcImports(self, file):
""" Adds the Python modules named by the indicated dc
file. """
dcFile = DCFile()
if not dcFile.read(file.filename):
self.notify.error("Unable to parse %s." % (file.filename))
for n in range(dcFile.getNumImportModules()):
moduleName = dcFile.getImportModule(n)
moduleSuffixes = []
if '/' in moduleName:
moduleName, suffixes = moduleName.split('/', 1)
moduleSuffixes = suffixes.split('/')
self.freezer.addModule(moduleName)
for suffix in self.packager.dcClientSuffixes:
if suffix in moduleSuffixes:
self.freezer.addModule(moduleName + suffix)
for i in range(dcFile.getNumImportSymbols(n)):
symbolName = dcFile.getImportSymbol(n, i)
symbolSuffixes = []
if '/' in symbolName:
symbolName, suffixes = symbolName.split('/', 1)
symbolSuffixes = suffixes.split('/')
# "from moduleName import symbolName".
# Maybe this symbol is itself a module; if that's
# the case, we need to add it to the list also.
self.freezer.addModule('%s.%s' % (moduleName, symbolName),
implicit = True)
for suffix in self.packager.dcClientSuffixes:
if suffix in symbolSuffixes:
self.freezer.addModule('%s.%s%s' % (moduleName, symbolName, suffix),
implicit = True)
def addPrcFile(self, file):
""" Adds a prc file to the archive. Like the dc file,
this strips comments and such before adding. It's also
possible to set prcEncryptionKey and/or prcSignCommand to
further manipulate prc files during processing. """
# First, read it in.
if file.text:
textLines = file.text.split('\n')
else:
textLines = open(file.filename.toOsSpecific(), 'rU').readlines()
# Then write it out again, without the comments.
tempFilename = Filename.temporary('', 'p3d_', '.prc')
temp = open(tempFilename.toOsSpecific(), 'w')
for line in textLines:
line = line.strip()
if line and line[0] != '#':
# Write the line out only if it's not a comment.
temp.write(line + '\n')
temp.close()
if self.packager.prcSignCommand:
# Now sign the file.
command = '%s -n "%s"' % (
self.packager.prcSignCommand, tempFilename.toOsSpecific())
self.notify.info(command)
exitStatus = os.system(command)
if exitStatus != 0:
self.notify.error('Command failed: %s' % (command))
if self.packager.prcEncryptionKey:
# And now encrypt it.
if file.newName.endswith('.prc'):
# Change .prc -> .pre
file.newName = file.newName[:-1] + 'e'
preFilename = Filename.temporary('', 'p3d_', '.pre')
encryptFile(tempFilename, preFilename, self.packager.prcEncryptionKey)
tempFilename.unlink()
tempFilename = preFilename
if file.deleteTemp:
file.filename.unlink()
file.filename = tempFilename
file.text = None
file.deleteTemp = True
self.addComponent(file)
def addComponent(self, file): def addComponent(self, file):
compressionLevel = 0 compressionLevel = 0
if file.compress: if file.compress:
compressionLevel = self.compressionLevel compressionLevel = self.compressionLevel
if file.text:
stream = StringStream(file.text)
self.multifile.addSubfile(file.newName, stream, compressionLevel)
self.multifile.flush()
else:
self.multifile.addSubfile(file.newName, file.filename, compressionLevel) self.multifile.addSubfile(file.newName, file.filename, compressionLevel)
if file.extract: if file.extract:
xextract = self.getFileSpec('extract', file.filename, file.newName) xextract = self.getFileSpec('extract', file.filename, file.newName)
@ -1286,7 +1430,7 @@ class Packager:
self.persistDir = None self.persistDir = None
# The download URL at which these packages will eventually be # The download URL at which these packages will eventually be
# hosted. This may also be changed with the "host" command. # hosted.
self.host = PandaSystem.getPackageHostUrl() self.host = PandaSystem.getPackageHostUrl()
self.hostDescriptiveName = None self.hostDescriptiveName = None
@ -1396,6 +1540,10 @@ class Packager:
# copied without compression. # copied without compression.
self.uncompressibleExtensions = [ 'mp3', 'ogg' ] self.uncompressibleExtensions = [ 'mp3', 'ogg' ]
# Files which are not to be processed further, but which
# should be added exactly byte-for-byte as they are.
self.unprocessedExtensions = []
# System files that should never be packaged. For # System files that should never be packaged. For
# case-insensitive filesystems (like Windows), put the # case-insensitive filesystems (like Windows), put the
# lowercase filename here. Case-sensitive filesystems should # lowercase filename here. Case-sensitive filesystems should
@ -1466,7 +1614,7 @@ class Packager:
""" Call this method to initialize the class after filling in """ Call this method to initialize the class after filling in
some of the values in the constructor. """ some of the values in the constructor. """
self.knownExtensions = self.imageExtensions + self.modelExtensions + self.textExtensions + self.binaryExtensions + self.uncompressibleExtensions self.knownExtensions = self.imageExtensions + self.modelExtensions + self.textExtensions + self.binaryExtensions + self.uncompressibleExtensions + self.unprocessedExtensions
self.currentPackage = None self.currentPackage = None
@ -1500,6 +1648,7 @@ class Packager:
globals['__name__'] = packageDef.getBasenameWoExtension() globals['__name__'] = packageDef.getBasenameWoExtension()
globals['platform'] = PandaSystem.getPlatform() globals['platform'] = PandaSystem.getPlatform()
globals['packager'] = self
# We'll stuff all of the predefined functions, and the # We'll stuff all of the predefined functions, and the
# predefined classes, in the global dictionary, so the pdef # predefined classes, in the global dictionary, so the pdef
@ -1533,7 +1682,7 @@ class Packager:
# Now iterate through the statements and operate on them. # Now iterate through the statements and operate on them.
statements = globals.get('__statements', []) statements = globals.get('__statements', [])
if not statements: if not statements:
print "No packages defined." self.notify.info("No packages defined.")
try: try:
for (lineno, stype, name, args, kw) in statements: for (lineno, stype, name, args, kw) in statements:
@ -1545,7 +1694,7 @@ class Packager:
solo = solo) solo = solo)
statements = classDef.__dict__.get('__statements', []) statements = classDef.__dict__.get('__statements', [])
if not statements: if not statements:
print "No files added to %s" % (name) self.notify.info("No files added to %s" % (name))
for (lineno, stype, name, args, kw) in statements: for (lineno, stype, name, args, kw) in statements:
if stype == 'class': if stype == 'class':
raise PackagerError, 'Nested classes not allowed' raise PackagerError, 'Nested classes not allowed'
@ -1609,22 +1758,6 @@ class Packager:
line = line[:whitespaceCount].lstrip() + line[whitespaceCount:] line = line[:whitespaceCount].lstrip() + line[whitespaceCount:]
return line return line
def do_host(self, hostUrl, descriptiveName = None):
""" Specifies the server that will eventually host this
published content. """
if self.currentPackage:
self.currentPackage.host = hostUrl
else:
# Outside of a package, the "host" command specifies the
# host for all future packages.
self.host = hostUrl
# The descriptive name, if specified, is kept until the end,
# where it may be written into the contents file.
if descriptiveName:
self.hostDescriptiveName = descriptiveName
def __parseArgs(self, words, argList): def __parseArgs(self, words, argList):
args = {} args = {}
@ -1792,7 +1925,7 @@ class Packager:
# from it. # from it.
filename = Filename(host.importsDir, package.importDescFile.basename) filename = Filename(host.importsDir, package.importDescFile.basename)
if not appRunner.freshenFile(host, package.importDescFile, filename): if not appRunner.freshenFile(host, package.importDescFile, filename):
print "Couldn't download import file." self.notify.error("Couldn't download import file.")
return None return None
# Now that we have the import desc file, use it to load one of # Now that we have the import desc file, use it to load one of
@ -1950,9 +2083,9 @@ class Packager:
# compiled with. # compiled with.
if package.packageName == 'panda3d': if package.packageName == 'panda3d':
if package.version != PandaSystem.getPackageVersionString(): if package.version != PandaSystem.getPackageVersionString():
print "Warning: requiring panda3d version %s, which does not match the current build of Panda, which is version %s." % (package, PandaSystem.getPackageVersionString()) self.notify.warning("Requiring panda3d version %s, which does not match the current build of Panda, which is version %s." % (package, PandaSystem.getPackageVersionString()))
elif package.host != PandaSystem.getPackageHostUrl(): elif package.host != PandaSystem.getPackageHostUrl():
print "Warning: requiring panda3d host %s, which does not match the current build of Panda, which is host %s." % (package, PandaSystem.getPackageHostUrl()) self.notify.warning("Requiring panda3d host %s, which does not match the current build of Panda, which is host %s." % (package, PandaSystem.getPackageHostUrl()))
self.currentPackage.requirePackage(package) self.currentPackage.requirePackage(package)
@ -2145,9 +2278,9 @@ class Packager:
files += thisFiles files += thisFiles
prefix = '' prefix = ''
if newDir: if newDir is not None:
prefix = Filename(newDir).cStr() prefix = Filename(newDir).cStr()
if prefix[-1] != '/': if prefix and prefix[-1] != '/':
prefix += '/' prefix += '/'
if newName: if newName:
@ -2162,13 +2295,6 @@ class Packager:
if not newName: if not newName:
newName = str(filenames[0]) newName = str(filenames[0])
tempFile = Filename.temporary('', self.currentPackage.packageName + '.', Filename(newName).getExtension())
temp = open(tempFile.toOsSpecific(), 'w')
temp.write(text)
temp.close()
files = [tempFile.toOsSpecific()]
deleteTemp = True
for filename in files: for filename in files:
filename = Filename.fromOsSpecific(filename) filename = Filename.fromOsSpecific(filename)
basename = filename.getBasename() basename = filename.getBasename()
@ -2179,7 +2305,7 @@ class Packager:
self.currentPackage.addFile( self.currentPackage.addFile(
filename, newName = name, extract = extract, filename, newName = name, extract = extract,
explicit = explicit, executable = executable, explicit = explicit, executable = executable,
deleteTemp = deleteTemp) text = text, deleteTemp = deleteTemp)
def do_exclude(self, filename): def do_exclude(self, filename):
""" Marks the indicated filename as not to be included. The """ Marks the indicated filename as not to be included. The
@ -2193,7 +2319,7 @@ class Packager:
self.currentPackage.excludeFile(filename) self.currentPackage.excludeFile(filename)
def do_dir(self, dirname, newDir = None): def do_dir(self, dirname, newDir = None, unprocessed = None):
""" Adds the indicated directory hierarchy to the current """ Adds the indicated directory hierarchy to the current
package. The directory hierarchy is walked recursively, and package. The directory hierarchy is walked recursively, and
@ -2203,6 +2329,12 @@ class Packager:
the contents of the named directory should be installed to. the contents of the named directory should be installed to.
If it is omitted, the contents of the named directory are If it is omitted, the contents of the named directory are
installed to the root of the package. installed to the root of the package.
If unprocessed is false (the default), bam files are loaded and
scanned for textures, and these texture paths within the bam
files are manipulated to point to the new paths within the
package. If unprocessed is true, this operation is bypassed,
and bam files are packed exactly as they are.
""" """
if not self.currentPackage: if not self.currentPackage:
@ -2212,9 +2344,9 @@ class Packager:
if not newDir: if not newDir:
newDir = '' newDir = ''
self.__recurseDir(dirname, newDir) self.__recurseDir(dirname, newDir, unprocessed = unprocessed)
def __recurseDir(self, filename, newName): def __recurseDir(self, filename, newName, unprocessed = None):
dirList = vfs.scanDirectory(filename) dirList = vfs.scanDirectory(filename)
if dirList: if dirList:
# It's a directory name. Recurse. # It's a directory name. Recurse.
@ -2230,7 +2362,7 @@ class Packager:
ext = filename.getExtension() ext = filename.getExtension()
if ext == 'py': if ext == 'py':
self.currentPackage.addFile(filename, newName = newName, self.currentPackage.addFile(filename, newName = newName,
explicit = False) explicit = False, unprocessed = unprocessed)
else: else:
if ext == 'pz': if ext == 'pz':
# Strip off an implicit .pz extension. # Strip off an implicit .pz extension.
@ -2241,7 +2373,7 @@ class Packager:
if ext in self.knownExtensions: if ext in self.knownExtensions:
self.currentPackage.addFile(filename, newName = newName, self.currentPackage.addFile(filename, newName = newName,
explicit = False) explicit = False, unprocessed = unprocessed)
def readContentsFile(self): def readContentsFile(self):

View File

@ -34,7 +34,7 @@ class panda3d(package):
config(display_name = "Panda3D") config(display_name = "Panda3D")
# This is the key Python module('that is imported at runtime to start') # This is the key Python module that is imported at runtime to start
# an application running. # an application running.
module('direct.p3d.AppRunner') module('direct.p3d.AppRunner')

View File

@ -507,7 +507,10 @@ class Freezer:
# Make sure we know how to find "direct". # Make sure we know how to find "direct".
for sourceTree in sourceTrees: for sourceTree in sourceTrees:
module = sys.modules.get(sourceTree, None) try:
module = __import__(sourceTree)
except:
module = None
if module and hasattr(module, '__path__'): if module and hasattr(module, '__path__'):
path = getattr(module, '__path__') path = getattr(module, '__path__')
modulefinder.AddPackagePath(sourceTree, path[0]) modulefinder.AddPackagePath(sourceTree, path[0])