3 # dmd - Generate and apply deltas between repository metadata
5 # Copyright (C) 2007 James Bowes <jbowes@redhat.com>
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 from lxml.etree import parse, tostring, Element
26 def __init__(self, namespace, rootelem):
27 self.ns = "http://linux.duke.edu/metadata/%s" % namespace
28 self.sns = "{%s}" % self.ns
29 self.deltasns = "{http://linux.duke.edu/metadata/delta}"
32 def get_pkg_id(self, pkg):
33 return pkg.findtext(self.sns + "checksum")
35 def make_hash(self, tree):
38 pkgid = self.get_pkg_id(pkg)
43 def make_pkg_elem(self, pkgid, pkg):
44 pkgelem = Element("package")
45 pkgelem.set('name', pkg.findtext(self.sns + 'name'))
46 pkgelem.set('arch', pkg.findtext(self.sns + 'arch'))
47 pkgelem.set('pkgid', pkgid)
48 verelem = pkg.find(self.sns + 'version')
49 verelem.tag = "version"
50 pkgelem.append(verelem)
54 def diff_trees(self, oldtree, newtree):
55 oldpkgs = oldtree.getroot().getchildren()
56 newpkgs = newtree.getroot().getchildren()
58 oldpkgshash = self.make_hash(oldpkgs)
59 newpkgshash = self.make_hash(newpkgs)
61 diff = Element(self.root,
62 nsmap = {None : self.ns,
63 "rpm" : "http://linux.duke.edu/metadata/rpm",
64 "delta" : "http://linux.duke.edu/metadata/delta"})
65 additions = Element("delta:additions")
66 diff.append(additions)
67 removals = Element("delta:removals")
71 for pkgid, pkg in newpkgshash.iteritems():
72 if not oldpkgshash.has_key(pkgid):
75 for pkgid, pkg in oldpkgshash.iteritems():
76 if not newpkgshash.has_key(pkgid):
77 pkgelem = self.make_pkg_elem(pkgid, pkg)
78 removals.append(pkgelem)
80 diff.set("packages", str(len(removals) + len(additions)))
82 print tostring(diff, pretty_print=True)
84 def patch_tree(self, oldtree, deltatree):
85 oldroot = oldtree.getroot()
86 oldpkgs = oldroot.getchildren()
88 oldpkgshash = self.make_hash(oldpkgs)
90 additions = deltatree.find(self.deltasns + 'additions').getchildren()
91 removals = deltatree.find(self.deltasns + 'removals').getchildren()
94 pkgid = self.get_pkg_id(pkg)
95 if oldpkgshash.has_key(pkgid):
96 print >> sys.stderr, "Package %s already exists" % pkgid
101 pkgid = pkg.get('pkgid')
102 if not oldpkgshash.has_key(pkgid):
103 print >> sys.stderr, "Package %s does not exist" % pkgid
105 oldroot.remove(oldpkgshash[pkgid])
107 oldcount = int(oldroot.get('packages'))
108 newcount = oldcount + len(additions) - len(removals)
109 oldroot.set('packages', str(newcount))
110 print tostring(oldtree, pretty_print=True)
113 class OtherMdType(MdType):
114 def get_pkg_id(self, pkg):
115 return pkg.get('pkgid')
117 def make_pkg_elem(self, pkgid, pkg):
118 pkgelem = Element("package")
119 pkgelem.set('name', pkg.get('name'))
120 pkgelem.set('arch', pkg.get('arch'))
121 pkgelem.set('pkgid', pkgid)
122 verelem = pkg.find(self.sns + 'version')
123 verelem.tag = "version"
129 'primary' : MdType('common', 'metadata'),
130 'filelists' : OtherMdType('filelists', 'filelists'),
131 'other' : OtherMdType('other', 'other'),
136 print "usage: %s [diff|patch] MDTYPE FILE1 FILE2" % progname
142 if args[1] not in ('diff', 'patch'):
144 if args[2] not in ('primary', 'filelists', 'other'):
147 oldtree = parse(args[3])
148 newtree = parse(args[4])
150 if args[1] == 'diff':
151 mdtypeinfo[args[2]].diff_trees(oldtree, newtree)
153 mdtypeinfo[args[2]].patch_tree(oldtree, newtree)
155 if __name__ == "__main__":