blob: e8caebdf802dbbef994c88ffd22eae04ddcbe186 [file] [log] [blame]
Philipp Schrader0e19c602018-03-07 21:07:22 -08001#!/usr/bin/env python3
2
3import sys
4import os
Brian Silverman4b90dd02022-01-08 18:16:22 -08005import os.path
Philipp Schrader0e19c602018-03-07 21:07:22 -08006import re
7import subprocess
8import tempfile
Brian Silverman4b90dd02022-01-08 18:16:22 -08009import urllib.request
Philipp Schrader0e19c602018-03-07 21:07:22 -080010import argparse
11import hashlib
12
Brian Silverman4b90dd02022-01-08 18:16:22 -080013def initialize_apt(apt_dir, apt_args, args):
14 os.mkdir(os.path.join(apt_dir, 'etc'))
15 os.mkdir(os.path.join(apt_dir, 'etc', 'apt'))
16 os.mkdir(os.path.join(apt_dir, 'etc', 'apt', 'trusted.gpg.d'))
17 os.mkdir(os.path.join(apt_dir, 'etc', 'apt', 'preferences.d'))
18 os.mkdir(os.path.join(apt_dir, 'var'))
19 os.mkdir(os.path.join(apt_dir, 'var', 'lib'))
20 os.mkdir(os.path.join(apt_dir, 'var', 'lib', 'dpkg'))
21 with open(os.path.join(apt_dir, 'var', 'lib', 'dpkg', 'status'), 'w'):
22 pass
23 with open(os.path.join(apt_dir, 'etc', 'apt', 'sources.list'), 'w') as f:
24 f.write("""
25deb http://deb.debian.org/debian/ {release} main contrib non-free
26deb-src http://deb.debian.org/debian/ {release} main contrib non-free
27
28deb https://security.debian.org/debian-security {release}-security main contrib non-free
29deb-src https://security.debian.org/debian-security {release}-security main contrib non-free
30
31deb http://deb.debian.org/debian/ {release}-updates main contrib non-free
32deb-src http://deb.debian.org/debian/ {release}-updates main contrib non-free
33
34deb http://deb.debian.org/debian {release}-backports main contrib non-free
35deb-src http://deb.debian.org/debian {release}-backports main contrib non-free
36""".format(release=args.release))
37 for key in args.apt_key:
38 basename = os.path.basename(key)
39 urllib.request.urlretrieve(key, os.path.join(apt_dir, 'etc', 'apt', 'trusted.gpg.d', basename))
40 subprocess.check_call(["apt-get"] + apt_args + ["update"])
41
42def get_deps(apt_args, package):
Brian Silverman4c7235a2021-11-17 19:04:37 -080043 env = dict(os.environ)
44 del env['LD_LIBRARY_PATH']
Brian Silverman4b90dd02022-01-08 18:16:22 -080045 out = subprocess.check_output(["apt-rdepends"] + apt_args + [package], env=env)
Philipp Schrader0e19c602018-03-07 21:07:22 -080046 deps = out.splitlines()
47 return set([dep for dep in deps if not dep.startswith(b" ")])
48
Brian Silverman4b90dd02022-01-08 18:16:22 -080049def get_all_deps(apt_args, packages):
Philipp Schrader0e19c602018-03-07 21:07:22 -080050 deps = set()
Philipp Schraderaedfc5c2018-03-10 19:32:30 -080051 for package in packages or ():
Brian Silverman4b90dd02022-01-08 18:16:22 -080052 deps.update(get_deps(apt_args, package))
Philipp Schrader0e19c602018-03-07 21:07:22 -080053 return deps
54
Brian Silverman6470f442018-08-05 12:08:16 -070055def map_virtual_packages(packages):
56 '''Maps known virtual packages to the preferred concrete packages which
57 provide them.'''
58 for package in packages:
59 if package == b'python-numpy-abi9':
60 yield b'python-numpy'
61 continue
62 if package == b'python3-numpy-abi9':
63 yield b'python3-numpy'
64 continue
Tyler Chatow60671d32020-02-26 19:49:30 -080065 if package == b'libjack-0.125':
66 yield b'libjack-jackd2-0'
67 continue
68 if package == b'fonts-freefont':
69 yield b'fonts-freefont-ttf'
70 continue
71 if package == b'gsettings-backend':
72 yield b'dconf-gsettings-backend'
73 continue
74 if package == b'gdal-abi-2-4-0':
75 yield b'libgdal20'
76 continue
77 if package == b'libglu1':
78 yield b'libglu1-mesa'
79 continue
80 if package == b'liblapack.so.3':
81 yield b'liblapack3'
82 continue
83 if package == b'libopencl1':
84 yield b'ocl-icd-libopencl1'
85 continue
Brian Silverman4c7235a2021-11-17 19:04:37 -080086 if package == b'libgcc1':
87 yield b'libgcc-s1'
88 continue
89 if package == b'libopencl-1.2-1':
90 yield b'ocl-icd-libopencl1'
91 continue
Tyler Chatow60671d32020-02-26 19:49:30 -080092 if package == b'libblas.so.3':
93 yield b'libblas3'
94 continue
Brian Silverman6470f442018-08-05 12:08:16 -070095 yield package
96
Brian Silverman4b90dd02022-01-08 18:16:22 -080097def download_deps(apt_args, packages, excludes, force_includes):
98 deps = get_all_deps(apt_args, packages)
99 exclude_deps = get_all_deps(apt_args, excludes)
Philipp Schrader0e19c602018-03-07 21:07:22 -0800100 deps -= exclude_deps
Brian Silverman4b90dd02022-01-08 18:16:22 -0800101 force_include_deps = get_all_deps(apt_args, force_includes)
Philipp Schraderaedfc5c2018-03-10 19:32:30 -0800102 deps |= force_include_deps
Brian Silverman4c7235a2021-11-17 19:04:37 -0800103 env = dict(os.environ)
104 del env['LD_LIBRARY_PATH']
Brian Silverman4b90dd02022-01-08 18:16:22 -0800105 subprocess.check_call([b"apt-get"] + [a.encode('utf-8') for a in apt_args] + [b"download"] + list(map_virtual_packages(deps)), env=env)
Philipp Schrader0e19c602018-03-07 21:07:22 -0800106
107def fixup_files():
108 # Gotta remove those pesky epoch numbers in the file names. Bazel doesn't
109 # like them.
110 regex = re.compile(".%3a")
111 contents = os.listdir(os.getcwd())
112 for deb in contents:
113 new_name = regex.sub("", deb)
114 if new_name != deb:
115 os.rename(deb, new_name)
116
117def sha256_checksum(filename, block_size=65536):
118 sha256 = hashlib.sha256()
119 with open(filename, 'rb') as f:
120 for block in iter(lambda: f.read(block_size), b''):
121 sha256.update(block)
122 return sha256.hexdigest()
123
124def print_file_list():
125 contents = os.listdir(os.getcwd())
126 contents.sort()
127 print("_files = {")
128 for deb in contents:
129 print(' "%s": "%s",' % (deb, sha256_checksum(deb)))
130 print("}")
131
Philipp Schraderaedfc5c2018-03-10 19:32:30 -0800132_ALWAYS_EXCLUDE = [
Tyler Chatow60671d32020-02-26 19:49:30 -0800133 "dbus-session-bus",
Philipp Schraderaedfc5c2018-03-10 19:32:30 -0800134 "debconf",
135 "debconf-2.0",
Tyler Chatow60671d32020-02-26 19:49:30 -0800136 "default-dbus-session-bus",
Philipp Schraderaedfc5c2018-03-10 19:32:30 -0800137 "dpkg",
138 "install-info",
139 "libc-dev",
140 "libc6",
141 "libc6-dev",
142]
143
Philipp Schrader0e19c602018-03-07 21:07:22 -0800144def main(argv):
145 parser = argparse.ArgumentParser()
146 parser.add_argument("--exclude", "-e", type=str, action="append", help="A package to exclude from the list")
Philipp Schraderaedfc5c2018-03-10 19:32:30 -0800147 parser.add_argument("--force-include", type=str, action="append", help="Force include this and its dependencies. Even if listed in excludes.")
Brian Silverman4b90dd02022-01-08 18:16:22 -0800148 parser.add_argument("--arch", type=str, default="amd64", help="Architecture to download files for.")
149 parser.add_argument("--apt-dir", type=str, help=" ".join([
150 "File to generate and store apt files in.",
151 "Helpful for saving time when downloading multiple groups of packages.",
152 "Some flags will be ignored in favor of the values used to create this folder, so be careful.",
153 ]))
154 parser.add_argument("--release", type=str, default="bullseye", help="Debian release to use.")
155 parser.add_argument("--apt-key", type=str, action="append", default=[
156 "https://ftp-master.debian.org/keys/archive-key-11.asc",
157 "https://ftp-master.debian.org/keys/archive-key-11-security.asc",
158 ], help="URL of an additional apt archive key to trust.")
Philipp Schrader0e19c602018-03-07 21:07:22 -0800159 parser.add_argument("package", nargs="+", help="The packages to download.")
160 args = parser.parse_args(argv[1:])
Brian Silverman4b90dd02022-01-08 18:16:22 -0800161 if args.apt_dir:
162 apt_dir = args.apt_dir
163 else:
164 apt_dir = tempfile.mkdtemp()
165 apt_args = ["-o", "Dir=" + apt_dir, "-o", "APT::Architecture=" + args.arch]
166 if not args.apt_dir:
167 print("Creating apt files in %s" % apt_dir)
168 initialize_apt(apt_dir, apt_args, args)
Philipp Schrader0e19c602018-03-07 21:07:22 -0800169 folder = tempfile.mkdtemp()
170 os.chdir(folder)
171 excludes = args.exclude or []
172 # Exclude common packages that don't make sense to include in everything all
173 # the time.
Philipp Schraderaedfc5c2018-03-10 19:32:30 -0800174 excludes += _ALWAYS_EXCLUDE
Brian Silverman4b90dd02022-01-08 18:16:22 -0800175 download_deps(apt_args, args.package, excludes, args.force_include)
Philipp Schrader0e19c602018-03-07 21:07:22 -0800176 fixup_files()
177 print_file_list()
178 print("Your packages are all in %s" % folder)
179
180if __name__ == "__main__":
181 sys.exit(main(sys.argv))