3 Helper code for contents generation.
5 @contact: Debian FTPMaster <ftpmaster@debian.org>
6 @copyright: 2011 Torsten Werner <twerner@debian.org>
7 @license: GNU General Public License version 2 or later
10 ################################################################################
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 2 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 ################################################################################
28 from daklib.dbconn import *
29 from daklib.config import Config
31 from sqlalchemy import desc, or_
32 from subprocess import Popen, PIPE
34 class ContentsWriter(object):
36 ContentsWriter writes the Contents-$arch.gz files.
38 def __init__(self, suite, architecture, overridetype, component = None):
40 The constructor clones its arguments into a new session object to make
41 sure that the new ContentsWriter object can be executed in a different
44 self.suite = suite.clone()
45 self.session = self.suite.session()
46 self.architecture = architecture.clone(self.session)
47 self.overridetype = overridetype.clone(self.session)
48 if component is not None:
49 self.component = component.clone(self.session)
55 Returns a query object that is doing most of the work.
58 'suite': self.suite.suite_id,
59 'arch_all': get_architecture('all', self.session).arch_id,
60 'arch': self.architecture.arch_id,
61 'type_id': self.overridetype.overridetype_id,
62 'type': self.overridetype.overridetype,
65 if self.component is not None:
66 params['component'] = component.component_id
68 create temp table newest_binaries (
69 id integer primary key,
72 create index newest_binaries_by_package on newest_binaries (package);
74 insert into newest_binaries (id, package)
75 select distinct on (package) id, package from binaries
76 where type = :type and
77 (architecture = :arch_all or architecture = :arch) and
78 id in (select bin from bin_associations where suite = :suite)
79 order by package, version desc;
84 (select o.package, s.section
85 from override o, section s
86 where o.suite = :suite and o.type = :type_id and o.section = s.id and
87 o.component = :component)
89 select bc.file, substring(o.section from position('/' in o.section) + 1) || '/' || b.package as package
90 from newest_binaries b, bin_contents bc, unique_override o
91 where b.id = bc.binary_id and o.package = b.package
92 order by bc.file, b.package'''
96 create temp table newest_binaries (
97 id integer primary key,
100 create index newest_binaries_by_package on newest_binaries (package);
102 insert into newest_binaries (id, package)
103 select distinct on (package) id, package from binaries
104 where type = :type and
105 (architecture = :arch_all or architecture = :arch) and
106 id in (select bin from bin_associations where suite = :suite)
107 order by package, version desc;
112 (select distinct on (o.package, s.section) o.package, s.section
113 from override o, section s
114 where o.suite = :suite and o.type = :type_id and o.section = s.id
115 order by o.package, s.section, o.modified desc)
117 select bc.file, substring(o.section from position('/' in o.section) + 1) || '/' || b.package as package
118 from newest_binaries b, bin_contents bc, unique_override o
119 where b.id = bc.binary_id and o.package = b.package
120 order by bc.file, b.package'''
122 return self.session.query("file", "package").from_statement(sql). \
125 def formatline(self, filename, package_list):
127 Returns a formatted string for the filename argument.
129 package_list = ','.join(package_list)
130 return "%-60s%s\n" % (filename, package_list)
134 Yields a new line of the Contents-$arch.gz file in filename order.
138 for filename, package in self.query().yield_per(100):
139 if filename != last_filename:
140 if last_filename is not None:
141 yield self.formatline(last_filename, package_list)
142 last_filename = filename
144 package_list.append(package)
145 yield self.formatline(last_filename, package_list)
146 # end transaction to return connection to pool
147 self.session.rollback()
151 Returns a list of lines for the Contents-$arch.gz file.
153 return [item for item in self.fetch()]
155 def output_filename(self):
157 Returns the name of the output file.
160 'root': Config()['Dir::Root'],
161 'suite': self.suite.suite_name,
162 'architecture': self.architecture.arch_string
164 if self.component is None:
165 return "%(root)s%(suite)s/Contents-%(architecture)s.gz" % values
166 values['component'] = self.component.component_name
167 return "%(root)s%(suite)s/%(component)s/Contents-%(architecture)s.gz" % values
169 def write_file(self):
171 Write the output file.
173 command = ['gzip', '--rsyncable']
174 output_file = open(self.output_filename(), 'w')
175 pipe = Popen(command, stdin = PIPE, stdout = output_file).stdin
176 for item in self.fetch():