blob: 01ed38f6092a244c7e0264d5e96caa3cd54e29bf [file] [log] [blame]
#!/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",
"riscv64gc-unknown-linux-gnu",
# Currently disabled; see https://fxbug.dev/325488864.
# "wasm32-unknown-unknown",
"x86_64-unknown-linux-gnu",
] + ([
"x86_64-apple-darwin",
"aarch64-apple-darwin",
] if any("mac-" in plat for plat in host_platforms) else [])
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():
possible_manifest_locations = [
# Open-source checkout
"toolchain",
# Internal checkout
os.path.join("fuchsia", "toolchain"),
# smart-integration checkout
os.path.join("platform", "fuchsia", "toolchain"),
]
default_jiri_manifest = None
for path in possible_manifest_locations:
abs_path = os.path.join(FUCHSIA_DIR, "integration", path)
if os.path.exists(abs_path):
default_jiri_manifest = abs_path
break
assert default_jiri_manifest, "failed to locate toolchain 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 host 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)
update_lockfiles = os.path.join(FUCHSIA_DIR, "integration", "update-lockfiles.sh")
if os.path.isfile(update_lockfiles):
run([update_lockfiles])
else:
sys.stderr.write("Warning: update-lockfiles.sh not found, falling back to jiri\n")
run([JIRI, "resolve", "-local-manifest"])
sys.stderr.write("\nRun this command to download the updated package:\n")
sys.stderr.write("$ jiri fetch-packages -local-manifest\n")
return 0
if __name__ == "__main__":
sys.exit(main())