| #!/usr/bin/env python |
| # Copyright 2022 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import argparse |
| import json |
| import os |
| import re |
| import urllib.request |
| |
| # NOTE: There are three potential sources for Bazel binaries: |
| # |
| # - The latest stable release (e.g. 5.3.2 currently), available from GitHub, |
| # and whose version is obtainable from the following API file: |
| # |
| # https://api.github.com/repos/bazelbuild/bazel/releases/latest |
| # |
| # - The latest pre-release binary, which is also available from GitHub, and |
| # whose version is obtained by scanning the content of the following API file, |
| # then sorting the entries in "natural order": |
| # |
| # https://api.github.com/repos/bazelbuild/bazel/tags |
| # |
| # - The release candidates, which are _not_ available from GitHub, but from |
| # releases.bazel.build instead, and which do not have an API file, and |
| # must be specified with explicit version/candidate name below. |
| # |
| # Which version is downloaded depends on the definition of the _BAZEL_VERSION |
| # configuration variable below, which should be one of the following constants: |
| _BAZEL_VERSION_LATEST_PRERELEASE = "latest-prerelease" |
| _BAZEL_VERSION_LATEST_RELEASE = "latest-release" |
| _BAZEL_VERSION_RELEASE_CANDIDATE = "6.0.0/rc4" # Must be <version>/<candidate> |
| |
| # Which Bazel binary to download, see notes above. |
| _BAZEL_VERSION = _BAZEL_VERSION_LATEST_RELEASE |
| |
| # Consistency check. |
| assert _BAZEL_VERSION in ( |
| _BAZEL_VERSION_LATEST_PRERELEASE, |
| _BAZEL_VERSION_LATEST_RELEASE, |
| _BAZEL_VERSION_RELEASE_CANDIDATE, |
| ) |
| |
| |
| def natural_sort_key(item): |
| """Convert a version number to a sorting key for natural ordering.""" |
| # Split the input item into a list of words and numbers (ignore everything else). |
| # E.g. "6.0-pre2022:1" -> ("6", "0", "pre", "2022", "1") |
| tups = re.findall("[^\W\d_]|\d+", item) |
| # Convert all numbers to their integer representation in the output list, but |
| # keep words as is. For example (6, 0, "pre", 2022, 1) |
| # Python will naturally sort tuple items in ascending order. |
| return [int(tup) if tup.isdigit() else tup for tup in tups] |
| |
| |
| def get_release_candidate_version(bazel_version): |
| """Return (version, candidate) information from formatted version string.""" |
| version, _, candidate = bazel_version.partition("/") |
| assert candidate, "Missing release candidate /<suffix> in: " + bazel_version |
| return version, candidate |
| |
| |
| def do_latest(): |
| """Retrieve tag of the latest pre-release to fetch.""" |
| if _BAZEL_VERSION == _BAZEL_VERSION_RELEASE_CANDIDATE: |
| # A release candidate is only available from releases.bazel.build |
| # and there is no API file provided to list them, which forces the use |
| # of hard-coded version + candidate |
| version, candidate = get_release_candidate_version(_BAZEL_VERSION) |
| print(f"{version}{candidate}") |
| elif _BAZEL_VERSION == _BAZEL_VERSION_LATEST_PRERELEASE: |
| # Pre-release versions are needed for the Fuchsia SDK and platform build. |
| # To solve this, download the full list of tags as a JSON array of objects, |
| # whose 'name' field gives the tag. Sort by increasing natural order, and |
| # keep the last one. |
| tags = json.load( |
| urllib.request.urlopen("https://api.github.com/repos/bazelbuild/bazel/tags") |
| ) |
| tag_names = sorted([tag["name"] for tag in tags], key=natural_sort_key) |
| assert len(tag_names) > 0, "Empty tags list?" |
| print(tag_names[-1]) |
| elif _BAZEL_VERSION == _BAZEL_VERSION_LATEST_RELEASE: |
| # This codes get the latest official release tag directly instead. |
| print( |
| json.load( |
| urllib.request.urlopen( |
| "https://api.github.com/repos/bazelbuild/bazel/releases/latest" |
| ) |
| )["tag_name"] |
| ) |
| else: |
| assert False, ( |
| "Invalid or unrecognized value for _BAZEL_VERSION: " + _BAZEL_VERSION |
| ) |
| |
| |
| _PLATFORMS = { |
| "linux-amd64": "linux-x86_64", |
| "linux-arm64": "linux-arm64", |
| "mac-amd64": "darwin-x86_64", |
| "mac-arm64": "darwin-arm64", |
| "windows-amd64": "windows-x86_64", |
| "windows-arm64": "windows-arm64", |
| } |
| |
| _EXTENSION = { |
| "linux": "", |
| "mac": "", |
| "windows": ".zip", |
| } |
| |
| |
| def get_download_url(version, platform): |
| if platform not in _PLATFORMS: |
| raise ValueError("unsupported platform {}".format(platform)) |
| |
| extension = _EXTENSION[platform.split("-")[0]] |
| |
| if _BAZEL_VERSION == _BAZEL_VERSION_RELEASE_CANDIDATE: |
| version, candidate = get_release_candidate_version(_BAZEL_VERSION) |
| url = ( |
| "https://releases.bazel.build/{version}/{candidate}/" |
| "bazel-{version}{candidate}-{platform}{extension}".format( |
| version=version, |
| candidate=candidate, |
| platform=_PLATFORMS[platform], |
| extension=extension, |
| ) |
| ) |
| else: |
| url = ( |
| "https://github.com/bazelbuild/bazel/releases/download/{version}/" |
| "bazel-{version}-{platform}{extension}".format( |
| version=version, |
| platform=_PLATFORMS[platform], |
| extension=extension, |
| ) |
| ) |
| |
| manifest = { |
| "url": [url], |
| "ext": extension, |
| } |
| |
| print(json.dumps(manifest)) |
| |
| |
| def main(): |
| ap = argparse.ArgumentParser() |
| sub = ap.add_subparsers() |
| |
| latest = sub.add_parser("latest") |
| latest.set_defaults(func=lambda _opts: do_latest()) |
| |
| download = sub.add_parser("get_url") |
| download.set_defaults( |
| func=lambda opts: get_download_url( |
| os.environ["_3PP_VERSION"], os.environ["_3PP_PLATFORM"] |
| ) |
| ) |
| |
| opts = ap.parse_args() |
| opts.func(opts) |
| |
| |
| if __name__ == "__main__": |
| main() |