blob: eb60adcd5e599e70a4f87d0ddb143c40bd0f1d38 [file] [log] [blame] [edit]
#!/usr/bin/env fuchsia-vendored-python
# Copyright 2019 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
## Roll a new compiler (really, any CIPD package) into Fuchsia
## Usage: See `fx roll-compiler --help`
import argparse
import json
import os
import subprocess
import sys
import tempfile
FUCHSIA_DIR = os.path.normpath(
os.path.join(__file__, os.pardir, os.pardir, os.pardir, os.pardir)
)
JIRI_BIN = os.path.join(FUCHSIA_DIR, ".jiri_root", "bin")
CIPD = os.path.join(JIRI_BIN, "cipd")
JIRI = os.path.join(JIRI_BIN, "jiri")
def CallCipd(args):
with tempfile.NamedTemporaryFile(mode="r", suffix=".json") as f:
cmd = [CIPD] + args + ["-json-output", f.name]
with open(os.devnull, "w") as devnull:
subprocess.check_call(cmd, stdout=devnull)
return json.load(f)["result"]
def ResolvePackage(package, version):
return CallCipd(["describe", package, "-version", version])
def GetSplitRustToolchainPaths(packages, host_platforms, target_platforms=None):
"""
Separating `package = rust` into Host/Target packages. This function will
remove the unified `rust` package when separating into Host/Target packages.
"""
CIPD_RUST_PREFIX = "fuchsia/third_party/rust"
DEFAULT_TARGET_PLATFORMS = [
"aarch64-unknown-linux-gnu",
"fuchsia",
"wasm32-unknown-unknown",
"x86_64-apple-darwin",
"x86_64-unknown-linux-gnu",
]
rust_toolchain_packages = []
if "rust" not in packages:
return rust_toolchain_packages
if target_platforms is None:
target_platforms = DEFAULT_TARGET_PLATFORMS
host_packages = [
"%s/%s/%s" % (CIPD_RUST_PREFIX, "host", host_platform)
for host_platform in host_platforms
]
target_packages = [
"%s/%s/%s" % (CIPD_RUST_PREFIX, "target", target_platform)
for target_platform in target_platforms
]
rust_toolchain_packages = host_packages + target_packages
# Removing unified `rust` package
packages.remove("rust")
return rust_toolchain_packages
def GetRustToolchainCommands(rust_toolchain_packages, tag):
rust_command = []
if not rust_toolchain_packages:
return rust_command
# Only download ${platform} Host toolchain packages
rust_command += ["-package", "fuchsia/third_party/rust/host/${platform}=%s" % (tag)]
for package in rust_toolchain_packages:
# Skip host packages, keep all target
if "fuchsia/third_party/rust/host" in package:
continue
rust_command += ["-package", "%s=%s" % (package, tag)]
return rust_command
def main():
default_jiri_manifest = os.path.join(FUCHSIA_DIR, "integration", "fuchsia", "toolchain")
open_source_jiri_manifest = os.path.join(FUCHSIA_DIR, "integration", "toolchain")
if not os.path.isfile(default_jiri_manifest) and os.path.isfile(open_source_jiri_manifest):
default_jiri_manifest = open_source_jiri_manifest
parser = argparse.ArgumentParser(
"fx roll-compiler",
description="Roll a new compiler into Fuchsia",
epilog="""
With multiple --package switches, all packages must resolve successfully
before commands are run for any package.
""",
)
parser.add_argument(
"version", nargs="?", default="latest", help="CIPD version to promote"
)
parser.add_argument("--package", "-p", action="append", help="CIPD package name")
parser.add_argument(
"--manifest",
default=default_jiri_manifest,
help="Jiri manifest file to edit",
)
parser.add_argument("--tag", default="git_revision", help="CIPD tag to publish")
parser.add_argument(
"--platforms",
action="append",
metavar="PLATFORM",
help="CIPD platforms with matching packages",
)
parser.add_argument(
"--dry-run",
action="store_const",
default=False,
const=True,
help="Only print final command but do not run it",
)
args = parser.parse_args()
if not args.package:
args.package = ["clang"]
if not args.platforms:
args.platforms = ["linux-arm64", "linux-amd64", "mac-amd64"]
if args.package == ["clang"]:
args.platforms.append("windows-amd64")
# Rust separate host/target toolchain
rust_toolchain_packages = GetSplitRustToolchainPaths(args.package, args.platforms)
packages = [
package if "/" in package else "fuchsia/third_party/" + package
for package in args.package
]
def PackageTag(package):
[tag] = [
tag["tag"]
for tag in package["tags"]
if tag["tag"].startswith(args.tag + ":")
]
return tag
packages_to_resolve = [
"%s/%s" % (package, platform)
for platform in args.platforms
for package in packages
] + rust_toolchain_packages
resolved_packages = [
ResolvePackage(package, args.version) for package in packages_to_resolve
]
package_tags = {
package["pin"]["package"]: PackageTag(package) for package in resolved_packages
}
tag_set = set(t for t in package_tags.values())
if len(tag_set) != 1:
sys.stderr.write(
f"Not all packages have matching {args.tag} tags at version {args.version}:\n"
)
json.dump(package_tags, sys.stderr, sort_keys=True, indent=4)
sys.stderr.write("\n")
sys.exit(1)
[tag] = tag_set
print(f"Resolved {args.version} to {tag}")
cmd = [JIRI, "edit"]
for package in packages:
cmd += ["-package", "%s/${platform}=%s" % (package, tag)]
cmd += GetRustToolchainCommands(rust_toolchain_packages, tag)
cmd.append(args.manifest)
def DryRun(args):
print(args)
run = DryRun if args.dry_run else subprocess.check_call
run(cmd)
run([os.path.join(FUCHSIA_DIR, "integration", "update-lockfiles.sh")])
return 0
if __name__ == "__main__":
sys.exit(main())