]> git.ipfire.org Git - people/stevee/pakfire.git/blame - pakfire/packages/packager.py
Add new server commands.
[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
8c617c20 8import re
be4a3422 9import shutil
47a4cb89
MT
10import sys
11import tarfile
12import tempfile
1317485d 13import uuid
47a4cb89 14import xattr
ce9ffa40 15import zlib
47a4cb89 16
c1fbb0b7 17import pakfire.compress
8c617c20 18import util
c1fbb0b7 19
47a4cb89
MT
20from pakfire.constants import *
21from pakfire.i18n import _
22
114ac7ee 23from file import InnerTarFile
47a4cb89 24
47a4cb89 25class Packager(object):
bc9a7929 26 ARCHIVE_FILES = ("info", "filelist", "data.img")
47a4cb89
MT
27
28 def __init__(self, pakfire, pkg, env):
29 self.pakfire = pakfire
30 self.pkg = pkg
31 self.env = env
32
33 self.tarball = None
34
d507be4d
MT
35 self.cleanup = True
36
47a4cb89
MT
37 # Store meta information
38 self.info = {
39 "package_format" : PACKAGE_FORMAT,
d507be4d 40 "package_type" : self.type,
1317485d 41 "package_uuid" : uuid.uuid4(),
74f4a5f2 42 "payload_comp" : "",
d507be4d
MT
43
44 "requires" : "",
45 "provides" : "",
47a4cb89
MT
46 }
47 self.info.update(self.pkg.info)
8537c16d 48 self.info["groups"] = " ".join(self.info["groups"])
47a4cb89 49 self.info.update(self.pakfire.distro.info)
fc4d4177 50 self.info.update(self.env.info)
47a4cb89
MT
51
52 ### Create temporary files
53 # Create temp directory to where we extract all files again and
54 # gather some information about them like requirements and provides.
55 self.tempdir = self.env.chrootPath("tmp", "%s_data" % self.pkg.friendly_name)
56 if not os.path.exists(self.tempdir):
57 os.makedirs(self.tempdir)
58
59 # Create files that have the archive data
60 self.archive_files = {}
61 for i in self.ARCHIVE_FILES:
62 self.archive_files[i] = \
63 self.env.chrootPath("tmp", "%s_%s" % (self.pkg.friendly_name, i))
64
65 def __call__(self):
66 logging.debug("Packaging %s" % self.pkg.friendly_name)
67
68 # Create the tarball and add all data to it.
69 self.create_tarball()
70
d507be4d
MT
71 if self.type == "binary":
72 e = self.env.do("/usr/lib/buildsystem-tools/dependency-tracker %s" % \
73 self.tempdir[len(self.env.chrootPath()):], returnOutput=True,
74 env=self.pkg.env)
8c617c20 75
d507be4d
MT
76 for line in e.splitlines():
77 m = re.match(r"^(\w+)=(.*)$", line)
78 if m is None:
79 continue
8c617c20 80
d507be4d 81 key, val = m.groups()
8c617c20 82
d507be4d
MT
83 if not key in ("requires", "provides"):
84 continue
8c617c20 85
d507be4d
MT
86 val = val.strip("\"")
87 val = val.split()
8c617c20 88
d507be4d 89 self.info[key] = " ".join(sorted(val))
47a4cb89
MT
90
91 self.create_info()
47a4cb89
MT
92
93 # Create the outer tarball.
94 resultdir = os.path.join(self.env.chrootPath("result", self.pkg.arch))
95 if not os.path.exists(resultdir):
96 os.makedirs(resultdir)
97
98 filename = os.path.join(resultdir, self.pkg.filename)
99
100 tar = tarfile.TarFile(filename, mode="w", format=tarfile.PAX_FORMAT)
101
102 for i in self.ARCHIVE_FILES:
103 tar.add(self.archive_files[i], arcname=i)
104
105 tar.close()
106
d507be4d 107 def create_tarball(self, compress=None):
114ac7ee 108 tar = InnerTarFile(self.archive_files["data.img"], mode="w")
47a4cb89 109
d507be4d
MT
110 prefix = self.env.buildroot
111 if self.type == "source":
112 prefix = "build"
113
114 if not compress and self.type == "binary":
115 compress = "xz"
116
715a78e9
MT
117 includes = []
118 excludes = []
119
47a4cb89 120 for pattern in self.pkg.file_patterns:
715a78e9
MT
121 # Check if we are running in include or exclude mode.
122 if pattern.startswith("!"):
123 files = excludes
124
125 # Strip the ! charater
126 pattern = pattern[1:]
127
128 else:
129 files = includes
130
47a4cb89
MT
131 if pattern.startswith("/"):
132 pattern = pattern[1:]
d507be4d 133 pattern = self.env.chrootPath(prefix, pattern)
47a4cb89
MT
134
135 # Recognize the type of the pattern. Patterns could be a glob
136 # pattern that is expanded here or just a directory which will
137 # be included recursively.
138 if "*" in pattern or "?" in pattern:
139 files += glob.glob(pattern)
140
141 elif os.path.exists(pattern):
142 # Add directories recursively...
143 if os.path.isdir(pattern):
144 for dir, subdirs, _files in os.walk(pattern):
145 for file in _files:
146 file = os.path.join(dir, file)
147 files.append(file)
148
149 # all other files are just added.
150 else:
151 files.append(pattern)
152
715a78e9
MT
153 files = []
154 for file in includes:
58d47ee6
MT
155 # Skip if file is already in the file set or
156 # marked to be excluded from this archive.
157 if file in excludes or file in files:
715a78e9
MT
158 continue
159
160 files.append(file)
161
47a4cb89
MT
162 files.sort()
163
0bab8cdb
MT
164 filelist = open(self.archive_files["filelist"], mode="w")
165
47a4cb89 166 for file_real in files:
d507be4d 167 file_tar = file_real[len(self.env.chrootPath(prefix)) + 1:]
0bab8cdb 168 file_tmp = os.path.join(self.tempdir, file_tar)
47a4cb89 169
8c617c20
MT
170 if file_tar in ORPHAN_DIRECTORIES and not os.listdir(file_real):
171 logging.debug("Found an orphaned directory: %s" % file_tar)
172 os.unlink(file_real)
173 continue
174
114ac7ee 175 tar.add(file_real, arcname=file_tar)
49e0d33b 176
0bab8cdb
MT
177 # Record the packaged file to the filelist.
178 filelist.write("/%s\n" % file_tar)
179
180 # "Copy" the file to the tmp path for later investigation.
181 if os.path.isdir(file_real):
182 file_dir = file_tmp
183 else:
184 file_dir = os.path.dirname(file_tmp)
185
186 if not os.path.exists(file_dir):
187 os.makedirs(file_dir)
188
189 if os.path.isfile(file_real):
190 os.link(file_real, file_tmp)
191
9a0737c7
MT
192 elif os.path.islink(file_real):
193 # Dead symlinks cannot be copied by shutil.
194 os.symlink(os.readlink(file_real), file_tmp)
195
556b4305
MT
196 elif os.path.isdir(file_real):
197 if not os.path.exists(file_tmp):
198 os.makedirs(file_tmp)
199
0bab8cdb
MT
200 else:
201 shutil.copy2(file_real, file_tmp)
202
203 # Unlink the file and remove empty directories.
d507be4d
MT
204 if self.cleanup:
205 if not os.path.isdir(file_real):
206 os.unlink(file_real)
47a4cb89 207
d507be4d
MT
208 elif os.path.isdir(file_real) and not os.listdir(file_real):
209 os.rmdir(file_real)
0bab8cdb 210
47a4cb89
MT
211 # Dump all files that are in the archive.
212 tar.list()
213
214 # Write all data to disk.
215 tar.close()
0bab8cdb 216 filelist.close()
47a4cb89 217
ce9ffa40
MT
218 # compress the tarball here
219 if compress:
220 # Save algorithm to metadata.
221 self.info["payload_comp"] = compress
222
223 logging.debug("Compressing package with %s algorithm." % compress or "no")
224
c1fbb0b7
MT
225 # Compress file (in place).
226 pakfire.compress.compress(self.archive_files["data.img"],
227 algo=compress, progress=True)
47a4cb89 228
8c617c20
MT
229 # Calc hashsum of the payload of the package.
230 self.info["payload_hash1"] = util.calc_hash1(self.archive_files["data.img"])
231
47a4cb89
MT
232 def create_info(self):
233 f = open(self.archive_files["info"], "w")
234 f.write(BINARY_PACKAGE_META % self.info)
235 f.close()
d507be4d
MT
236
237 @property
238 def type(self):
239 raise NotImplementedError
240
241
242class BinaryPackager(Packager):
243 @property
244 def type(self):
245 return "binary"
246
247
248class SourcePackager(Packager):
249 def __init__(self, *args, **kwargs):
250 Packager.__init__(self, *args, **kwargs)
251
252 self.cleanup = False
253
254 @property
255 def type(self):
256 return "source"