#!/usr/bin/env python # This is a happy-path tool to download the bindist # download paths and hashes, for maintainers. # It uses the hashes provided by download.haskell.org. from __future__ import print_function import pprint import sys import urllib2 # All GHC versions we generate. # `version` is the version number # `distribution_version` is a corrected name # (sometimes bindists have errors and are updated by new bindists) # `ignore_prefixes` is the prefix of files to ignore # `ignore_suffixes` is the suffix of files to ignore VERSIONS = [ { "version": "8.6.5" }, { "version": "8.6.4" }, { "version": "8.6.3" }, { "version": "8.6.2" }, { "version": "8.4.4" }, { "version": "8.4.3" }, { "version": "8.4.2" }, { "version": "8.4.1" }, { "version": "8.2.2" }, { "version": "8.0.2", "ignore_suffixes": [".patch"] }, { "version": "7.10.3", "distribution_version": "7.10.3b", "ignore_prefixes": ["ghc-7.10.3-", "ghc-7.10.3a-"], "ignore_suffixes": [".bz2", ".patch" ] } ] # All architectures we generate. # bazel: bazel name # upstream: download.haskell.org name ARCHES = [ { "bazel": "linux_amd64", "upstream": "x86_64-deb8-linux", }, { "bazel": "darwin_amd64", "upstream": "x86_64-apple-darwin" }, { "bazel": "windows_amd64", "upstream": "x86_64-unknown-mingw32" }, ] # An url to a bindist tarball. def link_for_tarball(arch, version): return "https://downloads.haskell.org/~ghc/{ver}/ghc-{ver}-{arch}.tar.xz".format( ver = version, arch = arch, ) # An url to a version's tarball hashsum file. # The files contain the hashsums for all arches. def link_for_sha256_file(version): return "https://downloads.haskell.org/~ghc/{ver}/SHA256SUMS".format( ver = version ) # Parses the tarball hashsum file for a distribution version. def parse_sha256_file(content, version, url): res = {} errs = [] for line in content: # f5763983a26dedd88b65a0b17267359a3981b83a642569b26334423f684f8b8c ./ghc-8.4.3-i386-deb8-linux.tar.xz (hash, file_) = line.strip().split(" ./") prefix = "ghc-{ver}-".format(ver = version.get("distribution_version", version['version'])) suffix = ".tar.xz" # filter ignored files if any([file_.startswith(p) for p in version.get("ignore_prefixes", [])]) \ or any([file_.endswith(s) for s in version.get("ignore_suffixes", [])]): continue if file_.startswith(prefix) and file_.endswith(suffix): # i386-deb8-linux name = file_[len(prefix):-len(suffix)] res[name] = hash else: errs.append("Can't parse the sha256 field for {ver}: {entry}".format( ver = version['version'], entry = line.strip())) if errs: eprint("Errors parsing file at " + url + ". Either fix or ignore the lines (ignore_suffixes/ignore_prefixes).") for e in errs: eprint(e) exit(1) return res # Print to stderr. def eprint(mes): print(mes, file = sys.stderr) # Main. if __name__ == "__main__": # Fetch all hashsum files # grab : { version: { arch: sha256 } } grab = {} for ver in VERSIONS: eprint("fetching " + ver['version']) url = link_for_sha256_file(ver['version']) res = urllib2.urlopen(url) if res.getcode() != 200: eprint("download of {} failed with status {}".format(url, res.getcode())) sys.exit(1) else: grab[ver['version']] = parse_sha256_file(res, ver, url) # check whether any version is missing arches we need # errs : { version: set(missing_arches) } errs = {} for ver, hashes in grab.items(): real_arches = frozenset(hashes.keys()) needed_arches = frozenset([a['upstream'] for a in ARCHES]) missing_arches = needed_arches.difference(real_arches) if missing_arches: errs[ver] = missing_arches if errs: for ver, missing in errs.items(): eprint("version {ver} is missing hashes for required architectures {arches}".format( ver = ver, arches = missing)) # fetch the arches we need and create the GHC_BINDISTS dict # ghc_bindists : { version: { bazel_arch: (tarball_url, sha256_hash) } } ghc_bindists = {} for ver, hashes in grab.items(): # { bazel_arch: (tarball_url, sha256_hash) } arch_dists = {} for arch in ARCHES: hashes[arch['upstream']] arch_dists[arch['bazel']] = ( link_for_tarball(arch['upstream'], ver), hashes[arch['upstream']] ) ghc_bindists[ver] = arch_dists # Print to stdout. Be aware that you can't `> foo.bzl`, # because that truncates the source file which is needed # for bazel to run in the first place. print(""" \ # Generated with `bazel run @io_tweag_rules_haskell//haskell:gen-ghc-bindist | sponge haskell/private/ghc_bindist_generated.bzl` # To add a version or architecture, edit the constants in haskell/gen_ghc_bindist.py GHC_BINDIST = \\""") pprint.pprint(ghc_bindists)