From a347263871ef78928b33a0fa7394e5a0a9ccc736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eray=20=C3=96zkural?= Date: Thu, 28 Jul 2005 02:28:43 +0000 Subject: [PATCH] * alpha-light testi multi-install olayini test ediyor, packages symlinki ister, deneyin isterseniz cok zor degil. once ln -s path/to/pisi/repo packages tests/alpha-light.sh le su anki debug outputunu gorursunuz * pgraph pisi relation'larini tutan bir graph'tir * cat-db'yi update et * specfile'a bos constructor'lar ekle, eksik attribute'lari hallet verify'lari elden gecir vs. * build: build nosunu 0 yap, FIXME de * metadata: - version,release,build yaz - packageURI diye bir tag ekle metadata.package'a - version ve release'i hem source'a hem package'a yaz, belki de iyi bir fikir degil uhuhuhuh * multi-install algoritmasinin bir kismini yaz * graph: - graphviz outputu sagla - edge data'larini implement et * daha varsa bilemeyecegim artik --- pisi/build.py | 4 + pisi/graph.py | 46 ++++++++++-- pisi/index.py | 7 +- pisi/metadata.py | 32 ++++++-- pisi/pgraph.py | 42 +++++++++++ pisi/specfile.py | 172 +++++++++++++++++++++++++------------------ pisi/toplevel.py | 69 ++++++++++++++++- pisi/util.py | 2 +- tests/alpha-light.sh | 9 ++- tests/alpha.sh | 13 ++-- tools/cat-db.py | 4 + 11 files changed, 299 insertions(+), 101 deletions(-) create mode 100644 pisi/pgraph.py diff --git a/pisi/build.py b/pisi/build.py index 327c7978..9ab22541 100644 --- a/pisi/build.py +++ b/pisi/build.py @@ -196,6 +196,10 @@ class PisiBuild: some additional information.""" metadata = MetaData() metadata.fromSpec(self.spec.source, package) + + # FIXME: MEREEEEEN :) + metadata.package.build = 0 # BOGUS. WRONG. + metadata.package.distribution = config.values.general.distribution metadata.package.distributionRelease = config.values.general.distribution_release metadata.package.architecture = "Any" diff --git a/pisi/graph.py b/pisi/graph.py index eb8552c5..2f9c9af5 100644 --- a/pisi/graph.py +++ b/pisi/graph.py @@ -1,7 +1,10 @@ # the most simple minded digraph class ever # for python 2.3 compatibility -from sets import Set as set +import sys +ver = sys.version_info +if ver[0] == 2 and ver[1] == 4: + from sets import Set as set class CycleError: pass @@ -12,7 +15,7 @@ class digraph(object): self.__v = set() self.__adj = {} self.__vdata = {} - #self.__edata = {} + self.__edata = {} def vertices(self): "return set of vertex descriptors" @@ -38,15 +41,21 @@ class digraph(object): self.__adj[u] = set() if data: self.__vdata[u] = data + self.__edata[u] = {} - def add_edge(self, u, v, edata = None): + def add_edge(self, u, v, edata = None, udata = None, vdata = None): "add edge u -> v" if not u in self.__v: - self.add_vertex(u) + self.add_vertex(u, udata) if not v in self.__v: - self.add_vertex(v) + self.add_vertex(v, vdata) self.__adj[u].add(v) - #if edata != None: + if edata != None: + self.__edata[u][v] = edata + + def add_biedge(self, u, v, edata = None): + self.add_edge(u, v, edata) + self.add_edge(v, u, edata) def set_vertex_data(self, u, data): self.__vdata[u] = data @@ -54,6 +63,9 @@ class digraph(object): def vertex_data(self, u): return self.__vdata[u] + def edge_data(self, u, v): + return self.__edata[u][v] + def has_vertex(self, u): return u in self.__v @@ -105,5 +117,25 @@ class digraph(object): self.dfs(lambda u: l.append(u)) l.reverse() return l - + + def write_graphviz(self, f): + f.write('digraph G {\n') + for u in self.vertices(): + f.write(u) + self.write_graphviz_vlabel(f, u) + f.write(';\n') + f.write('\n') + for u in self.vertices(): + for v in self.adj(u): + f.write(u + ' -- ' + v) + self.write_graphviz_elabel(f, u, v) + f.write(';\n') + f.write('\n') + f.write('}\n') + + def write_graphviz_vlabel(self, f, u): + pass + + def write_graphviz_elabel(self, f, u, v): + pass diff --git a/pisi/index.py b/pisi/index.py index 096f1f5e..bbaf46c9 100644 --- a/pisi/index.py +++ b/pisi/index.py @@ -50,11 +50,12 @@ class Index(XmlFile): self.writexml(filename) self.unlink() - def index(self, repo_dir): + def index(self, repo_uri): + self.repo_dir = repo_uri import os from os.path import join, getsize - for root, dirs, files in os.walk(repo_dir): + for root, dirs, files in os.walk(repo_uri): for fn in files: if fn.endswith('.pisi'): ui.info('Adding ' + fn + ' to package index\n') @@ -73,6 +74,8 @@ class Index(XmlFile): md = metadata.MetaData() md.read(config.install_dir() + '/metadata.xml') + # TODO: in the future we'll do all of this with purl/pfile/&helpers + md.package.packageURI = path # check package semantics if md.verify(): self.packages.append(md.package) diff --git a/pisi/metadata.py b/pisi/metadata.py index a9fe8ae9..4c7aeb54 100644 --- a/pisi/metadata.py +++ b/pisi/metadata.py @@ -17,6 +17,8 @@ class SourceInfo: self.name = getNodeText(node, "Name") self.homepage = getNodeText(node, "HomePage") self.packager = specfile.PackagerInfo(getNode(node, "Packager")) + else: + self.homepage = None def elt(self, xml): node = xml.newNode("Source") @@ -33,24 +35,40 @@ class SourceInfo: class PackageInfo(specfile.PackageInfo): - def __init__(self, node=None): + def __init__(self, node = None): if node: specfile.PackageInfo.__init__(self, node) + self.version = getNodeText(node, "Version") + self.release = getNodeText(node, "Release") + self.build = int(getNodeText(node, "Build")) self.distribution = getNodeText(node, "Distribution") self.distributionRelease = getNodeText(node, "DistributionRelease") self.architecture = getNodeText(node, "Architecture") self.installedSize = int(getNodeText(node, "InstalledSize")) + self.packageURI = getNodeText(node, "PackageURI") + else: + self.packageURI = None def elt(self, xml): node = specfile.PackageInfo.elt(self, xml) + xml.addTextNodeUnder(node, "Version", self.version) + xml.addTextNodeUnder(node, "Release", self.release) + xml.addTextNodeUnder(node, "Build", str(self.build)) xml.addTextNodeUnder(node, "Distribution", self.distribution) xml.addTextNodeUnder(node, "DistributionRelease", self.distributionRelease) xml.addTextNodeUnder(node, "Architecture", self.architecture) xml.addTextNodeUnder(node, "InstalledSize", str(self.installedSize)) + if self.packageURI: + xml.addTextNodeUnder(node, "PackageURI", str(self.packageURI)) return node def verify(self): - return specfile.PackageInfo.verify(self) + ret = specfile.PackageInfo.verify(self) and self.build!=None + ret = ret and self.distribution!=None + ret = ret and self.distributionRelease!=None + ret = ret and self.architecture!=None and self.installedSize!=None + ret = ret and self.packageURI!=None + return ret def __str__(self): s = specfile.PackageInfo.__str__(self) @@ -82,16 +100,18 @@ class MetaData(XmlFile): self.package.providesComar = pkg.providesComar self.package.requiresComar = pkg.requiresComar self.package.additionalFiles = pkg.additionalFiles - + + # FIXME: right way to do it? + self.source.version = src.version + self.source.release = src.release + self.package.version = src.version + self.package.release = src.release def read(self, filename): self.readxml(filename) self.source = SourceInfo(self.getNode("Source")) self.package = PackageInfo(self.getNode("Package")) - self.package.version = self.package.history[0].version - self.package.release = self.package.history[0].release - def write(self, filename): self.newDOM() self.addChild(self.source.elt(self)) diff --git a/pisi/pgraph.py b/pisi/pgraph.py new file mode 100644 index 00000000..958fefae --- /dev/null +++ b/pisi/pgraph.py @@ -0,0 +1,42 @@ +# PISI package relation graph that represents the state of packagedb + +from packagedb import packagedb +from sourcedb import sourcedb +from graph import * + +# Cache the results from packagedb queries in a graph + +class PGraph(digraph): + + def __init__(self): + super(PGraph, self).__init__() + + def add_dep(self, pkg, depinfo): + pkg1data = None + if not pkg in self.vertices(): + pkg1 = packagedb.get_package(pkg) + pkg2data = None + if not pkg in self.vertices(): + pkg2 = packagedb.get_package(depinfo.package) + + self.add_edge(pkg, depinfo.package, ('d', depinfo), + (pkg1.version, pkg1.release), + (pkg2.version, pkg2.release) ) + + def add_conflict(self, pkg, conflinfo): + pkg1data = None + if not pkg in self.vertices(): + pkg1 = packagedb.get_package(pkg) + pkg2data = None + if not pkg in self.vertices(): + pkg2 = packagedb.get_package(depinfo.package) + + self.add_biedge(pkg, conflinfo.package, ('c', conflinfo) + (pkg1.version, pkg1.release), + (pkg2.version, pkg2.release) ) + + + def write_graphviz_vlabel(self, f, u): + (v, r) = self.vertex_data(u) + f.write('[ ' + str(u) + '(' + str(v) + ',' + str(r) + ') ]') + diff --git a/pisi/specfile.py b/pisi/specfile.py index 13fb99de..41db431f 100644 --- a/pisi/specfile.py +++ b/pisi/specfile.py @@ -16,9 +16,10 @@ from xmlfile import XmlFile from ui import ui class PackagerInfo: - def __init__(self, node): - self.name = getNodeText(getNode(node, "Name")) - self.email = getNodeText(getNode(node, "Email")) + def __init__(self, node = None): + if node: + self.name = getNodeText(getNode(node, "Name")) + self.email = getNodeText(getNode(node, "Email")) def elt(self, xml): node = xml.newNode("Packager") @@ -32,11 +33,14 @@ class PackagerInfo: return True class AdditionalFileInfo: - def __init__(self, node): - self.filename = getNodeText(node) - self.target = getNodeAttribute(node, "target") - self.permission = getNodeAttribute(node, "permission") - self.owner = getNodeAttribute(node, "owner") + def __init__(self, node = None): + if node: + self.filename = getNodeText(node) + self.target = getNodeAttribute(node, "target") + self.permission = getNodeAttribute(node, "permission") + self.owner = getNodeAttribute(node, "owner") + else: + self.permission = self.owner = None def elt(self, xml): node = xml.newNode("AdditionalFile") @@ -53,18 +57,25 @@ class AdditionalFileInfo: return True class PatchInfo: - def __init__(self, node): - self.filename = getNodeText(node) - self.compressionType = getNodeAttribute(node, "compressionType") - self.level = getNodeAttribute(node, "level") + def __init__(self, node = None): + if node: + self.filename = getNodeText(node) + self.compressionType = getNodeAttribute(node, "compressionType") + self.level = getNodeAttribute(node, "level") + else: + self.compressionType = None if not self.level: self.level = 0 + else: + self.level = int(self.level) def elt(self, xml): node = xml.newNode("Patch") xml.addText(node, self.filename) if self.compressionType: node.setAttribute("compressionType", self.compressionType) + if self.level: + node.setAttribute("level", self.level) return node def verify(self): @@ -72,10 +83,16 @@ class PatchInfo: return True class DepInfo: - def __init__(self, node): - self.package = getNodeText(node).strip() - self.versionFrom = getNodeAttribute(node, "versionFrom") - self.versionTo = getNodeAttribute(node, "versionTo") + def __init__(self, node = None): + if node: + self.package = getNodeText(node).strip() + self.versionFrom = getNodeAttribute(node, "versionFrom") + self.versionTo = getNodeAttribute(node, "versionTo") + self.releaseFrom = getNodeAttribute(node, "releaseFrom") + self.releaseTo = getNodeAttribute(node, "releaseTo") + else: + self.versionFrom = self.versionTo = None + self.releaseFrom = self.releaseFrom = None def elt(self, xml): node = xml.newNode("Dependency") @@ -84,6 +101,10 @@ class DepInfo: node.setAttribute("versionFrom", self.versionFrom) if self.versionTo: node.setAttribute("versionTo", self.versionTo) + if self.releaseFrom: + node.setAttribute("releaseFrom", self.versionFrom) + if self.releaseTo: + node.setAttribute("releaseTo", self.versionTo) return node def verify(self): @@ -91,11 +112,14 @@ class DepInfo: return True class UpdateInfo: - def __init__(self, node): - self.date = getNodeText(getNode(node, "Date")) - self.version = getNodeText(getNode(node, "Version")) - self.release = getNodeText(getNode(node, "Release")) - self.type = getNodeText(getNode(node, "Type")) + def __init__(self, node = None): + if node: + self.date = getNodeText(getNode(node, "Date")) + self.version = getNodeText(getNode(node, "Version")) + self.release = getNodeText(getNode(node, "Release")) + self.type = getNodeText(getNode(node, "Type")) + else: + self.type = None def elt(self, xml): node = xml.newNode("Update") @@ -113,9 +137,10 @@ class UpdateInfo: return True class PathInfo: - def __init__(self, node): - self.pathname = getNodeText(node) - self.fileType = getNodeAttribute(node, "fileType") + def __init__(self, node = None): + if node: + self.pathname = getNodeText(node) + self.fileType = getNodeAttribute(node, "fileType") if not self.fileType: self.fileType = "other" @@ -130,9 +155,10 @@ class PathInfo: return True class ComarProvide: - def __init__(self, node): - self.om = getNodeText(node) - self.script=getNodeAttribute(node, "script") + def __init__(self, node = None): + if node: + self.om = getNodeText(node) + self.script=getNodeAttribute(node, "script") def elt(self, xml): node = xml.newNode("COMAR") @@ -148,27 +174,30 @@ class ComarProvide: class SourceInfo: """A structure to hold source information. Source information is located under tag in PSPEC file.""" - def __init__(self, node): - self.name = getNodeText(node, "Name") - self.homepage = getNodeText(node, "HomePage") - self.packager = PackagerInfo(getNode(node, "Packager")) - self.summary = getNodeText(node, "Summary") - self.description = getNodeText(node, "Description") - self.license = map(getNodeText, getAllNodes(node, "License")) - self.isa = map(getNodeText, getAllNodes(node, "IsA")) - self.partof = getNodeText(node, "PartOf") - archiveNode = getNode(node, "Archive") - self.archiveUri = getNodeText(archiveNode).strip() - self.archiveName = basename(self.archiveUri) - self.archiveType = getNodeAttribute(archiveNode, "type") - self.archiveSHA1 = getNodeAttribute(archiveNode, "sha1sum") - patchElts = getAllNodes(node, "Patches/Patch") - self.patches = [PatchInfo(p) for p in patchElts] - buildDepElts = getAllNodes(node, "BuildDependencies/Dependency") - self.buildDeps = [DepInfo(d) for d in buildDepElts] - historyElts = getAllNodes(node, "History/Update") - self.history = [UpdateInfo(x) for x in historyElts] - + def __init__(self, node = None): + if node: + self.name = getNodeText(node, "Name") + self.homepage = getNodeText(node, "HomePage") + self.packager = PackagerInfo(getNode(node, "Packager")) + self.summary = getNodeText(node, "Summary") + self.description = getNodeText(node, "Description") + self.license = map(getNodeText, getAllNodes(node, "License")) + self.isa = map(getNodeText, getAllNodes(node, "IsA")) + self.partof = getNodeText(node, "PartOf") + archiveNode = getNode(node, "Archive") + self.archiveUri = getNodeText(archiveNode).strip() + self.archiveName = basename(self.archiveUri) + self.archiveType = getNodeAttribute(archiveNode, "type") + self.archiveSHA1 = getNodeAttribute(archiveNode, + "sha1sum") + patchElts = getAllNodes(node, "Patches/Patch") + self.patches = [PatchInfo(p) for p in patchElts] + buildDepElts = getAllNodes(node, + "BuildDependencies/Dependency") + self.buildDeps = [DepInfo(d) for d in buildDepElts] + historyElts = getAllNodes(node, "History/Update") + self.history = [UpdateInfo(x) for x in historyElts] + def elt(self, xml): node = xml.newNode("Source") xml.addTextNodeUnder(node, "Name", self.name) @@ -198,9 +227,8 @@ class SourceInfo: if not self.description: return False if not self.packager: return False if not self.license: return False - if not self.archiveUri or \ - not self.archiveType or \ - not self.archiveSHA1: return False + if (not self.archiveUri) or (not self.archiveType): return False + if not self.archiveSHA1: return False if len(self.history) <= 0: return False if not self.packager.verify(): return False @@ -217,26 +245,27 @@ class PackageInfo(object): """A structure to hold package information. Package information is located under tag in PSPEC file. Opposite to Source each PSPEC file can have more than one Package tag.""" - def __init__(self, node): - self.name = getNodeText(node, "Name") - self.summary = getNodeText(node, "Summary") - self.description = getNodeText(node, "Description") - self.isa = map(getNodeText, getAllNodes(node, "IsA")) - self.partof = getNodeText(node, "PartOf") - self.license = map(getNodeText, getAllNodes(node, "License")) - rtDepElts = getAllNodes(node, "RuntimeDependencies/Dependency") - self.runtimeDeps = [DepInfo(x) for x in rtDepElts] - self.paths = [PathInfo(x) for x in getAllNodes(node, "Files/Path")] - historyElts = getAllNodes(node, "History/Update") - self.history = [UpdateInfo(x) for x in historyElts] - conflElts = getAllNodes(node, "Conflicts/Package") - self.conflicts = map(getNodeText, conflElts) - provComarElts = getAllNodes(node, "Provides/COMAR") - self.providesComar = [ComarProvide(x) for x in provComarElts] - reqComarElts = getAllNodes(node, "Requires/COMAR") - self.requiresComar = map(getNodeText, reqComarElts) - aFilesElts = getAllNodes(node, "AdditionalFiles/AdditionalFile") - self.additionalFiles = [AdditionalFileInfo(f) for f in aFilesElts] + def __init__(self, node = None): + if node: + self.name = getNodeText(node, "Name") + self.summary = getNodeText(node, "Summary") + self.description = getNodeText(node, "Description") + self.isa = map(getNodeText, getAllNodes(node, "IsA")) + self.partof = getNodeText(node, "PartOf") + self.license = map(getNodeText, getAllNodes(node, "License")) + rtDepElts = getAllNodes(node, "RuntimeDependencies/Dependency") + self.runtimeDeps = [DepInfo(x) for x in rtDepElts] + self.paths = [PathInfo(x) for x in getAllNodes(node, "Files/Path")] + historyElts = getAllNodes(node, "History/Update") + self.history = [UpdateInfo(x) for x in historyElts] + conflElts = getAllNodes(node, "Conflicts/Package") + self.conflicts = map(getNodeText, conflElts) + provComarElts = getAllNodes(node, "Provides/COMAR") + self.providesComar = [ComarProvide(x) for x in provComarElts] + reqComarElts = getAllNodes(node, "Requires/COMAR") + self.requiresComar = map(getNodeText, reqComarElts) + aFilesElts = getAllNodes(node, "AdditionalFiles/AdditionalFile") + self.additionalFiles = [AdditionalFileInfo(f) for f in aFilesElts] def elt(self, xml): node = xml.newNode("Package") @@ -283,6 +312,7 @@ class PackageInfo(object): def __str__(self): s = 'Name: ' + self.name s += '\nSummary: ' + self.summary + s += '\nDescription: ' + self.description return s class SpecFile(XmlFile): diff --git a/pisi/toplevel.py b/pisi/toplevel.py index 85a7ccb4..b5e0e502 100644 --- a/pisi/toplevel.py +++ b/pisi/toplevel.py @@ -2,20 +2,81 @@ # a facade to the entire PISI system import os +import sys from config import config from constants import const from ui import ui from purl import PUrl import util - +import dependency +import pgraph import operations +from packagedb import packagedb def install(packages): - """install a list of packages (either files, urls, or names)""" - #TODO: this for loop is just a placeholder + """install a list of packages (either files/urls, or names)""" + # determine if this is a list of files/urls or names + if packages[0].endswith(const.package_prefix): # they all have to! + install_pkg_files(packages) + else: + install_pkg_names(packages) + +def install_pkg_files(packages): + """install a number of pisi package files""" + + # read the package information into memory first + # regardless of which distribution they come from + d_t = {} for x in packages: - operations.install_single(x) + package = Package(x) + package.read() + d_t[package.name] = package.metadata + + # for this case, we have to determine the dependencies + # that aren't already satisfied and try to install them + # from the repository. + dep_unsatis = [] + for name in d_t.keys(): + pkg = d_t[name] + deps = pkg.runtimeDeps + if not dependency.satisfiesDep(pkg, deps): + dep_unsatis.append(deps) + + # now determine if these unsatisfied dependencies could + # be satisfied by installing packages from the repo + + # if so, then invoke install_pkg_names + install_pkg_names([x.package for x in dep_unsatis]) + +def install_pkg_names(A): + """This is the real thing. It installs packages from + the repository, trying to perform a minimum number of + installs""" + + # try to construct a pisi graph of packages to + # install / reinstall + + G_f = pgraph.PGraph() # construct G_f + + # find the "install closure" graph of G_f by package + # set A using packagedb + B = A + state = {} + while len(B) > 0: + Bp = set() + for x in B: + pkg = packagedb.get_package(x) + #print pkg + for dep in pkg.runtimeDeps: + # we don't deal with satisfied dependencies + if not dependency.satisfiesDep(pkg, dep): + if not dep.package in G_f.vertices(): + Bp.add(str(dep.package)) + G_f.add_dep(x, dep) + B = Bp + G_f.write_graphviz(sys.stdout) + def remove(packages): #TODO: this for loop is just a placeholder diff --git a/pisi/util.py b/pisi/util.py index 35b7ee14..e21a2df0 100644 --- a/pisi/util.py +++ b/pisi/util.py @@ -166,7 +166,7 @@ def dir_size(dir): yield sum([getsize(join(root, name)) for name in files if not islink(join(root,name))]) return sum( sizes() ) -def copy_file(src, dest): +def copy_file(src,dest): """copy source file to destination file""" check_file(src) check_dir(os.path.dirname(dest)) diff --git a/tests/alpha-light.sh b/tests/alpha-light.sh index 0a9fe510..54723cc9 100755 --- a/tests/alpha-light.sh +++ b/tests/alpha-light.sh @@ -2,9 +2,10 @@ pwd PATH=$PATH:. -pisi-cli build packages/s/star*/pspec.xml +set -x +pisi-cli build packages/z/zip/pspec.xml packages/u/unzip/pspec.xml pisi-cli index . pisi-cli updatedb pisi-index.xml -pisi-cli install start*pisi -echo database contents -find tmp -iname '*.bdb' | xargs ./cat-db.py +# database contents +find tmp -iname '*.bdb' | xargs tools/cat-db.py +pisi-cli install zip diff --git a/tests/alpha.sh b/tests/alpha.sh index 52c7f1d9..bab43b63 100755 --- a/tests/alpha.sh +++ b/tests/alpha.sh @@ -1,10 +1,11 @@ #!/bin/sh pwd -rm -rf tmp -./pisi-build samples/*/pspec.xml -./pisi-index . -./pisi-updatedb pisi-index.xml -./pisi-install start*pisi -./pisi-install popt*pisi +#rm -rf tmp +pisi-cli build packages/*/*/pspec.xml + +pisi-cli -index . +pisi-cli -updatedb pisi-index.xml +pisi-cli -install start*pisi +pisi-cli -install popt*pisi find tmp -iname '*.bdb' | xargs ./cat-db.py diff --git a/tools/cat-db.py b/tools/cat-db.py index ac4925b7..47ca4d99 100755 --- a/tools/cat-db.py +++ b/tools/cat-db.py @@ -5,6 +5,10 @@ import sys import bsddb.dbshelve as shelve +sys.path.append('.') + +import pisi + d = shelve.open( sys.argv[1] ) for (k, data) in d.iteritems():