|
10 | 10 | from urllib.request import urlopen |
11 | 11 | from distutils.version import StrictVersion |
12 | 12 |
|
13 | | -# All GHC versions we generate. |
14 | | -# `version` is the version number |
15 | | -# `distribution_version` is a corrected name |
16 | | -# (sometimes bindists have errors and are updated by new bindists) |
17 | | -# `ignore_prefixes` is the prefix of files to ignore |
18 | | -# `ignore_suffixes` is the suffix of files to ignore |
19 | | -VERSIONS = [ |
20 | | - { "version": '9.6.3', |
21 | | - "ignore_suffixes": [".bz2", ".lz", ".zip"] }, |
22 | | - { "version": "9.6.2", |
23 | | - "ignore_suffixes": [".bz2", ".lz", ".zip"] }, |
24 | | - { "version": "9.6.1", |
25 | | - "ignore_suffixes": [".bz2", ".lz", ".zip"] }, |
26 | | - { "version": '9.4.6', |
27 | | - "ignore_suffixes": [".bz2", ".lz", ".zip"] }, |
28 | | - { "version": '9.4.7', |
29 | | - "ignore_suffixes": [".bz2", ".lz", ".zip"] }, |
30 | | - { "version": "9.4.5", |
31 | | - "ignore_suffixes": [".bz2", ".lz", ".zip"] }, |
32 | | - { "version": '9.2.8', |
33 | | - "ignore_suffixes": [".bz2", ".lz", ".zip"] }, |
34 | | - { "version": "9.2.5", |
35 | | - "ignore_suffixes": [".bz2", ".lz", ".zip"] }, |
36 | | - { "version": "9.2.4", |
37 | | - "ignore_suffixes": [".bz2", ".lz", ".zip"] }, |
38 | | - { "version": "9.2.3", |
39 | | - "ignore_suffixes": [".bz2", ".lz", ".zip"] }, |
40 | | - { "version": "9.2.1", |
41 | | - "ignore_suffixes": [".bz2", ".lz", ".zip"] }, |
42 | | - { "version": "9.0.2", |
43 | | - "ignore_prefixes": ["ghc-9.0.2a"], |
44 | | - "ignore_suffixes": [".bz2", ".lz", ".zip"] }, |
45 | | - { "version": "9.0.1", |
46 | | - "ignore_suffixes": [".bz2", ".lz", ".zip"] }, |
47 | | - { "version": "8.10.7", |
48 | | - "ignore_suffixes": [".bz2", ".lz", ".zip"] }, |
49 | | - { "version": "8.10.4" }, |
50 | | - { "version": "8.10.3" }, |
51 | | - { "version": "8.8.4" }, |
52 | | - { "version": "8.6.5" }, |
53 | | - { "version": "8.4.4" }, |
54 | | - { "version": "8.4.3" }, |
55 | | - { "version": "8.4.2" }, |
56 | | - { "version": "8.4.1" }, |
57 | | - { "version": "8.2.2" }, |
58 | | -] |
| 13 | +# Sometimes bindists have errors and are updated by new bindists. |
| 14 | +# This dict is used to keep a version -> corrected_version mapping. |
| 15 | +VERSIONS_CORRECTED = {} |
59 | 16 |
|
60 | 17 | # All architectures we generate. |
61 | 18 | # bazel: bazel name |
62 | 19 | # upstream: list of download.haskell.org name |
63 | 20 | ARCHES = [ |
64 | | - { "bazel": "linux_amd64", |
65 | | - "upstream": ["x86_64-deb8-linux", "x86_64-deb9-linux", "x86_64-deb10-linux"], }, |
66 | | - { "bazel": "linux_arm64", |
67 | | - "upstream": ["aarch64-deb10-linux"], }, |
68 | | - { "bazel": "darwin_amd64", |
69 | | - "upstream": ["x86_64-apple-darwin"] }, |
70 | | - { "bazel": "darwin_arm64", |
71 | | - "upstream": ["aarch64-apple-darwin"] }, |
72 | | - { "bazel": "windows_amd64", |
73 | | - "upstream": ["x86_64-unknown-mingw32"] }, |
| 21 | + { |
| 22 | + "bazel": "linux_amd64", |
| 23 | + "upstream": ["x86_64-deb8-linux", "x86_64-deb9-linux", "x86_64-deb10-linux"], |
| 24 | + }, |
| 25 | + { |
| 26 | + "bazel": "linux_arm64", |
| 27 | + "upstream": ["aarch64-deb10-linux"], |
| 28 | + }, |
| 29 | + {"bazel": "darwin_amd64", "upstream": ["x86_64-apple-darwin"]}, |
| 30 | + {"bazel": "darwin_arm64", "upstream": ["aarch64-apple-darwin"]}, |
| 31 | + {"bazel": "windows_amd64", "upstream": ["x86_64-unknown-mingw32"]}, |
74 | 32 | ] |
75 | 33 |
|
76 | 34 |
|
77 | 35 | # An url to a bindist tarball. |
78 | 36 | def link_for_tarball(arch, version): |
79 | 37 | return "https://downloads.haskell.org/~ghc/{ver}/ghc-{ver}-{arch}.tar.xz".format( |
80 | | - ver = version, |
81 | | - arch = arch, |
| 38 | + ver=version, |
| 39 | + arch=arch, |
82 | 40 | ) |
83 | 41 |
|
| 42 | + |
84 | 43 | # An url to a version's tarball hashsum file. |
85 | 44 | # The files contain the hashsums for all arches. |
86 | 45 | def link_for_sha256_file(version): |
87 | | - return "https://downloads.haskell.org/~ghc/{ver}/SHA256SUMS".format( |
88 | | - ver = version |
89 | | - ) |
| 46 | + return "https://downloads.haskell.org/~ghc/{ver}/SHA256SUMS".format(ver=version) |
| 47 | + |
90 | 48 |
|
91 | 49 | # Parses the tarball hashsum file for a distribution version. |
92 | 50 | def parse_sha256_file(content, version, url): |
93 | 51 | res = {} |
94 | | - errs = [] |
| 52 | + |
| 53 | + prefix = "ghc-{ver}-".format(ver=VERSIONS_CORRECTED.get(version, version)) |
| 54 | + suffix = ".tar.xz" |
| 55 | + |
95 | 56 | for line in content: |
96 | 57 | # f5763983a26dedd88b65a0b17267359a3981b83a642569b26334423f684f8b8c ./ghc-8.4.3-i386-deb8-linux.tar.xz |
97 | 58 | (hash, file_) = line.decode().strip().split(" ./") |
98 | | - prefix = "ghc-{ver}-".format(ver = version.get("distribution_version", version['version'])) |
99 | | - suffix = ".tar.xz" |
100 | | - |
101 | | - # filter ignored files |
102 | | - if any([file_.startswith(p) for p in version.get("ignore_prefixes", [])]) \ |
103 | | - or any([file_.endswith(s) for s in version.get("ignore_suffixes", [])]): |
104 | | - continue |
105 | 59 |
|
106 | 60 | if file_.startswith(prefix) and file_.endswith(suffix): |
107 | 61 | # i386-deb8-linux |
108 | | - name = file_[len(prefix):-len(suffix)] |
| 62 | + name = file_[len(prefix) : -len(suffix)] |
109 | 63 | res[name] = hash |
110 | | - else: |
111 | | - errs.append("Can't parse the sha256 field for {ver}: {entry}".format( |
112 | | - ver = version['version'], entry = line.strip())) |
113 | 64 |
|
114 | | - if errs: |
115 | | - eprint("Errors parsing file at " + url + ". Either fix or ignore the lines (ignore_suffixes/ignore_prefixes).") |
116 | | - for e in errs: |
117 | | - eprint(e) |
| 65 | + if not res: |
| 66 | + eprint( |
| 67 | + f"Errors parsing file at {url}. Could not find entries for {prefix}…{suffix}" |
| 68 | + ) |
118 | 69 | exit(1) |
119 | 70 |
|
120 | 71 | return res |
121 | 72 |
|
| 73 | + |
122 | 74 | # Print to stderr. |
123 | 75 | def eprint(mes): |
124 | | - print(mes, file = sys.stderr) |
| 76 | + print(mes, file=sys.stderr) |
| 77 | + |
125 | 78 |
|
126 | 79 | def select_one(xs, ys): |
127 | 80 | """Select a single item from xs, prefer the first item also in ys.""" |
128 | 81 | items = [x for x in xs if x in ys] |
129 | 82 | return items[0] if items else xs[0] |
130 | 83 |
|
131 | | -# Main. |
132 | | -if __name__ == "__main__": |
| 84 | + |
| 85 | +def fetch_hashsums(versions): |
133 | 86 | # Fetch all hashsum files |
134 | 87 | # grab : { version: { arch: sha256 } } |
135 | 88 | grab = {} |
136 | | - for ver in VERSIONS: |
137 | | - eprint("fetching " + ver['version']) |
138 | | - url = link_for_sha256_file(ver['version']) |
| 89 | + for ver in versions: |
| 90 | + eprint("fetching " + ver) |
| 91 | + url = link_for_sha256_file(ver) |
139 | 92 | res = urlopen(url) |
140 | 93 | if res.getcode() != 200: |
141 | 94 | eprint("download of {} failed with status {}".format(url, res.getcode())) |
142 | 95 | sys.exit(1) |
143 | 96 | else: |
144 | | - grab[ver['version']] = parse_sha256_file(res, ver, url) |
| 97 | + grab[ver] = parse_sha256_file(res, ver, url) |
145 | 98 |
|
146 | 99 | # check whether any version is missing arches we need |
147 | 100 | # errs : { version: set(missing_arches) } |
148 | 101 | errs = {} |
149 | 102 | for ver, hashes in grab.items(): |
150 | | - real_arches = frozenset(hashes.keys()) |
151 | | - upstreams = [select_one(a['upstream'], real_arches) for a in ARCHES] |
152 | | - needed_arches = frozenset(upstreams) |
153 | | - missing_arches = needed_arches.difference(real_arches) |
154 | | - if missing_arches: |
155 | | - errs[ver] = missing_arches |
| 103 | + real_arches = frozenset(hashes.keys()) |
| 104 | + upstreams = [select_one(a["upstream"], real_arches) for a in ARCHES] |
| 105 | + needed_arches = frozenset(upstreams) |
| 106 | + missing_arches = needed_arches.difference(real_arches) |
| 107 | + if missing_arches: |
| 108 | + errs[ver] = missing_arches |
156 | 109 | if errs: |
157 | 110 | for ver, missing in errs.items(): |
158 | 111 | print( |
159 | 112 | "WARN: version {ver} is missing hashes for architectures {arches}".format( |
160 | | - ver = ver, |
161 | | - arches = ','.join(missing)), |
162 | | - file=sys.stderr |
| 113 | + ver=ver, arches=",".join(missing) |
| 114 | + ), |
| 115 | + file=sys.stderr, |
163 | 116 | ) |
164 | 117 |
|
| 118 | + return grab |
| 119 | + |
| 120 | + |
| 121 | +def fetch_bindists(grab): |
165 | 122 | # fetch the arches we need and create the GHC_BINDISTS dict |
166 | 123 | # ghc_bindists : { version: { bazel_arch: (tarball_url, sha256_hash) } } |
167 | 124 | ghc_bindists = {} |
168 | 125 | for ver, hashes in grab.items(): |
169 | 126 | # { bazel_arch: (tarball_url, sha256_hash) } |
170 | 127 | arch_dists = {} |
171 | 128 | for arch in ARCHES: |
172 | | - upstream = select_one(arch['upstream'], hashes) |
| 129 | + upstream = select_one(arch["upstream"], hashes) |
173 | 130 |
|
174 | 131 | if upstream in hashes: |
175 | | - arch_dists[arch['bazel']] = ( |
| 132 | + arch_dists[arch["bazel"]] = ( |
176 | 133 | link_for_tarball(upstream, ver), |
177 | | - hashes[upstream] |
| 134 | + hashes[upstream], |
178 | 135 | ) |
179 | 136 | ghc_bindists[ver] = arch_dists |
180 | 137 |
|
181 | | - ghc_versions = { version: ghc_bindists[version] for version in sorted(ghc_bindists.keys(), key=StrictVersion) } |
| 138 | + return ghc_bindists |
| 139 | + |
182 | 140 |
|
| 141 | +# Main. |
| 142 | +if __name__ == "__main__": |
183 | 143 | working_directory = os.environ.get("BUILD_WORKING_DIRECTORY", ".") |
184 | 144 |
|
185 | | - with open(os.path.join(working_directory, "haskell/private/ghc_bindist_generated.json"), "w", encoding="utf-8") as json_file: |
186 | | - json.dump(ghc_versions, json_file, indent=4) |
187 | | - json_file.write('\n') |
| 145 | + with open( |
| 146 | + os.path.join(working_directory, "haskell/private/ghc_bindist_generated.json"), |
| 147 | + "r+", |
| 148 | + encoding="utf-8", |
| 149 | + ) as json_file: |
| 150 | + ghc_versions = json.load(json_file) |
| 151 | + |
| 152 | + # All GHC versions we generate. |
| 153 | + versions = ghc_versions.keys() |
188 | 154 |
|
| 155 | + grab = fetch_hashsums(versions) |
189 | 156 |
|
| 157 | + ghc_bindists = fetch_bindists(grab) |
| 158 | + |
| 159 | + ghc_versions = { |
| 160 | + version: ghc_bindists[version] |
| 161 | + for version in sorted(ghc_bindists.keys(), key=StrictVersion) |
| 162 | + } |
| 163 | + |
| 164 | + json_file.truncate(0) |
| 165 | + json_file.seek(0) |
| 166 | + json.dump(ghc_versions, json_file, indent=4) |
| 167 | + json_file.write("\n") |
0 commit comments