4 Generate file lists for apt-ftparchive.
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
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.
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.
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
25 ################################################################################
27 # Ganneff> Please go and try to lock mhy now. After than try to lock NEW.
29 # dak> twerner: You suck, this is already locked by Ganneff
30 # Ganneff> now try with NEW
32 # dak> twerner: also locked NEW
33 # mhy> Ganneff: oy, stop using me for locks and highlighting me you tall muppet
36 ################################################################################
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
44 def fetch(query, args, session):
45 return [path + filename for (path, filename) in \
46 session.execute(query, args).fetchall()]
48 def getSources(suite, component, session, timestamp):
51 extra_cond = "AND extract(epoch from sa.created) > %d" % timestamp
53 SELECT l.path, f.filename
55 JOIN src_associations sa
56 ON s.id = sa.source AND sa.suite = :suite %s
60 ON f.location = l.id AND l.component = :component
63 args = { 'suite': suite.suite_id,
64 'component': component.component_id }
65 return fetch(query, args, session)
67 def getBinaries(suite, component, architecture, type, session, timestamp):
70 extra_cond = "AND extract(epoch from ba.created) > %d" % timestamp
72 CREATE TEMP TABLE b_candidates (
75 architecture integer);
77 INSERT INTO b_candidates (source, file, architecture)
78 SELECT b.source, b.file, b.architecture
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;
84 CREATE TEMP TABLE gf_candidates (
91 INSERT INTO gf_candidates (filename, path, architecture, src, source)
92 SELECT f.filename, l.path, bc.architecture, bc.source as src, s.source
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;
101 (SELECT path, filename FROM gf_candidates
102 WHERE architecture > 2),
105 (SELECT path, filename FROM gf_candidates
106 WHERE architecture = 2 AND
107 src IN (SELECT src FROM gf_candidates WHERE architecture > 2)),
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)),
115 (SELECT * FROM arch_any
117 SELECT * FROM arch_all_with_any
119 SELECT * FROM arch_all_without_any)
121 SELECT * FROM filelist ORDER BY filename
123 args = { 'suite': suite.suite_id,
124 'component': component.component_id,
125 'architecture': architecture.arch_id,
127 return fetch(query, args, session)
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-" }
135 suffix = suffixMap[type] + architecture.arch_string
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")
144 timestamp = os.fstat(file.fileno())[stat.ST_MTIME]
148 return (file, timestamp)
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')
160 def writeBinaryList(args):
161 (suite, component, architecture, type, incremental_mode) = args
162 (file, timestamp) = listPath(suite, component, architecture, type,
164 session = DBConn().session()
165 for filename in getBinaries(suite, component, architecture, type,
167 file.write(filename + '\n')
172 print """Usage: dak generate_filelist [OPTIONS]
173 Create filename lists for apt-ftparchive.
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
181 ARCH, COMPONENT and SUITE can be comma (or space) separated list, e.g.
182 --suite=testing,unstable
184 Incremental mode appends only newer files to existing lists."""
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)
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")
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()
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']))
239 # this script doesn't change the database
242 if __name__ == '__main__':