blob: ff793f5596fe04b36342c546cb043f475880bcfb [file] [log] [blame]
#!/usr/bin/env python3
# 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.
from collections import OrderedDict
from os import path, getcwd
import argparse
import glob
import json
import os
import platform
import sys
import tempfile
DEFAULT_TOOLS = [
"cargo",
"rustfmt",
"cargo-fmt",
"clippy",
"src",
"rust-demangler",
"llvm-tools",
"rust-analyzer",
"rustdoc",
]
# TODO: http://fxbug.dev/310271984 - Remove the duplicated triples that are
# missing "-unknown".
FUCHSIA_TRIPLE_TO_GN_ARCH = {
"aarch64-fuchsia": "arm64",
"aarch64-unknown-fuchsia": "arm64",
"riscv64gc-unknown-fuchsia": "riscv64",
"x86_64-unknown-fuchsia": "x64",
"x86_64-fuchsia": "x64",
}
FUCHSIA_TRIPLES = list(FUCHSIA_TRIPLE_TO_GN_ARCH.keys())
FUCHSIA_RUNTIME_TARGETS = [
("aarch64-fuchsia",),
("aarch64-unknown-fuchsia",),
("riscv64gc-unknown-fuchsia", "riscv64-unknown-fuchsia"),
("x86_64-fuchsia",),
("x86_64-unknown-fuchsia",),
]
CMAKE_SETTINGS = """
cflags = "{cflags}"
cxxflags = "{cxxflags}"
ldflags = "{ldflags}"
"""
BOOTSTRAP_CONFIG = """
# Use our own compiler as stage0 for bootstrapping, rather than downloading
cargo = "{stage0}/bin/cargo"
rustc = "{stage0}/bin/rustc"
rustfmt = "{stage0}/bin/rustfmt"
"""
BUILD_CONFIG = """
change-id = 123711
[llvm]
optimize = true
thin-lto = {llvm_thinlto}
ccache = {gomacc_quote}
ninja = true
targets = "AArch64;ARM;RISCV;WebAssembly;X86"
{cmake_settings}
use-libcxx = true
# TODO(https://github.com/rust-lang/rust/issues/94983)
# We shouldn't have to set this to false to enable statically linking libc++
static-libstdcpp = false
[build]
target = [{target}]
{host_triple}
build-dir = "fuchsia-build"
submodules = false
docs = false
extended = true
cargo-native-static = true
tools = [{tools}]
{bootstrap}
[install]
prefix = "{prefix}"
sysconfdir = "etc"
[rust]
optimize = true
codegen-units-std = 1
channel = "{channel}"
lld = false
llvm-libunwind = "in-tree"
jemalloc = {jemalloc}
{rust_thinlto}
# Enable full debuginfo for libstd.
debuginfo-level-std = 2
[dist]
"""
TARGET_CONFIG = """
[target.{triple}]
cc = "{cc}"
cxx = "{cxx}"
ar = "{ar}"
ranlib = "{ranlib}"
linker = "{linker}"
profiler = {profiler}
llvm-has-rust-patches = {llvm_has_rust_patches}
"""
# This uses % formatting to avoid interactions with the brace syntax used in bash.
TOOL_WRAPPER_SCRIPT = """#!/bin/bash
fail() {
cat >&2 <<EOF
FATAL: Detected system library path '$1' in the linker search directory.
This leads to interactions between Fuchsia sysroot linker scripts
and system libraries that cause horribly confusing linker errors,
so we're going to error here instead.
EOF
exit 1
}
# Check for system paths.
# For now we only look for paths likely to be hit on Linux.
shopt -s extglob
for arg in "$@"; do
case "$arg" in
?(-L)/lib) fail "$arg"; break;;
?(-L)/usr/lib) fail "$arg"; break;;
?(-L)/usr/lib/*) fail "$arg"; break;;
esac
done
exec %(linker)s %(flags)s "$@"
"""
def _cflags(sysroot):
return "--sysroot=%s" % sysroot
def _cxxflags(sysroot):
return "--sysroot=%s -stdlib=libc++" % sysroot
def _ldflags(args, sysroot):
return "--sysroot=%s" % sysroot + (
" -L%s" % path.join(args.clang_prefix, "lib")
if "apple" in args.host
else " -static-libstdc++"
)
def clang_tool(args, tool, flags=None):
tool_binary = path.join(args.clang_prefix, "bin", tool) + (
".exe" if "windows" in args.host else ""
)
# Create a wrapper script to include necessary args (e.g. --sysroot) as
# Cargo does not properly select RUSTFLAGS when `--target` is passed.
if flags:
fd, name = tempfile.mkstemp()
with os.fdopen(fd, "w") as f:
f.write(TOOL_WRAPPER_SCRIPT % dict(linker=tool_binary, flags=flags))
os.chmod(name, 0o775)
return name
return tool_binary
def generate_config_toml(args):
if args.thinlto is None:
args.thinlto = "linux" in args.host and args.channel in ["nightly", "dev"]
targets = args.targets.split(",")
if "windows" in args.host:
# don't build fuchsia targets on windows
pass
elif "apple" in args.host:
# TODO(https://fxbug.dev/126336): fix riscv64gc-unknown-fuchsia build on mac
targets += [t for t in FUCHSIA_TRIPLES if t != "riscv64gc-unknown-fuchsia"]
else:
targets += FUCHSIA_TRIPLES
tools = args.tools.split(",")
def string_list(items):
return ", ".join('"%s"' % item for item in items)
cmake_fragment = (
'build-config = { CMAKE_SYSROOT="%s" }' % args.host_sysroot
if "windows" in args.host
else CMAKE_SETTINGS.format(
cflags=_cflags(args.host_sysroot),
cxxflags=_cxxflags(args.host_sysroot),
ldflags=_ldflags(args, args.host_sysroot),
)
)
config_toml = BUILD_CONFIG.format(
prefix=args.prefix,
jemalloc=str(not "windows" in args.host).lower(),
gomacc_quote=(
('"%s"' % path.join(args.goma_dir, "gomacc")) if args.goma_dir else "false"
),
cmake_settings=cmake_fragment,
llvm_thinlto=str(args.thinlto).lower(),
rust_thinlto='lto = "thin"' if args.thinlto else "",
target=string_list(targets),
channel=args.channel,
tools=string_list(tools),
# For windows specify that host is msvc not mingw
host_triple=(
'build = "x86_64-pc-windows-msvc"' if "windows" in args.host else ""
),
bootstrap=BOOTSTRAP_CONFIG.format(stage0=args.stage0) if args.stage0 else "",
)
for target in targets:
cflags = None
cxxflags = None
ldflags = None
if args.host == target.lower():
cflags = _cflags(args.host_sysroot)
cxxflags = _cxxflags(args.host_sysroot)
ldflags = _ldflags(args, args.host_sysroot)
cc = clang_tool(args, "clang", cflags)
cxx = clang_tool(args, "clang++", cxxflags)
linker = clang_tool(args, "clang++", ldflags)
ar = clang_tool(args, "llvm-ar")
if "fuchsia" in target:
linker = clang_tool(args, "ld.lld")
elif "windows" in target:
cc = cxx = clang_tool(args, "clang-cl")
linker = clang_tool(args, "lld-link")
ar = clang_tool(args, "llvm-lib")
config_toml += TARGET_CONFIG.format(
triple=target,
cc=cc,
cxx=cxx,
ar=ar,
ranlib=clang_tool(args, "llvm-ranlib"),
linker=linker,
# Enabling the profiler for wasm caused the build to fail, so we
# disable it for now.
profiler="false" if "wasm" in target else "true",
llvm_has_rust_patches="false" if args.llvm_is_vanilla else "true",
)
print(config_toml.replace("\\", "/"))
def generate_env(args):
env = OrderedDict()
env["RUST_BACKTRACE"] = 1
if args.revision:
env["CFG_VERSION"] = args.revision
# Tell bootstrap binary not to link to system libs.
env["LZMA_API_STATIC"] = 1
# Configure Fuchsia targets.
for target, gn_arch in list(FUCHSIA_TRIPLE_TO_GN_ARCH.items()):
triple_lower = target.replace("-", "_").lower()
triple_upper = triple_lower.upper()
clang_target = target.replace("riscv64gc", "riscv64")
env["CC_%s" % triple_lower] = clang_tool(args, "clang")
env["CXX_%s" % triple_lower] = clang_tool(args, "clang++")
env["CFLAGS_%s" % triple_lower] = "--target=%s --sysroot=%s -I%s" % (
clang_target,
path.join(args.sdk_dir, "arch", gn_arch, "sysroot"),
path.join(args.sdk_dir, "pkg", "fdio", "include"),
)
env["CXXFLAGS_%s" % triple_lower] = "--target=%s --sysroot=%s -I%s" % (
clang_target,
path.join(args.sdk_dir, "arch", gn_arch, "sysroot"),
path.join(args.sdk_dir, "pkg", "fdio", "include"),
)
env["LDFLAGS_%s" % triple_lower] = "--target=%s --sysroot=%s -L%s" % (
clang_target,
path.join(args.sdk_dir, "arch", gn_arch, "sysroot"),
path.join(args.sdk_dir, "arch", gn_arch, "lib"),
)
env["CARGO_TARGET_%s_AR" % triple_upper] = clang_tool(args, "llvm-ar")
env["CARGO_TARGET_%s_RUSTFLAGS" % triple_upper] = " ".join(
[
"-Clink-arg=--sysroot=%s"
% path.join(args.sdk_dir, "arch", gn_arch, "sysroot"),
"-Lnative=%s/lib" % path.join(args.sdk_dir, "arch", gn_arch, "sysroot"),
"-Lnative=%s" % path.join(args.sdk_dir, "arch", gn_arch, "lib"),
"-Cpanic=abort",
"-Cforce-unwind-tables=yes",
]
)
target_sysroots = {
"aarch64-unknown-linux-gnu": args.linux_arm64_sysroot or args.linux_sysroot,
"x86_64-unknown-linux-gnu": args.linux_amd64_sysroot or args.linux_sysroot,
"riscv64-unknown-linux-gnu": args.linux_riscv64_sysroot or args.linux_sysroot,
}
if args.darwin_sysroot:
target_sysroots.update({"aarch64-apple-darwin": args.darwin_sysroot})
target_sysroots.update({"x86_64-apple-darwin": args.darwin_sysroot})
if args.win_sysroot:
target_sysroots.update({"x86_64-pc-windows-msvc": args.win_sysroot})
# Configure non-Fuchsia target sysroots.
for target in args.targets.split(","):
clang_target = target.replace("riscv64gc", "riscv64")
# These are the only targets we define a sysroot for.
if not ("linux" in target or "apple" in target):
continue
sysroot = target_sysroots[clang_target]
assert sysroot, f"Sysroot path not supplied for target {target}"
arch = clang_target.split("-")[0]
if "linux" in target:
assert any(
p.startswith(f"{arch}-")
for p in os.listdir(os.path.join(sysroot, "lib"))
), f"Sysroot does not appear to support {arch}"
triple_lower = target.replace("-", "_").lower()
triple_upper = triple_lower.upper()
env["CC_%s" % triple_lower] = clang_tool(args, "clang", _cflags(sysroot))
env["CXX_%s" % triple_lower] = clang_tool(args, "clang++", _cxxflags(sysroot))
env["CFLAGS_%s" % triple_lower] = " ".join(
["--target=" + clang_target, "--sysroot=" + sysroot]
# TODO(https://fxbug.dev/69260): Find a longer-term solution.
+ (["-mno-outline-atomics"] if "aarch64" in target else [])
)
env["CXXFLAGS_%s" % triple_lower] = "--target=%s --sysroot=%s" % (
clang_target,
sysroot,
)
env["LDFLAGS_%s" % triple_lower] = "--target=%s --sysroot=%s" % (
clang_target,
sysroot,
) + (" -fuse-ld=lld" if "apple" not in target else "")
env["CARGO_TARGET_%s_AR" % triple_upper] = clang_tool(args, "llvm-ar")
env["CARGO_TARGET_%s_RUSTFLAGS" % triple_upper] = " ".join(
[
"-Clink-arg=--target=%s" % clang_target,
"-Clink-arg=--sysroot=%s" % sysroot,
# In the cases where we link libc++ we need to let the driver (clang++) use
# its knowledge of other dependencies, like libunwind. This only happens
# when building for host (linking to libLLVM), but we specify this flag for
# all non-Fuchsia targets for now.
"-Cdefault-linker-libraries=yes",
]
+ (["-Clink-arg=-fuse-ld=lld"] if "apple" not in target else [])
)
# FIXME(http://b/326089386): Rust appears to have an issue generating the
# right target triple for riscv32imc when compiling code with clang.
# Explicitly specify the target to get the build to work while we figure
# out how to fix upstream rust.
env["CFLAGS_riscv32imc_unknown_none_elf"] = (
"-ffunction-sections -fdata-sections --target=riscv32"
)
for key, val in list(env.items()):
if args.eval:
print('export {}="{}"'.format(key, val).replace("\\", "/"))
else:
print("{}={}".format(key, val).replace("\\", "/"))
# pylint: disable=unused-argument
def generate_runtimes_spec(args):
"""
Unused argument `args` required to preserve usage pattern:
```
parser_env.set_defaults(func=generate_env)
parser_runtime.set_defaults(func=generate_runtimes_spec)
args = parser.parse_args()
args.func(args)
```
"""
runtimes = []
for dynlink_flags in [[], ["-Cprefer-dynamic"]]:
for targets in FUCHSIA_RUNTIME_TARGETS:
if "apple" in args.host and targets[0] == "riscv64gc-unknown-fuchsia":
# TODO(https://fxbug.dev/126336): fix riscv64gc-unknown-fuchsia build on mac
continue
runtime = []
if dynlink_flags:
for lib in ["libstd", "libtest"]:
# The runtimes module will expand this wildcard
# (it must match exactly one file.)
pattern = "rustlib/%s/lib/%s-*.so" % (targets[0], lib)
matches = glob.glob(os.path.join(args.prefix, "lib", pattern))
if "libtest" == lib and len(matches) == 0:
# TODO: http://fxbug.dev/318746783 - libtest might be missing due to recent
# upstream changes. Hopefully we can remove this branch once things are
# settled.
continue
runtime.append(
{
"dist": pattern,
"name": lib,
}
)
runtimes.append(
{
"target": targets,
"rustflags": dynlink_flags,
"runtime": runtime,
}
)
print(json.dumps(runtimes))
def main():
systems = {
"Linux": "unknown-linux-gnu",
"Darwin": "apple-darwin",
"Windows": "pc-windows",
}
host = f"{platform.machine()}-{systems[platform.system()]}".replace(
# Convert the arch returned by uname on Macs to the LLVM spelling.
"arm64",
"aarch64",
)
default_targets = [
"aarch64-unknown-linux-gnu",
"x86_64-unknown-linux-gnu",
"thumbv6m-none-eabi",
"thumbv7m-none-eabi",
# FIXME(https://fxbug.dev/325488864): Enable once the build for this target is working again.
# "wasm32-unknown-unknown",
]
if "apple" in host:
default_targets.append("aarch64-apple-darwin")
default_targets.append("x86_64-apple-darwin")
else:
# TODO(https://fxbug.dev/42079174): Enable for mac hosts.
default_targets.append("riscv32imc-unknown-none-elf")
default_targets.append("riscv64gc-unknown-linux-gnu")
if "windows" in host:
default_targets = ["x86_64-pc-windows-msvc"]
parser = argparse.ArgumentParser()
parser.set_defaults(host=host)
subparsers = parser.add_subparsers()
parser_config = subparsers.add_parser("config_toml")
parser_env = subparsers.add_parser("environment")
parser_runtime = subparsers.add_parser("runtime")
for subcommand in [parser_config, parser_env]:
subcommand.add_argument(
"--stage0",
help="path to stage0 toolchain, will download from upstream by default",
)
subcommand.add_argument(
"--targets",
default=",".join(default_targets),
help="comma-separated list of targets to build for, in addition to Fuchsia",
)
subcommand.add_argument(
"--clang-prefix", help="path to clang toolchain", required=True
)
parser_config.add_argument("--prefix", help="installation prefix", required=True)
parser_config.add_argument(
"--host-sysroot", help="path to host sysroot", required=True
)
parser_config.add_argument(
"--llvm-is-vanilla",
action="store_true",
help="set if using upstream LLVM",
required=False,
)
parser_config.add_argument("--goma-dir", help="path to goma", required=False)
parser_config.add_argument(
"--thinlto",
action="store_true",
help="enable thinlto (enabled by default on linux nightly builds)",
)
parser_config.add_argument(
"--channel",
help="what compiler channel to build: dev, nightly, beta, or stable",
default="nightly",
)
parser_config.add_argument(
"--tools",
help="comma-separated list of tools to build",
default=",".join(DEFAULT_TOOLS),
)
parser_config.set_defaults(func=generate_config_toml)
parser_env.add_argument("--source", help="rust source path", default=getcwd())
parser_env.add_argument("--sdk-dir", help="path to Fuchsia SDK", required=True)
parser_env.add_argument("--linux-sysroot", help="path to Linux sysroot")
parser_env.add_argument(
"--linux-amd64-sysroot",
help="path to Linux amd64 sysroot (overrides --linux-sysroot)",
)
parser_env.add_argument(
"--linux-arm64-sysroot",
help="path to Linux arm64 sysroot (overrides --linux-sysroot)",
)
parser_env.add_argument(
"--linux-riscv64-sysroot",
help="path to Linux riscv64 sysroot (overrides --linux-sysroot)",
)
parser_env.add_argument(
"--darwin-sysroot", help="path to Darwin sysroot, if building on Mac"
)
parser_env.add_argument(
"--win-sysroot", help="path to Windows sysroot, if building on Windows"
)
parser_env.add_argument("--revision", help="git commit of Rust repository")
parser_env.add_argument(
"--eval",
default=False,
action="store_true",
help="format output for evaluating in a terminal",
)
parser_env.set_defaults(func=generate_env)
parser_runtime.set_defaults(func=generate_runtimes_spec)
# TODO: http://fxbug.dev/318746783 - Maybe remove this option. It's
# currently defaulted to reduce churn on the local workflow, but that could
# be a bit error-prone.
parser_runtime.add_argument(
"--prefix", help="installation prefix", default="install/fuchsia-rust"
)
args = parser.parse_args()
args.func(args)
return 0
if __name__ == "__main__":
sys.exit(main())