import argparse
import logging
import os
import pathlib
import shutil
import subprocess
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "common"))
import installer
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument(
"-a", "--arch", required=True, help="deb package architecture")
parser.add_argument(
"-b", "--build-time", required=True, help="build timestamp")
parser.add_argument(
"-c", "--channel", required=True, help="package channel")
parser.add_argument("-d", "--branding", required=True, help="branding")
parser.add_argument(
"-f", "--official", action="store_true", help="official build")
parser.add_argument(
"-o", "--output-dir", required=True, help="output directory")
parser.add_argument("-s", "--sysroot", required=True, help="sysroot")
parser.add_argument("-t", "--target-os", required=True, help="target os")
return parser.parse_args()
def gen_control(context, staging_dir, deb_control, deb_changelog, deb_files,
pkg_name):
cmd = [
"dpkg-gencontrol",
f"-v{context['VERSIONFULL']}",
f"-c{deb_control}",
f"-l{deb_changelog}",
f"-f{deb_files}",
f"-p{pkg_name}",
f"-P{staging_dir}",
"-O",
]
output_control = staging_dir / "DEBIAN/control"
with output_control.open("w") as f:
if os.environ.get("VERBOSE"):
subprocess.check_call(cmd, stdout=f)
else:
subprocess.check_call(cmd, stdout=f, stderr=subprocess.DEVNULL)
deb_control.unlink()
def verify_package(context, deb_file):
depends = context["DEPENDS"]
expected_depends = [d.strip() for d in depends.split(", ") if d.strip()]
output = subprocess.check_output(["dpkg", "-I", str(deb_file)], text=True)
actual_depends = []
for line in output.splitlines():
if line.startswith(" Depends: "):
deps = line[len(" Depends: "):].split(", ")
actual_depends = [d.strip() for d in deps if d.strip()]
break
installer.verify_package_deps(expected_depends, actual_depends)
def main():
os.umask(0o022)
args = parse_args()
log_level = logging.INFO if os.environ.get("VERBOSE") else logging.ERROR
logging.basicConfig(level=log_level, format="%(message)s")
script_dir = pathlib.Path(__file__).parent.absolute()
output_dir = pathlib.Path(args.output_dir)
staging_dir = output_dir / f"deb-staging-{args.channel}"
tmp_file_dir = output_dir / f"deb-tmp-{args.channel}"
if staging_dir.exists():
shutil.rmtree(staging_dir)
if tmp_file_dir.exists():
shutil.rmtree(tmp_file_dir)
staging_dir.mkdir(parents=True, exist_ok=True)
tmp_file_dir.mkdir(parents=True, exist_ok=True)
deb_changelog = tmp_file_dir / "changelog"
deb_files = tmp_file_dir / "files"
deb_control = tmp_file_dir / "control"
inst = installer.Installer(
output_dir,
staging_dir,
args.channel,
args.branding,
args.arch,
args.target_os,
args.official,
)
inst.set_context({
"ARCHITECTURE": args.arch,
"BRANDING": args.branding,
"BUILD_TIMESTAMP": args.build_time,
"IS_OFFICIAL_BUILD": 1 if args.official else 0,
"OUTPUTDIR": output_dir,
"SCRIPTDIR": script_dir,
"STAGEDIR": staging_dir,
"TMPFILEDIR": tmp_file_dir,
})
inst.initialize()
channel = inst.channel
os.environ["ARCHITECTURE"] = args.arch
os.environ["DEBEMAIL"] = inst.context["MAINTMAIL"]
os.environ["DEBFULLNAME"] = inst.context["MAINTNAME"]
deb_common_deps_file = output_dir / "deb_common.deps"
with deb_common_deps_file.open("r") as f:
common_deps = f.read().strip().replace("\n", ", ")
inst.context["COMMON_DEPS"] = common_deps
inst.context["COMMON_PREDEPS"] = "dpkg (>= 1.14.0)"
inst.context["SHLIB_PERMS"] = 0o644
base_repo_config = "dl.google.com/linux/chrome/deb/ stable main"
default = f"deb [arch={args.arch}] https://{base_repo_config}"
inst.context["REPOCONFIG"] = os.environ.get("REPOCONFIG", default)
repo_config_regex = f"deb (\\[arch=[^]]*\\b{args.arch}\\b[^]]*\]"
repo_config_regex += f"[[:space:]]*) https?://{base_repo_config}"
inst.context["REPOCONFIGREGEX"] = repo_config_regex
inst.prep_staging_common()
(staging_dir / "DEBIAN").mkdir(parents=True, exist_ok=True)
(staging_dir / "DEBIAN").chmod(0o755)
(staging_dir / "etc/cron.daily").mkdir(parents=True, exist_ok=True)
(staging_dir / "etc/cron.daily").chmod(0o755)
(staging_dir /
f"usr/share/doc/{inst.context['USR_BIN_SYMLINK_NAME']}").mkdir(
parents=True, exist_ok=True)
(staging_dir /
f"usr/share/doc/{inst.context['USR_BIN_SYMLINK_NAME']}").chmod(0o755)
inst.stage_install_common()
logging.info(f"Staging Debian install files in '{staging_dir}'...")
install_dir = staging_dir / inst.context["INSTALLDIR"].lstrip("/")
cron_dir = install_dir / "cron"
cron_dir.mkdir(parents=True, exist_ok=True)
cron_dir.chmod(0o755)
cron_file = cron_dir / inst.context["PACKAGE"]
installer.process_template(output_dir / "installer/common/repo.cron",
cron_file, inst.context)
cron_file.chmod(0o755)
cron_daily_link = staging_dir / "etc/cron.daily" / inst.context["PACKAGE"]
if cron_daily_link.is_symlink() or cron_daily_link.exists():
cron_daily_link.unlink()
os.symlink(
os.path.join(inst.context["INSTALLDIR"], "cron",
inst.context["PACKAGE"]),
cron_daily_link,
)
for script in ["postinst", "prerm", "postrm"]:
dest = staging_dir / "DEBIAN" / script
installer.process_template(output_dir / f"installer/debian/{script}",
dest, inst.context)
dest.chmod(0o755)
inst.context["PACKAGE"] = inst.context["PACKAGE_ORIG"]
logging.info(f"Packaging {args.arch}...")
inst.context["PREDEPENDS"] = inst.context["COMMON_PREDEPS"]
inst.context["DEPENDS"] = inst.context["COMMON_DEPS"]
inst.context["PROVIDES"] = "www-browser"
installer.gen_changelog(inst.context, staging_dir, deb_changelog)
installer.process_template(script_dir / "control.template", deb_control,
inst.context)
os.environ["DEB_HOST_ARCH"] = args.arch
pkg_name = f"{inst.context['PACKAGE_ORIG']}-{channel}"
if deb_control.exists():
gen_control(
inst.context,
staging_dir,
deb_control,
deb_changelog,
deb_files,
pkg_name,
)
os.environ["SOURCE_DATE_EPOCH"] = args.build_time
staging_dir.chmod(0o750)
installer.run_command([
"fakeroot",
"dpkg-deb",
"-Znone",
"-b",
str(staging_dir),
str(tmp_file_dir),
])
package_file = f"{pkg_name}_{inst.context['VERSIONFULL']}_{args.arch}.deb"
package_path = tmp_file_dir / package_file
if args.official:
pkg_basename = package_path.name
installer.run_command(["ar", "-x", pkg_basename], cwd=tmp_file_dir)
installer.run_command([
"xz",
"-z9",
"-T0",
"--lzma2=dict=256MiB",
str(tmp_file_dir / "data.tar"),
])
installer.run_command(["xz", "-z0", str(tmp_file_dir / "control.tar")])
installer.run_command(
["ar", "-d", pkg_basename, "control.tar", "data.tar"],
cwd=tmp_file_dir,
)
installer.run_command(
["ar", "-r", pkg_basename, "control.tar.xz", "data.tar.xz"],
cwd=tmp_file_dir,
)
final_package_path = output_dir / package_file
shutil.move(package_path, final_package_path)
verify_package(inst.context, final_package_path)
shutil.rmtree(staging_dir)
shutil.rmtree(tmp_file_dir)
if __name__ == "__main__":
main()