]> git.decadent.org.uk Git - dak.git/blob - dak/generate_filelist.py
Merge remote branch 'drkranz/master' into merge
[dak.git] / dak / generate_filelist.py
1 #!/usr/bin/python
2
3 """
4 Generate file lists for apt-ftparchive.
5
6 @contact: Debian FTP Master <ftpmaster@debian.org>
7 @copyright: 2009  Torsten Werner <twerner@debian.org>
8 @license: GNU General Public License version 2 or later
9 """
10
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2 of the License, or
14 # (at your option) any later version.
15
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 # GNU General Public License for more details.
20
21 # You should have received a copy of the GNU General Public License
22 # along with this program; if not, write to the Free Software
23 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24
25 ################################################################################
26
27 # Ganneff> Please go and try to lock mhy now. After than try to lock NEW.
28 # twerner> !lock mhy
29 # dak> twerner: You suck, this is already locked by Ganneff
30 # Ganneff> now try with NEW
31 # twerner> !lock NEW
32 # dak> twerner: also locked NEW
33 # mhy> Ganneff: oy, stop using me for locks and highlighting me you tall muppet
34 # Ganneff> hehe :)
35
36 ################################################################################
37
38 from daklib.dbconn import *
39 from daklib.config import Config
40 from daklib.threadpool import ThreadPool
41 from daklib import utils
42 import apt_pkg, os, stat, sys
43
44 def fetch(query, args, session):
45     return [path + filename for (path, filename) in \
46         session.execute(query, args).fetchall()]
47
48 def getSources(suite, component, session, timestamp):
49     extra_cond = ""
50     if timestamp:
51         extra_cond = "AND extract(epoch from sa.created) > %d" % timestamp
52     query = """
53         SELECT l.path, f.filename
54             FROM source s
55             JOIN src_associations sa
56                 ON s.id = sa.source AND sa.suite = :suite %s
57             JOIN files f
58                 ON s.file = f.id
59             JOIN location l
60                 ON f.location = l.id AND l.component = :component
61             ORDER BY filename
62     """ % extra_cond
63     args = { 'suite': suite.suite_id,
64              'component': component.component_id }
65     return fetch(query, args, session)
66
67 def getBinaries(suite, component, architecture, type, session, timestamp):
68     extra_cond = ""
69     if timestamp:
70         extra_cond = "AND extract(epoch from ba.created) > %d" % timestamp
71     query = """
72 CREATE TEMP TABLE b_candidates (
73     source integer,
74     file integer,
75     architecture integer);
76
77 INSERT INTO b_candidates (source, file, architecture)
78     SELECT b.source, b.file, b.architecture
79         FROM binaries b
80         JOIN bin_associations ba ON b.id = ba.bin
81         WHERE b.type = :type AND ba.suite = :suite AND
82             b.architecture IN (2, :architecture) %s;
83
84 CREATE TEMP TABLE gf_candidates (
85     filename text,
86     path text,
87     architecture integer,
88     src integer,
89     source text);
90
91 INSERT INTO gf_candidates (filename, path, architecture, src, source)
92     SELECT f.filename, l.path, bc.architecture, bc.source as src, s.source
93         FROM b_candidates bc
94         JOIN source s ON bc.source = s.id
95         JOIN files f ON bc.file = f.id
96         JOIN location l ON f.location = l.id
97         WHERE l.component = :component;
98
99 WITH arch_any AS
100
101     (SELECT path, filename FROM gf_candidates
102         WHERE architecture > 2),
103
104      arch_all_with_any AS
105     (SELECT path, filename FROM gf_candidates
106         WHERE architecture = 2 AND
107               src IN (SELECT src FROM gf_candidates WHERE architecture > 2)),
108
109      arch_all_without_any AS
110     (SELECT path, filename FROM gf_candidates
111         WHERE architecture = 2 AND
112               source NOT IN (SELECT DISTINCT source FROM gf_candidates WHERE architecture > 2)),
113
114      filelist AS
115     (SELECT * FROM arch_any
116     UNION
117     SELECT * FROM arch_all_with_any
118     UNION
119     SELECT * FROM arch_all_without_any)
120
121     SELECT * FROM filelist ORDER BY filename
122     """ % extra_cond
123     args = { 'suite': suite.suite_id,
124              'component': component.component_id,
125              'architecture': architecture.arch_id,
126              'type': type }
127     return fetch(query, args, session)
128
129 def listPath(suite, component, architecture = None, type = None,
130         incremental_mode = False):
131     """returns full path to the list file"""
132     suffixMap = { 'deb': "binary-",
133                   'udeb': "debian-installer_binary-" }
134     if architecture:
135         suffix = suffixMap[type] + architecture.arch_string
136     else:
137         suffix = "source"
138     filename = "%s_%s_%s.list" % \
139         (suite.suite_name, component.component_name, suffix)
140     pathname = os.path.join(Config()["Dir::Lists"], filename)
141     file = utils.open_file(pathname, "a")
142     timestamp = None
143     if incremental_mode:
144         timestamp = os.fstat(file.fileno())[stat.ST_MTIME]
145     else:
146         file.seek(0)
147         file.truncate()
148     return (file, timestamp)
149
150 def writeSourceList(args):
151     (suite, component, incremental_mode) = args
152     (file, timestamp) = listPath(suite, component,
153             incremental_mode = incremental_mode)
154     session = DBConn().session()
155     for filename in getSources(suite, component, session, timestamp):
156         file.write(filename + '\n')
157     session.close()
158     file.close()
159
160 def writeBinaryList(args):
161     (suite, component, architecture, type, incremental_mode) = args
162     (file, timestamp) = listPath(suite, component, architecture, type,
163             incremental_mode)
164     session = DBConn().session()
165     for filename in getBinaries(suite, component, architecture, type,
166             session, timestamp):
167         file.write(filename + '\n')
168     session.close()
169     file.close()
170
171 def usage():
172     print """Usage: dak generate_filelist [OPTIONS]
173 Create filename lists for apt-ftparchive.
174
175   -s, --suite=SUITE            act on this suite
176   -c, --component=COMPONENT    act on this component
177   -a, --architecture=ARCH      act on this architecture
178   -h, --help                   show this help and exit
179   -i, --incremental            activate incremental mode
180
181 ARCH, COMPONENT and SUITE can be comma (or space) separated list, e.g.
182     --suite=testing,unstable
183
184 Incremental mode appends only newer files to existing lists."""
185     sys.exit()
186
187 def main():
188     cnf = Config()
189     Arguments = [('h', "help",         "Filelist::Options::Help"),
190                  ('s', "suite",        "Filelist::Options::Suite", "HasArg"),
191                  ('c', "component",    "Filelist::Options::Component", "HasArg"),
192                  ('a', "architecture", "Filelist::Options::Architecture", "HasArg"),
193                  ('i', "incremental",  "Filelist::Options::Incremental")]
194     session = DBConn().session()
195     query_suites = session.query(Suite)
196     suites = [suite.suite_name for suite in query_suites.all()]
197     if not cnf.has_key('Filelist::Options::Suite'):
198         cnf['Filelist::Options::Suite'] = ','.join(suites)
199     # we can ask the database for components if 'mixed' is gone
200     if not cnf.has_key('Filelist::Options::Component'):
201         cnf['Filelist::Options::Component'] = 'main,contrib,non-free'
202     query_architectures = session.query(Architecture)
203     architectures = \
204         [architecture.arch_string for architecture in query_architectures.all()]
205     if not cnf.has_key('Filelist::Options::Architecture'):
206         cnf['Filelist::Options::Architecture'] = ','.join(architectures)
207     cnf['Filelist::Options::Help'] = ''
208     cnf['Filelist::Options::Incremental'] = ''
209     apt_pkg.ParseCommandLine(cnf.Cnf, Arguments, sys.argv)
210     Options = cnf.SubTree("Filelist::Options")
211     if Options['Help']:
212         usage()
213     suite_arch = session.query(SuiteArchitecture)
214     threadpool = ThreadPool()
215     for suite_name in utils.split_args(Options['Suite']):
216         suite = query_suites.filter_by(suite_name = suite_name).one()
217         join = suite_arch.filter_by(suite_id = suite.suite_id)
218         for component_name in utils.split_args(Options['Component']):
219             component = session.query(Component).\
220                 filter_by(component_name = component_name).one()
221             for architecture_name in utils.split_args(Options['Architecture']):
222                 architecture = query_architectures.\
223                     filter_by(arch_string = architecture_name).one()
224                 try:
225                     join.filter_by(arch_id = architecture.arch_id).one()
226                     if architecture_name == 'source':
227                         threadpool.queueTask(writeSourceList,
228                             (suite, component, Options['Incremental']))
229                     elif architecture_name != 'all':
230                         threadpool.queueTask(writeBinaryList,
231                             (suite, component, architecture, 'deb',
232                                 Options['Incremental']))
233                         threadpool.queueTask(writeBinaryList,
234                             (suite, component, architecture, 'udeb',
235                                 Options['Incremental']))
236                 except:
237                     pass
238     threadpool.joinAll()
239     # this script doesn't change the database
240     session.close()
241
242 if __name__ == '__main__':
243     main()
244