blob: aed3a05367fc2958abf0ba8a4a6b742ef959d2ab [file] [log] [blame]
# Copyright 2022 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/bazel/bazel_workspace.gni")
import("//build/bazel/legacy_ninja_build_outputs.gni")
import("//build/config/clang/clang.gni")
import("//build/config/compiler.gni")
# Run a Bazel build action, which is run in the special workspace
# setup by `//build/bazel:bazel_workspace`.
#
# The caller should pass a list of Bazel target patterns for the
# `bazel build` command, as well as a list Bazel output files, relative
# to the workspace's `bazel-bin/` that will be copied into the
# GN target's target_gen_dir by default.
#
# All inputs seen by the Bazel command must be defined in the
# @legacy_ninja_build_outputs Bazel repository, which is populated by
# bazel_input_xxx() targets that must appear as dependencies of the
# //build/bazel:legacy_ninja_build_outputs target.
#
# The bazel-generated files will be copied (possibly hard-linked) into the
# `target_gen_dir` matching the GN action for this template call (since
# GN requires that an action's output file be in either target_gen_dir
# or target_out_dir).
#
# This also takes a list of Bazel target patterns, since Bazel does not
# support building specific files, only targets. For now it is up to the
# caller to know exactly which files are generated by Bazel for a given
# set of target patterns.
#
# Args:
# bazel_inputs: (optional)
# List of labels to bazel_input_xxx() targets that define
# the set of Ninja-generated inputs that the Bazel build command will
# use. These must be in the dependency tree of
# `//build/bazel:legacy_ninja_build_outputs`. Note that this is only used to
# ensure that said inputs are properly generated by Ninja before this
# action's command is run. The command will be able to see any input
# files exported by the @legacy_ninja_build_outputs Bazel repository,
# as there is no way to restrict them for now.
# Type: list of GN labels
#
# bazel_targets:
# A list of Bazel target patterns for the `bazel build` command.
# This list cannot be empty since there is no concept of 'default'
# targets in Bazel. Note target sets (i.e. `...`) are not supported.
# Type: list of Bazel target patterns.
#
# bazel_outputs:
# A list of paths, relative to the Bazel workspace's `bazel-bin/`
# directory, corresponding to the files generated by the Bazel build
# command. For now it is up to the caller to ensure that this matches
# the patterns in `bazel_targets`. By default, these files will be
# hard-linked or copied into the GN target_gen_dir for this action
# with the same relative paths, unless `ninja_outputs` is also used.
# Type: list of paths (cannot be empty).
#
# ninja_outputs: (optional)
# If provided, must be a list with the same length as `bazel_outputs`,
# providing paths relative to the target_gen_dir for this action,
# specifying where each one of the corresponding item in `bazel_outputs`
# should be hard-linked / copied to. If not provided, this is equivalent
# to using `bazel_outputs` directly.
# Type: a list of output file paths, relative to target_gen_dir.
#
# extra_bazel_args: (optional)
# A list extra command line arguments to pass to Bazel.
# Type: list of string
#
# deps:
# no_output_dir_leaks:
# testonly:
# visibility:
# Usual GN meaning.
#
template("bazel_build_action") {
assert(defined(invoker.bazel_targets), "bazel_targets must be defined!")
assert(invoker.bazel_targets != [], "bazel_targets cannot be empty!")
assert(defined(invoker.bazel_outputs), "bazel_outputs must be defined!")
assert(invoker.bazel_outputs != [], "bazel_outputs cannot be empty!")
_num_bazel_outputs = 0
foreach(path, invoker.bazel_outputs) {
assert(rebase_path(path, "//") != path,
"bazel_outputs path must be relative: $path")
assert(string_replace("XX$path", "XX-", "") == "XX$path",
"bazel_outputs path cannot begin with dash: $path")
_num_bazel_outputs += 1
}
if (defined(invoker.ninja_outputs)) {
_ninja_outputs = invoker.ninja_outputs
_num_ninja_outputs = 0
foreach(path, _ninja_outputs) {
assert(rebase_path(path, "//") != path,
"ninja_outputs path must be relative: $path")
assert(string_replace("XX$path", "XX-", "") == "XX$path",
"ninja_outputs path cannot begin with dash: $path")
_num_ninja_outputs += 1
}
assert(_num_bazel_outputs == _num_ninja_outputs,
"The ninja_outputs list, if provided, should have the same length " +
"as the bazel_outputs one " +
"found ${_num_ninja_outputs}, expected ${_num_bazel_outputs})")
} else {
_ninja_outputs = invoker.bazel_outputs
}
_outputs = []
foreach(path, _ninja_outputs) {
_outputs += [ target_gen_dir + "/" + path ]
}
_bazel_inputs = []
if (defined(invoker.bazel_inputs)) {
_bazel_inputs = invoker.bazel_inputs
}
_inputs = []
foreach(label, _bazel_inputs) {
_inputs += [ get_label_info(label, "label_with_toolchain") ]
}
_bazel_build_files = []
foreach(label, invoker.bazel_targets) {
dir = get_label_info(label, "dir")
assert(get_path_info(dir, "file") != "...",
"`...` is not supported in bazel_targets")
_bazel_build_files += [ "${dir}/BUILD.bazel" ]
}
action(target_name) {
script = "//build/bazel/scripts/bazel_build_action.py"
# NOTE: Do not add bazel_inputs_manifest to the list of inputs
# because *nothing* should depend on the target that generates
# it. Moreoever, it is testonly=true, which would prevent
# non-testonly bazel_build_action() from being added to the GN graph.
# This is "safe" because the file is generated at GN gen time.
inputs = [ bazel_launcher ] + _bazel_build_files
outputs = _outputs
args = [
"--bazel-launcher",
rebase_path(bazel_launcher, root_build_dir),
"--workspace-dir",
rebase_path(bazel_workspace_dir, root_build_dir),
"--inputs-manifest",
rebase_path(bazel_inputs_manifest, root_build_dir),
"--bazel-inputs",
] + _inputs + [ "--bazel-outputs" ] + invoker.bazel_outputs +
[ "--bazel-targets" ] + invoker.bazel_targets +
[ "--ninja-outputs" ] + rebase_path(_outputs, root_build_dir)
# Ensure that the default platform is set correctly when this build action
# is invoked from a Fuchsia or host toolchain. For other ones. Other toolchains
# will inherit the 'default' platforms which may be set to
# //build/bazel/platforms:common in the future, and does not target any
# specific system intentionally.
if (is_host || is_fuchsia) {
args += [
"--bazel-platform",
"//build/bazel/platforms:${current_os}_${current_cpu}",
# NOTE: The SDK build rules still require the --cpu value to be set properly aa
# well (see https://fxbug.dev/116571#c19).
"--bazel-cpu=${current_cpu}",
]
}
args += [ "--" ]
if (defined(invoker.extra_bazel_args)) {
args += invoker.extra_bazel_args
}
# Below are extra args to Bazel
# Forward GN sanitizer configuration to Bazel
if (is_asan) {
args += [ "--features=asan" ]
}
if (is_lsan) {
args += [ "--features=lsan" ]
}
if (is_msan) {
args += [ "--features=msan" ]
}
if (is_tsan) {
args += [ "--features=tsan" ]
}
if (is_ubsan) {
args += [ "--features=ubsan" ]
}
# Forward GN test coverage configuration to Bazel
if (is_coverage) {
args += [ "--features=coverage" ]
}
# Forward GN C/C++ compiler optimization configuration to Bazel
if (optimize == "none" || optimize == "default" ||
optimize == "sanitizer" || optimize == "coverage") {
args += [ "--compilation_mode=fastbuild" ]
} else if (optimize == "debug") {
args += [ "--compilation_mode=dbg" ]
} else if (optimize == "size" || optimize == "speed" ||
optimize == "profile") {
args += [ "--compilation_mode=opt" ]
} else {
assert("Unsupported mode optimize = $optimize")
}
# Forward ML inliner
if (clang_ml_inliner && optimize == "size") {
args += [ "--features=ml_inliner" ]
}
deps = _bazel_inputs +
[ "//build/bazel:generate_main_workspace($default_toolchain)" ]
if (defined(invoker.deps)) {
deps += invoker.deps
}
forward_variables_from(invoker,
[
"no_output_dir_leaks",
"testonly",
"visibility",
])
# Prevent concurrent bazel build actions to run, otherwise
# the `bazel-bin` symlink target would become unpredictable resulting
# in erroneous outputs or errors.
pool = "//build/bazel:build_action_pool($default_toolchain)"
# This action definitely cannot be hermetic. Callers should add
# themselves to the //build:non_hermetic_deps visibility list.
hermetic_deps = false
}
}