blob: 80fe666dc5d26781eb612ae430cfc479bd4fb325 [file] [log] [blame]
#!/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()