]> git.ipfire.org Git - people/stevee/pakfire.git/blame - pakfire/packages/packager.py
Copy packages to repository if we cannot hard-link them.
[people/stevee/pakfire.git] / pakfire / packages / packager.py
CommitLineData
47a4cb89
MT
1#!/usr/bin/python
2
3import glob
4import logging
5import lzma
6import os
7import progressbar
8import sys
9import tarfile
10import tempfile
11import xattr
12
13from pakfire.constants import *
14from pakfire.i18n import _
15
16class Extractor(object):
17 def __init__(self, pakfire, pkg):
18 self.pakfire = pakfire
19 self.pkg = pkg
20
21 self.data = pkg.get_file("data.img")
22
23 self.archive = None
24 self._tempfile = None
25
fa6d335b
MT
26 if pkg.payload_compression == "XXX":
27 self.archive = tarfile.open(fileobj=self.data)
28 else:
29 self._uncompress_data()
47a4cb89
MT
30
31 def cleanup(self):
32 # XXX not called by anything
33 if self._tempfile:
34 os.unlink(self._tempfile)
35
36 def _uncompress_data(self):
37 # XXX this function uncompresses the data.img file
38 # and saves the bare tarball to /tmp which takes a lot
39 # of space.
40
41 self.data.seek(0)
42
43 # Create a temporary file to save the content in
44 f, self._tempfile = tempfile.mkstemp()
45 f = open(self._tempfile, "w")
46
47 decompressor = lzma.LZMADecompressor()
48
49 buf = self.data.read(BUFFER_SIZE)
50 while buf:
51 f.write(decompressor.decompress(buf))
52
53 buf = self.data.read(BUFFER_SIZE)
54
55 f.write(decompressor.flush())
56 f.close()
57
58 self.archive = tarfile.open(self._tempfile)
59
60 @property
61 def files(self):
62 return self.archive.getnames()
63
64 def extractall(self, path="/", callback=None):
65 pbar = self._make_progressbar()
66
67 if pbar:
68 pbar.start()
69 else:
70 print " %s %-20s" % (_("Extracting"), self.pkg.name)
71
72 i = 0
73 for name in self.files:
74 i += 1
75 self.extract(name, path, callback=callback)
76
77 if pbar:
78 pbar.update(i)
79
80 if pbar:
81 pbar.finish()
82 #sys.stdout.write("\n")
83
84 def extract(self, filename, path="/", callback=None):
85 member = self.archive.getmember(filename)
86 target = os.path.join(path, filename)
87
88 # If the member is a directory and if it already exists, we
89 # don't need to create it again.
90 if member.isdir() and os.path.exists(target):
91 return
92
fa6d335b
MT
93 #if self.pakfire.config.get("debug"):
94 # msg = "Creating file (%s:%03d:%03d) " % \
95 # (tarfile.filemode(member.mode), member.uid, member.gid)
96 # if member.issym():
97 # msg += "/%s -> %s" % (member.name, member.linkname)
98 # elif member.islnk():
99 # msg += "/%s link to /%s" % (member.name, member.linkname)
100 # else:
101 # msg += "/%s" % member.name
102 # logging.debug(msg)
47a4cb89
MT
103
104 # Remove file if it has been existant
105 if not member.isdir() and os.path.exists(target):
106 os.unlink(target)
107
108 self.archive.extract(member, path=path)
109
110 # XXX implement setting of xattrs/acls here
111
112 if callback and not member.isdir():
113 callback(member.name, hash1="XXX", size=member.size)
114
115 def _make_progressbar(self):
116 # Don't display a progressbar if we are running in debug mode.
117 if self.pakfire.config.get("debug"):
118 return
119
120 if not sys.stdout.isatty():
121 return
122
123 widgets = [
124 " ",
125 "%s %-20s" % (_("Extracting:"), self.pkg.name),
126 " ",
127 progressbar.Bar(left="[", right="]"),
128 " ",
129# progressbar.Percentage(),
130# " ",
131 progressbar.ETA(),
132 " ",
133 ]
134
64302ca2
MT
135 # maxval must be > 0 and so we assume that
136 # empty packages have at least one file.
137 maxval = len(self.files) or 1
138
47a4cb89
MT
139 return progressbar.ProgressBar(
140 widgets=widgets,
64302ca2 141 maxval=maxval,
47a4cb89
MT
142 term_width=80,
143 )
144
145
146class InnerTarFile(tarfile.TarFile):
147 def __init__(self, *args, **kwargs):
148 # Force the pax format
149 kwargs["format"] = tarfile.PAX_FORMAT
150
151 if kwargs.has_key("env"):
152 self.env = kwargs.pop("env")
153
154 tarfile.TarFile.__init__(self, *args, **kwargs)
155
156 def __filter_xattrs(self, tarinfo):
157 logging.debug("Adding file: %s" % tarinfo.name)
158
159 filename = self.env.chrootPath(self.env.buildroot, tarinfo.name)
47a4cb89 160
9f2980a9
MT
161 # xattrs do only exists for regular files. If we don't have one,
162 # simply skip.
163 if os.path.isfile(filename):
164 for attr, value in xattr.get_all(filename):
165 tarinfo.pax_headers[attr] = value
166
167 logging.debug(" xattr: %s=%s" % (attr, value))
47a4cb89
MT
168
169 return tarinfo
170
171 def add(self, *args, **kwargs):
172 # Add filter for xattrs if no other filter is set.
173 if not kwargs.has_key("filter") and len(args) < 5:
174 kwargs["filter"] = self.__filter_xattrs
175
176 tarfile.TarFile.add(self, *args, **kwargs)
177
178
179# XXX this is totally ugly and needs to be done right!
180
181class Packager(object):
2d2f5762 182 ARCHIVE_FILES = ("info", "filelist", "signature", "data.img")
47a4cb89
MT
183
184 def __init__(self, pakfire, pkg, env):
185 self.pakfire = pakfire
186 self.pkg = pkg
187 self.env = env
188
189 self.tarball = None
190
191 # Store meta information
192 self.info = {
193 "package_format" : PACKAGE_FORMAT,
194 }
195 self.info.update(self.pkg.info)
196 self.info.update(self.pakfire.distro.info)
197
198 ### Create temporary files
199 # Create temp directory to where we extract all files again and
200 # gather some information about them like requirements and provides.
201 self.tempdir = self.env.chrootPath("tmp", "%s_data" % self.pkg.friendly_name)
202 if not os.path.exists(self.tempdir):
203 os.makedirs(self.tempdir)
204
205 # Create files that have the archive data
206 self.archive_files = {}
207 for i in self.ARCHIVE_FILES:
208 self.archive_files[i] = \
209 self.env.chrootPath("tmp", "%s_%s" % (self.pkg.friendly_name, i))
210
211 def __call__(self):
212 logging.debug("Packaging %s" % self.pkg.friendly_name)
213
214 # Create the tarball and add all data to it.
215 self.create_tarball()
216
217 chroot_tempdir = self.tempdir[len(self.env.chrootPath()):]
218 self.info.update({
219 "requires" : self.env.do("/usr/lib/buildsystem-tools/dependency-tracker requires %s" % chroot_tempdir,
a0577731 220 returnOutput=True, env=self.pkg.env).strip(),
47a4cb89 221 "provides" : self.env.do("/usr/lib/buildsystem-tools/dependency-tracker provides %s" % chroot_tempdir,
a0577731 222 returnOutput=True, env=self.pkg.env).strip(),
47a4cb89
MT
223 })
224
225 self.create_info()
226 self.create_signature()
227
228 # Create the outer tarball.
229 resultdir = os.path.join(self.env.chrootPath("result", self.pkg.arch))
230 if not os.path.exists(resultdir):
231 os.makedirs(resultdir)
232
233 filename = os.path.join(resultdir, self.pkg.filename)
234
235 tar = tarfile.TarFile(filename, mode="w", format=tarfile.PAX_FORMAT)
236
237 for i in self.ARCHIVE_FILES:
238 tar.add(self.archive_files[i], arcname=i)
239
240 tar.close()
241
242 def create_tarball(self):
243 tar = InnerTarFile(self.archive_files["data.img"], mode="w", env=self.env)
244
715a78e9
MT
245 includes = []
246 excludes = []
247
47a4cb89 248 for pattern in self.pkg.file_patterns:
715a78e9
MT
249 # Check if we are running in include or exclude mode.
250 if pattern.startswith("!"):
251 files = excludes
252
253 # Strip the ! charater
254 pattern = pattern[1:]
255
256 else:
257 files = includes
258
47a4cb89
MT
259 if pattern.startswith("/"):
260 pattern = pattern[1:]
261 pattern = self.env.chrootPath(self.env.buildroot, pattern)
262
263 # Recognize the type of the pattern. Patterns could be a glob
264 # pattern that is expanded here or just a directory which will
265 # be included recursively.
266 if "*" in pattern or "?" in pattern:
267 files += glob.glob(pattern)
268
269 elif os.path.exists(pattern):
270 # Add directories recursively...
271 if os.path.isdir(pattern):
272 for dir, subdirs, _files in os.walk(pattern):
273 for file in _files:
274 file = os.path.join(dir, file)
275 files.append(file)
276
277 # all other files are just added.
278 else:
279 files.append(pattern)
280
281 else:
282 logging.warning("Unrecognized pattern type: %s" % pattern)
283
715a78e9
MT
284 files = []
285 for file in includes:
58d47ee6
MT
286 # Skip if file is already in the file set or
287 # marked to be excluded from this archive.
288 if file in excludes or file in files:
715a78e9
MT
289 continue
290
291 files.append(file)
292
47a4cb89
MT
293 files.sort()
294
295 for file_real in files:
296 file_tar = file_real[len(self.env.chrootPath(self.env.buildroot)) + 1:]
297
715a78e9 298 tar.add(file_real, arcname=file_tar, recursive=False)
58d47ee6 299 os.unlink(file_real)
47a4cb89
MT
300
301 # Dump all files that are in the archive.
302 tar.list()
303
304 # Write all data to disk.
305 tar.close()
306
307 # Reopen the tarfile in read mode and extract all content to tempdir
308 tar = InnerTarFile(self.archive_files["data.img"])
309 tar.extractall(path=self.tempdir)
2d2f5762
MT
310
311 # Write filelist
312 f = open(self.archive_files["filelist"], mode="w")
313 for filename in tar.getnames():
314 if not filename.startswith("/"):
315 filename = "/%s" % filename
316
317 f.write("%s\n" % filename)
318 f.close()
319
47a4cb89
MT
320 tar.close()
321
322 # XXX compress the tarball here
323
324 def create_info(self):
325 f = open(self.archive_files["info"], "w")
326 f.write(BINARY_PACKAGE_META % self.info)
327 f.close()
328
329 def create_signature(self):
330 # Create an empty signature.
331 f = open(self.archive_files["signature"], "w")
332 f.close()