]> git.decadent.org.uk Git - dak.git/blob - jenna
Dir rationalization
[dak.git] / jenna
1 #!/usr/bin/env python
2
3 # Generate file list which is then fed to apt-ftparchive to generate Packages and Sources files
4 # Copyright (C) 2000, 2001, 2002  James Troup <james@nocrew.org>
5 # $Id: jenna,v 1.16 2002-05-08 11:13:02 troup Exp $
6
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.
11
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.
16
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
21 ################################################################################
22
23 # BTAF: "GOD *DAMMIT*!!  What the FUCK happened to my free will??"
24 #
25 # -- http://www.angryflower.com/timelo.gif
26
27 ################################################################################
28
29 import pg, string, os, sys
30 import apt_pkg
31 import db_access, utils, claire, logging
32
33 ################################################################################
34
35 projectB = None
36 Cnf = None
37 Logger = None;
38
39 ################################################################################
40
41 def usage (exit_code=0):
42     print """Usage: jenna [OPTION]
43 Write out file lists suitable for use with apt-ftparchive.
44
45   -a, --architecture=ARCH   only write file lists for this architecture
46   -c, --component=COMPONENT only write file lists for this component
47   -s, --suite=SUITE         only write file lists for this suite
48   -h, --help                show this help and exit
49
50 ARCH, COMPONENT and SUITE can be space seperated lists, e.g.
51     --architecture=\"m68k i386\""""
52     sys.exit(exit_code)
53
54 ################################################################################
55
56 def generate_src_list(suite, component, output, dislocated_files):
57     sources = {}
58
59     suite_id = db_access.get_suite_id(suite);
60
61     if component == "-":
62         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"
63                            % (suite_id));
64     else:
65         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"
66                            % (component, suite_id));
67     entries = q.getresult();
68     for entry in entries:
69         (source, path, filename, file_id) = entry;
70         if dislocated_files.has_key(file_id):
71             filename = dislocated_files[file_id];
72         else:
73             filename = path + filename;
74         if sources.has_key(source):
75             utils.warn("%s in %s / %s / source is duplicated." % (source, suite, component));
76         else:
77             sources[source] = filename;
78
79     # Write the list of files out
80     source_keys = sources.keys();
81     source_keys.sort();
82     for source in source_keys:
83         output.write(sources[source]+'\n')
84
85 #########################################################################################
86
87 def generate_bin_list(suite, component, architecture, output, type, dislocated_files):
88     packages = {}
89
90     suite_id = db_access.get_suite_id(suite);
91
92     if component == "-":
93         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));
94     else:
95         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));
96     entries = q.getresult();
97     for entry in entries:
98         (package, path, filename, file_id) = entry;
99         if dislocated_files.has_key(file_id):
100             filename = dislocated_files[file_id];
101         else:
102             filename = path + filename;
103         if packages.has_key(package):
104             utils.warn("%s in %s / %s / %s / %s is duplicated." % (package, suite, component, architecture, type));
105         else:
106             packages[package] = filename;
107
108     # Write the list of files out
109     package_keys = packages.keys();
110     package_keys.sort();
111     for package in package_keys:
112         output.write(packages[package]+'\n')
113
114 #########################################################################################
115
116 ##########
117 # <elmo> I'm doing it in python btw.. nothing against your monster
118 #        SQL, but the python wins in terms of speed and readiblity
119 # <aj> bah
120 # <aj> you suck!!!!!
121 # <elmo> sorry :(
122 # <aj> you are not!!!
123 # <aj> you mock my SQL!!!!
124 # <elmo> you want have contest of skillz??????
125 # <aj> all your skillz are belong to my sql!!!!
126 # <elmo> yo momma are belong to my python!!!!
127 # <aj> yo momma was SQLin' like a pig last night!
128 ##########
129
130 # If something has gone from arch:all to arch:any or vice-versa,
131 # clean out the old versions here.  The rest of jenna won't do this
132 # because it's lame. I have no idea. </aj>
133
134 def clean_duplicate_packages(suite):
135     Logger.log(["Cleaning duplicate packages", suite]);
136
137     suite_id = db_access.get_suite_id(suite)
138     q = projectB.query("""
139 SELECT b1.package,
140        b1.id, b1.version, a1.arch_string,
141        b2.id, b2.version, a2.arch_string
142   FROM bin_associations ba1, binaries b1, architecture a1,
143        bin_associations ba2, binaries b2, architecture a2
144  WHERE ba1.suite = ba2.suite AND ba1.suite = %s
145    AND ba1.bin = b1.id AND b1.architecture = a1.id
146    AND ba2.bin = b2.id AND b2.architecture = a2.id
147    AND b1.package = b2.package
148    AND (a1.id = a2.id OR a1.arch_string = 'all' OR a2.arch_string = 'all')
149    AND b1.id != b2.id
150    AND versioncmp(b1.version, b2.version) <= 0
151 ORDER BY b1.package, b1.version, a1.arch_string;""" % (suite_id))
152
153     ql = q.getresult()
154     seen = {}
155     for i in ql:
156         (package, oldid, oldver, oldarch, newid, newver, newarch) = i
157         if not seen.has_key(oldid):
158             seen[oldid] = newid
159             Logger.log(["Removing", package, oldver, oldarch, newver, newarch]);
160             projectB.query("DELETE FROM bin_associations WHERE suite = %s AND bin = %s" % (suite_id, oldid))
161         else:
162             Logger.log(["Superceded", package, oldver, oldarch, newver, newarch]);
163
164 # If something has moved from one component to another we need to
165 # clean out the old versions here.  The rest of jenna won't do this
166 # because it works on a per-component level for flexibility.
167
168 def clean_suite (suite):
169     Logger.log(["Cleaning out packages", suite]);
170
171     suite_id = db_access.get_suite_id(suite)
172     q = projectB.query("""
173 SELECT b.id, b.package, a.arch_string, b.version, l.path, f.filename, c.name
174   FROM binaries b, bin_associations ba, files f, location l, architecture a, component c
175   WHERE ba.suite = %s AND ba.bin = b.id AND b.file = f.id AND
176         f.location = l.id AND l.component = c.id AND b.architecture = a.id
177 UNION
178 SELECT s.id, s.source, 'source', s.version, l.path, f.filename, c.name
179   FROM source s, src_associations sa, files f, location l, component c
180   WHERE sa.suite = %s AND sa.source = s.id AND s.file = f.id AND
181         f.location = l.id AND l.component = c.id;""" % (suite_id, suite_id));
182     ql = q.getresult();
183     d = {};
184     for i in ql:
185         (id, package, architecture, version, path, filename, component) = i;
186         filename = path + filename;
187         if architecture == "source":
188             delete_table = "src_associations";
189             delete_col = "source";
190         else:
191             delete_table = "bin_associations";
192             delete_col = "bin";
193         key = "%s~%s" % (package, architecture);
194         if os.path.exists(filename):
195             if d.has_key(key):
196                 (other_version, other_component, other_id) = d[key];
197                 if apt_pkg.VersionCompare(version, other_version) != 1:
198                     (keep_version, keep_component) = (other_version, other_component)
199                     (delete_id, delete_version, delete_component) = (id, version, component)
200                 else:
201                     (keep_version, keep_component) = (version, component)
202                     (delete_id, delete_version, delete_component) = (other_id, other_version, other_component)
203                     d[key] = (version, component, id);
204                 if not Cnf.Find("Suite::%s::Untouchable" % (suite)):
205                     Logger.log(["deleting", package, architecture, delete_version, delete_component, keep_version, keep_component]);
206                     projectB.query("DELETE FROM %s WHERE suite = %s AND %s = %s" % (delete_table, suite_id, delete_col, delete_id));
207                 else:
208                     Logger.log(["[untouchable]", package, architecture, delete_version, delete_component, keep_version, keep_component]);
209             else:
210                 d[key] = (version, component, id);
211         else:
212             utils.warn("%s is in %s but doesn't appear to exist?" % (filename, suite));
213
214 #########################################################################################
215
216 def main():
217     global Cnf, projectB, Logger;
218     dislocated_files = {};
219
220     Cnf = utils.get_conf()
221
222     Arguments = [('a',"architecture","Jenna::Options::Architecture", "HasArg"),
223                  ('c',"component","Jenna::Options::Component", "HasArg"),
224                  ('h',"help","Jenna::Options::Help"),
225                  ('s',"suite", "Jenna::Options::Suite", "HasArg")];
226
227     for i in ["architecture", "component", "help", "suite" ]:
228         if not Cnf.has_key("Jenna::Options::%s" % (i)):
229             Cnf["Jenna::Options::%s" % (i)] = "";
230
231     apt_pkg.ParseCommandLine(Cnf,Arguments,sys.argv);
232     Options = Cnf.SubTree("Jenna::Options");
233
234     if Options["Help"]:
235         usage();
236
237     projectB = pg.connect(Cnf["DB::Name"], Cnf["DB::Host"], int(Cnf["DB::Port"]));
238     db_access.init(Cnf, projectB);
239     Logger = logging.Logger(Cnf, "jenna");
240
241     if Options["Suite"] == "":
242         Options["Suite"] = string.join(Cnf.SubTree("Suite").List());
243     for suite in string.split(Options["Suite"]):
244         suite = string.lower(suite);
245         if suite == 'stable':
246             dislocated_files = claire.find_dislocated_stable(Cnf, projectB);
247         else:
248             dislocated_files = {};
249         clean_suite(suite);
250         clean_duplicate_packages(suite)
251         components = Options["Component"];
252         if not Cnf.has_key("Suite::%s::Components" % (suite)):
253             components = "-";
254         if components == "":
255             components = string.join(Cnf.SubTree("Suite::%s::Components" % (suite)).List());
256         for component in string.split(components):
257             component = string.lower(component)
258             architectures = Options["Architecture"];
259             if architectures == "":
260                 architectures = string.join(Cnf.SubTree("Suite::%s::Architectures" % (suite)).List());
261             for architecture in string.split(architectures):
262                 architecture = string.lower(architecture)
263                 if architecture == "all":
264                     continue
265                 if architecture == "source":
266                     Logger.log(["Processing dists/%s/%s/%s..." % (suite, component, architecture)]);
267                     output = utils.open_file("%s/%s_%s_%s.list" % (Cnf["Dir::Lists"], suite, component, architecture), "w")
268                     generate_src_list(suite, component, output, dislocated_files);
269                     output.close();
270                 else:
271                     Logger.log(["Processing dists/%s/%s/binary-%s..." % (suite, component, architecture)]);
272                     output = utils.open_file("%s/%s_%s_binary-%s.list" % (Cnf["Dir::Lists"], suite, component, architecture), "w");
273                     generate_bin_list(suite, component, architecture, output, "deb", dislocated_files);
274                     output.close();
275                     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?
276                         Logger.log(["Processing dists/%s/%s/debian-installer/binary-%s..." % (suite,component, architecture)]);
277                         output = utils.open_file("%s/%s_%s_debian-installer_binary-%s.list" % (Cnf["Dir::Lists"], suite, component, architecture), "w");
278                         generate_bin_list(suite, component, architecture, output, "udeb", dislocated_files);
279                         output.close();
280     Logger.close();
281
282 #########################################################################################
283
284 if __name__ == '__main__':
285     main()