blob: c6c4d3824f7cea79fb1cb0062da6685a964f4ac9 [file] [log] [blame]
# Copyright 2016 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.
import("//build/config/clang/clang.gni")
import("//build/rust/config.gni")
import("//build/toolchain/breakpad.gni")
import("//build/toolchain/ccache.gni")
import("//build/toolchain/concurrent_jobs.gni")
import("//build/toolchain/default_tools.gni")
import("//build/toolchain/goma.gni")
import("//build/toolchain/rbe.gni")
import("//build/toolchain/restat.gni")
if (support_rust) {
import("//build/rust/config.gni")
}
# Defines a Clang-based toolchain.
#
# Parameters
#
# toolchain_cpu
# Required: Value of "current_cpu" inside this toolchain.
#
# toolchain_os
# Required: Value of "current_os" inside this toolchain.
#
# toolchain_args
# Optional: Scope specifying build arguments to override in this
# toolchain context.
# NOTE! Do not use this in the toolchain used as the default toolchain.
# It will have no effect, and you will be confused.
#
# use_strip
# Required: Whether to strip binaries, leaving unstripped ones
# in lib.unstripped and exe.unstripped subdirectories.
#
# strip_cxx_bin_args
# Optional: When use_strip is true, can be used to override
# the arguments to the tool used to strip C++ executables.
# For example, when targeting Fuchsia, using --strip-sections
# generates smaller binaries than the default. Does not affect
# shared libraries, or Rust executables.
# Type: string
# Default: "--strip-all" except on macOS where it is "-Sx"
#
# prefix
# Optional: The path from which "cc", "cxx", "ar", "ld" and
# "strip" will be found (unless overridden). If not given,
# prefix defaults to $rebased_clang_prefix.
#
# cc
# cxx
# ar
# ld
# strip
# Optional: Override default tool names.
#
# enable_rust
# Optional: Whether to enable the rust tools in the toolchain.
# Type: bool
# Default: true
#
# use_ccache, use_goma, cxx_rbe_enable, link_rbe_enable
# Optional: Override the global setting, useful to opt out of
# ccache/goma in a particular toolchain.
#
# deps
# Optional: Dependencies of this toolchain.
#
# propagates_configs
# Optional: public_configs should escape this toolchain via deps
#
template("clang_toolchain") {
toolchain(target_name) {
assert(defined(invoker.toolchain_cpu),
"clang_toolchain() must specify a \"toolchain_cpu\"")
assert(defined(invoker.toolchain_os),
"clang_toolchain() must specify a \"toolchain_os\"")
enable_rust =
support_rust && (!defined(invoker.enable_rust) || invoker.enable_rust)
compiler_prefix_list = []
if (enable_rust) {
if (defined(invoker.rust_rbe_enable)) {
rust_rbe_enable = invoker.rust_rbe_enable
}
}
if (defined(invoker.cxx_rbe_enable)) {
_cxx_rbe_enable = invoker.cxx_rbe_enable
} else {
_cxx_rbe_enable = cxx_rbe_enable
}
if (defined(invoker.use_goma)) {
use_goma = invoker.use_goma
}
if (defined(invoker.link_rbe_enable)) {
_link_rbe_enable = invoker.link_rbe_enable
} else {
_link_rbe_enable = link_rbe_enable
}
# re-client provides write-if-change with --preserve_unchanged_output_mtime.
_restat_link = _link_rbe_enable
if (_cxx_rbe_enable) {
# Use reclient to remotely build C++.
if (cxx_rbe_minimalist_wrapper) {
_cxx_remote_wrapper_path =
rebase_path(cxx_remote_wrapper_minimalist, root_build_dir)
_working_subdir = rebase_path(root_build_dir, "//")
compiler_prefix_list = [
_cxx_remote_wrapper_path,
"--working-subdir=" + _working_subdir,
]
} else {
_cxx_remote_wrapper_path =
rebase_path(cxx_remote_wrapper, root_build_dir)
compiler_prefix_list = [
rebase_path(python_exe_src, root_build_dir),
"-S",
_cxx_remote_wrapper_path,
]
}
if (cxx_rbe_check == "consistency") {
# Compare local vs. remote build.
# Allow caching.
# Use local concurrency.
# TODO(https://fxbug.dev/42079382): enable file trace comparison
compiler_prefix_list += [
"--compare",
# "--fsatrace-path",
# rebase_path("//prebuilt/fsatrace/fsatrace", root_build_dir),
"--miscomparison-export-dir",
rebase_path(comparison_diagnostics_dir, root_build_dir),
"--exec_strategy=remote",
]
_cxx_concurrent_jobs = concurrent_jobs.local
} else if (cxx_rbe_check == "determinism") {
# Determinism check is run locally, and doesn't use RBE,
# even though it uses the same wrapper script.
compiler_prefix_list += [
"--local",
"--check-determinism",
"--miscomparison-export-dir",
rebase_path(comparison_diagnostics_dir, root_build_dir),
]
_cxx_concurrent_jobs = concurrent_jobs.local
} else {
compiler_prefix_list += RBE_EXEC_STRATEGY_MAP[cxx_rbe_exec_strategy]
if (!cxx_rbe_download_obj_files) { # download everything except the
# object file
compiler_prefix_list += [ "--download_regex='-.*\\.o\$\$'" ]
}
_cxx_concurrent_jobs = concurrent_jobs.remote
}
if (restat_cc) {
# re-client has built-in support for write-if-changed behavior
compiler_prefix_list += [ "--preserve_unchanged_output_mtime" ]
}
compiler_prefix_list += [ "--" ]
not_needed([ "use_goma" ])
} else if (use_goma) {
compiler_prefix_list =
[ rebase_path(goma_dir, root_build_dir) + "/gomacc" ]
_cxx_concurrent_jobs = concurrent_jobs.remote
} else {
if (defined(invoker.use_ccache)) {
use_ccache = invoker.use_ccache
}
if (use_ccache) {
compiler_prefix_list = [ "ccache " ]
}
_cxx_concurrent_jobs = concurrent_jobs.local
}
compiler_prefix = string_join(" ", compiler_prefix_list) + " "
prefix = rebased_clang_prefix
if (defined(invoker.prefix)) {
prefix = invoker.prefix
}
if (link_rbe_enable) {
_link_rbe_exec_strategy_flags =
RBE_EXEC_STRATEGY_MAP[link_rbe_exec_strategy]
}
# Configure builds to be restat-friendly by preserving timestamps of
# unchanged outputs, for both local and remote builds.
_restat_rustc_prefix_list = []
if (restat_rust) {
if (!rust_rbe_enable) {
# Local-only execution may use this wrapper script directly.
_restat_rust_wrapper = [
rebase_path(python_exe_src, root_build_dir),
"-S",
rebase_path(restat_wrapper, root_build_dir),
]
_restat_rustc_prefix_list = _restat_rust_wrapper + [
"--outputs",
"{{output}}",
"{{DEPFILE}}",
"--",
]
}
# For remote-building, --preserve_unchanged_output_mtime is sufficient
# to preserve timestamps of unchanged outputs, and provide restat benefit.
# However, in modes that also (conditionally) use local execution
# (i.e. --exec_strategy={racing,remote_local_fallback}),
# the local execution could unconditionally overwrite outputs.
# While this may be suboptimal as a missed opportunity to prune
# the action graph, it does not violate correctness.
}
_restat_rustc_prefix = string_join(" ", _restat_rustc_prefix_list) + " "
_restat_cc_prefix_list = []
if (restat_cc) {
if (!_cxx_rbe_enable) {
# Local-only execution may use this wrapper script directly.
_restat_cc_wrapper = [
rebase_path(python_exe_src, root_build_dir),
"-S",
rebase_path(restat_wrapper, root_build_dir),
]
_restat_cc_prefix_list = _restat_cc_wrapper + [
"--outputs",
"{{output}}",
"{{DEPFILE}}",
"--",
]
}
# For remote-building, --preserve_unchanged_output_mtime is sufficient
# to preserve timestamps of unchanged outputs, and provide restat benefit.
# However, in modes that also (conditionally) use local execution
# (i.e. --exec_strategy={racing,remote_local_fallback}),
# the local execution could unconditionally overwrite outputs.
# While this may be suboptimal as a missed opportunity to prune
# the action graph, it does not violate correctness.
}
_restat_cc_prefix = string_join(" ", _restat_cc_prefix_list) + " "
if (enable_rust) {
# Configure Rust remote builds.
_rustc_wrapper = ""
_rust_concurrent_jobs = {
}
if (rust_rbe_enable) {
assert(
rust_rbe_check == "none" || rust_rbe_check == "consistency" ||
rust_rbe_check == "determinism",
"rust_rbe_check must be one of {none,consistency,determinism}, but got: ${rust_rbe_check}.")
if (rust_rbe_check == "consistency") {
# Compare local vs. remote build.
# Allow caching.
# Use local concurrency.
_rewrapper_flags = [
"--compare",
"--fsatrace-path",
rebase_path("//prebuilt/fsatrace/fsatrace", root_build_dir),
"--exec_strategy=remote",
"--miscomparison-export-dir",
rebase_path(comparison_diagnostics_dir, root_build_dir),
]
_rust_concurrent_jobs = concurrent_jobs.local
} else if (rust_rbe_check == "determinism") {
# Determinism check is run locally, and doesn't use RBE,
# even though it uses the same wrapper script.
_rewrapper_flags = [
"--local",
"--check-determinism",
"--miscomparison-export-dir",
rebase_path(comparison_diagnostics_dir, root_build_dir),
]
_rust_concurrent_jobs = concurrent_jobs.local
} else {
_rust_rbe_exec_strategy_flags =
RBE_EXEC_STRATEGY_MAP[rust_rbe_exec_strategy]
_rewrapper_flags = _rust_rbe_exec_strategy_flags
}
if (restat_rust) {
# re-client has built-in support for write-if-changed behavior
_rewrapper_flags += [ "--preserve_unchanged_output_mtime" ]
}
_rustc_remote_wrapper_path =
rebase_path(rustc_remote_wrapper, root_build_dir)
_rustc_wrapper_tokens = [
rebase_path(python_exe_src, root_build_dir),
"-S",
_rustc_remote_wrapper_path,
]
_rustc_wrapper = string_join(" ",
_rustc_wrapper_tokens + _rewrapper_flags +
[ "--" ]) + " "
_rust_concurrent_jobs = concurrent_jobs.remote
} else {
_rust_concurrent_jobs = concurrent_jobs.local
}
}
# Set all tools to defaults
cc = "${prefix}/clang"
cxx = "${prefix}/clang++"
ar = "${prefix}/llvm-ar"
ld = cxx
if (enable_rust) {
rustc = "${rebased_rustc_prefix}/bin/rustc"
}
verify_depfile = string_join(" ",
[
rebase_path(python_exe_src, root_build_dir),
# Speed up startup time by not searching for
# site packages.
"-S",
rebase_path("//build/gn/verify_depfile.py",
root_build_dir),
])
use_buildidtool =
invoker.toolchain_os == "linux" || invoker.toolchain_os == "fuchsia"
if (use_buildidtool) {
buildidtool = rebase_path(
"//prebuilt/tools/buildidtool/${host_platform}/buildidtool",
root_build_dir)
}
forward_variables_from(invoker, [ "use_strip" ])
if (use_strip) {
if (invoker.toolchain_os == "mac") {
strip = "${prefix}/llvm-strip"
} else {
strip = "${prefix}/llvm-objcopy"
if (output_breakpad_syms && invoker.toolchain_os == "fuchsia") {
dump_syms = breakpad_dump_syms
# The breakpad module name for executables and loadable modules.
breakpad_name = "<_>"
}
if (output_gsym && invoker.toolchain_os == "fuchsia") {
# TODO(https://fxbug.dev/42136235): enable for other targets.
gsymutil = "${prefix}/llvm-gsymutil"
}
}
}
# Override any tools as requested by the invoker
if (defined(invoker.cc)) {
cc = invoker.cc
}
if (defined(invoker.cxx)) {
cxx = invoker.cxx
}
forward_variables_from(invoker,
[
"ar",
"deps",
"ld",
"propagates_configs",
"strip",
])
# These library switches can apply to all tools below.
lib_switch = "-l"
lib_dir_switch = "-L"
tool("cc") {
forward_variables_from(_cxx_concurrent_jobs, "*")
depfile = "{{output}}.d"
command = "$cc -MD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}"
command_launcher =
string_replace(_restat_cc_prefix, "{{DEPFILE}}", depfile) +
compiler_prefix
depsformat = "gcc"
description = "CC {{output}}"
outputs =
[ "{{source_out_dir}}/{{target_output_name}}.{{source_file_part}}.o" ]
restat = restat_cc
}
tool("cxx") {
forward_variables_from(_cxx_concurrent_jobs, "*")
depfile = "{{output}}.d"
command = "$cxx -MD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}"
command_launcher =
string_replace(_restat_cc_prefix, "{{DEPFILE}}", depfile) +
compiler_prefix
depsformat = "gcc"
description = "CXX {{output}}"
outputs =
[ "{{source_out_dir}}/{{target_output_name}}.{{source_file_part}}.o" ]
restat = restat_cc
}
tool("asm") {
forward_variables_from(_cxx_concurrent_jobs, "*")
depfile = "{{output}}.d"
command = "$cc -MD -MF $depfile {{defines}} {{include_dirs}} {{asmflags}} -c {{source}} -o {{output}}"
command_launcher =
string_replace(_restat_cc_prefix, "{{DEPFILE}}", depfile) +
compiler_prefix
depsformat = "gcc"
description = "ASM {{output}}"
outputs =
[ "{{source_out_dir}}/{{target_output_name}}.{{source_file_part}}.o" ]
restat = restat_cc
}
tool("objc") {
forward_variables_from(concurrent_jobs.local, "*")
depfile = "{{output}}.d"
command = "$cc -MD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} {{cflags_objc}} -c {{source}} -o {{output}}"
command_launcher =
string_replace(_restat_cc_prefix, "{{DEPFILE}}", depfile) +
compiler_prefix
depsformat = "gcc"
description = "OBJC {{output}}"
outputs =
[ "{{source_out_dir}}/{{target_output_name}}.{{source_file_part}}.o" ]
restat = restat_cc
}
tool("objcxx") {
forward_variables_from(concurrent_jobs.local, "*")
depfile = "{{output}}.d"
command = "$cxx -MD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} {{cflags_objcc}} -c {{source}} -o {{output}}"
command_launcher =
string_replace(_restat_cc_prefix, "{{DEPFILE}}", depfile) +
compiler_prefix
depsformat = "gcc"
description = "OBJCXX {{output}}"
outputs =
[ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
restat = restat_cc
}
# Compute default dynamic library extension
if (invoker.toolchain_os == "mac") {
_dylib_extension = ".dylib"
} else if (invoker.toolchain_cpu == "wasm32") {
_dylib_extension = ".wasm"
} else if (invoker.toolchain_os == "win") {
_dylib_extension = ".dll"
} else if (invoker.toolchain_os == "fuchsia" ||
invoker.toolchain_os == "linux") {
_dylib_extension = ".so"
} else {
assert(false,
"Unsupported toolchain_os value \"${invoker.toolchain_os}\"")
}
# The _link_common scope is used to capture common computations that
# are needed by the C++ and rust link tools. It should define the
# following keys:
#
# command: The command used to build the binary.
#
# command_suffix: A suffix to append to commands after linking it. Used
# to perform optional symbol extraction and stripping.
#
# depfile: The depfile to use.
#
# outfile: Path to the final output file.
#
# outname: Base name of final output file (including extension).
#
# outputs: The list of outputs for the command.
#
# pool: Pool name (optional).
#
# cxx_link_remote_strip_prefix: The strip tool prefix to use when
# building remotely with RBE.
#
# unstripped_outfile: Path to unstripped output file.
#
# Note that the 'command' and 'outputs' keys will use template parameters
# in their value, with the following expressions, which must be expanded
# based on the final target type:
#
# - {{UNSTRIPPED_OUTFILE}}: path of the unstripped output file. Should be
# 'unstripped_outfile', except for tool("rust_bin") when toolchain_os is
# "wasm32", where it should be "${unstripped_outfile}.wasm" instead.
# The reason for this is that setting default_output_extension doesn't
# work for binaries as it does for libraries.
#
# - {{UNSTRIPPED_DIR}}: name of directory holding unstripped binaries.
# 'lib.unstripped' for shared libraries and modules, and 'exe.unstripped'
# for executables. Must be expanded after {{UNSTRIPPED_OUTFILE}}.
#
# - {{BREAKPAD_NAME}}: Either an empty string, or "-n \"$breakpad_name\" "
# (note the trailing space) for loadable modules and executables
# (but not shared libraries), when breakpad symbols must be generated.
# Note that this is only used if 'breakpad_name' is defined.
#
# - {{STRIP_ARGS}}: The argument to the tool being used to strip binaries,
# when use_strip is true. Expansion depends on the target type, the
# host os and whether invoker.strip_cxx_bin_args is defined.
#
# - {{REMOTE_STRIP_PREFIX}}: Either an empty string, or the value of
# `rust_link_remote_strip_prefix` or `cxx_link_remote_strip_prefix`
# if the target type and RBE settings need it.
#
# Note that the choice of GN-style source expansion parameters is
# intentional: If by mistake the parameters are not properly expanded
# inside the tool() definitions, GN will complain loudly about it!
#
_link_common = {
outname = "{{target_output_name}}{{output_extension}}"
outfile = "{{output_dir}}/$outname"
depfile = "$outfile.d"
command = ""
command_suffix = ""
unstripped_outfile = outfile
if (use_strip) {
command += "mkdir -p {{output_dir}}/{{UNSTRIPPED_DIR}} && "
unstripped_outfile = "{{output_dir}}/{{UNSTRIPPED_DIR}}/$outname"
}
if (defined(invoker.pool)) {
pool = invoker.pool
}
outputs = [ outfile ]
if (outfile != unstripped_outfile) {
outputs += [ unstripped_outfile ]
}
if (use_strip) {
if (rust_rbe_enable || _link_rbe_enable) {
_remote_strip_prefix_common = [
rebase_path(python_exe_src, root_build_dir),
"-S",
rebase_path(prebuilt_tool_remote_wrapper, root_build_dir),
"--inputs",
"\"$unstripped_outfile\"",
"--output_files",
"\"$outfile\"",
"--preserve_unchanged_output_mtime",
]
if (rust_rbe_enable) {
_exec_strategy_flags = []
if (rust_rbe_check == "") {
_exec_strategy_flags = _rust_rbe_exec_strategy_flags
}
rust_link_remote_strip_prefix =
string_join(" ",
_remote_strip_prefix_common + _exec_strategy_flags +
[ "-- " ])
}
if (_link_rbe_enable) {
cxx_link_remote_strip_prefix =
string_join(" ",
_remote_strip_prefix_common +
_link_rbe_exec_strategy_flags + [ "-- " ])
}
}
if (invoker.toolchain_os == "mac") {
command_suffix += " && {{REMOTE_STRIP_PREFIX}}$strip {{STRIP_ARGS}} \"{{UNSTRIPPED_OUTFILE}}\" -o \"$outfile\""
} else {
# TODO(https://fxbug.dev/42137337): Strip .rustc section from packaged dylibs.
command_suffix += " && {{REMOTE_STRIP_PREFIX}}$strip {{STRIP_ARGS}} \"{{UNSTRIPPED_OUTFILE}}\" \"$outfile\""
}
if (use_buildidtool) {
buildid_stampfile = "${outfile}.build-id.stamp"
outputs += [ buildid_stampfile ]
command_suffix +=
" && $buildidtool -build-id-dir .build-id" +
" -stamp \"$buildid_stampfile\"" + " -entry \"=$outfile\"" +
" -entry \".debug={{UNSTRIPPED_OUTFILE}}\""
}
}
if (defined(breakpad_name) && host_os != "mac") {
assert(use_buildidtool)
breakpad_outfile = "${outfile}.sym"
outputs += [ breakpad_outfile ]
rust_link_remote_dump_syms_prefix = ""
if (rust_rbe_enable || _link_rbe_enable) {
_remote_dump_syms_prefix_list = [
rebase_path(python_exe_src, root_build_dir),
"-S",
rebase_path(prebuilt_tool_remote_wrapper, root_build_dir),
"--label_toolname=$breakpad_dump_syms",
"--inputs", # need to specify the executable, due to wrapper script
"$breakpad_dump_syms,\"$unstripped_outfile\"",
"--output_files",
"\"$breakpad_outfile\"",
"--preserve_unchanged_output_mtime",
]
if (_link_rbe_enable) {
cxx_link_remote_dump_syms_prefix =
string_join(" ",
_remote_dump_syms_prefix_list +
_link_rbe_exec_strategy_flags + [ "-- " ])
}
if (rust_rbe_enable) {
_exec_strategy_flags = []
if (rust_rbe_check == "") {
_exec_strategy_flags = _rust_rbe_exec_strategy_flags
}
rust_link_remote_dump_syms_prefix =
string_join(" ",
_remote_dump_syms_prefix_list +
_exec_strategy_flags + [ "-- " ])
}
}
# dump_syms will fail if there is no build ID. buildidtool succeeds and
# writes an empty stamp file for that case, so use it to tell whether
# dump_syms should be run. In any case always create a .sym because we
# told ninja we would create one above.
# TODO(b/321834013): Cases that clearly do not have a build-id should
# ideally not attempt to dump_syms.
log_it = rebase_path(log_it_script, root_build_dir)
command_suffix += " && { " + "test ! -s \"$buildid_stampfile\" && " +
"touch \"$outfile.sym\" || " + "{{REMOTE_DUMP_SYMS_PREFIX}}$log_it --log \"$breakpad_outfile\" -- $dump_syms -r {{BREAKPAD_NAME}}-o Fuchsia \"{{UNSTRIPPED_OUTFILE}}\"" + " ;}"
}
if (output_gsym && invoker.toolchain_os == "fuchsia") {
gsym_outfile = "${outfile}.gsym"
outputs += [ "$gsym_outfile" ]
command_suffix += " && $gsymutil --convert=\"{{UNSTRIPPED_OUTFILE}}\" --out-file=\"$gsym_outfile\""
}
}
# Quiet GN for wasm32 toolchain
not_needed(_link_common, [ "command_suffix" ])
if (enable_rust) {
# The _rust_link_common scope is used to capture common computations that
# are needed by the "rust_bin", "rust_dylib" and "rust_cdylib" tool()
# definitions below. It should define the following keys:
#
# command: The command used to build the binary.
# depfile: The depfile to use.
# outfile: Path to final output file.
# outputs: The list of outputs for the command.
# unstripped_outfile: Path to unstripped output file (see below).
#
# Note that the 'command' and 'outputs' keys will use template parameters
# in their string, with the following values, which must be expanded
# based on the final target type (see `_link_common` comment for details):
#
# {{UNSTRIPPED_OUTFILE}}
# {{UNSTRIPPED_DIR}}
# {{BREAKPAD_NAME}}
# {{STRIP_ARGS}}
# {{REMOTE_STRIP_PREFIX}}
# {{REMOTE_DUMP_SYMS_PREFIX}}
#
_rust_link_common = {
forward_variables_from(_link_common,
"*",
[
"cxx_link_remote_strip_prefix",
"cxx_link_remote_dump_syms_prefix",
"rust_link_remote_strip_prefix",
"rust_link_remote_dump_syms_prefix",
])
forward_variables_from(_rust_concurrent_jobs, "*")
ld_location_env = ""
link_map_args = ""
if (invoker.toolchain_os == "fuchsia") {
# On Fuchsia, lld is directly used to perform linking
link_map_args = "-C link-args=--Map=\"{{UNSTRIPPED_OUTFILE}}.map\""
} else if (invoker.toolchain_cpu == "wasm32") {
# TODO(https://fxbug.dev/42133775): Remove need for ld location.
# On WASM builds, rust-ldd has trouble finding libLLVM.
# This workaround allows the build to proceed.
shared_lib_dir = rebase_path("$rustc_prefix/lib", root_build_dir)
if (host_os == "mac") {
ld_location_env = " DYLD_FALLBACK_LIBRARY_PATH=\"$shared_lib_dir\""
} else if (host_os == "linux") {
ld_location_env = " LD_LIBRARY_PATH=\"$shared_lib_dir\""
} else {
assert(false, "Unsupported host os: $host_os")
}
# Disable link map args, which also seems to have a problem with WASM.
} else if (invoker.toolchain_os == "mac") {
# NOTE: MacOs does not support '--Map' in linker flags
} else {
# On host, clang seems to be used to perform linking
link_map_args =
"-C link-args=-Wl,--Map=\"{{UNSTRIPPED_OUTFILE}}.map\""
}
# Overwrite an existing output file only if its contents have changed.
command += string_replace(_restat_rustc_prefix, "{{DEPFILE}}", depfile)
restat = restat_rust
# Possibly enable remote building for the rustc command.
command += _rustc_wrapper
# TODO(https://fxbug.dev/42124325): Replacement for rustenv
command += "{{rustenv}}${ld_location_env} RUST_BACKTRACE=1 $rustc --color=always --crate-name {{crate_name}} {{source}}"
command += " --crate-type {{crate_type}} --emit=dep-info=$depfile,link{{EMIT_RMETA}} -Zdep-info-omit-d-target $link_map_args {{rustflags}}"
command += " -o \"{{UNSTRIPPED_OUTFILE}}\""
if (rustc_use_response_files) {
rspfile = outfile + ".rsp"
rspfile_content = "{{rustdeps}} {{externs}}"
command += " -Zshell-argfiles @shell:" + rspfile
} else {
command += " {{rustdeps}} {{externs}}"
}
command +=
" && $verify_depfile -t \"{{label}}\" -d $depfile {{sources}}"
# Append command suffix and expand {{STRIP_ARGS}} and {{REMOTE_STRIP_PREFIX}}
if (invoker.toolchain_os == "mac") {
_strip_args = "-Sx"
} else {
_strip_args = "--strip-all --keep-section=.rustc"
}
command += string_replace(_link_common.command_suffix,
"{{STRIP_ARGS}}",
_strip_args)
_remote_strip_prefix = ""
if (use_strip && rust_rbe_enable) {
_remote_strip_prefix = _link_common.rust_link_remote_strip_prefix
}
command = string_replace(command,
"{{REMOTE_STRIP_PREFIX}}",
_remote_strip_prefix)
_remote_dump_syms_prefix = ""
if (defined(_link_common.rust_link_remote_dump_syms_prefix)) {
_remote_dump_syms_prefix =
_link_common.rust_link_remote_dump_syms_prefix
}
command = string_replace(command,
"{{REMOTE_DUMP_SYMS_PREFIX}}",
_remote_dump_syms_prefix)
rust_sysroot = "$rebased_rustc_prefix"
}
# Compute common values for "rust_dylib" and "rust_cdylib".
# All template parameters will be expanded.
_rust_dylib_common = {
forward_variables_from(_rust_link_common, "*", [ "outputs" ])
# Expand UNSTRIPPED_OUTFILE
command = string_replace(command,
"{{UNSTRIPPED_OUTFILE}}",
unstripped_outfile)
default_output_extension = _dylib_extension
# Expand UNSTRIPPED_DIR
_unstripped_dir = "lib.unstripped"
command = string_replace(command, "{{UNSTRIPPED_DIR}}", _unstripped_dir)
outputs = []
foreach(output, _rust_link_common.outputs) {
output = string_replace(output,
"{{UNSTRIPPED_OUTFILE}}",
unstripped_outfile)
output = string_replace(output, "{{UNSTRIPPED_DIR}}", _unstripped_dir)
outputs += [ output ]
}
# Expand BREAKPAD_NAME, if present, which is unused for dynamic libraries.
command = string_replace(command, "{{BREAKPAD_NAME}}", "")
if (invoker.toolchain_os == "fuchsia") {
# On Fuchsia, lld is directly used to perform linking
command = string_replace(
command,
"{{rustflags}}",
"-C link-arg=--soname=\"{{target_output_name}}{{output_extension}}\" {{rustflags}}")
} else {
# TODO: should soname be set on other platforms?
}
}
tool("rust_bin") {
description = "RUST {{output}}"
default_output_dir = "{{root_out_dir}}"
rust_sysroot = "$rebased_rustc_prefix"
forward_variables_from(_rust_link_common, "*", [ "outputs" ])
if (invoker.toolchain_cpu == "wasm32") {
unstripped_outfile += ".wasm"
}
_unstripped_dir = "exe.unstripped"
_outputs = _rust_link_common.outputs
if (rust_emit_rmeta) {
rmeta = "$unstripped_outfile.rmeta"
# Even though rmeta is not needed as an output,
# we use emit=metadata to signal to the
# remote wrapper that rmetas should be used
# for dependency scanning, as opposed to
# complete rlibs.
command =
string_replace(command, "{{EMIT_RMETA}}", ",metadata=$rmeta")
_outputs += [ rmeta ]
} else {
command = string_replace(command, "{{EMIT_RMETA}}", "")
}
# Expand UNSTRIPPED_OUTFILE
command = string_replace(command,
"{{UNSTRIPPED_OUTFILE}}",
unstripped_outfile)
# Expand UNSTRIPPED_DIR
command = string_replace(command, "{{UNSTRIPPED_DIR}}", _unstripped_dir)
outputs = []
foreach(output, _outputs) {
output = string_replace(output,
"{{UNSTRIPPED_OUTFILE}}",
unstripped_outfile)
output = string_replace(output, "{{UNSTRIPPED_DIR}}", _unstripped_dir)
outputs += [ output ]
}
# Expand BREAKPAD_NAME if present.
if (defined(breakpad_name)) {
command = string_replace(command,
"{{BREAKPAD_NAME}}",
"-n \"$breakpad_name\" ")
}
# As a special case, on Fuchsia, replace --strip-all with --strip-sections.
# It is stronger, and thus generates smaller binaries, but also creates
# crashes for host binaries (e.g. https://fxbug.dev/42126969).
if (invoker.toolchain_os == "fuchsia") {
command =
string_replace(command, " --strip-all ", " --strip-sections ")
}
}
tool("rust_rlib") {
forward_variables_from(_rust_concurrent_jobs, "*")
rmeta = "{{output_dir}}/lib{{target_output_name}}.rmeta"
rlib = "{{output_dir}}/lib{{target_output_name}}{{output_extension}}"
depfile = "$rlib.d"
# Overwrite an existing output file only if its contents have changed.
command = string_replace(_restat_rustc_prefix, "{{DEPFILE}}", depfile)
restat = restat_rust
# Possibly enable remote building for the rustc command.
command += _rustc_wrapper
# TODO(https://fxbug.dev/42124325): Replacement for rustenv
command += "{{rustenv}} RUST_BACKTRACE=1 $rustc --color=always --crate-name {{crate_name}} {{source}}"
extra_emit_opts = ""
if (rust_emit_rmeta) {
extra_emit_opts = ",metadata=$rmeta"
} else {
not_needed([ "rmeta" ])
}
command += " --crate-type {{crate_type}} --emit=dep-info=$depfile,link$extra_emit_opts -Zdep-info-omit-d-target {{rustflags}}"
if (!rust_rbe_download_rlibs) { # download everything except the rlib
command += " --remote-flag=--download_outputs=false"
}
command += " -o $rlib"
if (rustc_use_response_files) {
rspfile = "$rlib.rsp"
rspfile_content = "{{rustdeps}} {{externs}}"
command += " -Zshell-argfiles @shell:" + rspfile
} else {
command += " {{rustdeps}} {{externs}}"
}
command +=
" && $verify_depfile -t \"{{label}}\" -d $depfile {{sources}}"
description = "RUST {{output}}"
outputs = [ rlib ]
if (rust_emit_rmeta) {
outputs += [ rmeta ]
}
default_output_dir = "{{target_out_dir}}"
default_output_extension = ".rlib"
rust_sysroot = "$rebased_rustc_prefix"
}
tool("rust_staticlib") {
forward_variables_from(_rust_concurrent_jobs, "*")
_main_output =
"{{output_dir}}/{{target_output_name}}{{output_extension}}"
depfile = "$_main_output.d"
# Overwrite an existing output file only if its contents have changed.
command = string_replace(_restat_rustc_prefix, "{{DEPFILE}}", depfile)
restat = restat_rust
# Possibly enable remote building for the rustc command.
command += _rustc_wrapper
# TODO(https://fxbug.dev/42124325): Replacement for rustenv
command += "{{rustenv}} RUST_BACKTRACE=1 $rustc --color=always --crate-name {{crate_name}} {{source}}"
command += " --crate-type {{crate_type}} --emit=dep-info=$depfile,link{{EMIT_RMETA}} -Zdep-info-omit-d-target {{rustflags}}"
command += " -o $_main_output "
if (rustc_use_response_files) {
rspfile = "$_main_output.rsp"
rspfile_content = "{{rustdeps}} {{externs}}"
command += " -Zshell-argfiles @shell:" + rspfile
} else {
command += " {{rustdeps}} {{externs}}"
}
command +=
" && $verify_depfile -t \"{{label}}\" -d $depfile {{sources}}"
description = "RUST {{output}}"
outputs = [ _main_output ]
default_output_dir = "{{target_out_dir}}"
default_output_extension = ".a"
output_prefix = "lib"
rust_sysroot = "$rebased_rustc_prefix"
# emit=metadata signals to scan dependencies using only .rmetas
if (rust_emit_rmeta) {
rmeta = "{{output_dir}}/{{target_output_name}}.rmeta"
command =
string_replace(command, "{{EMIT_RMETA}}", ",metadata=$rmeta")
outputs += [ rmeta ]
} else {
command = string_replace(command, "{{EMIT_RMETA}}", "")
}
}
tool("rust_cdylib") {
forward_variables_from(_rust_dylib_common, "*")
description = "RUST {{output}}"
default_output_dir = "{{target_out_dir}}"
output_prefix = "lib"
# emit=metadata signals to scan dependencies using only .rmetas
if (rust_emit_rmeta) {
rmeta = "{{output_dir}}/{{target_output_name}}.rmeta"
command =
string_replace(command, "{{EMIT_RMETA}}", ",metadata=$rmeta")
outputs += [ rmeta ]
} else {
command = string_replace(command, "{{EMIT_RMETA}}", "")
}
}
tool("rust_dylib") {
forward_variables_from(_rust_dylib_common, "*")
description = "RUST {{output}}"
default_output_dir = "{{root_out_dir}}/rust-shared"
output_prefix = "lib"
if (rust_emit_rmeta) {
rmeta = "{{output_dir}}/{{target_output_name}}.rmeta"
command =
string_replace(command, "{{EMIT_RMETA}}", ",metadata=$rmeta")
outputs += [ rmeta ]
} else {
command = string_replace(command, "{{EMIT_RMETA}}", "")
}
}
tool("rust_macro") {
forward_variables_from(_rust_concurrent_jobs, "*")
_main_output =
"{{output_dir}}/{{target_output_name}}{{output_extension}}"
depfile = "$_main_output.d"
# Overwrite an existing output file only if its contents have changed.
command = string_replace(_restat_rustc_prefix, "{{DEPFILE}}", depfile)
restat = restat_rust
# Possibly enable remote building for the rustc command.
command += _rustc_wrapper
# TODO(https://fxbug.dev/42124325): Replacement for rustenv
command += "{{rustenv}} RUST_BACKTRACE=1 $rustc --color=always --crate-name {{crate_name}} {{source}} "
command += " --crate-type {{crate_type}} --emit=dep-info=$depfile,link{{EMIT_RMETA}} -Zdep-info-omit-d-target {{rustflags}}"
command += " -o $_main_output --extern proc_macro"
if (rustc_use_response_files) {
rspfile = "$_main_output.rsp"
rspfile_content = "{{rustdeps}} {{externs}}"
command += " -Zshell-argfiles @shell:" + rspfile
} else {
command += " {{rustdeps}} {{externs}}"
}
command +=
" && $verify_depfile -t \"{{label}}\" -d $depfile {{sources}}"
description = "RUST {{output}}"
outputs = [ _main_output ]
default_output_dir = "{{root_out_dir}}"
default_output_extension = _dylib_extension
output_prefix = "lib"
rust_sysroot = "$rebased_rustc_prefix"
if (rust_emit_rmeta) {
rmeta = "{{output_dir}}/{{target_output_name}}.rmeta"
command =
string_replace(command, "{{EMIT_RMETA}}", ",metadata=$rmeta")
outputs += [ rmeta ]
} else {
command = string_replace(command, "{{EMIT_RMETA}}", "")
}
}
}
tool("alink") {
rspfile = "{{output}}.rsp"
linker_prefix = ""
_link_concurrent_jobs = concurrent_jobs.local
if (_link_rbe_enable) { # Assume non-thin archives, the simplest case of
# direct inclusion.
_linker_prefix_list = [
rebase_path(python_exe_src, root_build_dir),
"-S",
rebase_path(prebuilt_tool_remote_wrapper, root_build_dir),
]
if (link_rbe_check == "determinism") {
_linker_prefix_list += [
"--local",
"--check-determinism",
]
} else if (link_rbe_check == "consistency") {
_linker_prefix_list += [ "--compare" ]
} else if (link_rbe_check == "none") {
_link_concurrent_jobs = concurrent_jobs.remote
}
_linker_prefix_list += [
"--inputs",
"\"$rspfile\"",
"--input_list_paths",
"\"$rspfile\"",
"--output_files",
"{{output}}",
"--preserve_unchanged_output_mtime",
"--",
]
linker_prefix = string_join(" ", _linker_prefix_list)
}
forward_variables_from(_link_concurrent_jobs, "*")
restat = _restat_link
command = "rm -f {{output}} && $linker_prefix $ar {{arflags}} rcsD {{output}} \"@$rspfile\""
description = "AR {{output}}"
rspfile_content = "{{inputs}}"
outputs = [ "{{output_dir}}/{{target_output_name}}{{output_extension}}" ]
default_output_dir = "{{target_out_dir}}"
default_output_extension = ".a"
output_prefix = "lib"
}
# The _cxx_link_common captures common computations used by the "solink",
# "solink_module" and "link" tool() definitions below. In particular it may
# contain the following important keys:
#
# - command
# - command_suffix
# - depfile
# - depsformat
# - rspfile
# - rspfile_content
# - outputs
#
# Note that the 'command' and 'outputs' keys will use template parameters
# in their string, with the following values, which must be expanded based
# on the final target type:
#
# - {{UNSTRIPPED_OUTFILE}}, {{UNSTRIPPED_DIR}}, {{BREAKPAD_NAME}},
# {{STRIP_ARGS}}: see `_link_common` comment.
#
# - {{SOLINK_FLAGS}}: linker flags used to generate shared libraries,
# should be empty for executables. Must use a trailing space if not empty.
#
# - {{START_GROUP}}: should expand to "-Wl,--start-group " on ELF systems
# that support it when generating executables, and to an empty string
# otherwise. Must include a trailing space if not empty.
#
# - {{END_GROUP}}: similar to {{START_GROUP}}, but should expand to
# "-Wl,--end-group " instead. Must include a trailing space if not empty.
#
_cxx_link_common = {
forward_variables_from(_link_common,
"*",
[
"command_suffix",
"depfile",
"cxx_link_remote_strip_prefix",
"cxx_link_remote_dump_syms_prefix",
])
_concurrent_jobs = concurrent_jobs.local
rspfile = "$outfile.rsp"
use_llvm_ifs =
invoker.toolchain_os != "mac" && invoker.toolchain_os != "win"
if (invoker.toolchain_os == "mac") {
# TODO(https://fxbug.dev/42079107): enable depfile support in ld64.lld.
command += "$ld {{SOLINK_FLAGS}}{{ldflags}} -o \"$unstripped_outfile\" -Wl,-filelist,\"$rspfile\" {{solibs}} {{libs}} {{frameworks}}"
rspfile_content = "{{inputs_newline}}"
} else {
if (_link_rbe_enable) {
_linker_prefix_list = [
rebase_path(python_exe_src, root_build_dir),
"-S",
rebase_path(cxx_link_remote_wrapper, root_build_dir),
]
if (link_rbe_check == "determinism") {
_linker_prefix_list += [
"--local",
"--check-determinism",
]
not_needed([ "_link_rbe_exec_strategy_flags" ])
} else if (link_rbe_check == "consistency") {
_linker_prefix_list += [ "--compare" ]
not_needed([ "_link_rbe_exec_strategy_flags" ])
} else if (link_rbe_check == "none") {
_concurrent_jobs = concurrent_jobs.remote
_linker_prefix_list += _link_rbe_exec_strategy_flags
}
_linker_prefix_list += [
"--preserve_unchanged_output_mtime",
"--",
]
linker_prefix = string_join(" ", _linker_prefix_list)
} else {
linker_prefix = ""
}
depfile = _link_common.depfile
depsformat = "gcc"
command += "$linker_prefix $ld {{SOLINK_FLAGS}}{{ldflags}} -o \"$unstripped_outfile\" -Wl,--dependency-file=\"$depfile\" -Wl,--Map=\"$unstripped_outfile.map\""
command +=
" {{START_GROUP}}\"@$rspfile\" {{solibs}} {{END_GROUP}}{{libs}}"
rspfile_content = "{{inputs}}"
}
forward_variables_from(_concurrent_jobs, "*")
restat = _restat_link
# Expand {{REMOTE_STRIP_PREFIX}}
_remote_strip_prefix = ""
# If remotely linking, also remotely strip.
# TODO(https://fxbug.dev/42083070): disable remote stripping when
# config includes //build/config:no_remote_link.
if (use_strip) {
if (_link_rbe_enable) {
_remote_strip_prefix = _link_common.cxx_link_remote_strip_prefix
}
}
_remote_dump_syms_prefix = ""
if (defined(_link_common.cxx_link_remote_dump_syms_prefix)) {
_remote_dump_syms_prefix = _link_common.cxx_link_remote_dump_syms_prefix
}
command_suffix =
string_replace(string_replace(_link_common.command_suffix,
"{{REMOTE_STRIP_PREFIX}}",
_remote_strip_prefix),
"{{REMOTE_DUMP_SYMS_PREFIX}}",
_remote_dump_syms_prefix)
}
# Expand _cxx_link_common into a scope that can be used for "solink" and "solink_module" tools
# below directly. All template parameters will be expanded, except BREAKPAD_NAME.
_cxx_link_solink = {
forward_variables_from(_cxx_link_common, "*", [ "command_suffix" ])
if (invoker.toolchain_os == "mac") {
command = string_replace(
command,
"{{SOLINK_FLAGS}}",
"-shared -Wl,-install_name,@rpath/\"{{target_output_name}}{{output_extension}}\" ")
} else {
command = string_replace(command,
"{{SOLINK_FLAGS}}",
"-shared -Wl,-soname=\"$outname\" ")
}
# Never use --start-group and --end-group for libraries.
command = string_replace(command, "{{START_GROUP}}", "")
command = string_replace(command, "{{END_GROUP}}", "")
# Expand STRIP_ARGS, which is always --strip-all for shared objects / libraries.
command += string_replace(_cxx_link_common.command_suffix,
"{{STRIP_ARGS}}",
"--strip-all")
# Expand {{UNSTRIPPED_OUTFILE}}
command =
string_replace(command, "{{UNSTRIPPED_OUTFILE}}", unstripped_outfile)
# Expand {{UNSTRIPPED_DIR}}
_unstripped_dir = "lib.unstripped"
command = string_replace(command, "{{UNSTRIPPED_DIR}}", _unstripped_dir)
outputs = []
foreach(output, _cxx_link_common.outputs) {
outputs +=
[ string_replace(output, "{{UNSTRIPPED_DIR}}", _unstripped_dir) ]
}
}
# Expand _cxx_link_common into a scope that can be used for the "link" tool below.
# All template parameters will be expanded.
_cxx_link_bin = {
forward_variables_from(_cxx_link_common, "*", [ "command_suffix" ])
# Append command suffix and Expand STRIP_ARGS
if (defined(invoker.strip_cxx_bin_args)) {
_strip_args = invoker.strip_cxx_bin_args
} else if (invoker.toolchain_os == "mac") {
_strip_args = "-Sx"
} else {
_strip_args = "--strip-all"
}
command += string_replace(_cxx_link_common.command_suffix,
"{{STRIP_ARGS}}",
_strip_args)
# There is no SOLINK_FLAGS for binaries
command = string_replace(command, "{{SOLINK_FLAGS}}", "")
# Use --start-group and --eng-group for binaries, except on Mac.
# IMPORTANT: The trailing spaces in the replacement strings are critical!
if (invoker.toolchain_os != "mac") {
command =
string_replace(command, "{{START_GROUP}}", "-Wl,--start-group ")
command = string_replace(command, "{{END_GROUP}}", "-Wl,--end-group ")
} else {
command = string_replace(command, "{{START_GROUP}}", "")
command = string_replace(command, "{{END_GROUP}}", "")
}
# Expand BREADKPAD_NAME
if (defined(breakpad_name)) {
command = string_replace(command,
"{{BREAKPAD_NAME}}",
"-n \"$breakpad_name\" ")
}
# Expand {{UNSTRIPPED_OUTFILE}}
command =
string_replace(command, "{{UNSTRIPPED_OUTFILE}}", unstripped_outfile)
# Expand {{UNSTRIPPED_DIR}}
_unstripped_dir = "exe.unstripped"
command = string_replace(command, "{{UNSTRIPPED_DIR}}", _unstripped_dir)
outputs = []
foreach(output, _cxx_link_common.outputs) {
outputs +=
[ string_replace(output, "{{UNSTRIPPED_DIR}}", _unstripped_dir) ]
}
}
tool("solink") {
forward_variables_from(_cxx_link_solink, "*")
description = "SOLINK $outfile"
default_output_dir = "{{root_out_dir}}"
default_output_extension = _dylib_extension
output_prefix = "lib"
# Do not use BREAKPAD_NAME in shared libraries.
command = string_replace(command, "{{BREAKPAD_NAME}}", "")
if (use_llvm_ifs) {
# The Ninja dependency for linking in the shared library will be
# the .ifs file (depend_output), though the actual linking *input*
# will be the original .so file (link_output). Ninja will restat
# the output files after running the commands. llvm-ifs will
# not touch the .ifs file if its contents haven't changed. Hence
# Ninja will only re-run any linking commands depending on this
# shared library if the .ifs file has actually changed, indicating
# that the linking ABI has actually changed.
restat = true
depend_output = "{{output_dir}}/{{target_output_name}}.ifs"
unstripped_output = string_replace(unstripped_outfile,
"{{UNSTRIPPED_DIR}}",
"lib.unstripped")
link_output = "{{output_dir}}/link_stub/{{target_output_name}}.so"
runtime_outputs = [ outfile ]
outputs += [
depend_output,
link_output,
]
command += " && ${prefix}/llvm-ifs --write-if-changed --output-ifs=$depend_output --output-elf=$link_output $unstripped_output"
}
}
tool("solink_module") {
forward_variables_from(_cxx_link_solink, "*")
description = "SOLINK $outfile"
default_output_dir = "{{root_out_dir}}"
default_output_extension = _dylib_extension
# Modules do not have a 'lib' prefix.
# Modules do use breakpad_name, unlike shared libraries.
if (defined(breakpad_name)) {
command = string_replace(command,
"{{BREAKPAD_NAME}}",
"-n \"$breakpad_name\" ")
}
}
tool("link") {
forward_variables_from(_cxx_link_bin, "*")
description = "LINK $outfile"
default_output_dir = "{{root_out_dir}}"
# Executables do use breakpad_name.
if (defined(breakpad_name)) {
command = string_replace(command,
"{{BREAKPAD_NAME}}",
"-n \"$breakpad_name\" ")
}
}
tool("stamp") {
command = stamp_command
description = stamp_description
}
tool("copy") {
command = copy_command
description = copy_description
}
# When invoking this toolchain not as the default one, these args will be
# passed to the build. They are ignored when this is the default toolchain.
toolchain_args = {
current_cpu = invoker.toolchain_cpu
current_os = invoker.toolchain_os
# These values need to be passed through unchanged.
target_os = target_os
target_cpu = target_cpu
if (defined(invoker.toolchain_args)) {
# The invoker isn't allowed to fiddle with the essential settings.
forward_variables_from(invoker.toolchain_args,
"*",
[
"current_cpu",
"current_os",
"target_os",
"target_cpu",
])
}
}
}
}