Gentoo Archives: gentoo-portage-dev

From: "Manuel Rüger" <mrueg@g.o>
To: gentoo-portage-dev@l.g.o
Cc: "Manuel Rüger" <mrueg@g.o>
Subject: [gentoo-portage-dev] [PATCH v2] Support different compressors for binary packages
Date: Fri, 28 Jul 2017 09:59:34
Message-Id: 20170728095912.28471-1-mrueg@gentoo.org
1 This patch allows to set the compressor for binary packages via a
2 BINPKG_COMPRESSION variable. BINPKG_COMPRESSION_ARGS allows to specify
3 command-line arguments for the chosen compressor.
4 ---
5 bin/misc-functions.sh | 6 ++-
6 bin/quickpkg | 62 ++++++++++++++++------
7 man/make.conf.5 | 25 +++++++++
8 pym/_emerge/BinpkgExtractorAsync.py | 43 +++++++++++++--
9 pym/portage/dbapi/bintree.py | 8 +--
10 .../package/ebuild/_config/special_env_vars.py | 2 +-
11 pym/portage/package/ebuild/doebuild.py | 34 ++++++++++--
12 pym/portage/util/compression_probe.py | 45 +++++++++++++---
13 8 files changed, 186 insertions(+), 39 deletions(-)
14
15 diff --git a/bin/misc-functions.sh b/bin/misc-functions.sh
16 index 58755a1e1..079369313 100755
17 --- a/bin/misc-functions.sh
18 +++ b/bin/misc-functions.sh
19 @@ -453,7 +453,7 @@ __dyn_package() {
20 # Make sure $PWD is not ${D} so that we don't leave gmon.out files
21 # in there in case any tools were built with -pg in CFLAGS.
22
23 - cd "${T}"
24 + cd "${T}" || die
25
26 if [[ -n ${PKG_INSTALL_MASK} ]] ; then
27 PROOT=${T}/packaging/
28 @@ -478,8 +478,10 @@ __dyn_package() {
29 [ -z "${PORTAGE_BINPKG_TMPFILE}" ] && \
30 die "PORTAGE_BINPKG_TMPFILE is unset"
31 mkdir -p "${PORTAGE_BINPKG_TMPFILE%/*}" || die "mkdir failed"
32 + [ -z "${PORTAGE_COMPRESSION_COMMAND}" ] && \
33 + die "PORTAGE_COMPRESSION_COMMAND is unset"
34 tar $tar_options -cf - $PORTAGE_BINPKG_TAR_OPTS -C "${PROOT}" . | \
35 - $PORTAGE_BZIP2_COMMAND -c > "$PORTAGE_BINPKG_TMPFILE"
36 + $PORTAGE_COMPRESSION_COMMAND -c > "$PORTAGE_BINPKG_TMPFILE"
37 assert "failed to pack binary package: '$PORTAGE_BINPKG_TMPFILE'"
38 PYTHONPATH=${PORTAGE_PYTHONPATH:-${PORTAGE_PYM_PATH}} \
39 "${PORTAGE_PYTHON:-/usr/bin/python}" "$PORTAGE_BIN_PATH"/xpak-helper.py recompose \
40 diff --git a/bin/quickpkg b/bin/quickpkg
41 index 4f26ee912..750400592 100755
42 --- a/bin/quickpkg
43 +++ b/bin/quickpkg
44 @@ -8,6 +8,7 @@ import argparse
45 import errno
46 import math
47 import signal
48 +import subprocess
49 import sys
50 import tarfile
51
52 @@ -22,11 +23,13 @@ from portage.dbapi.dep_expand import dep_expand
53 from portage.dep import Atom, use_reduce
54 from portage.exception import (AmbiguousPackageName, InvalidAtom, InvalidData,
55 InvalidDependString, PackageSetNotFound, PermissionDenied)
56 -from portage.util import ConfigProtect, ensure_dirs, shlex_split, _xattr
57 +from portage.util import ConfigProtect, ensure_dirs, shlex_split, varexpand, _xattr
58 xattr = _xattr.xattr
59 from portage.dbapi.vartree import dblink, tar_contents
60 from portage.checksum import perform_md5
61 from portage._sets import load_default_config, SETPREFIX
62 +from portage.process import find_binary
63 +from portage.util.compression_probe import _compressors
64
65 def quickpkg_atom(options, infos, arg, eout):
66 settings = portage.settings
67 @@ -50,16 +53,16 @@ def quickpkg_atom(options, infos, arg, eout):
68 " ".join(e.args[0]))
69 del e
70 infos["missing"].append(arg)
71 - return
72 + return 1
73 except (InvalidAtom, InvalidData):
74 eout.eerror("Invalid atom: %s" % (arg,))
75 infos["missing"].append(arg)
76 - return
77 + return 1
78 if atom[:1] == '=' and arg[:1] != '=':
79 # dep_expand() allows missing '=' but it's really invalid
80 eout.eerror("Invalid atom: %s" % (arg,))
81 infos["missing"].append(arg)
82 - return
83 + return 1
84
85 matches = vardb.match(atom)
86 pkgs_for_arg = 0
87 @@ -108,16 +111,16 @@ def quickpkg_atom(options, infos, arg, eout):
88 in settings.features))
89 def protect(filename):
90 if not confprot.isprotected(filename):
91 - return False
92 + return 1
93 if include_unmodified_config:
94 file_data = contents[filename]
95 if file_data[0] == "obj":
96 orig_md5 = file_data[2].lower()
97 cur_md5 = perform_md5(filename, calc_prelink=1)
98 if orig_md5 == cur_md5:
99 - return False
100 + return 1
101 excluded_config_files.append(filename)
102 - return True
103 + return os.EX_OK
104 existing_metadata = dict(zip(fix_metadata_keys,
105 vardb.aux_get(cpv, fix_metadata_keys)))
106 category, pf = portage.catsplit(cpv)
107 @@ -134,12 +137,32 @@ def quickpkg_atom(options, infos, arg, eout):
108 binpkg_tmpfile = os.path.join(bintree.pkgdir,
109 cpv + ".tbz2." + str(os.getpid()))
110 ensure_dirs(os.path.dirname(binpkg_tmpfile))
111 - # The tarfile module will write pax headers holding the
112 - # xattrs only if PAX_FORMAT is specified here.
113 - tar = tarfile.open(binpkg_tmpfile, "w:bz2",
114 - format=tarfile.PAX_FORMAT if xattrs else tarfile.DEFAULT_FORMAT)
115 - tar_contents(contents, root, tar, protect=protect, xattrs=xattrs)
116 - tar.close()
117 + binpkg_compression = settings.get("BINPKG_COMPRESSION", "bzip2")
118 + try:
119 + compression = _compressors[binpkg_compression]
120 + except KeyError as e:
121 + eout.eerror("Invalid or unsupported compression method: %s" % e.args[0])
122 + return 1
123 + try:
124 + compression_binary = shlex_split(varexpand(compression["compress"], mydict=settings))[0]
125 + except IndexError as e:
126 + eout.eerror("Invalid or unsupported compression method: %s" % e.args[0])
127 + return 1
128 + if find_binary(compression_binary) is None:
129 + missing_package = compression["package"]
130 + eout.eerror("File compression unsupported %s. Missing package: %s" % (binpkg_compression, missing_package))
131 + return 1
132 + cmd = [varexpand(x, mydict=settings) for x in shlex_split(compression["compress"])]
133 + # Filter empty elements that make Popen fail
134 + cmd = [x for x in cmd if x != ""]
135 + with open(binpkg_tmpfile, "wb") as fobj:
136 + proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=fobj)
137 + # The tarfile module will write pax headers holding the
138 + # xattrs only if PAX_FORMAT is specified here.
139 + with tarfile.open(mode="w|",format=tarfile.PAX_FORMAT if xattrs else tarfile.DEFAULT_FORMAT, fileobj=proc.stdin) as tar:
140 + tar_contents(contents, root, tar, protect=protect, xattrs=xattrs)
141 + proc.stdin.close()
142 + proc.wait()
143 xpak.tbz2(binpkg_tmpfile).recompose_mem(xpdata)
144 finally:
145 if have_lock:
146 @@ -154,16 +177,20 @@ def quickpkg_atom(options, infos, arg, eout):
147 eout.eerror(str(e))
148 del e
149 eout.eerror("Failed to create package: '%s'" % binpkg_path)
150 + return 1
151 else:
152 eout.eend(0)
153 infos["successes"].append((cpv, s.st_size))
154 infos["config_files_excluded"] += len(excluded_config_files)
155 for filename in excluded_config_files:
156 eout.ewarn("Excluded config: '%s'" % filename)
157 + return os.EX_OK
158 if not pkgs_for_arg:
159 eout.eerror("Could not find anything " + \
160 "to match '%s'; skipping" % arg)
161 infos["missing"].append(arg)
162 + return 1
163 + return os.EX_OK
164
165 def quickpkg_set(options, infos, arg, eout):
166 eroot = portage.settings['EROOT']
167 @@ -179,7 +206,7 @@ def quickpkg_set(options, infos, arg, eout):
168 if not set in sets:
169 eout.eerror("Package set not found: '%s'; skipping" % (arg,))
170 infos["missing"].append(arg)
171 - return
172 + return 1
173
174 try:
175 atoms = setconfig.getSetAtoms(set)
176 @@ -187,10 +214,11 @@ def quickpkg_set(options, infos, arg, eout):
177 eout.eerror("Failed to process package set '%s' because " % set +
178 "it contains the non-existent package set '%s'; skipping" % e)
179 infos["missing"].append(arg)
180 - return
181 -
182 + return 1
183 + retval = os.EX_OK
184 for atom in atoms:
185 - quickpkg_atom(options, infos, atom, eout)
186 + retval |= quickpkg_atom(options, infos, atom, eout)
187 + return retval
188
189
190 def quickpkg_extended_atom(options, infos, atom, eout):
191 diff --git a/man/make.conf.5 b/man/make.conf.5
192 index aea189e4a..8e0d04967 100644
193 --- a/man/make.conf.5
194 +++ b/man/make.conf.5
195 @@ -110,6 +110,31 @@ ACCEPT_RESTRICT="*"
196 ACCEPT_RESTRICT="* -bindist"
197 .fi
198 .TP
199 +\fBBINPKG_COMPRESSION\fR = \fI"compression"\fR
200 +This variable is used to determine the compression used for \fIbinary
201 +packages\fR. Supported settings and compression algorithms are: bzip2, gzip,
202 +lz4, lzip, lzop, xz, zstd.
203 +.br
204 +Defaults to "bzip2".
205 +.br
206 +.I Example:
207 +.nf
208 +# Set it to use lz4:
209 +BINPKG_COMPRESSION="lz4"
210 +.fi
211 +.TP
212 +\fBBINPKG_COMPRESSION_ARGS\fR = \fI"arguments for compression command"\fR
213 +This variable is used to add additional arguments to the compression command
214 +selected by \fBBINPKG_COMPRESSION\fR.
215 +.br
216 +Defaults to "".
217 +.br
218 +.I Example:
219 +.nf
220 +# Set it to use compression level 9:
221 +BINPKG_COMPRESSION_ARGS="-9"
222 +.fi
223 +.TP
224 .B CBUILD
225 This variable is passed by the \fIebuild scripts\fR to the \fIconfigure\fR
226 as \fI\-\-build=${CBUILD}\fR only if it is defined. Do not set this yourself
227 diff --git a/pym/_emerge/BinpkgExtractorAsync.py b/pym/_emerge/BinpkgExtractorAsync.py
228 index 0bf3c74c9..e85f4ecac 100644
229 --- a/pym/_emerge/BinpkgExtractorAsync.py
230 +++ b/pym/_emerge/BinpkgExtractorAsync.py
231 @@ -6,8 +6,15 @@ import logging
232 from _emerge.SpawnProcess import SpawnProcess
233 import portage
234 from portage.localization import _
235 -from portage.util.compression_probe import (compression_probe,
236 - _decompressors)
237 +from portage.util.compression_probe import (
238 + compression_probe,
239 + _compressors,
240 +)
241 +from portage.process import find_binary
242 +from portage.util import (
243 + shlex_split,
244 + varexpand,
245 +)
246 import signal
247 import subprocess
248
249 @@ -28,8 +35,11 @@ class BinpkgExtractorAsync(SpawnProcess):
250 tar_options.append(portage._shell_quote("--xattrs-exclude=%s" % x))
251 tar_options = " ".join(tar_options)
252
253 - decomp_cmd = _decompressors.get(
254 - compression_probe(self.pkg_path))
255 + decomp = _compressors.get(compression_probe(self.pkg_path))
256 + if decomp is not None:
257 + decomp_cmd = decomp.get("decompress")
258 + else:
259 + decomp_cmd = None
260 if decomp_cmd is None:
261 self.scheduler.output("!!! %s\n" %
262 _("File compression header unrecognized: %s") %
263 @@ -39,6 +49,31 @@ class BinpkgExtractorAsync(SpawnProcess):
264 self._async_wait()
265 return
266
267 + try:
268 + decompression_binary = shlex_split(varexpand(decomp_cmd, mydict=self.env))[0]
269 + except IndexError:
270 + decompression_binary = ""
271 +
272 + if find_binary(decompression_binary) is None:
273 + # Try alternative command if it exists
274 + if _compressors.get(compression_probe(self.pkg_path)).get("decompress_alt"):
275 + decomp_cmd = _compressors.get(
276 + compression_probe(self.pkg_path)).get("decompress_alt")
277 + try:
278 + decompression_binary = shlex_split(varexpand(decomp_cmd, mydict=self.env))[0]
279 + except IndexError:
280 + decompression_binary = ""
281 +
282 + if find_binary(decompression_binary) is None:
283 + missing_package = _compressors.get(compression_probe(self.pkg_path)).get("package")
284 + self.scheduler.output("!!! %s\n" %
285 + _("File compression unsupported %s.\n Command was: %s.\n Maybe missing package: %s") %
286 + (self.pkg_path, varexpand(decomp_cmd, mydict=self.env), missing_package), log_path=self.logfile,
287 + background=self.background, level=logging.ERROR)
288 + self.returncode = 1
289 + self._async_wait()
290 + return
291 +
292 # Add -q to decomp_cmd opts, in order to avoid "trailing garbage
293 # after EOF ignored" warning messages due to xpak trailer.
294 # SIGPIPE handling (128 + SIGPIPE) should be compatible with
295 diff --git a/pym/portage/dbapi/bintree.py b/pym/portage/dbapi/bintree.py
296 index ca90ba8f9..c833968c2 100644
297 --- a/pym/portage/dbapi/bintree.py
298 +++ b/pym/portage/dbapi/bintree.py
299 @@ -141,7 +141,6 @@ class bindbapi(fakedbapi):
300 return [aux_cache.get(x, "") for x in wants]
301 mysplit = mycpv.split("/")
302 mylist = []
303 - tbz2name = mysplit[1]+".tbz2"
304 if not self.bintree._remotepkgs or \
305 not self.bintree.isremote(mycpv):
306 try:
307 @@ -1448,9 +1447,10 @@ class binarytree(object):
308 @staticmethod
309 def _parse_build_id(filename):
310 build_id = -1
311 - hyphen = filename.rfind("-", 0, -6)
312 + suffixlen = len(".xpak")
313 + hyphen = filename.rfind("-", 0, -(suffixlen + 1))
314 if hyphen != -1:
315 - build_id = filename[hyphen+1:-5]
316 + build_id = filename[hyphen+1:-suffixlen]
317 try:
318 build_id = long(build_id)
319 except ValueError:
320 @@ -1497,7 +1497,7 @@ class binarytree(object):
321 if self._remote_has_index:
322 rel_url = self._remotepkgs[instance_key].get("PATH")
323 if not rel_url:
324 - rel_url = pkgname+".tbz2"
325 + rel_url = pkgname + ".tbz2"
326 remote_base_uri = self._remotepkgs[instance_key]["BASE_URI"]
327 url = remote_base_uri.rstrip("/") + "/" + rel_url.lstrip("/")
328 else:
329 diff --git a/pym/portage/package/ebuild/_config/special_env_vars.py b/pym/portage/package/ebuild/_config/special_env_vars.py
330 index f7810e007..f9b29af93 100644
331 --- a/pym/portage/package/ebuild/_config/special_env_vars.py
332 +++ b/pym/portage/package/ebuild/_config/special_env_vars.py
333 @@ -56,7 +56,7 @@ environ_whitelist += [
334 "PORTAGE_BIN_PATH",
335 "PORTAGE_BUILDDIR", "PORTAGE_BUILD_GROUP", "PORTAGE_BUILD_USER",
336 "PORTAGE_BUNZIP2_COMMAND", "PORTAGE_BZIP2_COMMAND",
337 - "PORTAGE_COLORMAP", "PORTAGE_COMPRESS",
338 + "PORTAGE_COLORMAP", "PORTAGE_COMPRESS", "PORTAGE_COMPRESSION_COMMAND",
339 "PORTAGE_COMPRESS_EXCLUDE_SUFFIXES",
340 "PORTAGE_CONFIGROOT", "PORTAGE_DEBUG", "PORTAGE_DEPCACHEDIR",
341 "PORTAGE_DOHTML_UNWARNED_SKIPPED_EXTENSIONS",
342 diff --git a/pym/portage/package/ebuild/doebuild.py b/pym/portage/package/ebuild/doebuild.py
343 index e7db54bcf..3e75d1a51 100644
344 --- a/pym/portage/package/ebuild/doebuild.py
345 +++ b/pym/portage/package/ebuild/doebuild.py
346 @@ -68,11 +68,19 @@ from portage.exception import (DigestException, FileNotFound,
347 from portage.localization import _
348 from portage.output import colormap
349 from portage.package.ebuild.prepare_build_dirs import prepare_build_dirs
350 -from portage.util import apply_recursive_permissions, \
351 - apply_secpass_permissions, noiselimit, \
352 - writemsg, writemsg_stdout, write_atomic
353 +from portage.process import find_binary
354 +from portage.util import ( apply_recursive_permissions,
355 + apply_secpass_permissions,
356 + noiselimit,
357 + shlex_split,
358 + varexpand,
359 + writemsg,
360 + writemsg_stdout,
361 + write_atomic
362 + )
363 from portage.util.cpuinfo import get_cpu_count
364 from portage.util.lafilefixer import rewrite_lafile
365 +from portage.util.compression_probe import _compressors
366 from portage.util.socks5 import get_socks5_proxy
367 from portage.versions import _pkgsplit
368 from _emerge.BinpkgEnvExtractor import BinpkgEnvExtractor
369 @@ -518,6 +526,26 @@ def doebuild_environment(myebuild, mydo, myroot=None, settings=None,
370 mysettings["KV"] = ""
371 mysettings.backup_changes("KV")
372
373 + binpkg_compression = mysettings.get("BINPKG_COMPRESSION", "bzip2")
374 + try:
375 + compression = _compressors[binpkg_compression]
376 + except KeyError as e:
377 + writemsg("Warning: Invalid or unsupported compression method: %s" % e.args[0])
378 + else:
379 + try:
380 + compression_binary = shlex_split(varexpand(compression["compress"], mydict=settings))[0]
381 + except IndexError as e:
382 + writemsg("Warning: Invalid or unsupported compression method: %s" % e.args[0])
383 + else:
384 + if find_binary(compression_binary) is None:
385 + missing_package = compression["package"]
386 + writemsg("Warning: File compression unsupported %s. Missing package: %s" % (binpkg_compression, missing_package))
387 + else:
388 + cmd = [varexpand(x, mydict=settings) for x in shlex_split(compression["compress"])]
389 + # Filter empty elements
390 + cmd = [x for x in cmd if x != ""]
391 + mysettings['PORTAGE_COMPRESSION_COMMAND'] = ' '.join(cmd)
392 +
393 _doebuild_manifest_cache = None
394 _doebuild_broken_ebuilds = set()
395 _doebuild_broken_manifests = set()
396 diff --git a/pym/portage/util/compression_probe.py b/pym/portage/util/compression_probe.py
397 index 754621016..b15200044 100644
398 --- a/pym/portage/util/compression_probe.py
399 +++ b/pym/portage/util/compression_probe.py
400 @@ -11,14 +11,43 @@ if sys.hexversion >= 0x3000000:
401 from portage import _encodings, _unicode_encode
402 from portage.exception import FileNotFound, PermissionDenied
403
404 -_decompressors = {
405 - "bzip2": "${PORTAGE_BUNZIP2_COMMAND:-${PORTAGE_BZIP2_COMMAND} -d}",
406 - "gzip": "gzip -d",
407 - "lz4": "lz4 -d",
408 - "lzip": "lzip -d",
409 - "lzop": "lzop -d",
410 - "xz": "xz -d",
411 - "zstd": "zstd -d",
412 +_compressors = {
413 + "bzip2": {
414 + "compress": "${PORTAGE_BZIP2_COMMAND} ${BINPKG_COMPRESSION_ARGS}",
415 + "decompress": "${PORTAGE_BUNZIP2_COMMAND}",
416 + "decompress_alt": "${PORTAGE_BZIP2_COMMAND} -d",
417 + "package": "app-arch/bzip2",
418 + },
419 + "gzip": {
420 + "compress": "gzip ${BINPKG_COMPRESSION_ARGS}",
421 + "decompress": "gzip -d",
422 + "package": "app-arch/gzip",
423 + },
424 + "lz4": {
425 + "compress": "lz4 ${BINPKG_COMPRESSION_ARGS}",
426 + "decompress": "lz4 -d",
427 + "package": "app-arch/lz4",
428 + },
429 + "lzip": {
430 + "compress": "lzip ${BINPKG_COMPRESSION_ARGS}",
431 + "decompress": "lzip -d",
432 + "package": "app-arch/lzip",
433 + },
434 + "lzop": {
435 + "compress": "lzop ${BINPKG_COMPRESSION_ARGS}",
436 + "decompress": "lzop -d",
437 + "package": "app-arch/lzop",
438 + },
439 + "xz": {
440 + "compress": "xz ${BINPKG_COMPRESSION_ARGS}",
441 + "decompress": "xz -d",
442 + "package": "app-arch/xz-utils",
443 + },
444 + "zstd": {
445 + "compress": "zstd ${BINPKG_COMPRESSION_ARGS}",
446 + "decompress": "zstd -d",
447 + "package": "app-arch/zstd",
448 + },
449 }
450
451 _compression_re = re.compile(b'^(' +
452 --
453 2.13.3

Replies