]> git.ipfire.org Git - people/stevee/pakfire.git/blame - src/pakfire/packages/packager.py
packager: Set approprivate file permissions in archive
[people/stevee/pakfire.git] / src / pakfire / packages / packager.py
CommitLineData
47a4cb89 1#!/usr/bin/python
b792d887
MT
2###############################################################################
3# #
4# Pakfire - The IPFire package management system #
5# Copyright (C) 2011 Pakfire development team #
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 3 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, see <http://www.gnu.org/licenses/>. #
19# #
20###############################################################################
47a4cb89 21
c07a3ca7
MT
22import collections
23import fnmatch
47a4cb89 24import glob
c07a3ca7 25import hashlib
47a4cb89 26import os
8c617c20 27import re
be4a3422 28import shutil
47a4cb89
MT
29import sys
30import tarfile
31import tempfile
c07a3ca7 32import time
1317485d 33import uuid
ce9ffa40 34import zlib
47a4cb89 35
8b6bc023
MT
36import logging
37log = logging.getLogger("pakfire")
38
102c5ee7 39import pakfire.util as util
c1fbb0b7 40
47a4cb89
MT
41from pakfire.constants import *
42from pakfire.i18n import _
43
964aa579 44from . import tar
47a4cb89 45
47a4cb89 46class Packager(object):
ebd961c3
MT
47 payload_compression = None
48
c07a3ca7 49 def __init__(self, pakfire, pkg):
47a4cb89
MT
50 self.pakfire = pakfire
51 self.pkg = pkg
8c617c20 52
c07a3ca7
MT
53 self.files = []
54 self.tmpfiles = []
8c617c20 55
c07a3ca7
MT
56 def __del__(self):
57 for file in self.tmpfiles:
58 if not os.path.exists(file):
59 continue
8c617c20 60
8b6bc023 61 log.debug("Removing tmpfile: %s" % file)
8c617c20 62
5dda54e4
MT
63 if os.path.isdir(file):
64 util.rm(file)
65 else:
66 os.remove(file)
67
68 def mktemp(self, directory=False):
5dda54e4 69 if directory:
ebd961c3 70 filename = os.path.join("/", LOCAL_TMP_PATH, util.random_string())
5dda54e4 71 os.makedirs(filename)
ebd961c3
MT
72 else:
73 f = tempfile.NamedTemporaryFile(mode="w", delete=False)
74 f.close()
75
76 filename = f.name
5dda54e4 77
c07a3ca7 78 self.tmpfiles.append(filename)
4496b160 79
c07a3ca7 80 return filename
47a4cb89 81
c07a3ca7
MT
82 def save(self, filename):
83 # Create a new tar archive.
84 tar = tarfile.TarFile(filename, mode="w", format=tarfile.PAX_FORMAT)
47a4cb89 85
c07a3ca7
MT
86 # Add package formation information.
87 # Must always be the first file in the archive.
88 formatfile = self.create_package_format()
89 tar.add(formatfile, arcname="pakfire-format")
47a4cb89 90
c07a3ca7
MT
91 # XXX make sure all files belong to the root user
92
93 # Create checksum file.
94 chksumsfile = self.mktemp()
95 chksums = open(chksumsfile, "w")
47a4cb89 96
c07a3ca7
MT
97 # Add all files to tar file.
98 for arcname, filename in self.files:
99 tar.add(filename, arcname=arcname)
47a4cb89 100
c07a3ca7
MT
101 # Calculating the hash sum of the added file
102 # and store it in the chksums file.
103 f = open(filename)
e3bcfd23 104 h = hashlib.new("sha512")
c07a3ca7
MT
105 while True:
106 buf = f.read(BUFFER_SIZE)
107 if not buf:
108 break
109
110 h.update(buf)
111 f.close()
112
113 chksums.write("%-10s %s\n" % (arcname, h.hexdigest()))
114
115 # Close checksum file and attach it to the end.
116 chksums.close()
117 tar.add(chksumsfile, "chksums")
118
119 # Close the tar file.
47a4cb89
MT
120 tar.close()
121
c07a3ca7
MT
122 def add(self, filename, arcname=None):
123 if not arcname:
124 arcname = os.path.basename(filename)
125
8b6bc023 126 log.debug("Adding %s (as %s) to tarball." % (filename, arcname))
c07a3ca7
MT
127 self.files.append((arcname, filename))
128
129 def create_package_format(self):
130 filename = self.mktemp()
131
132 f = open(filename, "w")
133 f.write("%s\n" % PACKAGE_FORMAT)
134 f.close()
135
136 return filename
137
138 def create_filelist(self, datafile):
139 filelist = self.mktemp()
140
141 f = open(filelist, "w")
ebd961c3
MT
142
143 if self.payload_compression == "xz":
cd808413 144 datafile = tar.InnerTarFileXz.open(datafile)
ebd961c3 145 else:
cd808413 146 datafile = tar.InnerTarFile.open(datafile)
c07a3ca7 147
3ce6a8ad 148 while True:
964aa579 149 m = next(datafile)
3ce6a8ad
MT
150 if not m:
151 break
152
8b6bc023 153 log.debug(" %s %-8s %-8s %s %6s %s" % \
c07a3ca7
MT
154 (tarfile.filemode(m.mode), m.uname, m.gname,
155 "%d-%02d-%02d %02d:%02d:%02d" % time.localtime(m.mtime)[:6],
156 util.format_size(m.size), m.name))
c07a3ca7 157
efe141c7 158 f.write("%(type)1s %(size)-10d %(uname)-10s %(gname)-10s %(mode)-6d %(mtime)-12d" \
b41705e4 159 % m.get_info(tarfile.ENCODING, "strict"))
677ff42a 160
c07a3ca7
MT
161 # Calculate SHA512 hash of regular files.
162 if m.isreg():
163 mobj = datafile.extractfile(m)
e3bcfd23 164 h = hashlib.new("sha512")
47a4cb89 165
c07a3ca7
MT
166 while True:
167 buf = mobj.read(BUFFER_SIZE)
168 if not buf:
169 break
170 h.update(buf)
d507be4d 171
c07a3ca7 172 mobj.close()
cabf1fbe
MT
173 f.write(" %s" % h.hexdigest())
174 else:
175 f.write(" -")
c07a3ca7 176
cabf1fbe
MT
177 caps = m.pax_headers.get("PAKFIRE.capabilities", None)
178 if caps:
179 f.write(" %s" % caps)
c07a3ca7 180 else:
cabf1fbe
MT
181 f.write(" -")
182
efe141c7
MT
183 # The file name must be the last argument to contain spaces.
184 f.write(" %s" % m.name)
185
cabf1fbe 186 f.write("\n")
3e4a9b06 187
8b6bc023 188 log.info("")
c07a3ca7
MT
189
190 datafile.close()
191 f.close()
192
193 return filelist
194
195 def run(self):
196 raise NotImplementedError
d507be4d 197
675f2ca0 198 def getsize(self, datafile):
97d7d682 199 size = 0
675f2ca0
MT
200
201 if self.payload_compression == "xz":
cd808413 202 t = tar.InnerTarFileXz.open(datafile)
675f2ca0 203 else:
cd808413 204 t = tar.InnerTarFile.open(datafile)
97d7d682
MT
205
206 while True:
964aa579 207 m = next(t)
675f2ca0 208 if not m:
97d7d682
MT
209 break
210
675f2ca0 211 size += m.size
97d7d682 212
cd808413 213 t.close()
97d7d682
MT
214 return size
215
c07a3ca7
MT
216
217class BinaryPackager(Packager):
ebd961c3
MT
218 payload_compression = "xz"
219
5dda54e4 220 def __init__(self, pakfire, pkg, builder, buildroot):
c07a3ca7
MT
221 Packager.__init__(self, pakfire, pkg)
222
5dda54e4 223 self.builder = builder
c07a3ca7
MT
224 self.buildroot = buildroot
225
226 def create_metafile(self, datafile):
227 info = collections.defaultdict(lambda: "")
228
5dda54e4
MT
229 # Extract datafile in temporary directory and scan for dependencies.
230 tmpdir = self.mktemp(directory=True)
231
ebd961c3 232 if self.payload_compression == "xz":
cd808413 233 tarfile = tar.InnerTarFileXz.open(datafile)
ebd961c3 234 else:
cd808413 235 tarfile = tar.InnerTarFile.open(datafile)
ebd961c3 236
5dda54e4
MT
237 tarfile.extractall(path=tmpdir)
238 tarfile.close()
239
240 # Run the dependency tracker.
241 self.pkg.track_dependencies(self.builder, tmpdir)
242
c07a3ca7
MT
243 # Generic package information including Pakfire information.
244 info.update({
245 "pakfire_version" : PAKFIRE_VERSION,
c62d93f1 246 "uuid" : self.pkg.uuid,
1b59091e 247 "type" : "binary",
c07a3ca7
MT
248 })
249
250 # Include distribution information.
251 info.update(self.pakfire.distro.info)
252 info.update(self.pkg.info)
253
254 # Update package information for string formatting.
255 info.update({
5dda54e4
MT
256 "groups" : " ".join(self.pkg.groups),
257 "prerequires" : "\n".join([PACKAGE_INFO_DEPENDENCY_LINE % d \
258 for d in self.pkg.prerequires]),
259 "requires" : "\n".join([PACKAGE_INFO_DEPENDENCY_LINE % d \
260 for d in self.pkg.requires]),
261 "provides" : "\n".join([PACKAGE_INFO_DEPENDENCY_LINE % d \
262 for d in self.pkg.provides]),
263 "conflicts" : "\n".join([PACKAGE_INFO_DEPENDENCY_LINE % d \
264 for d in self.pkg.conflicts]),
265 "obsoletes" : "\n".join([PACKAGE_INFO_DEPENDENCY_LINE % d \
266 for d in self.pkg.obsoletes]),
a60f0f7d
MT
267 "recommends" : "\n".join([PACKAGE_INFO_DEPENDENCY_LINE % d \
268 for d in self.pkg.recommends]),
269 "suggests" : "\n".join([PACKAGE_INFO_DEPENDENCY_LINE % d \
270 for d in self.pkg.suggests]),
c07a3ca7
MT
271 })
272
273 # Format description.
274 description = [PACKAGE_INFO_DESCRIPTION_LINE % l \
275 for l in util.text_wrap(self.pkg.description, length=80)]
276 info["description"] = "\n".join(description)
277
278 # Build information.
279 info.update({
280 # Package it built right now.
281 "build_time" : int(time.time()),
282 "build_id" : uuid.uuid4(),
283 })
284
285 # Installed size (equals size of the uncompressed tarball).
286 info.update({
97d7d682 287 "inst_size" : self.getsize(datafile),
c07a3ca7
MT
288 })
289
290 metafile = self.mktemp()
291
292 f = open(metafile, "w")
293 f.write(PACKAGE_INFO % info)
294 f.close()
295
296 return metafile
297
298 def create_datafile(self):
715a78e9
MT
299 includes = []
300 excludes = []
301
c07a3ca7
MT
302 # List of all patterns, which grows.
303 patterns = self.pkg.files
304
13b54713
MT
305 # ...
306 orphan_directories = []
307 for d in ORPHAN_DIRECTORIES:
308 if d.startswith("usr/"):
309 b = os.path.basename(d)
310 b = os.path.join(self.buildroot, b)
311
312 if os.path.islink(b):
313 continue
314
315 d = os.path.join(self.buildroot, d)
316 if not os.path.islink(d):
317 orphan_directories.append(d)
318
c07a3ca7 319 for pattern in patterns:
715a78e9
MT
320 # Check if we are running in include or exclude mode.
321 if pattern.startswith("!"):
322 files = excludes
323
c07a3ca7 324 # Strip the ! character.
715a78e9 325 pattern = pattern[1:]
715a78e9
MT
326 else:
327 files = includes
328
c07a3ca7 329 # Expand file to point to chroot.
47a4cb89
MT
330 if pattern.startswith("/"):
331 pattern = pattern[1:]
c07a3ca7 332 pattern = os.path.join(self.buildroot, pattern)
47a4cb89
MT
333
334 # Recognize the type of the pattern. Patterns could be a glob
335 # pattern that is expanded here or just a directory which will
336 # be included recursively.
75bb74a7
MT
337 if "*" in pattern or "?" in pattern or ("[" in pattern and "]" in pattern):
338 _patterns = glob.glob(pattern)
339 else:
340 _patterns = [pattern,]
341
342 for pattern in _patterns:
e70f166b
MT
343 # Try to stat the pattern. If that is not successful, we cannot go on.
344 try:
345 os.lstat(pattern)
346 except OSError:
75bb74a7 347 continue
47a4cb89 348
26639c54
MT
349 # Add directories recursively but skip those symlinks
350 # that point to a directory.
351 if os.path.isdir(pattern) and not os.path.islink(pattern):
c07a3ca7
MT
352 # Add directory itself.
353 files.append(pattern)
354
47a4cb89 355 for dir, subdirs, _files in os.walk(pattern):
c07a3ca7 356 for subdir in subdirs:
13b54713 357 if subdir in orphan_directories:
c07a3ca7
MT
358 continue
359
360 subdir = os.path.join(dir, subdir)
361 files.append(subdir)
362
47a4cb89
MT
363 for file in _files:
364 file = os.path.join(dir, file)
365 files.append(file)
366
26639c54 367 # All other files are just added.
47a4cb89
MT
368 else:
369 files.append(pattern)
370
715a78e9
MT
371 files = []
372 for file in includes:
58d47ee6
MT
373 # Skip if file is already in the file set or
374 # marked to be excluded from this archive.
375 if file in excludes or file in files:
715a78e9
MT
376 continue
377
c00c6f33
MT
378 # Skip orphan directories.
379 if file in orphan_directories and not os.listdir(file):
8b6bc023 380 log.debug("Found an orphaned directory: %s" % file)
c00c6f33
MT
381 continue
382
715a78e9 383 files.append(file)
c00c6f33
MT
384
385 while True:
386 file = os.path.dirname(file)
387
388 if file == self.buildroot:
389 break
390
391 if not file in files:
392 files.append(file)
393
47a4cb89
MT
394 files.sort()
395
c07a3ca7
MT
396 # Load progressbar.
397 message = "%-10s : %s" % (_("Packaging"), self.pkg.friendly_name)
398 pb = util.make_progress(message, len(files), eta=False)
399
400 datafile = self.mktemp()
ebd961c3 401 if self.payload_compression == "xz":
cd808413 402 t = tar.InnerTarFileXz.open(datafile, mode="w")
ebd961c3 403 else:
cd808413 404 t = tar.InnerTarFile.open(datafile, mode="w")
c07a3ca7
MT
405
406 # All files in the tarball are relative to this directory.
407 basedir = self.buildroot
0bab8cdb 408
c07a3ca7
MT
409 i = 0
410 for file in files:
411 if pb:
412 i += 1
413 pb.update(i)
47a4cb89 414
c07a3ca7
MT
415 # Never package /.
416 if os.path.normpath(file) == os.path.normpath(basedir):
8c617c20
MT
417 continue
418
c00c6f33 419 # Name of the file in the archive.
c07a3ca7 420 arcname = "/%s" % os.path.relpath(file, basedir)
49e0d33b 421
c07a3ca7 422 # Add file to tarball.
cd808413 423 t.add(file, arcname=arcname, recursive=False)
c07a3ca7
MT
424
425 # Remove all packaged files.
426 for file in reversed(files):
c07a3ca7
MT
427 # It's okay if we cannot remove directories,
428 # when they are not empty.
429 if os.path.isdir(file):
430 try:
431 os.rmdir(file)
432 except OSError:
433 continue
0bab8cdb 434 else:
dcb5bc82
MT
435 try:
436 os.unlink(file)
437 except OSError:
438 pass
c07a3ca7
MT
439
440 while True:
441 file = os.path.dirname(file)
442
443 if not file.startswith(basedir):
444 break
445
446 try:
447 os.rmdir(file)
448 except OSError:
449 break
450
451 # Close the tarfile.
cd808413 452 t.close()
c07a3ca7
MT
453
454 # Finish progressbar.
455 if pb:
456 pb.finish()
457
458 return datafile
0bab8cdb 459
c07a3ca7
MT
460 def create_scriptlets(self):
461 scriptlets = []
0bab8cdb 462
08e95360
MT
463 # Collect all prerequires for the scriptlets.
464 prerequires = []
465
c07a3ca7
MT
466 for scriptlet_name in SCRIPTS:
467 scriptlet = self.pkg.get_scriptlet(scriptlet_name)
0bab8cdb 468
c07a3ca7
MT
469 if not scriptlet:
470 continue
471
472 # Write script to a file.
473 scriptlet_file = self.mktemp()
474
536bec81
MT
475 lang = scriptlet["lang"]
476
477 if lang == "bin":
c07a3ca7
MT
478 path = lang["path"]
479 try:
480 f = open(path, "b")
481 except OSError:
964aa579 482 raise Exception("Cannot open script file: %s" % lang["path"])
c07a3ca7
MT
483
484 s = open(scriptlet_file, "wb")
485
486 while True:
487 buf = f.read(BUFFER_SIZE)
488 if not buf:
489 break
490
491 s.write(buf)
492
493 f.close()
494 s.close()
9a0737c7 495
536bec81 496 elif lang == "shell":
c07a3ca7
MT
497 s = open(scriptlet_file, "w")
498
499 # Write shell script to file.
500 s.write("#!/bin/sh -e\n\n")
501 s.write(scriptlet["scriptlet"])
502 s.write("\n\nexit 0\n")
c07a3ca7 503 s.close()
556b4305 504
08e95360
MT
505 if scriptlet_name in SCRIPTS_PREREQUIRES:
506 # Shell scripts require a shell to be executed.
507 prerequires.append("/bin/sh")
508
509 prerequires += self.builder.find_prerequires(scriptlet_file)
510
536bec81
MT
511 elif lang == "python":
512 # Write the code to the scriptlet file.
513 s = open(scriptlet_file, "w")
514 s.write(scriptlet["scriptlet"])
515 s.close()
516
0bab8cdb 517 else:
964aa579 518 raise Exception("Unknown scriptlet language: %s" % scriptlet["lang"])
0bab8cdb 519
c07a3ca7 520 scriptlets.append((scriptlet_name, scriptlet_file))
47a4cb89 521
08e95360
MT
522 # Cleanup prerequires.
523 self.pkg.update_prerequires(prerequires)
0bab8cdb 524
c07a3ca7 525 return scriptlets
47a4cb89 526
3c5a85f3 527 def find_files(self, datafile, patterns):
ebd961c3 528 if self.payload_compression == "xz":
cd808413 529 datafile = tar.InnerTarFileXz.open(datafile)
ebd961c3 530 else:
cd808413 531 datafile = tar.InnerTarFile.open(datafile)
c07a3ca7
MT
532
533 members = datafile.getmembers()
534
3c5a85f3
MT
535 files = []
536 dirs = []
c07a3ca7 537
3c5a85f3
MT
538 # Find all directories in the file list.
539 for file in patterns:
c07a3ca7
MT
540 if file.startswith("/"):
541 file = file[1:]
542
543 for member in members:
544 if member.name == file and member.isdir():
3c5a85f3 545 dirs.append(file)
47a4cb89 546
3c5a85f3 547 for d in dirs:
c07a3ca7 548 for member in members:
3c5a85f3
MT
549 if not member.isdir() and member.name.startswith(d):
550 files.append(member.name)
ce9ffa40 551
3c5a85f3 552 for pattern in patterns:
c07a3ca7
MT
553 if pattern.startswith("/"):
554 pattern = pattern[1:]
555
556 for member in members:
557 if not fnmatch.fnmatch(member.name, pattern):
558 continue
559
3c5a85f3 560 if member.name in files:
c07a3ca7
MT
561 continue
562
3c5a85f3 563 files.append(member.name)
ce9ffa40 564
c07a3ca7 565 # Sort list alphabetically.
3c5a85f3
MT
566 files.sort()
567
568 return files
569
570 def create_configfiles(self, datafile):
571 files = self.find_files(datafile, self.pkg.configfiles)
47a4cb89 572
c07a3ca7 573 configsfile = self.mktemp()
8c617c20 574
c07a3ca7 575 f = open(configsfile, "w")
3c5a85f3 576 for file in files:
c07a3ca7 577 f.write("%s\n" % file)
47a4cb89 578 f.close()
d507be4d 579
c07a3ca7 580 return configsfile
d507be4d 581
3c5a85f3
MT
582 def create_datafiles(self, datafile):
583 files = self.find_files(datafile, self.pkg.datafiles)
584
585 datafile = self.mktemp()
586
587 f = open(datafile, "w")
588 for file in files:
589 f.write("%s\n" % file)
590 f.close()
591
592 return datafile
593
75bb74a7 594 def run(self, resultdir):
c07a3ca7
MT
595 # Add all files to this package.
596 datafile = self.create_datafile()
597
598 # Get filelist from datafile.
599 filelist = self.create_filelist(datafile)
3c5a85f3
MT
600 configfiles = self.create_configfiles(datafile)
601 datafiles = self.create_datafiles(datafile)
c07a3ca7
MT
602
603 # Create script files.
604 scriptlets = self.create_scriptlets()
605
606 metafile = self.create_metafile(datafile)
607
c07a3ca7
MT
608 # Add files to the tar archive in correct order.
609 self.add(metafile, "info")
610 self.add(filelist, "filelist")
3c5a85f3
MT
611 self.add(configfiles, "configfiles")
612 self.add(datafiles, "datafiles")
c07a3ca7
MT
613 self.add(datafile, "data.img")
614
615 for scriptlet_name, scriptlet_file in scriptlets:
616 self.add(scriptlet_file, "scriptlets/%s" % scriptlet_name)
617
618 # Build the final package.
619 tempfile = self.mktemp()
620 self.save(tempfile)
621
75bb74a7
MT
622 # Add architecture information to path.
623 resultdir = "%s/%s" % (resultdir, self.pkg.arch)
c07a3ca7 624
75bb74a7
MT
625 if not os.path.exists(resultdir):
626 os.makedirs(resultdir)
c07a3ca7 627
75bb74a7 628 resultfile = os.path.join(resultdir, self.pkg.package_filename)
8b6bc023 629 log.info("Saving package to %s" % resultfile)
75bb74a7
MT
630 try:
631 os.link(tempfile, resultfile)
632 except OSError:
633 shutil.copy2(tempfile, resultfile)
d507be4d
MT
634
635
636class SourcePackager(Packager):
ebd961c3
MT
637 payload_compression = None
638
c07a3ca7
MT
639 def create_metafile(self, datafile):
640 info = collections.defaultdict(lambda: "")
641
642 # Generic package information including Pakfire information.
643 info.update({
644 "pakfire_version" : PAKFIRE_VERSION,
1b59091e 645 "type" : "source",
c07a3ca7
MT
646 })
647
648 # Include distribution information.
649 info.update(self.pakfire.distro.info)
650 info.update(self.pkg.info)
651
8b88b494 652 # Size is the size of the (uncompressed) datafile.
97d7d682 653 info["inst_size"] = self.getsize(datafile)
8b88b494 654
c07a3ca7
MT
655 # Update package information for string formatting.
656 requires = [PACKAGE_INFO_DEPENDENCY_LINE % r for r in self.pkg.requires]
657 info.update({
658 "groups" : " ".join(self.pkg.groups),
659 "requires" : "\n".join(requires),
660 })
661
662 # Format description.
663 description = [PACKAGE_INFO_DESCRIPTION_LINE % l \
664 for l in util.text_wrap(self.pkg.description, length=80)]
665 info["description"] = "\n".join(description)
666
667 # Build information.
668 info.update({
669 # Package it built right now.
670 "build_time" : int(time.time()),
671 "build_id" : uuid.uuid4(),
672 })
673
1b59091e
MT
674 # Arches equals supported arches.
675 info["arch"] = self.pkg.supported_arches
676
c07a3ca7
MT
677 # Set UUID
678 # XXX replace this by the payload hash
679 info.update({
680 "uuid" : uuid.uuid4(),
681 })
682
683 metafile = self.mktemp()
684
685 f = open(metafile, "w")
686 f.write(PACKAGE_INFO % info)
687 f.close()
688
689 return metafile
690
691 def create_datafile(self):
7d40ac70
MT
692 # Create a list of all files that have to be put into the
693 # package.
694 files = []
c07a3ca7 695
7d40ac70 696 # Download all files that go into the package.
c07a3ca7 697 for file in self.pkg.download():
7d40ac70
MT
698 assert os.path.getsize(file), "Don't package empty files"
699 files.append(("files/%s" % os.path.basename(file), file))
c07a3ca7
MT
700
701 # Add all files in the package directory.
7d40ac70
MT
702 for file in self.pkg.files:
703 files.append((os.path.relpath(file, self.pkg.path), file))
704
705 # Add files in alphabetical order.
706 files.sort()
707
708 # Load progressbar.
709 message = "%-10s : %s" % (_("Packaging"), self.pkg.friendly_name)
710 pb = util.make_progress(message, len(files), eta=False)
711
712 filename = self.mktemp()
ebd961c3 713 if self.payload_compression == "xz":
cd808413 714 datafile = tar.InnerTarFileXz.open(filename, mode="w")
ebd961c3 715 else:
cd808413 716 datafile = tar.InnerTarFile.open(filename, mode="w")
7d40ac70
MT
717
718 i = 0
719 for arcname, file in files:
720 if pb:
721 i += 1
722 pb.update(i)
c07a3ca7 723
7d40ac70 724 datafile.add(file, arcname)
c07a3ca7
MT
725 datafile.close()
726
7d40ac70
MT
727 if pb:
728 pb.finish()
729
c07a3ca7
MT
730 return filename
731
7d40ac70
MT
732 def run(self, resultdir):
733 # Create resultdir if it does not exist yet.
734 if not os.path.exists(resultdir):
735 os.makedirs(resultdir)
c07a3ca7 736
8b6bc023 737 log.info(_("Building source package %s:") % self.pkg.package_filename)
c07a3ca7 738
7d40ac70
MT
739 # The filename where this source package is saved at.
740 target_filename = os.path.join(resultdir, self.pkg.package_filename)
741
c07a3ca7
MT
742 # Add datafile to package.
743 datafile = self.create_datafile()
744
745 # Create filelist out of data.
746 filelist = self.create_filelist(datafile)
747
748 # Create metadata.
749 metafile = self.create_metafile(datafile)
750
751 # Add files to the tar archive in correct order.
752 self.add(metafile, "info")
753 self.add(filelist, "filelist")
754 self.add(datafile, "data.img")
755
756 # Build the final tarball.
7d40ac70
MT
757 try:
758 self.save(target_filename)
759 except:
760 # Remove the target file when anything went wrong.
761 os.unlink(target_filename)
762 raise
d507be4d 763
7d40ac70 764 return target_filename