# Generate file list which is then fed to apt-ftparchive to generate Packages and Sources files
# Copyright (C) 2000, 2001 James Troup <james@nocrew.org>
-# $Id: jenna,v 1.6 2001-03-02 02:24:33 troup Exp $
+# $Id: jenna,v 1.15 2001-11-18 19:57:58 rmurray Exp $
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-#######################################################################################
+################################################################################
# BTAF: "GOD *DAMMIT*!! What the FUCK happened to my free will??"
#
# -- http://www.angryflower.com/timelo.gif
-#######################################################################################
+################################################################################
import pg, string, os, sys
import apt_pkg
-import db_access, utils, claire
+import db_access, utils, claire, logging
+
+################################################################################
projectB = None
Cnf = None
+Logger = None;
+
+################################################################################
+
+def usage (exit_code=0):
+ print """Usage: jenna [OPTION]
+Write out file lists suitable for use with apt-ftparchive.
+
+ -a, --architecture=ARCH only write file lists for this architecture
+ -c, --component=COMPONENT only write file lists for this component
+ -s, --suite=SUITE only write file lists for this suite
+ -h, --help show this help and exit
+
+ARCH, COMPONENT and SUITE can be space seperated lists, e.g.
+ --architecture=\"m68k i386\""""
+ sys.exit(exit_code)
+
+################################################################################
def generate_src_list(suite, component, output, dislocated_files):
sources = {}
suite_id = db_access.get_suite_id(suite);
-
+
if component == "-":
- q = projectB.query("SELECT s.source, s.version, l.path, f.filename, s.id, f.id FROM source s, src_associations sa, location l, files f WHERE sa.source = s.id AND sa.suite = '%d' AND l.id = f.location AND s.file = f.id ORDER BY s.source, s.version"
+ q = projectB.query("SELECT s.source, l.path, f.filename, f.id FROM source s, src_associations sa, location l, files f WHERE sa.source = s.id AND sa.suite = '%d' AND l.id = f.location AND s.file = f.id ORDER BY s.source, s.version"
% (suite_id));
else:
- q = projectB.query("SELECT s.source, s.version, l.path, f.filename, s.id, f.id FROM source s, src_associations sa, location l, component c, files f WHERE lower(c.name) = '%s' AND (c.id = l.component OR l.component = NULL) AND sa.source = s.id AND sa.suite = '%d' AND l.id = f.location AND s.file = f.id ORDER BY s.source, s.version"
+ q = projectB.query("SELECT s.source, l.path, f.filename, f.id FROM source s, src_associations sa, location l, component c, files f WHERE lower(c.name) = '%s' AND (c.id = l.component OR l.component = NULL) AND sa.source = s.id AND sa.suite = '%d' AND l.id = f.location AND s.file = f.id ORDER BY s.source, s.version"
% (component, suite_id));
entries = q.getresult();
for entry in entries:
- source = entry[0]
- version = entry[1]
- filename = entry[2]+entry[3];
- id = entry[4]
- add_new = 0
- file_id = entry[5];
+ (source, path, filename, file_id) = entry;
if dislocated_files.has_key(file_id):
filename = dislocated_files[file_id];
- if os.path.exists(filename):
- if sources.has_key(source):
- if apt_pkg.VersionCompare(sources[source]["version"], version) == -1:
- if not Cnf.Find("Suite::%s::Untouchable" % (suite)):
- print "deleting %s (%s) in favour of newer version %s..." % (source, sources[source]["version"], version)
- projectB.query("DELETE FROM src_associations WHERE source = %s AND suite = %d" % (sources[source]["id"], suite_id))
- else:
- if Cnf.Find("Jenna::Options::Verbose"):
- print "[untouchable] would delete %s (%s) in favour of newer version %s..." % (source, sources[source]["version"], version)
- sources[source] = { "id": id, "version": version, "filename": filename }
- else:
- if not Cnf.Find("Suite::%s::Untouchable" % (suite)):
- print "deleting %s (%s) in favour of newer version %s..." % (source, version, sources[source]["version"])
- projectB.query("DELETE FROM src_associations WHERE source = %s AND suite = %d" % (id, suite_id))
- else:
- if Cnf.Find("Jenna::Options::Verbose"):
- print "[untouchable] would delete %s (%s) in favour of newer version %s..." % (source, version, sources[source]["version"])
- else:
- sources[source] = { "id": id, "version": version, "filename": filename }
else:
- if not Cnf.Find("Suite::%s::Untouchable" % (suite)):
- sys.stderr.write("WARNING: deleting %s because it doesn't exist.\n" % (filename));
- projectB.query("DELETE FROM src_associations WHERE source = %s AND suite = %d" % (id, suite_id))
+ filename = path + filename;
+ if sources.has_key(source):
+ utils.warn("%s in %s / %s / source is duplicated." % (source, suite, component));
+ else:
+ sources[source] = filename;
# Write the list of files out
source_keys = sources.keys();
source_keys.sort();
for source in source_keys:
- output.write(sources[source]["filename"]+'\n')
-
+ output.write(sources[source]+'\n')
+
+#########################################################################################
+
def generate_bin_list(suite, component, architecture, output, type, dislocated_files):
packages = {}
suite_id = db_access.get_suite_id(suite);
-
+
if component == "-":
- q = projectB.query("SELECT b.package, b.version, l.path, f.filename, b.id, f.id FROM architecture a, binaries b, bin_associations ba, location l, files f WHERE ( a.arch_string = '%s' OR a.arch_string = 'all' ) AND a.id = b.architecture AND ba.bin = b.id AND ba.suite = '%d' AND l.id = f.location AND b.file = f.id AND b.type = '%s' ORDER BY b.package, b.version, a.arch_string" % (architecture, suite_id, type));
+ q = projectB.query("SELECT b.package, l.path, f.filename, f.id FROM architecture a, binaries b, bin_associations ba, location l, files f WHERE ( a.arch_string = '%s' OR a.arch_string = 'all' ) AND a.id = b.architecture AND ba.bin = b.id AND ba.suite = '%d' AND l.id = f.location AND b.file = f.id AND b.type = '%s' ORDER BY b.package, b.version, a.arch_string" % (architecture, suite_id, type));
else:
- q = projectB.query("SELECT b.package, b.version, l.path, f.filename, b.id, f.id FROM architecture a, binaries b, bin_associations ba, location l, component c, files f WHERE lower(c.name) = '%s' AND (c.id = l.component OR l.component = NULL) AND (a.arch_string = '%s' OR a.arch_string = 'all') AND a.id = b.architecture AND ba.bin = b.id AND ba.suite = '%d' AND l.id = f.location AND b.file = f.id AND b.type = '%s' ORDER BY b.package, b.version, a.arch_string" % (component, architecture, suite_id, type));
+ q = projectB.query("SELECT b.package, l.path, f.filename, f.id FROM architecture a, binaries b, bin_associations ba, location l, component c, files f WHERE lower(c.name) = '%s' AND (c.id = l.component OR l.component = NULL) AND (a.arch_string = '%s' OR a.arch_string = 'all') AND a.id = b.architecture AND ba.bin = b.id AND ba.suite = '%d' AND l.id = f.location AND b.file = f.id AND b.type = '%s' ORDER BY b.package, b.version, a.arch_string" % (component, architecture, suite_id, type));
entries = q.getresult();
for entry in entries:
- package = entry[0]
- version = entry[1]
- filename = entry[2]+entry[3];
- id = entry[4]
- add_new = 0
- file_id = entry[5];
+ (package, path, filename, file_id) = entry;
if dislocated_files.has_key(file_id):
filename = dislocated_files[file_id];
-
- if os.path.exists(filename):
- if packages.has_key(package):
- if apt_pkg.VersionCompare(packages[package]["version"], version) == -1:
- if not Cnf.Find("Suite::%s::Untouchable" % (suite)):
- print "deleting %s (%s) in favour of newer version %s..." % (package, packages[package]["version"], version)
- projectB.query("DELETE FROM bin_associations WHERE bin = %s AND suite = %d" % (packages[package]["id"], suite_id))
- else:
- if Cnf.Find("Jenna::Options::Verbose"):
- print "[untouchable] would delete %s (%s) in favour of newer version %s..." % (package, packages[package]["version"], version)
- packages[package] = { "id": id, "version": version, "filename": filename }
- else:
- if not Cnf.Find("Suite::%s::Untouchable" % (suite)):
- print "deleting %s (%s) in favour of newer version %s..." % (package, version, packages[package]["version"])
- projectB.query("DELETE FROM bin_associations WHERE bin = %s AND suite = %d" % (id, suite_id))
- else:
- if Cnf.Find("Jenna::Options::Verbose"):
- print "[untochable] would delete %s (%s) in favour of newer version %s..." % (package, version, packages[package]["version"])
- else:
- packages[package] = { "id": id, "version": version, "filename": filename }
else:
- if not Cnf.Find("Suite::%s::Untouchable" % (suite)):
- sys.stderr.write("WARNING: deleting %s because it doesn't exist.\n" % (filename));
- projectB.query("DELETE FROM bin_associations WHERE bin = %s AND suite = %d" % (id, suite_id))
+ filename = path + filename;
+ if packages.has_key(package):
+ utils.warn("%s in %s / %s / %s / %s is duplicated." % (package, suite, component, architecture, type));
+ else:
+ packages[package] = filename;
# Write the list of files out
package_keys = packages.keys();
package_keys.sort();
for package in package_keys:
- output.write(packages[package]["filename"]+'\n')
-
-
+ output.write(packages[package]+'\n')
+
+#########################################################################################
+
+##########
+# <elmo> I'm doing it in python btw.. nothing against your monster
+# SQL, but the python wins in terms of speed and readiblity
+# <aj> bah
+# <aj> you suck!!!!!
+# <elmo> sorry :(
+# <aj> you are not!!!
+# <aj> you mock my SQL!!!!
+# <elmo> you want have contest of skillz??????
+# <aj> all your skillz are belong to my sql!!!!
+# <elmo> yo momma are belong to my python!!!!
+# <aj> yo momma was SQLin' like a pig last night!
+##########
+
+# If something has gone from arch:all to arch:any or vice-versa,
+# clean out the old versions here. The rest of jenna won't do this
+# because it's lame. I have no idea. </aj>
+
+def clean_duplicate_packages(suite):
+ Logger.log(["Cleaning duplicate packages", suite]);
+
+ suite_id = db_access.get_suite_id(suite)
+ q = projectB.query("""
+SELECT b1.package,
+ b1.id, b1.version, a1.arch_string,
+ b2.id, b2.version, a2.arch_string
+ FROM bin_associations ba1, binaries b1, architecture a1,
+ bin_associations ba2, binaries b2, architecture a2
+ WHERE ba1.suite = ba2.suite AND ba1.suite = %s
+ AND ba1.bin = b1.id AND b1.architecture = a1.id
+ AND ba2.bin = b2.id AND b2.architecture = a2.id
+ AND b1.package = b2.package
+ AND (a1.id = a2.id OR a1.arch_string = 'all' OR a2.arch_string = 'all')
+ AND b1.id != b2.id
+ AND versioncmp(b1.version, b2.version) <= 0
+ORDER BY b1.package, b1.version, a1.arch_string;""" % (suite_id))
+
+ ql = q.getresult()
+ seen = {}
+ for i in ql:
+ (package, oldid, oldver, oldarch, newid, newver, newarch) = i
+ if not seen.has_key(oldid):
+ seen[oldid] = newid
+ Logger.log(["Removing", package, oldver, oldarch, newver, newarch]);
+ projectB.query("DELETE FROM bin_associations WHERE suite = %s AND bin = %s" % (suite_id, oldid))
+ else:
+ Logger.log(["Superceded", package, oldver, oldarch, newver, newarch]);
+
+# If something has moved from one component to another we need to
+# clean out the old versions here. The rest of jenna won't do this
+# because it works on a per-component level for flexibility.
+
+def clean_suite (suite):
+ Logger.log(["Cleaning out packages", suite]);
+
+ suite_id = db_access.get_suite_id(suite)
+ q = projectB.query("""
+SELECT b.id, b.package, a.arch_string, b.version, l.path, f.filename, c.name
+ FROM binaries b, bin_associations ba, files f, location l, architecture a, component c
+ WHERE ba.suite = %s AND ba.bin = b.id AND b.file = f.id AND
+ f.location = l.id AND l.component = c.id AND b.architecture = a.id
+UNION
+SELECT s.id, s.source, 'source', s.version, l.path, f.filename, c.name
+ FROM source s, src_associations sa, files f, location l, component c
+ WHERE sa.suite = %s AND sa.source = s.id AND s.file = f.id AND
+ f.location = l.id AND l.component = c.id;""" % (suite_id, suite_id));
+ ql = q.getresult();
+ d = {};
+ for i in ql:
+ (id, package, architecture, version, path, filename, component) = i;
+ filename = path + filename;
+ if architecture == "source":
+ delete_table = "src_associations";
+ delete_col = "source";
+ else:
+ delete_table = "bin_associations";
+ delete_col = "bin";
+ key = "%s~%s" % (package, architecture);
+ if os.path.exists(filename):
+ if d.has_key(key):
+ (other_version, other_component, other_id) = d[key];
+ if apt_pkg.VersionCompare(version, other_version) != 1:
+ (keep_version, keep_component) = (other_version, other_component)
+ (delete_id, delete_version, delete_component) = (id, version, component)
+ else:
+ (keep_version, keep_component) = (version, component)
+ (delete_id, delete_version, delete_component) = (other_id, other_version, other_component)
+ d[key] = (version, component, id);
+ if not Cnf.Find("Suite::%s::Untouchable" % (suite)):
+ Logger.log(["deleting", package, architecture, delete_version, delete_component, keep_version, keep_component]);
+ projectB.query("DELETE FROM %s WHERE suite = %s AND %s = %s" % (delete_table, suite_id, delete_col, delete_id));
+ else:
+ Logger.log(["[untouchable]", package, architecture, delete_version, delete_component, keep_version, keep_component]);
+ else:
+ d[key] = (version, component, id);
+ else:
+ utils.warn("%s is in %s but doesn't appear to exist?" % (filename, suite));
+
+#########################################################################################
def main():
- global Cnf, projectB;
+ global Cnf, projectB, Logger;
dislocated_files = {};
-
- projectB = pg.connect('projectb', 'localhost');
-
- apt_pkg.init();
-
- Cnf = apt_pkg.newConfiguration();
- apt_pkg.ReadConfigFileISC(Cnf,utils.which_conf_file());
+
+ Cnf = utils.get_conf()
Arguments = [('a',"architecture","Jenna::Options::Architecture", "HasArg"),
('c',"component","Jenna::Options::Component", "HasArg"),
- ('d',"debug","Jenna::Options::Debug", "IntVal"),
('h',"help","Jenna::Options::Help"),
- ('s',"suite", "Jenna::Options::Suite", "HasArg"),
- ('v',"verbose","Jenna::Options::Verbose"),
- ('V',"version","Jenna::Options::Version")];
+ ('s',"suite", "Jenna::Options::Suite", "HasArg")];
+
+ for i in ["architecture", "component", "help", "suite" ]:
+ if not Cnf.has_key("Jenna::Options::%s" % (i)):
+ Cnf["Jenna::Options::%s" % (i)] = "";
apt_pkg.ParseCommandLine(Cnf,Arguments,sys.argv);
+ Options = Cnf.SubTree("Jenna::Options");
+
+ if Options["Help"]:
+ usage();
+ projectB = pg.connect(Cnf["DB::Name"], Cnf["DB::Host"], int(Cnf["DB::Port"]));
db_access.init(Cnf, projectB);
+ Logger = logging.Logger(Cnf, "jenna");
- if Cnf["Jenna::Options::Suite"] == "":
- Cnf["Jenna::Options::Suite"] = string.join(Cnf.SubTree("Suite").List());
- for suite in string.split(Cnf["Jenna::Options::Suite"]):
+ if Options["Suite"] == "":
+ Options["Suite"] = string.join(Cnf.SubTree("Suite").List());
+ for suite in string.split(Options["Suite"]):
suite = string.lower(suite);
if suite == 'stable':
dislocated_files = claire.find_dislocated_stable(Cnf, projectB);
- components = Cnf["Jenna::Options::Component"];
+ else:
+ dislocated_files = {};
+ clean_suite(suite);
+ clean_duplicate_packages(suite)
+ components = Options["Component"];
if not Cnf.has_key("Suite::%s::Components" % (suite)):
components = "-";
if components == "":
components = string.join(Cnf.SubTree("Suite::%s::Components" % (suite)).List());
for component in string.split(components):
component = string.lower(component)
- architectures = Cnf["Jenna::Options::Architecture"];
+ architectures = Options["Architecture"];
if architectures == "":
architectures = string.join(Cnf.SubTree("Suite::%s::Architectures" % (suite)).List());
for architecture in string.split(architectures):
if architecture == "all":
continue
if architecture == "source":
- print "Processing dists/%s/%s/%s..." % (suite, component, architecture);
+ Logger.log(["Processing dists/%s/%s/%s..." % (suite, component, architecture)]);
output = utils.open_file("%s/%s_%s_%s.list" % (Cnf["Dir::ListsDir"], suite, component, architecture), "w")
generate_src_list(suite, component, output, dislocated_files);
output.close();
else:
- print "Processing dists/%s/%s/binary-%s..." % (suite, component, architecture);
+ Logger.log(["Processing dists/%s/%s/binary-%s..." % (suite, component, architecture)]);
output = utils.open_file("%s/%s_%s_binary-%s.list" % (Cnf["Dir::ListsDir"], suite, component, architecture), "w");
generate_bin_list(suite, component, architecture, output, "deb", dislocated_files);
output.close();
- if component == "main" and (suite == "unstable" or suite == "testing"): # FIXME: must be a cleaner way to say debian-installer is main only?
- print "Processing dists/%s/%s/debian-installer/binary-%s..." % (suite,component, architecture);
+ if component == "main" and (suite == "unstable" or suite == "testing") and Cnf.has_key("Section::debian-installer"): # FIXME: must be a cleaner way to say debian-installer is main only?
+ Logger.log(["Processing dists/%s/%s/debian-installer/binary-%s..." % (suite,component, architecture)]);
output = utils.open_file("%s/%s_%s_debian-installer_binary-%s.list" % (Cnf["Dir::ListsDir"], suite, component, architecture), "w");
generate_bin_list(suite, component, architecture, output, "udeb", dislocated_files);
output.close();
+ Logger.close();
+
+#########################################################################################
if __name__ == '__main__':
main()
-