|
@@ -23,9 +23,10 @@ class PatchMaker:
|
|
|
self.printName = None
|
|
self.printName = None
|
|
|
|
|
|
|
|
# The Package object that produces this version, if this
|
|
# The Package object that produces this version, if this
|
|
|
- # is the current form or the base form, respectively.
|
|
|
|
|
|
|
+ # is the current, base, or top form, respectively.
|
|
|
self.packageCurrent = None
|
|
self.packageCurrent = None
|
|
|
self.packageBase = None
|
|
self.packageBase = None
|
|
|
|
|
+ self.packageTop = None
|
|
|
|
|
|
|
|
# A list of patchfiles that can produce this version.
|
|
# A list of patchfiles that can produce this version.
|
|
|
self.fromPatches = []
|
|
self.fromPatches = []
|
|
@@ -41,7 +42,7 @@ class PatchMaker:
|
|
|
if self.tempFile:
|
|
if self.tempFile:
|
|
|
self.tempFile.unlink()
|
|
self.tempFile.unlink()
|
|
|
|
|
|
|
|
- def getPatchChain(self, startPv):
|
|
|
|
|
|
|
+ def getPatchChain(self, startPv, alreadyVisited = []):
|
|
|
""" Returns a list of patches that, when applied in
|
|
""" Returns a list of patches that, when applied in
|
|
|
sequence to the indicated PackageVersion object, will
|
|
sequence to the indicated PackageVersion object, will
|
|
|
produce this PackageVersion object. Returns None if no
|
|
produce this PackageVersion object. Returns None if no
|
|
@@ -51,11 +52,18 @@ class PatchMaker:
|
|
|
# We're already here. A zero-length patch chain is
|
|
# We're already here. A zero-length patch chain is
|
|
|
# therefore the answer.
|
|
# therefore the answer.
|
|
|
return []
|
|
return []
|
|
|
|
|
+ if self in alreadyVisited:
|
|
|
|
|
+ # We've already been here; this is a loop. Avoid
|
|
|
|
|
+ # infinite recursion.
|
|
|
|
|
+ return None
|
|
|
|
|
+
|
|
|
|
|
+ alreadyVisited = alreadyVisited[:]
|
|
|
|
|
+ alreadyVisited.append(self)
|
|
|
|
|
|
|
|
bestPatchChain = None
|
|
bestPatchChain = None
|
|
|
for patchfile in self.fromPatches:
|
|
for patchfile in self.fromPatches:
|
|
|
fromPv = patchfile.fromPv
|
|
fromPv = patchfile.fromPv
|
|
|
- patchChain = fromPv.getPatchChain(startPv)
|
|
|
|
|
|
|
+ patchChain = fromPv.getPatchChain(startPv, alreadyVisited)
|
|
|
if patchChain is not None:
|
|
if patchChain is not None:
|
|
|
# There's a path through this patchfile.
|
|
# There's a path through this patchfile.
|
|
|
patchChain = patchChain + [patchfile]
|
|
patchChain = patchChain + [patchfile]
|
|
@@ -66,19 +74,27 @@ class PatchMaker:
|
|
|
# paths found.
|
|
# paths found.
|
|
|
return bestPatchChain
|
|
return bestPatchChain
|
|
|
|
|
|
|
|
- def getRecreateFilePlan(self):
|
|
|
|
|
|
|
+ def getRecreateFilePlan(self, alreadyVisited = []):
|
|
|
""" Returns the tuple (startFile, startPv, plan),
|
|
""" Returns the tuple (startFile, startPv, plan),
|
|
|
describing how to recreate the archive file for this
|
|
describing how to recreate the archive file for this
|
|
|
version. startFile and startPv is the Filename and
|
|
version. startFile and startPv is the Filename and
|
|
|
packageVersion of the file to start with, and plan is a
|
|
packageVersion of the file to start with, and plan is a
|
|
|
list of tuples (patchfile, pv), listing the patches to
|
|
list of tuples (patchfile, pv), listing the patches to
|
|
|
apply in sequence, and the packageVersion object
|
|
apply in sequence, and the packageVersion object
|
|
|
- associated with each patch. Returns (None, None) if there
|
|
|
|
|
- is no way to recreate this archive file. """
|
|
|
|
|
|
|
+ associated with each patch. Returns (None, None, None) if
|
|
|
|
|
+ there is no way to recreate this archive file. """
|
|
|
|
|
|
|
|
if self.tempFile:
|
|
if self.tempFile:
|
|
|
return (self.tempFile, self, [])
|
|
return (self.tempFile, self, [])
|
|
|
|
|
|
|
|
|
|
+ if self in alreadyVisited:
|
|
|
|
|
+ # We've already been here; this is a loop. Avoid
|
|
|
|
|
+ # infinite recursion.
|
|
|
|
|
+ return (None, None, None)
|
|
|
|
|
+
|
|
|
|
|
+ alreadyVisited = alreadyVisited[:]
|
|
|
|
|
+ alreadyVisited.append(self)
|
|
|
|
|
+
|
|
|
if self.packageCurrent:
|
|
if self.packageCurrent:
|
|
|
# This PackageVersion instance represents the current
|
|
# This PackageVersion instance represents the current
|
|
|
# version of some package.
|
|
# version of some package.
|
|
@@ -97,7 +113,7 @@ class PatchMaker:
|
|
|
bestStartPv = None
|
|
bestStartPv = None
|
|
|
for patchfile in self.fromPatches:
|
|
for patchfile in self.fromPatches:
|
|
|
fromPv = patchfile.fromPv
|
|
fromPv = patchfile.fromPv
|
|
|
- startFile, startPv, plan = fromPv.getRecreateFilePlan()
|
|
|
|
|
|
|
+ startFile, startPv, plan = fromPv.getRecreateFilePlan(alreadyVisited)
|
|
|
if plan is not None:
|
|
if plan is not None:
|
|
|
# There's a path through this patchfile.
|
|
# There's a path through this patchfile.
|
|
|
plan = plan + [(patchfile, self)]
|
|
plan = plan + [(patchfile, self)]
|
|
@@ -279,6 +295,7 @@ class PatchMaker:
|
|
|
self.patchVersion = 1
|
|
self.patchVersion = 1
|
|
|
self.currentPv = None
|
|
self.currentPv = None
|
|
|
self.basePv = None
|
|
self.basePv = None
|
|
|
|
|
+ self.topPv = None
|
|
|
|
|
|
|
|
self.packageName = None
|
|
self.packageName = None
|
|
|
self.platform = None
|
|
self.platform = None
|
|
@@ -303,14 +320,22 @@ class PatchMaker:
|
|
|
|
|
|
|
|
return (self.packageName, self.platform, self.version, self.hostUrl, self.baseFile)
|
|
return (self.packageName, self.platform, self.version, self.hostUrl, self.baseFile)
|
|
|
|
|
|
|
|
|
|
+ def getTopKey(self):
|
|
|
|
|
+ """ Returns the key to locate the "top" or newest version
|
|
|
|
|
+ of this package. """
|
|
|
|
|
+
|
|
|
|
|
+ return (self.packageName, self.platform, self.version, self.hostUrl, self.topFile)
|
|
|
|
|
+
|
|
|
def getGenericKey(self, fileSpec):
|
|
def getGenericKey(self, fileSpec):
|
|
|
""" Returns the key that has the indicated hash. """
|
|
""" Returns the key that has the indicated hash. """
|
|
|
return (self.packageName, self.platform, self.version, self.hostUrl, fileSpec)
|
|
return (self.packageName, self.platform, self.version, self.hostUrl, fileSpec)
|
|
|
|
|
|
|
|
- def readDescFile(self):
|
|
|
|
|
|
|
+ def readDescFile(self, doProcessing = False):
|
|
|
""" Reads the existing package.xml file and stores it in
|
|
""" Reads the existing package.xml file and stores it in
|
|
|
- this class for later rewriting. Returns true on success,
|
|
|
|
|
- false on failure. """
|
|
|
|
|
|
|
+ this class for later rewriting. if doProcessing is true,
|
|
|
|
|
+ it may massage the file and the directory contents in
|
|
|
|
|
+ preparation for building patches. Returns true on
|
|
|
|
|
+ success, false on failure. """
|
|
|
|
|
|
|
|
self.anyChanges = False
|
|
self.anyChanges = False
|
|
|
|
|
|
|
@@ -361,7 +386,6 @@ class PatchMaker:
|
|
|
isNewVersion = False
|
|
isNewVersion = False
|
|
|
else:
|
|
else:
|
|
|
# There's a new version this pass. Update it.
|
|
# There's a new version this pass. Update it.
|
|
|
- self.topFile = copy.copy(self.currentFile)
|
|
|
|
|
self.anyChanges = True
|
|
self.anyChanges = True
|
|
|
|
|
|
|
|
else:
|
|
else:
|
|
@@ -399,15 +423,18 @@ class PatchMaker:
|
|
|
compressedFile.loadXml(xcompressed)
|
|
compressedFile.loadXml(xcompressed)
|
|
|
|
|
|
|
|
oldCompressedFilename = compressedFile.filename
|
|
oldCompressedFilename = compressedFile.filename
|
|
|
- newCompressedFilename = '%s.%s.pz' % (self.currentFile.filename, self.patchVersion)
|
|
|
|
|
- oldCompressedPathname = Filename(self.packageDir, oldCompressedFilename)
|
|
|
|
|
- newCompressedPathname = Filename(self.packageDir, newCompressedFilename)
|
|
|
|
|
- if oldCompressedPathname.renameTo(newCompressedPathname):
|
|
|
|
|
- compressedFile.fromFile(self.packageDir, newCompressedFilename)
|
|
|
|
|
- compressedFile.storeXml(xcompressed)
|
|
|
|
|
-
|
|
|
|
|
- self.compressedFilename = newCompressedFilename
|
|
|
|
|
- self.anyChanges = True
|
|
|
|
|
|
|
+ self.compressedFilename = oldCompressedFilename
|
|
|
|
|
+
|
|
|
|
|
+ if doProcessing:
|
|
|
|
|
+ newCompressedFilename = '%s.%s.pz' % (self.currentFile.filename, self.patchVersion)
|
|
|
|
|
+ oldCompressedPathname = Filename(self.packageDir, oldCompressedFilename)
|
|
|
|
|
+ newCompressedPathname = Filename(self.packageDir, newCompressedFilename)
|
|
|
|
|
+ if oldCompressedPathname.renameTo(newCompressedPathname):
|
|
|
|
|
+ compressedFile.fromFile(self.packageDir, newCompressedFilename)
|
|
|
|
|
+ compressedFile.storeXml(xcompressed)
|
|
|
|
|
+
|
|
|
|
|
+ self.compressedFilename = newCompressedFilename
|
|
|
|
|
+ self.anyChanges = True
|
|
|
|
|
|
|
|
# Get the base_version--the bottom (oldest) of the patch
|
|
# Get the base_version--the bottom (oldest) of the patch
|
|
|
# chain.
|
|
# chain.
|
|
@@ -429,7 +456,7 @@ class PatchMaker:
|
|
|
self.baseFile.filename += '.base'
|
|
self.baseFile.filename += '.base'
|
|
|
|
|
|
|
|
# Also duplicate the (compressed) file itself.
|
|
# Also duplicate the (compressed) file itself.
|
|
|
- if self.compressedFilename:
|
|
|
|
|
|
|
+ if doProcessing and self.compressedFilename:
|
|
|
fromPathname = Filename(self.packageDir, self.compressedFilename)
|
|
fromPathname = Filename(self.packageDir, self.compressedFilename)
|
|
|
toPathname = Filename(self.packageDir, self.baseFile.filename + '.pz')
|
|
toPathname = Filename(self.packageDir, self.baseFile.filename + '.pz')
|
|
|
fromPathname.copyTo(toPathname)
|
|
fromPathname.copyTo(toPathname)
|
|
@@ -460,7 +487,7 @@ class PatchMaker:
|
|
|
# Remove all of the old patch entries from the desc file
|
|
# Remove all of the old patch entries from the desc file
|
|
|
# we read earlier.
|
|
# we read earlier.
|
|
|
xremove = []
|
|
xremove = []
|
|
|
- for value in ['base_version', 'patch']:
|
|
|
|
|
|
|
+ for value in ['base_version', 'top_version', 'patch']:
|
|
|
xpatch = xpackage.FirstChildElement(value)
|
|
xpatch = xpackage.FirstChildElement(value)
|
|
|
while xpatch:
|
|
while xpatch:
|
|
|
xremove.append(xpatch)
|
|
xremove.append(xpatch)
|
|
@@ -478,8 +505,9 @@ class PatchMaker:
|
|
|
self.baseFile.storeXml(xarchive)
|
|
self.baseFile.storeXml(xarchive)
|
|
|
xpackage.InsertEndChild(xarchive)
|
|
xpackage.InsertEndChild(xarchive)
|
|
|
|
|
|
|
|
|
|
+ # The current version is now the top version.
|
|
|
xarchive = TiXmlElement('top_version')
|
|
xarchive = TiXmlElement('top_version')
|
|
|
- self.topFile.storeXml(xarchive)
|
|
|
|
|
|
|
+ self.currentFile.storeXml(xarchive)
|
|
|
xpackage.InsertEndChild(xarchive)
|
|
xpackage.InsertEndChild(xarchive)
|
|
|
|
|
|
|
|
for patchfile in self.patches:
|
|
for patchfile in self.patches:
|
|
@@ -554,7 +582,7 @@ class PatchMaker:
|
|
|
object, or None on failure. """
|
|
object, or None on failure. """
|
|
|
|
|
|
|
|
package = self.Package(Filename(descFilename), self)
|
|
package = self.Package(Filename(descFilename), self)
|
|
|
- if not package.readDescFile():
|
|
|
|
|
|
|
+ if not package.readDescFile(doProcessing = False):
|
|
|
return None
|
|
return None
|
|
|
|
|
|
|
|
self.packages.append(package)
|
|
self.packages.append(package)
|
|
@@ -581,7 +609,7 @@ class PatchMaker:
|
|
|
if filename and not solo:
|
|
if filename and not solo:
|
|
|
filename = Filename(filename)
|
|
filename = Filename(filename)
|
|
|
package = self.Package(filename, self, xpackage)
|
|
package = self.Package(filename, self, xpackage)
|
|
|
- package.readDescFile()
|
|
|
|
|
|
|
+ package.readDescFile(doProcessing = True)
|
|
|
self.packages.append(package)
|
|
self.packages.append(package)
|
|
|
|
|
|
|
|
xpackage = xpackage.NextSiblingElement('package')
|
|
xpackage = xpackage.NextSiblingElement('package')
|
|
@@ -638,6 +666,10 @@ class PatchMaker:
|
|
|
package.basePv = basePv
|
|
package.basePv = basePv
|
|
|
basePv.packageBase = package
|
|
basePv.packageBase = package
|
|
|
basePv.printName = package.baseFile.filename
|
|
basePv.printName = package.baseFile.filename
|
|
|
|
|
+
|
|
|
|
|
+ topPv = self.getPackageVersion(package.getTopKey())
|
|
|
|
|
+ package.topPv = topPv
|
|
|
|
|
+ topPv.packageTop = package
|
|
|
|
|
|
|
|
for patchfile in package.patches:
|
|
for patchfile in package.patches:
|
|
|
self.recordPatchfile(patchfile)
|
|
self.recordPatchfile(patchfile)
|
|
@@ -682,22 +714,15 @@ class PatchMaker:
|
|
|
# No versions.
|
|
# No versions.
|
|
|
return
|
|
return
|
|
|
|
|
|
|
|
- # Starting with the package base, how far forward can we go?
|
|
|
|
|
|
|
+ # What's the current version on the top of the tree?
|
|
|
|
|
+ topPv = package.topPv
|
|
|
currentPv = package.currentPv
|
|
currentPv = package.currentPv
|
|
|
- basePv = package.basePv
|
|
|
|
|
-
|
|
|
|
|
- pv = basePv
|
|
|
|
|
- nextPv = pv.getNext(package)
|
|
|
|
|
- while nextPv:
|
|
|
|
|
- pv = nextPv
|
|
|
|
|
- nextPv = pv.getNext(package)
|
|
|
|
|
|
|
|
|
|
- if pv.packageCurrent != package:
|
|
|
|
|
- # It doesn't reach all the way to the latest version, so
|
|
|
|
|
- # build a new patch.
|
|
|
|
|
|
|
+ if topPv != currentPv:
|
|
|
|
|
+ # They're different, so build a new patch.
|
|
|
filename = Filename(package.currentFile.filename + '.%s.patch' % (package.patchVersion))
|
|
filename = Filename(package.currentFile.filename + '.%s.patch' % (package.patchVersion))
|
|
|
assert filename not in self.patchFilenames
|
|
assert filename not in self.patchFilenames
|
|
|
- if not self.buildPatch(pv, currentPv, package, filename):
|
|
|
|
|
|
|
+ if not self.buildPatch(topPv, currentPv, package, filename):
|
|
|
raise StandardError, "Couldn't build patch."
|
|
raise StandardError, "Couldn't build patch."
|
|
|
|
|
|
|
|
def buildPatch(self, v1, v2, package, patchFilename):
|
|
def buildPatch(self, v1, v2, package, patchFilename):
|