mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-01 09:23:03 -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