new merge tool

This commit is contained in:
David Rose 2009-10-19 16:14:07 +00:00
parent 8e7ba3333e
commit d7f56e852d
2 changed files with 254 additions and 0 deletions

View File

@ -0,0 +1,170 @@
from direct.p3d.FileSpec import FileSpec
from pandac.PandaModules import *
import copy
import shutil
class PackageMergerError(StandardError):
pass
class PackageMerger:
""" This class will combine two or more separately-build stage
directories, the output of Packager.py or the ppackage tool, into
a single output directory. It assumes that the clocks on all
hosts are in sync, so that the file across all builds with the
most recent timestamp (indicated in the contents.xml file) is
always the most current version of the file. """
class PackageEntry:
""" This corresponds to a <package> entry in the contents.xml
file. """
def __init__(self, xpackage, sourceDir):
self.sourceDir = sourceDir
self.loadXml(xpackage)
def getKey(self):
""" Returns a tuple used for sorting the PackageEntry
objects uniquely per package. """
return (self.packageName, self.platform, self.version)
def isNewer(self, other):
return self.descFile.timestamp > other.descFile.timestamp
def loadXml(self, xpackage):
self.packageName = xpackage.Attribute('name')
self.platform = xpackage.Attribute('platform')
self.version = xpackage.Attribute('version')
solo = xpackage.Attribute('solo')
self.solo = int(solo or '0')
self.descFile = FileSpec()
self.descFile.loadXml(xpackage)
self.importDescFile = None
ximport = xpackage.FirstChildElement('import')
if ximport:
self.importDescFile = FileSpec()
self.importDescFile.loadXml(ximport)
def makeXml(self):
""" Returns a new TiXmlElement. """
xpackage = TiXmlElement('package')
xpackage.SetAttribute('name', self.packageName)
if self.platform:
xpackage.SetAttribute('platform', self.platform)
if self.version:
xpackage.SetAttribute('version', self.version)
if self.solo:
xpackage.SetAttribute('solo', '1')
self.descFile.storeXml(xpackage)
if self.importDescFile:
ximport = TiXmlElement('import')
self.importDescFile.storeXml(ximport)
xpackage.InsertEndChild(ximport)
return xpackage
# PackageMerger constructor
def __init__(self, installDir):
self.installDir = installDir
self.xhost = None
self.contents = {}
# We allow the first one to fail quietly.
self.__readContentsFile(self.installDir)
def __readContentsFile(self, sourceDir):
""" Reads the contents.xml file from the indicated sourceDir,
and updates the internal set of packages appropriately. """
contentsFilename = Filename(sourceDir, 'contents.xml')
doc = TiXmlDocument(contentsFilename.toOsSpecific())
if not doc.LoadFile():
# Couldn't read file.
return False
xcontents = doc.FirstChildElement('contents')
if xcontents:
xhost = xcontents.FirstChildElement('host')
if xhost:
self.xhost = xhost.Clone()
xpackage = xcontents.FirstChildElement('package')
while xpackage:
pe = self.PackageEntry(xpackage, sourceDir)
other = self.contents.get(pe.getKey(), None)
if not other or pe.isNewer(other):
# Store this package in the resulting output.
self.contents[pe.getKey()] = pe
xpackage = xpackage.NextSiblingElement('package')
self.contentsDoc = doc
return True
def __writeContentsFile(self):
""" Writes the contents.xml file at the end of processing. """
filename = Filename(self.installDir, 'contents.xml')
doc = TiXmlDocument(filename.toOsSpecific())
decl = TiXmlDeclaration("1.0", "utf-8", "")
doc.InsertEndChild(decl)
xcontents = TiXmlElement('contents')
if self.xhost:
xcontents.InsertEndChild(self.xhost)
contents = self.contents.items()
contents.sort()
for key, pe in contents:
xpackage = pe.makeXml()
xcontents.InsertEndChild(xpackage)
doc.InsertEndChild(xcontents)
doc.SaveFile()
def __copySubdirectory(self, pe):
""" Copies the subdirectory referenced in the indicated
PackageEntry object into the installDir, replacing the
contents of any similarly-named subdirectory already
there. """
dirname = Filename(pe.descFile.filename).getDirname()
print "copying %s" % (dirname)
sourceDirname = Filename(pe.sourceDir, dirname)
targetDirname = Filename(self.installDir, dirname)
if targetDirname.exists():
# The target directory already exists; we have to clean it
# out first.
shutil.rmtree(targetDirname.toOsSpecific())
shutil.copytree(sourceDirname.toOsSpecific(), targetDirname.toOsSpecific())
def merge(self, sourceDir):
""" Adds the contents of the indicated source directory into
the current pool. """
if not self.__readContentsFile(sourceDir):
message = "Couldn't read %s" % (sourceDir)
raise PackageMergerError, message
def close(self):
""" Finalizes the results of all of the previous calls to
merge(), writes the new contents.xml file, and copies in all
of the new contents. """
dirname = Filename(self.installDir, '')
dirname.makeDir()
for pe in self.contents.values():
if pe.sourceDir != self.installDir:
# Here's a new subdirectory we have to copy in.
self.__copySubdirectory(pe)
self.__writeContentsFile()

84
direct/src/p3d/pmerge.py Executable file
View File

@ -0,0 +1,84 @@
#! /usr/bin/env python
usageText = """
This script can be used to merge together the contents of two or more
separately-built stage directories, built independently via ppackage,
or via Packager.py.
This script is actually a wrapper around Panda's PackageMerger.py.
Usage:
%(prog)s [opts] [inputdir1 .. inputdirN]
Parameters:
inputdir1 .. inputdirN
Specify the full path to the input directories you wish to merge.
These are the directories specified by -i on the previous
invocations of ppackage. The order is mostly unimportant.
Options:
-i install_dir
The full path to the final install directory. This may also
contain some pre-existing contents; if so, it is merged with all
of the input directories as well.
-h
Display this help
"""
import sys
import getopt
import os
from direct.p3d import PackageMerger
from pandac.PandaModules import *
def usage(code, msg = ''):
print >> sys.stderr, usageText % {'prog' : os.path.split(sys.argv[0])[1]}
print >> sys.stderr, msg
sys.exit(code)
try:
opts, args = getopt.getopt(sys.argv[1:], 'i:h')
except getopt.error, msg:
usage(1, msg)
installDir = None
for opt, arg in opts:
if opt == '-i':
installDir = Filename.fromOsSpecific(arg)
elif opt == '-h':
usage(0)
else:
print 'illegal option: ' + flag
sys.exit(1)
inputDirs = []
for arg in args:
inputDirs.append(Filename.fromOsSpecific(arg))
if not inputDirs:
print "no input directories specified."
sys.exit(1)
try:
pm = PackageMerger.PackageMerger(installDir)
for dir in inputDirs:
pm.merge(dir)
pm.close()
except PackageMerger.PackageMergerError:
# Just print the error message and exit gracefully.
inst = sys.exc_info()[1]
print inst.args[0]
sys.exit(1)
# An explicit call to exit() is required to exit the program, when
# this module is packaged in a p3d file.
sys.exit(0)