mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-01 01:07:51 -04:00
new merge tool
This commit is contained in:
parent
8e7ba3333e
commit
d7f56e852d
170
direct/src/p3d/PackageMerger.py
Normal file
170
direct/src/p3d/PackageMerger.py
Normal 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
84
direct/src/p3d/pmerge.py
Executable 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)
|
Loading…
x
Reference in New Issue
Block a user