blob: be431202b31a768874e4cfe301a18806641834f4 [file] [log] [blame]
#!/usr/bin/env python3
import sys
import os
import os.path
import re
import subprocess
import tempfile
import urllib.request
import argparse
import hashlib
def initialize_apt(apt_dir, apt_args, args):
os.mkdir(os.path.join(apt_dir, 'etc'))
os.mkdir(os.path.join(apt_dir, 'etc', 'apt'))
os.mkdir(os.path.join(apt_dir, 'etc', 'apt', 'trusted.gpg.d'))
os.mkdir(os.path.join(apt_dir, 'etc', 'apt', 'preferences.d'))
os.mkdir(os.path.join(apt_dir, 'var'))
os.mkdir(os.path.join(apt_dir, 'var', 'lib'))
os.mkdir(os.path.join(apt_dir, 'var', 'lib', 'dpkg'))
with open(os.path.join(apt_dir, 'var', 'lib', 'dpkg', 'status'), 'w'):
pass
with open(os.path.join(apt_dir, 'etc', 'apt', 'sources.list'), 'w') as f:
f.write("""
deb http://deb.debian.org/debian/ {release} main contrib non-free
deb-src http://deb.debian.org/debian/ {release} main contrib non-free
deb https://security.debian.org/debian-security {release}-security main contrib non-free
deb-src https://security.debian.org/debian-security {release}-security main contrib non-free
deb http://deb.debian.org/debian/ {release}-updates main contrib non-free
deb-src http://deb.debian.org/debian/ {release}-updates main contrib non-free
deb http://deb.debian.org/debian {release}-backports main contrib non-free
deb-src http://deb.debian.org/debian {release}-backports main contrib non-free
""".format(release=args.release))
for key in args.apt_key:
basename = os.path.basename(key)
urllib.request.urlretrieve(key, os.path.join(apt_dir, 'etc', 'apt', 'trusted.gpg.d', basename))
subprocess.check_call(["apt-get"] + apt_args + ["update"])
def get_deps(apt_args, package):
env = dict(os.environ)
del env['LD_LIBRARY_PATH']
out = subprocess.check_output(["apt-rdepends"] + apt_args + [package], env=env)
deps = out.splitlines()
return set([dep for dep in deps if not dep.startswith(b" ")])
def get_all_deps(apt_args, packages):
deps = set()
for package in packages or ():
deps.update(get_deps(apt_args, package))
return deps
def map_virtual_packages(packages):
'''Maps known virtual packages to the preferred concrete packages which
provide them.'''
for package in packages:
if package == b'python-numpy-abi9':
yield b'python-numpy'
continue
if package == b'python3-numpy-abi9':
yield b'python3-numpy'
continue
if package == b'libjack-0.125':
yield b'libjack-jackd2-0'
continue
if package == b'fonts-freefont':
yield b'fonts-freefont-ttf'
continue
if package == b'gsettings-backend':
yield b'dconf-gsettings-backend'
continue
if package == b'gdal-abi-2-4-0':
yield b'libgdal20'
continue
if package == b'libglu1':
yield b'libglu1-mesa'
continue
if package == b'liblapack.so.3':
yield b'liblapack3'
continue
if package == b'libopencl1':
yield b'ocl-icd-libopencl1'
continue
if package == b'libgcc1':
yield b'libgcc-s1'
continue
if package == b'libopencl-1.2-1':
yield b'ocl-icd-libopencl1'
continue
if package == b'libblas.so.3':
yield b'libblas3'
continue
if package == b'debconf-2.0':
yield b'debconf'
continue
yield package
def download_deps(apt_args, packages, excludes, force_includes):
deps = get_all_deps(apt_args, packages)
exclude_deps = get_all_deps(apt_args, excludes)
deps -= exclude_deps
force_include_deps = get_all_deps(apt_args, force_includes)
deps |= force_include_deps
env = dict(os.environ)
del env['LD_LIBRARY_PATH']
subprocess.check_call([b"apt-get"] + [a.encode('utf-8') for a in apt_args] + [b"download"] + list(map_virtual_packages(deps)), env=env)
def fixup_files():
# Gotta remove those pesky epoch numbers in the file names. Bazel doesn't
# like them.
regex = re.compile(".%3a")
contents = os.listdir(os.getcwd())
for deb in contents:
new_name = regex.sub("", deb)
if new_name != deb:
os.rename(deb, new_name)
def sha256_checksum(filename, block_size=65536):
sha256 = hashlib.sha256()
with open(filename, 'rb') as f:
for block in iter(lambda: f.read(block_size), b''):
sha256.update(block)
return sha256.hexdigest()
def print_file_list():
contents = os.listdir(os.getcwd())
contents.sort()
print("_files = {")
for deb in contents:
print(' "%s": "%s",' % (deb, sha256_checksum(deb)))
print("}")
_ALWAYS_EXCLUDE = [
"dbus-session-bus",
"debconf",
"debconf-2.0",
"default-dbus-session-bus",
"dpkg",
"install-info",
"libc-dev",
"libc6",
"libc6-dev",
]
def main(argv):
parser = argparse.ArgumentParser()
parser.add_argument("--exclude", "-e", type=str, action="append", help="A package to exclude from the list")
parser.add_argument("--force-include", type=str, action="append", help="Force include this and its dependencies. Even if listed in excludes.")
parser.add_argument("--arch", type=str, default="amd64", help="Architecture to download files for.")
parser.add_argument("--apt-dir", type=str, help=" ".join([
"File to generate and store apt files in.",
"Helpful for saving time when downloading multiple groups of packages.",
"Some flags will be ignored in favor of the values used to create this folder, so be careful.",
]))
parser.add_argument("--release", type=str, default="bullseye", help="Debian release to use.")
parser.add_argument("--apt-key", type=str, action="append", default=[
"https://ftp-master.debian.org/keys/archive-key-11.asc",
"https://ftp-master.debian.org/keys/archive-key-11-security.asc",
], help="URL of an additional apt archive key to trust.")
parser.add_argument("package", nargs="+", help="The packages to download.")
args = parser.parse_args(argv[1:])
if args.apt_dir:
apt_dir = args.apt_dir
else:
apt_dir = tempfile.mkdtemp()
apt_args = ["-o", "Dir=" + apt_dir, "-o", "APT::Architecture=" + args.arch]
if not args.apt_dir:
print("Creating apt files in %s" % apt_dir)
initialize_apt(apt_dir, apt_args, args)
folder = tempfile.mkdtemp()
os.chdir(folder)
excludes = args.exclude or []
# Exclude common packages that don't make sense to include in everything all
# the time.
excludes += _ALWAYS_EXCLUDE
download_deps(apt_args, args.package, excludes, args.force_include)
fixup_files()
print_file_list()
print("Your packages are all in %s" % folder)
if __name__ == "__main__":
sys.exit(main(sys.argv))