blob: be431202b31a768874e4cfe301a18806641834f4 [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
Philipp Schraderd0e33a42022-01-22 21:55:15 -080095 if package == b'debconf-2.0':
96 yield b'debconf'
97 continue
Brian Silverman6470f442018-08-05 12:08:16 -070098 yield package
99
Brian Silverman4b90dd02022-01-08 18:16:22 -0800100def download_deps(apt_args, packages, excludes, force_includes):
101 deps = get_all_deps(apt_args, packages)
102 exclude_deps = get_all_deps(apt_args, excludes)
Philipp Schrader0e19c602018-03-07 21:07:22 -0800103 deps -= exclude_deps
Brian Silverman4b90dd02022-01-08 18:16:22 -0800104 force_include_deps = get_all_deps(apt_args, force_includes)
Philipp Schraderaedfc5c2018-03-10 19:32:30 -0800105 deps |= force_include_deps
Brian Silverman4c7235a2021-11-17 19:04:37 -0800106 env = dict(os.environ)
107 del env['LD_LIBRARY_PATH']
Brian Silverman4b90dd02022-01-08 18:16:22 -0800108 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 -0800109
110def fixup_files():
111 # Gotta remove those pesky epoch numbers in the file names. Bazel doesn't
112 # like them.
113 regex = re.compile(".%3a")
114 contents = os.listdir(os.getcwd())
115 for deb in contents:
116 new_name = regex.sub("", deb)
117 if new_name != deb:
118 os.rename(deb, new_name)
119
120def sha256_checksum(filename, block_size=65536):
121 sha256 = hashlib.sha256()
122 with open(filename, 'rb') as f:
123 for block in iter(lambda: f.read(block_size), b''):
124 sha256.update(block)
125 return sha256.hexdigest()
126
127def print_file_list():
128 contents = os.listdir(os.getcwd())
129 contents.sort()
130 print("_files = {")
131 for deb in contents:
132 print(' "%s": "%s",' % (deb, sha256_checksum(deb)))
133 print("}")
134
Philipp Schraderaedfc5c2018-03-10 19:32:30 -0800135_ALWAYS_EXCLUDE = [
Tyler Chatow60671d32020-02-26 19:49:30 -0800136 "dbus-session-bus",
Philipp Schraderaedfc5c2018-03-10 19:32:30 -0800137 "debconf",
138 "debconf-2.0",
Tyler Chatow60671d32020-02-26 19:49:30 -0800139 "default-dbus-session-bus",
Philipp Schraderaedfc5c2018-03-10 19:32:30 -0800140 "dpkg",
141 "install-info",
142 "libc-dev",
143 "libc6",
144 "libc6-dev",
145]
146
Philipp Schrader0e19c602018-03-07 21:07:22 -0800147def main(argv):
148 parser = argparse.ArgumentParser()
149 parser.add_argument("--exclude", "-e", type=str, action="append", help="A package to exclude from the list")
Philipp Schraderaedfc5c2018-03-10 19:32:30 -0800150 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 -0800151 parser.add_argument("--arch", type=str, default="amd64", help="Architecture to download files for.")
152 parser.add_argument("--apt-dir", type=str, help=" ".join([
153 "File to generate and store apt files in.",
154 "Helpful for saving time when downloading multiple groups of packages.",
155 "Some flags will be ignored in favor of the values used to create this folder, so be careful.",
156 ]))
157 parser.add_argument("--release", type=str, default="bullseye", help="Debian release to use.")
158 parser.add_argument("--apt-key", type=str, action="append", default=[
159 "https://ftp-master.debian.org/keys/archive-key-11.asc",
160 "https://ftp-master.debian.org/keys/archive-key-11-security.asc",
161 ], help="URL of an additional apt archive key to trust.")
Philipp Schrader0e19c602018-03-07 21:07:22 -0800162 parser.add_argument("package", nargs="+", help="The packages to download.")
163 args = parser.parse_args(argv[1:])
Brian Silverman4b90dd02022-01-08 18:16:22 -0800164 if args.apt_dir:
165 apt_dir = args.apt_dir
166 else:
167 apt_dir = tempfile.mkdtemp()
168 apt_args = ["-o", "Dir=" + apt_dir, "-o", "APT::Architecture=" + args.arch]
169 if not args.apt_dir:
170 print("Creating apt files in %s" % apt_dir)
171 initialize_apt(apt_dir, apt_args, args)
Philipp Schrader0e19c602018-03-07 21:07:22 -0800172 folder = tempfile.mkdtemp()
173 os.chdir(folder)
174 excludes = args.exclude or []
175 # Exclude common packages that don't make sense to include in everything all
176 # the time.
Philipp Schraderaedfc5c2018-03-10 19:32:30 -0800177 excludes += _ALWAYS_EXCLUDE
Brian Silverman4b90dd02022-01-08 18:16:22 -0800178 download_deps(apt_args, args.package, excludes, args.force_include)
Philipp Schrader0e19c602018-03-07 21:07:22 -0800179 fixup_files()
180 print_file_list()
181 print("Your packages are all in %s" % folder)
182
183if __name__ == "__main__":
184 sys.exit(main(sys.argv))