#!/usr/bin/env python3 # -*- coding: utf-8 -*- import argparse import os import shutil import subprocess from pathlib import Path root = Path(__file__).parent.parent.parent with open(os.path.join(root, "share", "version.txt")) as f: version = f.read().strip() def remove_contents(d): """Remove all the contents of a directory.""" for p in d.iterdir(): if p.is_file() or p.is_symlink(): p.unlink() else: shutil.rmtree(p) def build(build_dir, qubes=False): """Build an RPM package in a temporary directory. The build process is the following: 1. Clean up any stale data from previous runs under ./dist. Note that this directory is used by `uv build` and `rpmbuild`. 2. Create the necessary RPM project structure under the specified build directory (default: ~/rpmbuild), and use symlinks to point to ./dist, so that we don't need to move files explicitly. 3. Create a Python source distribution using `uv build --sdist`. If we are building a Qubes package and there is a container image under `share/`, stash it temporarily under a different directory. 4. Build both binary and source RPMs using rpmbuild. Optionally, pass to the SPEC `_qubes` flag, that denotes we want to build a package for Qubes. """ dist_path = root / "dist" specfile_name = "dangerzone.spec" specfile_path = root / "install" / "linux" / specfile_name sdist_name = f"dangerzone-{version}.tar.gz" sdist_path = dist_path / sdist_name print("* Deleting old dist") if os.path.exists(dist_path): remove_contents(dist_path) else: dist_path.mkdir() print(f"* Creating RPM project structure under {build_dir}") build_dir.mkdir(exist_ok=True) for d in ["BUILD", "BUILDROOT", "RPMS", "SOURCES", "SPECS"]: subdir = build_dir / d subdir.mkdir(exist_ok=True) remove_contents(subdir) shutil.copy2(specfile_path, build_dir / "SPECS") rpm_dir = build_dir / "RPMS" / "x86_64" srpm_dir = build_dir / "SRPMS" srpm_dir.unlink(missing_ok=True) os.symlink(dist_path, rpm_dir) os.symlink(dist_path, srpm_dir) print("* Creating a Python sdist") tessdata = root / "share" / "tessdata" tessdata_bak = root / "tessdata.bak" container_tar = root / "share" / "container.tar" container_tar_bak = root / "container.tar.bak" if tessdata.exists(): tessdata.rename(tessdata_bak) stash_container = qubes and container_tar.exists() if stash_container and container_tar.exists(): container_tar.rename(container_tar_bak) try: subprocess.run(["uv", "build", "--sdist"], cwd=root, check=True) # Copy and unlink the Dangerzone sdist, instead of just renaming it. If the # build directory is outside the filesystem boundary (e.g., due to a container # mount), then a simple rename will not work. shutil.copy2(sdist_path, build_dir / "SOURCES" / sdist_name) sdist_path.unlink() finally: if tessdata_bak.exists(): tessdata_bak.rename(tessdata) if stash_container and container_tar_bak.exists(): container_tar_bak.rename(container_tar) print("* Building RPM package") cmd = [ "rpmbuild", "-v", "--define", f"_topdir {build_dir}", "-ba", "--nodebuginfo", f"{build_dir}/SPECS/dangerzone.spec", ] # In case of qubes, set the `%{_qubes}` SPEC variable to 1. See the dangerzone.spec # file for more details on how that's used. if qubes: cmd += [ "--define", "_qubes 1", ] subprocess.run(cmd, check=True) print("") print("The following files have been created:") print("\n".join([str(p) for p in dist_path.iterdir()])) def main(): parser = argparse.ArgumentParser() parser.add_argument( "--qubes", action="store_true", help="Build RPM package for a Qubes OS system" ) parser.add_argument( "--build-dir", default=Path.home() / "rpmbuild", help="Working directory for rpmbuild command", ) args = parser.parse_args() build(args.build_dir, args.qubes) if __name__ == "__main__": main()