mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-30 16:58:40 -04:00
better xml, better requires
This commit is contained in:
parent
383a6e512c
commit
57e6e80491
@ -83,18 +83,15 @@ class Packager:
|
||||
self.packageDesc = packageDir + self.packageDesc
|
||||
self.packageImportDesc = packageDir + self.packageImportDesc
|
||||
|
||||
Filename(self.packageFilename).makeDir()
|
||||
|
||||
try:
|
||||
os.unlink(self.packageFilename)
|
||||
except OSError:
|
||||
pass
|
||||
self.packageFullpath = Filename(self.packager.installDir, self.packageFilename)
|
||||
self.packageFullpath.makeDir()
|
||||
self.packageFullpath.unlink()
|
||||
|
||||
if self.dryRun:
|
||||
self.multifile = None
|
||||
else:
|
||||
self.multifile = Multifile()
|
||||
self.multifile.openReadWrite(self.packageFilename)
|
||||
self.multifile.openReadWrite(self.packageFullpath)
|
||||
|
||||
self.extracts = []
|
||||
self.components = []
|
||||
@ -183,12 +180,14 @@ class Packager:
|
||||
""" Compresses the .mf file into an .mf.pz file. """
|
||||
|
||||
compressedName = self.packageFilename + '.pz'
|
||||
if not compressFile(self.packageFilename, compressedName, 6):
|
||||
message = 'Unable to write %s' % (compressedName)
|
||||
compressedPath = Filename(self.packager.installDir, compressedName)
|
||||
if not compressFile(self.packageFullpath, compressedPath, 6):
|
||||
message = 'Unable to write %s' % (compressedPath)
|
||||
raise PackagerError, message
|
||||
|
||||
def writeDescFile(self):
|
||||
doc = TiXmlDocument(self.packageDesc)
|
||||
packageDescFullpath = Filename(self.packager.installDir, self.packageDesc)
|
||||
doc = TiXmlDocument(packageDescFullpath.toOsSpecific())
|
||||
decl = TiXmlDeclaration("1.0", "utf-8", "")
|
||||
doc.InsertEndChild(decl)
|
||||
|
||||
@ -200,9 +199,11 @@ class Packager:
|
||||
xpackage.SetAttribute('version', self.version)
|
||||
|
||||
xuncompressedArchive = self.getFileSpec(
|
||||
'uncompressed_archive', self.packageFilename, self.packageBasename)
|
||||
'uncompressed_archive', self.packageFullpath,
|
||||
self.packageBasename)
|
||||
xcompressedArchive = self.getFileSpec(
|
||||
'compressed_archive', self.packageFilename + '.pz', self.packageBasename + '.pz')
|
||||
'compressed_archive', self.packageFullpath + '.pz',
|
||||
self.packageBasename + '.pz')
|
||||
xpackage.InsertEndChild(xuncompressedArchive)
|
||||
xpackage.InsertEndChild(xcompressedArchive)
|
||||
|
||||
@ -213,7 +214,8 @@ class Packager:
|
||||
doc.SaveFile()
|
||||
|
||||
def writeImportDescFile(self):
|
||||
doc = TiXmlDocument(self.packageImportDesc)
|
||||
packageImportDescFullpath = Filename(self.packager.installDir, self.packageImportDesc)
|
||||
doc = TiXmlDocument(packageImportDescFullpath.toOsSpecific())
|
||||
decl = TiXmlDeclaration("1.0", "utf-8", "")
|
||||
doc.InsertEndChild(decl)
|
||||
|
||||
@ -230,19 +232,52 @@ class Packager:
|
||||
doc.InsertEndChild(xpackage)
|
||||
doc.SaveFile()
|
||||
|
||||
def readImportDescFile(self, filename):
|
||||
""" Reads the import desc file. Returns True on success,
|
||||
False on failure. """
|
||||
|
||||
def getFileSpec(self, element, filename, newName):
|
||||
doc = TiXmlDocument(filename.toOsSpecific())
|
||||
if not doc.LoadFile():
|
||||
return False
|
||||
xpackage = doc.FirstChildElement('package')
|
||||
if not xpackage:
|
||||
return False
|
||||
|
||||
self.packageName = xpackage.Attribute('name')
|
||||
self.platform = xpackage.Attribute('platform')
|
||||
self.version = xpackage.Attribute('version')
|
||||
|
||||
self.targetFilenames = {}
|
||||
xcomponent = xpackage.FirstChildElement('component')
|
||||
while xcomponent:
|
||||
xcomponent = xcomponent.ToElement()
|
||||
name = xcomponent.Attribute('filename')
|
||||
if name:
|
||||
self.targetFilenames[name] = True
|
||||
xcomponent = xcomponent.NextSibling()
|
||||
|
||||
self.moduleNames = {}
|
||||
xmodule = xpackage.FirstChildElement('module')
|
||||
while xmodule:
|
||||
xmodule = xmodule.ToElement()
|
||||
moduleName = xmodule.Attribute('name')
|
||||
if moduleName:
|
||||
self.moduleNames[moduleName] = True
|
||||
xmodule = xmodule.NextSibling()
|
||||
|
||||
return True
|
||||
|
||||
def getFileSpec(self, element, pathname, newName):
|
||||
""" Returns an xcomponent or similar element with the file
|
||||
information for the indicated file. """
|
||||
|
||||
xspec = TiXmlElement(element)
|
||||
|
||||
filename = Filename(filename)
|
||||
size = filename.getFileSize()
|
||||
timestamp = filename.getTimestamp()
|
||||
size = pathname.getFileSize()
|
||||
timestamp = pathname.getTimestamp()
|
||||
|
||||
hv = HashVal()
|
||||
hv.hashFile(filename)
|
||||
hv.hashFile(pathname)
|
||||
hash = hv.asHex()
|
||||
|
||||
xspec.SetAttribute('filename', newName)
|
||||
@ -426,6 +461,10 @@ class Packager:
|
||||
self.installDir = None
|
||||
self.persistDir = None
|
||||
|
||||
# A search list of directories and/or URL's to search for
|
||||
# installed packages.
|
||||
self.installSearch = []
|
||||
|
||||
# The platform string.
|
||||
self.platform = PandaSystem.getPlatform()
|
||||
|
||||
@ -500,28 +539,26 @@ class Packager:
|
||||
|
||||
self.currentPackage = None
|
||||
|
||||
# The persist dir is the directory in which the results from
|
||||
# past publishes are stored so we can generate patches against
|
||||
# them. There must be a nonempty directory name here.
|
||||
assert(self.persistDir)
|
||||
# We must have an actual install directory.
|
||||
assert(self.installDir)
|
||||
|
||||
# If the persist dir names an empty or nonexistent directory,
|
||||
# we will be generating a brand new publish with no previous
|
||||
# patches.
|
||||
self.persistDir.makeDir()
|
||||
## # If the persist dir names an empty or nonexistent directory,
|
||||
## # we will be generating a brand new publish with no previous
|
||||
## # patches.
|
||||
## self.persistDir.makeDir()
|
||||
|
||||
# Within the persist dir, we make a temporary holding dir for
|
||||
# generating multifiles.
|
||||
self.mfTempDir = Filename(self.persistDir, Filename('mftemp/'))
|
||||
#self.mfTempDir.makeDir()
|
||||
## # Within the persist dir, we make a temporary holding dir for
|
||||
## # generating multifiles.
|
||||
## self.mfTempDir = Filename(self.persistDir, Filename('mftemp/'))
|
||||
## self.mfTempDir.makeDir()
|
||||
|
||||
# We also need a temporary holding dir for squeezing py files.
|
||||
self.pyzTempDir = Filename(self.persistDir, Filename('pyz/'))
|
||||
#self.pyzTempDir.makeDir()
|
||||
## # We also need a temporary holding dir for squeezing py files.
|
||||
## self.pyzTempDir = Filename(self.persistDir, Filename('pyz/'))
|
||||
## self.pyzTempDir.makeDir()
|
||||
|
||||
# Change to the persist directory so the temp files will be
|
||||
# created there
|
||||
os.chdir(self.persistDir.toOsSpecific())
|
||||
## # Change to the persist directory so the temp files will be
|
||||
## # created there
|
||||
## os.chdir(self.persistDir.toOsSpecific())
|
||||
|
||||
def readPackageDef(self, packageDef):
|
||||
""" Reads the lines in the .pdef file named by packageDef and
|
||||
@ -755,21 +792,140 @@ class Packager:
|
||||
self.packages[package.packageName] = package
|
||||
self.currentPackage = None
|
||||
|
||||
def findPackage(self, packageName, searchUrl = None):
|
||||
def findPackage(self, packageName, version = None, searchUrl = None):
|
||||
""" Searches for the named package from a previous publish
|
||||
operation, either at the indicated URL or along the default
|
||||
operation, either at the indicated URL or along the install
|
||||
search path.
|
||||
|
||||
Returns the Package object, or None if the package cannot be
|
||||
located. """
|
||||
|
||||
# Is it a package we already have resident?
|
||||
package = self.packages.get(packageName, None)
|
||||
package = self.packages.get((packageName, version), None)
|
||||
if package:
|
||||
return package
|
||||
|
||||
# Look on the searchlist.
|
||||
for path in self.installSearch:
|
||||
packageDir = Filename(path, packageName)
|
||||
if packageDir.isDirectory():
|
||||
# Hey, this package appears to exist!
|
||||
package = self.scanPackageDir(packageDir, packageName, version)
|
||||
if package:
|
||||
self.packages[(packageName, version)] = package
|
||||
return package
|
||||
|
||||
return None
|
||||
|
||||
def scanPackageDir(self, packageDir, packageName, packageVersion):
|
||||
""" Scans a directory on disk, looking for _import.xml files
|
||||
that match the indicated packageName and option version. If a
|
||||
suitable xml file is found, reads it and returns the assocated
|
||||
Package definition. """
|
||||
|
||||
bestVersion = None
|
||||
bestPackage = None
|
||||
|
||||
# First, look for a platform-specific file.
|
||||
platformDir = Filename(packageDir, self.platform)
|
||||
prefix = '%s_%s_' % (packageName, self.platform)
|
||||
|
||||
if packageVersion:
|
||||
# Do we have a specific version request?
|
||||
basename = prefix + '%s_import.xml' % (packageVersion)
|
||||
filename = Filename(platformDir, basename)
|
||||
if filename.exists():
|
||||
# Got it!
|
||||
package = self.readPackageImportDescFile(filename)
|
||||
if package:
|
||||
return package
|
||||
else:
|
||||
# Look for a generic version request. Get the highest
|
||||
# version available.
|
||||
files = vfs.scanDirectory(platformDir) or []
|
||||
for file in files:
|
||||
filename = file.getFilename()
|
||||
basename = filename.getBasename()
|
||||
if not basename.startswith(prefix) or \
|
||||
not basename.endswith('_import.xml'):
|
||||
continue
|
||||
package = self.readPackageImportDescFile(filename)
|
||||
if not package:
|
||||
continue
|
||||
|
||||
parts = basename.split('_')
|
||||
if len(parts) == 3:
|
||||
# No version number.
|
||||
if bestVersion is None:
|
||||
bestPackage = package
|
||||
|
||||
elif len(parts) == 4:
|
||||
# Got a version number.
|
||||
version = self.parseVersionForCompare(parts[2])
|
||||
if bestVersion > version:
|
||||
bestVersion = version
|
||||
bestPackage = package
|
||||
|
||||
# Didn't find a suitable platform-specific file, so look for a
|
||||
# platform-nonspecific one.
|
||||
prefix = '%s_' % (packageName)
|
||||
if packageVersion:
|
||||
# Do we have a specific version request?
|
||||
basename = prefix + '%s_import.xml' % (packageVersion)
|
||||
filename = Filename(packageDir, basename)
|
||||
if filename.exists():
|
||||
# Got it!
|
||||
package = self.readPackageImportDescFile(filename)
|
||||
if package:
|
||||
return package
|
||||
else:
|
||||
# Look for a generic version request. Get the highest
|
||||
# version available.
|
||||
files = vfs.scanDirectory(packageDir) or []
|
||||
for file in files:
|
||||
filename = file.getFilename()
|
||||
basename = filename.getBasename()
|
||||
if not basename.startswith(prefix) or \
|
||||
not basename.endswith('_import.xml'):
|
||||
continue
|
||||
package = self.readPackageImportDescFile(filename)
|
||||
if not package:
|
||||
continue
|
||||
|
||||
parts = basename.split('_')
|
||||
if len(parts) == 2:
|
||||
# No version number.
|
||||
if bestVersion is None:
|
||||
bestPackage = package
|
||||
|
||||
elif len(parts) == 3:
|
||||
# Got a version number.
|
||||
version = self.parseVersionForCompare(parts[1])
|
||||
if bestVersion > version:
|
||||
bestVersion = version
|
||||
bestPackage = package
|
||||
|
||||
return bestPackage
|
||||
|
||||
def readPackageImportDescFile(self, filename):
|
||||
""" Reads the named xml file as a Package, and returns it if
|
||||
valid, or None otherwise. """
|
||||
|
||||
package = self.Package('', self)
|
||||
if package.readImportDescFile(filename):
|
||||
return package
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def parseVersionForCompare(self, version):
|
||||
""" Given a formatted version string, breaks it up into a
|
||||
tuple, grouped so that putting the tuples into sorted order
|
||||
will put the order numbers in increasing order. """
|
||||
|
||||
# TODO. For now, we just use a dumb string compare.
|
||||
return (version,)
|
||||
|
||||
def require(self, package):
|
||||
""" Indicates a dependency on the indicated package.
|
||||
|
||||
|
@ -15,7 +15,7 @@ This script is actually a wrapper around Panda's Packager.py.
|
||||
|
||||
Usage:
|
||||
|
||||
ppackage.py [opts] package.pdef command
|
||||
ppackage.py [opts] package.pdef
|
||||
|
||||
Required:
|
||||
|
||||
@ -24,29 +24,15 @@ Required:
|
||||
to be built, in excruciating detail. Use "ppackage.py -H" to
|
||||
describe the syntax of this file.
|
||||
|
||||
command
|
||||
The action to perform. The following commands are supported:
|
||||
|
||||
"build": Builds the package file(s) named in the package.pdef, and
|
||||
places the result in the install_dir. Does not attempt to
|
||||
generate any patches.
|
||||
|
||||
"publish": Builds a package file, as above, and then generates
|
||||
patches against previous builds, and updates install_dir and
|
||||
persist_dir appropriately. Instead of the current directory,
|
||||
the built package file(s) are placed in the install_dir where
|
||||
they may be downloaded.
|
||||
|
||||
Options:
|
||||
|
||||
-i install_dir
|
||||
The full path to a local directory to copy the
|
||||
ready-to-be-published files into. This directory structure is
|
||||
populated by the "publish" command, and it may contain multiple
|
||||
different packages from multiple different invocations of the
|
||||
"publish" command. It is the user's responsibility to copy this
|
||||
directory structure to a web host where it may be downloaded by
|
||||
the client.
|
||||
ready-to-be-published files into. This directory structure may
|
||||
contain multiple different packages from multiple different
|
||||
invocations of this script. It is the user's responsibility to
|
||||
copy this directory structure to a web host where it may be
|
||||
downloaded by the client.
|
||||
|
||||
-d persist_dir
|
||||
The full path to a local directory that retains persistant state
|
||||
@ -69,133 +55,6 @@ Options:
|
||||
Display this help
|
||||
"""
|
||||
|
||||
|
||||
#
|
||||
# package.pdef syntax:
|
||||
#
|
||||
# multifile <mfname> <phase>
|
||||
#
|
||||
# Begins a new multifile. All files named after this line and
|
||||
# until the next multifile line will be a part of this multifile.
|
||||
#
|
||||
# <mfname>
|
||||
# The filename of the multifile, no directory.
|
||||
#
|
||||
# <phase>
|
||||
# The numeric phase in which this multifile should be downloaded.
|
||||
#
|
||||
#
|
||||
# file <extractFlag> <filename> <dirname> <platforms>
|
||||
#
|
||||
# Adds a single file to the current multifile.
|
||||
#
|
||||
# <extractFlag>
|
||||
# One of:
|
||||
# 0 - Leave this file within the multifile; it can be read by
|
||||
# Panda from there.
|
||||
# 1 - Extract this file from the multifile, but do not bother
|
||||
# to hash check it on restart.
|
||||
# 2 - Extract this file, and hash check it every time the
|
||||
# client starts, to ensure it is not changed.
|
||||
#
|
||||
# <filename>
|
||||
# The name of the file to add. This is the full path to the
|
||||
# file on the publishing machine at the time this script is run.
|
||||
#
|
||||
# <dirname>
|
||||
# The directory in which to install the file, on the client.
|
||||
# This should be a relative pathname from the game directory.
|
||||
# The file is written to the multifile with its directory part
|
||||
# taken from this, and its basename taken from the source
|
||||
# filename, above. Also, if the file is extracted, it will be
|
||||
# written into this directory on the client machine.
|
||||
#
|
||||
# The directory name "toplevel" is treated as a special case;
|
||||
# this maps to the game directory itself, and is used for files
|
||||
# in the initial download.
|
||||
#
|
||||
# <platforms>
|
||||
# A comma-delimited list of platforms for which this file should
|
||||
# be included with the distribution. Presently, the only
|
||||
# options are WIN32 and/or OSX.
|
||||
#
|
||||
#
|
||||
# dir <extractFlag> <localDirname> <dirname>
|
||||
#
|
||||
# Adds an entire directory tree to the current multifile. The
|
||||
# named directory is searched recursively and all files found are
|
||||
# added to the current multifile, as if they were listed one a
|
||||
# time with a file command.
|
||||
#
|
||||
# <extractFlag>
|
||||
# (Same as for the file command, above.)
|
||||
#
|
||||
# <localDirname>
|
||||
# The name of the local directory to scan on the publishing
|
||||
# machine.
|
||||
#
|
||||
# <dirname>
|
||||
# The name of the corresponding local directory on the client
|
||||
# machine; similar to <dirname> in the file command, above.
|
||||
#
|
||||
#
|
||||
# module modulename
|
||||
#
|
||||
# Adds the named Python module to the exe or dll archive. All files
|
||||
# named by module, until the next freeze_exe or freeze_dll command (below),
|
||||
# will be compiled and placed into the same archive.
|
||||
#
|
||||
# exclude_module modulename
|
||||
#
|
||||
# Excludes the named Python module from the archive. This module
|
||||
# will not be included in the archive, but an import command may
|
||||
# still find it if a .py file exists on disk.
|
||||
#
|
||||
# forbid_module modulename
|
||||
#
|
||||
# Excludes the named Python module from the archive. This module
|
||||
# will specifically never be imported by the resulting executable,
|
||||
# even if a .py file exists on disk. (However, a module command
|
||||
# appearing in a later phase--e.g. Phase3.pyd--can override an
|
||||
# earlier forbid_module command.)
|
||||
#
|
||||
# dc_module file.dc
|
||||
#
|
||||
# Adds the modules imported by the indicated dc file to the
|
||||
# archive. Normally this is not necessary if the file.dc is
|
||||
# explicitly included in the package.pdef; but this command may be
|
||||
# useful if the file.dc is imported in a later phase.
|
||||
#
|
||||
# freeze_exe <extractFlag> <exeFilename> <mainModule> <dirname>
|
||||
#
|
||||
# <extractFlag>
|
||||
# (Same as for the file command, above.)
|
||||
#
|
||||
# <exeFilename>
|
||||
# The name of the executable file to generate. Do not include
|
||||
# an extension name; on Windows, the default extension is .exe;
|
||||
# on OSX there is no extension.
|
||||
#
|
||||
# <mainModule>
|
||||
# The name of the python module that will be invoked first (like
|
||||
# main()) when the resulting executable is started.
|
||||
#
|
||||
# <dirname>
|
||||
# (Same as for the file command, above.)
|
||||
#
|
||||
# freeze_dll <extractFlag> <dllFilename> <dirname>
|
||||
#
|
||||
# <extractFlag>
|
||||
# (Same as for the file command, above.)
|
||||
#
|
||||
# <dllFilename>
|
||||
# The name of the shared library file to generate. Do not include
|
||||
# an extension name; on Windows, the default extension is .pyd;
|
||||
# on OSX the extension is .so.
|
||||
#
|
||||
# <dirname>
|
||||
# (Same as for the file command, above.)
|
||||
|
||||
import sys
|
||||
import getopt
|
||||
import os
|
||||
@ -235,24 +94,14 @@ for opt, arg in opts:
|
||||
if not args:
|
||||
usage(0)
|
||||
|
||||
if len(args) != 2:
|
||||
if len(args) != 1:
|
||||
usage(1)
|
||||
|
||||
packageDef = Filename.fromOsSpecific(args[0])
|
||||
command = args[1]
|
||||
|
||||
if not packager.installDir:
|
||||
packager.installDir = Filename('install')
|
||||
packager.installSearch = [packager.installDir]
|
||||
|
||||
if command == 'build':
|
||||
#packager.doBuild()
|
||||
if not packager.persistDir:
|
||||
packager.persistDir = Filename('.')
|
||||
packager.setup()
|
||||
packager.readPackageDef(packageDef)
|
||||
elif command == 'publish':
|
||||
packager.setup()
|
||||
packager.doPublish()
|
||||
else:
|
||||
print 'Undefined command: ' + command
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
packager.setup()
|
||||
packager.readPackageDef(packageDef)
|
||||
|
Loading…
x
Reference in New Issue
Block a user