| # 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 |
| } |
| } |