blob: d012ff697799688f6565e514fc09d4aba3586550 [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
Ravago Jones5127ccc2022-07-31 16:32:45 -070013
Brian Silverman4b90dd02022-01-08 18:16:22 -080014def initialize_apt(apt_dir, apt_args, args):
Ravago Jones5127ccc2022-07-31 16:32:45 -070015 os.mkdir(os.path.join(apt_dir, 'etc'))
16 os.mkdir(os.path.join(apt_dir, 'etc', 'apt'))
17 os.mkdir(os.path.join(apt_dir, 'etc', 'apt', 'trusted.gpg.d'))
18 os.mkdir(os.path.join(apt_dir, 'etc', 'apt', 'preferences.d'))
19 os.mkdir(os.path.join(apt_dir, 'var'))
20 os.mkdir(os.path.join(apt_dir, 'var', 'lib'))
21 os.mkdir(os.path.join(apt_dir, 'var', 'lib', 'dpkg'))
22 with open(os.path.join(apt_dir, 'var', 'lib', 'dpkg', 'status'), 'w'):
23 pass
24 with open(os.path.join(apt_dir, 'etc', 'apt', 'sources.list'), 'w') as f:
25 f.write("""
Brian Silverman4b90dd02022-01-08 18:16:22 -080026deb http://deb.debian.org/debian/ {release} main contrib non-free
27deb-src http://deb.debian.org/debian/ {release} main contrib non-free
28
29deb https://security.debian.org/debian-security {release}-security main contrib non-free
30deb-src https://security.debian.org/debian-security {release}-security main contrib non-free
31
32deb http://deb.debian.org/debian/ {release}-updates main contrib non-free
33deb-src http://deb.debian.org/debian/ {release}-updates main contrib non-free
34
35deb http://deb.debian.org/debian {release}-backports main contrib non-free
36deb-src http://deb.debian.org/debian {release}-backports main contrib non-free
37""".format(release=args.release))
Ravago Jones5127ccc2022-07-31 16:32:45 -070038 for key in args.apt_key:
39 basename = os.path.basename(key)
40 urllib.request.urlretrieve(
41 key, os.path.join(apt_dir, 'etc', 'apt', 'trusted.gpg.d',
42 basename))
43 subprocess.check_call(["apt-get"] + apt_args + ["update"])
44
Brian Silverman4b90dd02022-01-08 18:16:22 -080045
46def get_deps(apt_args, package):
Ravago Jones5127ccc2022-07-31 16:32:45 -070047 env = dict(os.environ)
48 del env['LD_LIBRARY_PATH']
49 out = subprocess.check_output(["apt-rdepends"] + apt_args + [package],
50 env=env)
51 deps = out.splitlines()
52 return set([dep for dep in deps if not dep.startswith(b" ")])
53
Philipp Schrader0e19c602018-03-07 21:07:22 -080054
Brian Silverman4b90dd02022-01-08 18:16:22 -080055def get_all_deps(apt_args, packages):
Ravago Jones5127ccc2022-07-31 16:32:45 -070056 deps = set()
57 for package in packages or ():
58 deps.update(get_deps(apt_args, package))
59 return deps
60
Philipp Schrader0e19c602018-03-07 21:07:22 -080061
Brian Silverman6470f442018-08-05 12:08:16 -070062def map_virtual_packages(packages):
Ravago Jones5127ccc2022-07-31 16:32:45 -070063 '''Maps known virtual packages to the preferred concrete packages which
Brian Silverman6470f442018-08-05 12:08:16 -070064 provide them.'''
Ravago Jones5127ccc2022-07-31 16:32:45 -070065 for package in packages:
66 if package == b'python-numpy-abi9':
67 yield b'python-numpy'
68 continue
69 if package == b'python3-numpy-abi9':
70 yield b'python3-numpy'
71 continue
72 if package == b'libjack-0.125':
73 yield b'libjack-jackd2-0'
74 continue
75 if package == b'fonts-freefont':
76 yield b'fonts-freefont-ttf'
77 continue
78 if package == b'gsettings-backend':
79 yield b'dconf-gsettings-backend'
80 continue
81 if package == b'gdal-abi-2-4-0':
82 yield b'libgdal20'
83 continue
84 if package == b'libglu1':
85 yield b'libglu1-mesa'
86 continue
87 if package == b'liblapack.so.3':
88 yield b'liblapack3'
89 continue
90 if package == b'libopencl1':
91 yield b'ocl-icd-libopencl1'
92 continue
93 if package == b'libgcc1':
94 yield b'libgcc-s1'
95 continue
96 if package == b'libopencl-1.2-1':
97 yield b'ocl-icd-libopencl1'
98 continue
99 if package == b'libblas.so.3':
100 yield b'libblas3'
101 continue
102 if package == b'debconf-2.0':
103 yield b'debconf'
104 continue
105 yield package
106
Brian Silverman6470f442018-08-05 12:08:16 -0700107
Philipp Schraderfd5489f2022-09-17 17:31:09 -0700108def download_deps(apt_args, packages, excludes, force_includes,
109 force_excludes):
Ravago Jones5127ccc2022-07-31 16:32:45 -0700110 deps = get_all_deps(apt_args, packages)
111 exclude_deps = get_all_deps(apt_args, excludes)
112 deps -= exclude_deps
113 force_include_deps = get_all_deps(apt_args, force_includes)
114 deps |= force_include_deps
Philipp Schraderfd5489f2022-09-17 17:31:09 -0700115 force_exclude_deps = get_all_deps(apt_args, force_excludes)
116 deps -= force_exclude_deps
Ravago Jones5127ccc2022-07-31 16:32:45 -0700117 env = dict(os.environ)
118 del env['LD_LIBRARY_PATH']
119 subprocess.check_call([b"apt-get"] + [a.encode('utf-8')
120 for a in apt_args] + [b"download"] +
121 list(map_virtual_packages(deps)),
122 env=env)
123
Philipp Schrader0e19c602018-03-07 21:07:22 -0800124
125def fixup_files():
Ravago Jones5127ccc2022-07-31 16:32:45 -0700126 # Gotta remove those pesky epoch numbers in the file names. Bazel doesn't
127 # like them.
128 regex = re.compile(".%3a")
129 contents = os.listdir(os.getcwd())
130 for deb in contents:
131 new_name = regex.sub("", deb)
132 if new_name != deb:
133 os.rename(deb, new_name)
134
Philipp Schrader0e19c602018-03-07 21:07:22 -0800135
136def sha256_checksum(filename, block_size=65536):
Ravago Jones5127ccc2022-07-31 16:32:45 -0700137 sha256 = hashlib.sha256()
138 with open(filename, 'rb') as f:
139 for block in iter(lambda: f.read(block_size), b''):
140 sha256.update(block)
141 return sha256.hexdigest()
142
Philipp Schrader0e19c602018-03-07 21:07:22 -0800143
144def print_file_list():
Ravago Jones5127ccc2022-07-31 16:32:45 -0700145 contents = os.listdir(os.getcwd())
146 contents.sort()
147 print("_files = {")
148 for deb in contents:
149 print(' "%s": "%s",' % (deb, sha256_checksum(deb)))
150 print("}")
151
Philipp Schrader0e19c602018-03-07 21:07:22 -0800152
Philipp Schraderaedfc5c2018-03-10 19:32:30 -0800153_ALWAYS_EXCLUDE = [
Tyler Chatow60671d32020-02-26 19:49:30 -0800154 "dbus-session-bus",
Philipp Schraderaedfc5c2018-03-10 19:32:30 -0800155 "debconf",
156 "debconf-2.0",
Tyler Chatow60671d32020-02-26 19:49:30 -0800157 "default-dbus-session-bus",
Philipp Schraderaedfc5c2018-03-10 19:32:30 -0800158 "dpkg",
159 "install-info",
160 "libc-dev",
161 "libc6",
162 "libc6-dev",
163]
164
Ravago Jones5127ccc2022-07-31 16:32:45 -0700165
Philipp Schrader0e19c602018-03-07 21:07:22 -0800166def main(argv):
Ravago Jones5127ccc2022-07-31 16:32:45 -0700167 parser = argparse.ArgumentParser()
168 parser.add_argument("--exclude",
169 "-e",
170 type=str,
171 action="append",
172 help="A package to exclude from the list")
173 parser.add_argument(
174 "--force-include",
175 type=str,
176 action="append",
177 help=
178 "Force include this and its dependencies. Even if listed in excludes.")
Philipp Schraderfd5489f2022-09-17 17:31:09 -0700179 parser.add_argument(
180 "--force-exclude",
181 type=str,
182 action="append",
183 help=
184 "Force exclude this and its dependencies. Even if listed via --force-include."
185 )
Ravago Jones5127ccc2022-07-31 16:32:45 -0700186 parser.add_argument("--arch",
187 type=str,
188 default="amd64",
189 help="Architecture to download files for.")
190 parser.add_argument(
191 "--apt-dir",
192 type=str,
193 help=" ".join([
194 "File to generate and store apt files in.",
195 "Helpful for saving time when downloading multiple groups of packages.",
196 "Some flags will be ignored in favor of the values used to create this folder, so be careful.",
197 ]))
198 parser.add_argument("--release",
199 type=str,
200 default="bullseye",
201 help="Debian release to use.")
202 parser.add_argument(
203 "--apt-key",
204 type=str,
205 action="append",
206 default=[
207 "https://ftp-master.debian.org/keys/archive-key-11.asc",
208 "https://ftp-master.debian.org/keys/archive-key-11-security.asc",
209 ],
210 help="URL of an additional apt archive key to trust.")
211 parser.add_argument("package", nargs="+", help="The packages to download.")
212 args = parser.parse_args(argv[1:])
213 if args.apt_dir:
214 apt_dir = args.apt_dir
215 else:
216 apt_dir = tempfile.mkdtemp()
217 apt_args = ["-o", "Dir=" + apt_dir, "-o", "APT::Architecture=" + args.arch]
218 if not args.apt_dir:
219 print("Creating apt files in %s" % apt_dir)
220 initialize_apt(apt_dir, apt_args, args)
221 folder = tempfile.mkdtemp()
222 os.chdir(folder)
223 excludes = args.exclude or []
224 # Exclude common packages that don't make sense to include in everything all
225 # the time.
226 excludes += _ALWAYS_EXCLUDE
Philipp Schraderfd5489f2022-09-17 17:31:09 -0700227 download_deps(apt_args, args.package, excludes, args.force_include,
228 args.force_exclude)
Ravago Jones5127ccc2022-07-31 16:32:45 -0700229 fixup_files()
230 print_file_list()
231 print("Your packages are all in %s" % folder)
232
Philipp Schrader0e19c602018-03-07 21:07:22 -0800233
234if __name__ == "__main__":
Ravago Jones5127ccc2022-07-31 16:32:45 -0700235 sys.exit(main(sys.argv))