diff --git a/metapkg/commands/build.py b/metapkg/commands/build.py index 3011c63..f9cc887 100644 --- a/metapkg/commands/build.py +++ b/metapkg/commands/build.py @@ -41,7 +41,10 @@ class Build(base.Command): { --pkg-revision= : Override package revision number (defaults to 1). } { --pkg-subdist= : Set package sub-distribution (e.g. nightly). } { --pkg-tags= : Comma-separated list of key=value pairs to include in - pckage metadata } + pckage metadata. } + { --pkg-compression= : Comma-separated list of compression encodings to + apply if building for the specified target + produces a tarball (defaults to gzip,zstd). } { --extra-optimizations : Enable extra optimization (increases build times). } """ @@ -66,6 +69,7 @@ def handle(self) -> int: extra_opt = self.option("extra-optimizations") jobs = self.option("jobs") tags_string = self.option("pkg-tags") + compression_string = self.option("pkg-compression") # Older yum, fakeroot and possibly other tools # customarily attempt to iterate over _all_ @@ -86,6 +90,10 @@ def handle(self) -> int: k, _, v = pair.strip().partition("=") tags[k.strip()] = v.strip() + compression = [] + if compression_string: + compression = compression_string.split(",") + modname, _, clsname = pkgname.rpartition(":") mod = importlib.import_module(modname) @@ -131,6 +139,10 @@ def handle(self) -> int: os.chmod(workdir, 0o755) + extra = {} + if compression: + extra["compression"] = compression + try: target.build( targets.BuildRequest( @@ -148,6 +160,7 @@ def handle(self) -> int: subdist=subdist, extra_opt=extra_opt, jobs=jobs or 0, + **extra, ), ) finally: diff --git a/metapkg/targets/base.py b/metapkg/targets/base.py index 46554c7..2bf2334 100644 --- a/metapkg/targets/base.py +++ b/metapkg/targets/base.py @@ -85,6 +85,7 @@ class BuildRequest(NamedTuple): subdist: str | None = None extra_opt: bool = False jobs: int = 1 + compression: list[str] = ["gzip", "zstd"] class Target: diff --git a/metapkg/targets/generic/build.py b/metapkg/targets/generic/build.py index 3a8a669..7a2572a 100644 --- a/metapkg/targets/generic/build.py +++ b/metapkg/targets/generic/build.py @@ -17,10 +17,29 @@ from metapkg import tools +_supported_compression_schemes = { + "gzip", + "zip", + "zstd", +} + + class Build(targets.Build): _srcroot: pathlib.Path _pkgroot: pathlib.Path + def __init__( + self, + target: targets.Target, + request: targets.BuildRequest, + ) -> None: + super().__init__(target, request) + self._compression = frozenset(request.compression) + if unsup := self._compression - _supported_compression_schemes: + raise ValueError( + f"unsupported compression scheme(s): {', '.join(unsup)}" + ) + def prepare(self) -> None: super().prepare() @@ -560,18 +579,19 @@ def _package(self, files: list[pathlib.Path]) -> None: mime = magic.from_file(str(src_root / dest), mime=True) - tools.cmd( - "zstd", - "-19", - f"{an}{fn.suffix}", - cwd=archives_abs, - ) - installrefs = [ f"{an}{fn.suffix}", f"{an}{fn.suffix}.zst", ] + installrefs_ct = { + f"{an}{fn.suffix}": { + "type": mime, + "encoding": "identity", + "suffix": fn.suffix, + }, + } + installrefs_ct = { f"{an}{fn.suffix}": { "type": mime, @@ -585,56 +605,134 @@ def _package(self, files: list[pathlib.Path]) -> None: }, } + if "zstd" in self._compression: + tools.cmd( + "zstd", + "--keep", + "-19", + f"{an}{fn.suffix}", + cwd=archives_abs, + ) + installrefs.append(f"{an}{fn.suffix}.zst") + installrefs_ct[f"{an}{fn.suffix}.zst"] = { + "type": mime, + "encoding": "zstd", + "suffix": ".zst", + } + + if "gzip" in self._compression: + tools.cmd( + "gzip", + "-k", + "-9", + f"{an}{fn.suffix}", + cwd=archives_abs, + ) + installrefs.append(f"{an}{fn.suffix}.gz") + installrefs_ct[f"{an}{fn.suffix}.gz"] = { + "type": mime, + "encoding": "gzip", + "suffix": ".gz", + } + + if "zip" in self._compression: + tools.cmd( + "zip", + "-9", + f"{an}{fn.suffix}.zip", + f"{an}{fn.suffix}", + cwd=archives_abs, + ) + installrefs.append(f"{an}{fn.suffix}.zip") + installrefs_ct[f"{an}{fn.suffix}.zip"] = { + "type": "application/zip", + "encoding": "identity", + "suffix": ".zip", + } + else: - if layout is packages.PackageFileLayout.FLAT: - src = image_root - else: - prefix = self.get_bundle_install_prefix().relative_to("/") - src = image_root / prefix + prefix = self.get_bundle_install_prefix().relative_to("/") - tarball = f"{an}.tar" - tools.cmd( - self.sh_get_command("tar"), - "--transform", - f"flags=r;s|^\\./|{an}/|", - "-c", - "-f", - os.path.relpath(archives_abs / tarball, start=src), - ".", - cwd=src, - ) + installrefs = [] + installrefs_ct = {} - tools.cmd( - "zstd", - "-19", - tarball, - cwd=archives_abs, - ) + if self._compression & {"gzip", "zstd"}: + if layout is packages.PackageFileLayout.FLAT: + src = image_root + else: + src = image_root / prefix + + tarball = f"{an}.tar" + tools.cmd( + self.sh_get_command("tar"), + "--transform", + f"flags=r;s|^\\./|{an}/|", + "-c", + "-f", + os.path.relpath(archives_abs / tarball, start=src), + ".", + cwd=src, + ) - tools.cmd( - "gzip", - "-9", - tarball, - cwd=archives_abs, - ) + if "zstd" in self._compression: + tools.cmd( + "zstd", + "--keep", + "-19", + tarball, + cwd=archives_abs, + ) + installrefs.append(f"{tarball}.zst") + installrefs_ct[f"{tarball}.zst"] = { + "type": "application/x-tar", + "encoding": "zstd", + "suffix": ".tar.zst", + } + + if "gzip" in self._compression: + tools.cmd( + "gzip", + "-k", + "-9", + tarball, + cwd=archives_abs, + ) - installrefs = [ - f"{tarball}.gz", - f"{tarball}.zst", - ] + installrefs.append(f"{tarball}.gz") + installrefs_ct[f"{tarball}.gz"] = { + "type": "application/x-tar", + "encoding": "gzip", + "suffix": ".tar.gz", + } - installrefs_ct = { - f"{tarball}.gz": { - "type": "application/x-tar", - "encoding": "gzip", - "suffix": ".tar.gz", - }, - f"{tarball}.zst": { - "type": "application/x-tar", - "encoding": "zstd", - "suffix": ".tar.zst", - }, - } + (archives_abs / tarball).unlink(missing_ok=True) + + if "zip" in self._compression: + if layout is packages.PackageFileLayout.FLAT: + src = "." + else: + os.symlink( + prefix, + image_root / an, + target_is_directory=True, + ) + src = an + + tools.cmd( + "zip", + "-9", + "-r", + archives_abs / f"{an}.zip", + src, + cwd=image_root, + ) + + installrefs.append(f"{an}.zip") + installrefs_ct[f"{an}.zip"] = { + "type": "application/zip", + "encoding": "identity", + "suffix": ".zip", + } with open(archives_abs / "build-metadata.json", "w") as vf: json.dump(