blob: 3545e2fb7fd03f03ea87a82af2bf025b33622f41 [file] [log] [blame]
# Copyright (C) 2019 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Handles generation of config.toml for the rustc build."""
import argparse
import os
from pathlib import Path
import subprocess
import stat
from string import Template
from typing import Any
import build_platform
from paths import *
HOST_TARGETS: list[str] = [build_platform.triple()] + build_platform.alt_triples()
DEVICE_TARGETS: list[str] = ["aarch64-linux-android", "armv7-linux-androideabi",
"x86_64-linux-android", "i686-linux-android"]
ALL_TARGETS: list[str] = HOST_TARGETS + DEVICE_TARGETS
TARGET_SPECIFIC_LINKER_FLAGS: dict[str, str] = {
# When performing LTO, the LLVM IR generator doesn't know about these
# target specific symbols. By telling the linker about them ahead of time
# we avoid an error when they are encountered when the native code is
# emitted. See b/201551165 for more information.
"armv7-linux-androideabi": "-u __aeabi_uidiv -u __aeabi_idiv0"
}
ANDROID_TARGET_VERSION: str = "31"
CONFIG_TOML_TEMPLATE: Path = TEMPLATES_PATH / "config.toml.template"
DEVICE_CC_WRAPPER_TEMPLATE: Path = TEMPLATES_PATH / "device_cc_wrapper.template"
DEVICE_LINKER_WRAPPER_TEMPLATE: Path = TEMPLATES_PATH / "device_linker_wrapper.template"
DEVICE_TARGET_TEMPLATE: Path = TEMPLATES_PATH / "device_target.template"
HOST_CC_WRAPPER_TEMPLATE: Path = TEMPLATES_PATH / "host_cc_wrapper.template"
HOST_CXX_WRAPPER_TEMPLATE: Path = TEMPLATES_PATH / "host_cxx_wrapper.template"
HOST_LINKER_WRAPPER_TEMPLATE: Path = TEMPLATES_PATH / "host_linker_wrapper.template"
HOST_TARGET_TEMPLATE: Path = TEMPLATES_PATH / "host_target.template"
LINKER_PIC_FLAG: str = "-Wl,-mllvm,-relocation-model=pic"
MACOSX_VERSION_FLAG: str = "-mmacosx-version-min=10.14"
def instantiate_template_exec(template_path: Path, output_path: Path, **kwargs: Any) -> None:
instantiate_template_file(template_path, output_path, make_exec=True, **kwargs)
def instantiate_template_file(template_path: Path, output_path: Path, make_exec: bool = False, **kwargs: Any) -> None:
with open(template_path) as template_file:
template = Template(template_file.read())
with open(output_path, "w") as output_file:
output_file.write(template.substitute(**kwargs))
if make_exec:
output_path.chmod(output_path.stat().st_mode | stat.S_IEXEC)
def host_config(target: str, sysroot: str, linker_flags: str) -> str:
cc_wrapper_name = OUT_PATH_WRAPPERS / f"clang-{target}"
cxx_wrapper_name = OUT_PATH_WRAPPERS / f"clang++-{target}"
linker_wrapper_name = OUT_PATH_WRAPPERS / f"linker-{target}"
macosx_version = MACOSX_VERSION_FLAG if build_platform.is_darwin() else ""
if target in TARGET_SPECIFIC_LINKER_FLAGS:
linker_flags += " " + TARGET_SPECIFIC_LINKER_FLAGS[target]
instantiate_template_exec(
HOST_CC_WRAPPER_TEMPLATE,
cc_wrapper_name,
real_cc=CC_PATH,
target=target,
macosx_flags=macosx_version,
sysroot=sysroot)
instantiate_template_exec(
HOST_CXX_WRAPPER_TEMPLATE,
cxx_wrapper_name,
real_cxx=CXX_PATH,
target=target,
macosx_flags=macosx_version,
sysroot=sysroot,
cxxstd=CXXSTD_PATH)
instantiate_template_exec(
HOST_LINKER_WRAPPER_TEMPLATE,
linker_wrapper_name,
real_cxx=CXX_PATH,
target=target,
macosx_flags=macosx_version,
sysroot=sysroot,
linker_flags=linker_flags)
with open(HOST_TARGET_TEMPLATE, "r") as template_file:
return Template(template_file.read()).substitute(
target=target,
cc=cc_wrapper_name,
cxx=cxx_wrapper_name,
linker=linker_wrapper_name,
ar=AR_PATH,
ranlib=RANLIB_PATH)
def device_config(target: str, linker_flags: str) -> str:
cc_wrapper_name = OUT_PATH_WRAPPERS / f"clang-{target}"
linker_wrapper_name = OUT_PATH_WRAPPERS / f"linker-{target}"
clang_target = target + ANDROID_TARGET_VERSION
if target in TARGET_SPECIFIC_LINKER_FLAGS:
linker_flags += " " + TARGET_SPECIFIC_LINKER_FLAGS[target]
instantiate_template_exec(
DEVICE_CC_WRAPPER_TEMPLATE,
cc_wrapper_name,
real_cc=CC_PATH,
target=clang_target,
sysroot=NDK_SYSROOT_PATH)
instantiate_template_exec(
DEVICE_LINKER_WRAPPER_TEMPLATE,
linker_wrapper_name,
real_cc=CC_PATH,
target=clang_target,
sysroot=NDK_SYSROOT_PATH,
linker_flags=linker_flags)
with open(DEVICE_TARGET_TEMPLATE, "r") as template_file:
return Template(template_file.read()).substitute(
target=target,
cc=cc_wrapper_name,
cxx=cc_wrapper_name,
linker=linker_wrapper_name,
ar=AR_PATH)
def configure(args: argparse.Namespace, env: dict[str, str]) -> None:
"""Generates config.toml and compiler wrapers for the rustc build."""
#
# Compute compiler/linker flags
#
host_sysroot: str = ""
lto_flag: str = f"-flto={args.lto}" if args.lto != "none" else ""
host_linker_flags: list[str] = [
lto_flag,
f"-Wl,-rpath,{build_platform.rpath_origin()}/../lib64",
f"-L{LLVM_CXX_RUNTIME_PATH.as_posix()}"
]
if build_platform.is_linux():
host_sysroot = GCC_SYSROOT_PATH
host_linker_flags += [
"-fuse-ld=lld",
f"-B{GCC_LIBGCC_PATH}",
f"-L{GCC_LIBGCC_PATH}",
f"-L{GCC_LIB_PATH}"
]
elif build_platform.is_darwin():
# Apple removed the normal sysroot at / on Mojave+, so we need
# to go hunt for it on OSX
# On pre-Mojave, this command will output the empty string.
output = subprocess.check_output(
["xcrun", "--sdk", "macosx", "--show-sdk-path"])
host_sysroot = output.rstrip().decode("utf-8")
# The `$` character should be escaped in the wrappers but not in the
# config.toml llvm::ldflags value (it causes Rust's boostrap system to
# complain and CMake does its own escaping).
host_linker_flags_str = " ".join(host_linker_flags)
host_linker_flags_str_escaped = host_linker_flags_str.replace("$", "\\$")
device_linker_flags = LINKER_PIC_FLAG
#
# Update environment variables
#
env["PATH"] = os.pathsep.join(
[p.as_posix() for p in [
RUST_HOST_STAGE0_PATH / "bin",
CMAKE_PREBUILT_PATH / "bin",
NINJA_PREBUILT_PATH,
BUILD_TOOLS_PREBUILT_PATH,
]] + [env["PATH"]])
# Only adjust the library path on Linux - on OSX, use the devtools curl
if build_platform.is_linux():
if "LIBRARY_PATH" in env:
old_library_path = f":{env['LIBRARY_PATH']}"
else:
old_library_path = ""
env["LIBRARY_PATH"] = f"{CURL_PREBUILT_PATH / 'lib'}{old_library_path}"
# Use LD_LIBRARY_PATH to tell the build system where to find libc++.so.1
# without polluting the rpath of the produced artifacts.
env["LD_LIBRARY_PATH"] = LLVM_CXX_RUNTIME_PATH.as_posix()
# Tell the rust bootstrap system where to place its final products
env["DESTDIR"] = OUT_PATH_PACKAGE.as_posix()
# Pass additional flags to the Rust compiler
env["RUSTFLAGS"] = "-C relocation-model=pic"
if args.lto != "none":
env["RUSTFLAGS"] += " -C linker-plugin-lto"
# The LTO flag must be passed via the CFLAGS environment variable due to
# the fact that including it in the host c/cxx wrappers will cause the
# CMake compiler detection routine to fail during LLVM configuration.
#
# The Rust bootstrap system will include the value of CFLAGS in all
# invocations of either the C or C++ compiler for all host targets. The
# LLVM build system will receive the LTO flag value from the llvm::cflags
# and llvm::cxxflags values in the config.toml file instantiated below.
#
# Because Rust's bootstrap system doesn't pass the linker wrapper into the
# LLVM build system AND doesn't respect the LDFLAGS environment variable
# this value gets into the LLVM build system via the llvm::ldflags value in
# config.toml and the Rust build system via the host linker wrapper, both
# of which are instantiated below using the same value for host_linker_flags.
#
# Note: Rust's bootstrap system will use CFLAGS for both C and C++ compiler
# invocations.
#
# Note: The Rust bootstrap system will copy CFLAGS into CFLAGS when
# invoking the LLVM build system. As a result the LTO argument will
# appear twice in the CMake language flag variables.
env["CFLAGS"] = lto_flag
#
# Intantiate wrappers
#
host_configs = "\n".join(
[host_config(target, host_sysroot, host_linker_flags_str_escaped) for target in HOST_TARGETS])
device_configs = "\n".join(
[device_config(target, device_linker_flags) for target in DEVICE_TARGETS])
all_targets = "[" + ",".join(
['"' + target + '"' for target in ALL_TARGETS]) + ']'
instantiate_template_file(
CONFIG_TOML_TEMPLATE,
OUT_PATH_RUST_SOURCE / "config.toml",
llvm_cflags=lto_flag,
llvm_cxxflags=lto_flag,
llvm_ldflags=host_linker_flags_str,
all_targets=all_targets,
cargo=CARGO_PATH,
rustc=RUSTC_PATH,
python=PYTHON_PATH,
host_configs=host_configs,
device_configs=device_configs)