| # 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. |
| |
| # A set of GN templates to expose Ninja-generated output files as inputs |
| # to bazel_action() targets. The latter run a `bazel build` command in |
| # the Bazel workspace setup by the platform build, which cannot access |
| # the Ninja output directory directly. |
| # |
| # A bazel_input_xxx() target defines a mapping from Ninja-generated outputs |
| # to files and Bazel filegroup() targets defined in the special repository |
| # named @legacy_ninja_build_outputs. |
| # |
| # For example, to expose the files generated by an action `foo` as a |
| # filegroup() with Bazel label @legacy_ninja_build_outputs//:foo_files, one |
| # can use the following definition: |
| # |
| # ```gn |
| # # Expose all outputs generated by :foo in Bazel. |
| # bazel_input_resource("foo_files") { |
| # deps = [ ":foo" ] |
| # sources = get_target_outputs(":foo") |
| # outputs = [ "{{source_file_part}}" ] |
| # } |
| # |
| # IMPORTANT: Simply defining a bazel_input_xxx() target is NOT ENOUGH, it must |
| # ALSO be part of the transitive dependencies listed from one of the lists |
| # defined in //build/bazel/legacy_ninja_build_outputs.gni |
| # |
| # There is no way to enforce that with GN at the moment. |
| # |
| |
| # Expose a set of Ninja output files as a corresponding Bazel filegroup() |
| # of prebuilt input sources for the Bazel build. |
| # |
| # This template works like copy() or resource(). Consider using |
| # bazel_input_resource_tree() if you have multiple source files with |
| # non-trivial outputs paths. |
| # |
| # ```gn |
| # # Expose all outputs generated by :foo in Bazel. |
| # bazel_input_resource("foo_files") { |
| # deps = [ ":foo" ] |
| # sources = get_target_outputs(":foo") |
| # outputs = [ "{{source_file_part}}" ] |
| # } |
| # |
| # ... |
| # |
| # Make sure this is reachable from the transitive dependencies listed in |
| # //build/bazel/legacy_ninja_build_outputs.gni, e.g.: |
| # |
| # ``` |
| # gn_labels_for_bazel_inputs += [ "//src/foo:foo_files" ] |
| # ``` |
| # |
| # Assuming that :foo generates the following files: |
| # |
| # out/default/gen/src/foo/foo.h |
| # out/default/gen/src/foo/foo.cc |
| # |
| # Then this will generate something like that @legacy_ninja_build_outputs/BUILD.bazel: |
| # |
| # ```bazel |
| # filegroup("foo_files") { |
| # srcs = [ |
| # "foo.h", |
| # "foo.cc", |
| # ] |
| # ) |
| # ``` |
| # |
| # After creating the symlinks as: |
| # |
| # $BAZEL_TOPDIR/output_base/external/legacy_ninja_build_outputs/ |
| # foo.h --> ../../../../../src/foo/foo.h |
| # foo.cc --> ../../../../../src/foo/foo.cc |
| # |
| # Arguments: |
| # name: (optional) |
| # Bazel filegroup name for the set of files covered by this target. |
| # If not specified, defaults to target_name. Note that the filegroup() |
| # will be part of the top-level BUILD.bazel file generated by the |
| # build)_inputs_workspace() dependent. This name cannot contain a |
| # directory separator or colon. |
| # Type: string |
| # |
| # sources |
| # Required: List of output files that become sources in the Bazel |
| # workspace. Technically works with Ninja source files as well, but these |
| # are already exposed to Bazel by default, and don't need to be |
| # listed in a bazel_input_resource() target. See copy() for details. |
| # Type: list(file) |
| # |
| # outputs |
| # Required: List of one runtime path. This must be a relative path (no |
| # leading `/`). It can use placeholders based on $sources; see copy() |
| # and `gn help source_expansion`. |
| # Type: list(path) |
| # |
| # deps |
| # Optional: Targets that produce $sources. Any files listed in |
| # $sources that are produced by the build should be produced by a |
| # target listed here. |
| # Type: list(label) |
| # |
| template("bazel_input_resource") { |
| _name = target_name |
| if (defined(invoker.name)) { |
| _name = invoker.name |
| assert(string_replace(_name, "/", "") == _name, |
| "name cannot contain directory separators: $_name") |
| assert(string_replace(_name, ":", "") == _name, |
| "name cannot contain colon: $_name") |
| } |
| |
| _sources = rebase_path(invoker.sources, root_build_dir) |
| _dest = process_file_template(invoker.sources, invoker.outputs) |
| |
| foreach(dst_path, _dest) { |
| assert( |
| rebase_path(dst_path, "//") != dst_path, |
| "`outputs` in bazel_input_resource() cannot start with /: ${dst_path}") |
| } |
| |
| group(target_name) { |
| forward_variables_from(invoker, |
| "*", |
| [ |
| "metadata", |
| "name", |
| "outputs", |
| "sources", |
| ]) |
| |
| metadata = { |
| # Used by bazel_inputs_manifest() template. |
| # See its documentation for the metadata schema. |
| bazel_inputs = [ |
| { |
| name = _name |
| sources = _sources |
| destinations = _dest |
| gn_label = get_label_info(":$target_name", "label_with_toolchain") |
| }, |
| ] |
| bazel_inputs_barrier = [] |
| } |
| } |
| } |
| |
| # bazel_input_resource_tree() is similar to bazel_input_resource() but |
| # makes it easy to replicate a tree of sources files, relative to a given |
| # sources path prefix, to a given destination directory. |
| # |
| # For example: |
| # |
| # bazel_input_resource_tree("my-resources") { |
| # sources_root = "res" |
| # sources = [ |
| # foo.resource", |
| # bar/bar-1.resource", |
| # ] |
| # dest_dir = "data/resources" |
| # } |
| # |
| # Will declare the following symlinks: |
| # |
| # $BAZEL_TOPDIR/legacy_ninja_build_outputs/ |
| # data/resources/ |
| # foo.resource ---> .../res/foo.resource |
| # bar/bar.resource ---> .../res/bar/bar.resource |
| # |
| # And will generate the following Bazel fragment: |
| # |
| # filegroup("my-resources") { |
| # srcs = [ |
| # "data/resources/foo.resource", |
| # "data/resources/bar/bar.resource", |
| # ] |
| # ) |
| # |
| # This is difficult to do with resource() because GN source expansion |
| # cannot preserve the original sources input paths. |
| # |
| # This template is useful to avoid calling bazel_input_resource() multiple |
| # times in a loop when dealing with resource files laid out into different |
| # sub-directories. Note that the files cannot be renamed though! |
| # |
| # Parameters |
| # name: (optional) |
| # Bazel filegroup name for the set of files covered by this target. |
| # If not specified, defaults to target_name. Note that the filegroup() |
| # will be part of the top-level BUILD.bazel file generated by the |
| # build)_inputs_workspace() dependent. This name cannot contain a |
| # directory separator or colon. |
| # Type: string |
| # |
| # source_root |
| # Optional: A path prefix that is prepended to all items in the 'sources' |
| # list. If not specified, the current target's directory is used. |
| # Type: string(path) |
| # |
| # sources |
| # Required: List of files in the source tree or build that will be |
| # installed. Each 'file' item in this list is exposed as |
| # '$dest_dir/$file', as a symlink to '$sources_root/$file'. |
| # Note that unlike bazel_input_resource(), there is no way to transform |
| # or expand source paths. |
| # Type: list(file) |
| # |
| # dest_dir |
| # Required: Destination path where all sources are exposed. |
| # Cannot start with a "/". Use an empty string to expose files directly |
| # to the workspace's top-level directory. |
| # Type: string(path) |
| # |
| # deps |
| # Optional: Targets that produce $sources. Any files listed in |
| # $sources that are produced by the build should be produced by a |
| # target listed here. |
| # Type: list(label) |
| # |
| template("bazel_input_resource_tree") { |
| _name = target_name |
| if (defined(invoker.name)) { |
| _name = invoker.name |
| assert(string_replace(_name, "/", "") == _name, |
| "name cannot contain directory separators: $_name") |
| assert(string_replace(_name, ":", "") == _name, |
| "name cannot contain colon: $_name") |
| } |
| assert(defined(invoker.sources), "sources is required") |
| assert(defined(invoker.dest_dir), "dest_dir is required") |
| dest_dir = invoker.dest_dir |
| if (dest_dir != "") { |
| assert(rebase_path(dest_dir, "foo") != dest_dir, |
| "dest_dir cannot start with /: $dest_dir") |
| assert(dest_dir != "." && dest_dir != ".." && |
| string_replace(dest_dir, "./", "") == dest_dir, |
| "dest_dir cannot contain . or .. path elements: $dest_dir") |
| |
| # Add trailing directory separator. |
| dest_dir = string_replace(dest_dir + "//", "//", "/") |
| } |
| |
| if (defined(invoker.sources_root)) { |
| assert(invoker.sources_root != "", "sources_root cannot be empty!") |
| sources_prefix = invoker.sources_root |
| |
| # Append trailing separator if needed |
| if (string_replace(sources_prefix + "###", "/###", "") == |
| sources_prefix + "###") { |
| sources_prefix += "/" |
| } |
| } else { |
| sources_prefix = "" |
| } |
| |
| _name = target_name |
| if (defined(invoker.name)) { |
| _name = invoker.name |
| assert(string_replace(_name, "/", "") == _name, |
| "name cannot contain directory separators: $_name") |
| assert(string_replace(_name, ":", "") == _name, |
| "name cannot contain colon: $_name") |
| } |
| |
| _sources = [] |
| _dest = [] |
| foreach(_source, invoker.sources) { |
| _sources += [ dest_dir + _source ] |
| _dest += [ rebase_path(sources_prefix + _source, root_build_dir) ] |
| } |
| |
| group(target_name) { |
| forward_variables_from(invoker, |
| "*", |
| [ |
| "dest_dir", |
| "metadata", |
| "sources", |
| "sources_root", |
| ]) |
| metadata = { |
| # Used by bazel_inputs_manifest() template. |
| bazel_inputs = [ |
| { |
| name = _name |
| sources = _sources |
| destinations = _dest |
| gn_label = get_label_info(":$target_name", "label_with_toolchain") |
| }, |
| ] |
| bazel_inputs_barrier = [] |
| } |
| } |
| } |
| |
| # bazel_input_resource_directory() is used to expose the content of a Ninja |
| # output directory as a Bazel input filegroup. Instead of passing a list of |
| # source files, `source_root` source point to a directory whose content will |
| # be symlinks into a bazel_build_inputs_workspace(). |
| # |
| # IMPORTANT: This is not hermetic, and can lead to incorrect results during |
| # incremental builds, unless the directory's content is always cleared |
| # before the Ninja action that generates its content is run. |
| # |
| # For example: |
| # |
| # bazel_input_resource_directory"my-generated-resources") { |
| # source_dir = get_label_info(":resource-generator", "target_gen_dir") |
| # dest_dir = "data/resources" |
| # deps = [ ยจ:resource-generator" ] |
| # } |
| # |
| # Parameters |
| # name: (optional) |
| # Bazel filegroup name for the set of files covered by this target. |
| # If not specified, defaults to target_name. Note that the filegroup() |
| # will be part of the top-level BUILD.bazel file generated by the |
| # build)_inputs_workspace() dependent. This name cannot contain a |
| # directory separator or colon. |
| # Type: string |
| # |
| # source_dir |
| # Required: GN path to the source directory that contains all outputs |
| # files to be exposed as a Bazel filegroup(). |
| # Type: string(path) |
| # |
| # dest_dir |
| # Required: Destination path where all sources are exposed. |
| # Cannot start with a "/". Use an empty string to expose files directly |
| # to the workspace's top-level directory. |
| # Type: string(path) |
| # |
| # deps |
| # Optional: Targets that produce $sources. Any files listed in |
| # $sources that are produced by the build should be produced by a |
| # target listed here. |
| # Type: list(label) |
| # |
| template("bazel_input_resource_directory") { |
| _name = target_name |
| if (defined(invoker.name)) { |
| _name = invoker.name |
| assert(string_replace(_name, "/", "") == _name, |
| "name cannot contain directory separators: $_name") |
| assert(string_replace(_name, ":", "") == _name, |
| "name cannot contain colon: $_name") |
| } |
| assert(defined(invoker.dest_dir), "dest_dir is required") |
| dest_dir = invoker.dest_dir |
| if (dest_dir != "") { |
| assert(rebase_path(dest_dir, "foo") != dest_dir, |
| "dest_dir cannot start with /: $dest_dir") |
| assert(dest_dir != "." && dest_dir != ".." && |
| string_replace(dest_dir, "./", "") == dest_dir, |
| "dest_dir cannot contain . or .. path elements: $dest_dir") |
| |
| # Add trailing directory separator. |
| dest_dir = string_replace(dest_dir + "//", "//", "/") |
| } |
| |
| group(target_name) { |
| forward_variables_from(invoker, |
| [ |
| "deps", |
| "testonly", |
| "visibility", |
| ]) |
| metadata = { |
| # Used by bazel_inputs_manifest() template. |
| bazel_inputs = [ |
| { |
| name = _name |
| source_dir = rebase_path(invoker.source_dir, root_build_dir) |
| dest_dir = dest_dir |
| gn_label = get_label_info(":$target_name", "label_with_toolchain") |
| }, |
| ] |
| bazel_inputs_barrier = [] |
| } |
| } |
| } |
| |
| # Generate a manifest file describing the content of the Bazel inputs |
| # repository that will be used by the Bazel workspace to read Ninja outputs |
| # as sources / prebuilts. |
| # |
| # Args: |
| # output: |
| # Path to generated manifest file. |
| # |
| # inputs_deps: |
| # A list of targets, all transitive dependencies which are bazel_input_xxx() |
| # target will generate one entry in the manifest. |
| # |
| template("generate_bazel_inputs_manifest") { |
| # Generate a single manifest file that collects all bazel_input_xxx() |
| # resources. Each metadata entry is a scope that describes a single |
| # Bazel filegroup() target that will appear at the top of the |
| # auto-generated workspace. |
| # |
| # There are two types of entries: |
| # |
| # ## REGULAR ENTRIES |
| # |
| # These entries expose an explicit list of files, they look like: |
| # |
| # name: (required) |
| # Bazel filegroup name. |
| # Type: string |
| # |
| # destinations: (required) |
| # List of input files, relative to the top of the generated workspace. |
| # Each one will appear in the `srcs` list of the corresponding |
| # filegroup. |
| # |
| # sources: (required) |
| # List of source files for the filegroup. Must have the same |
| # size as the `destinations` list. |
| # Type: list of paths relative to the root_build_dir |
| # |
| # gn_label: |
| # GN label of target that defines this entry. |
| # Type: GN label string |
| # |
| # They should generate Bazel targets that look like: |
| # |
| # filegroup( |
| # name = "<name>", |
| # srcs = [ |
| # <destination_1>, |
| # <destination_2>, |
| # ... |
| # ], |
| # ) |
| # |
| # Where <destination_N> is the N-th entry in `destinations`, and will be |
| # the path to a symlink (in the repository) to the corresponding |
| # <sources_N> file. |
| # |
| # ## DIRECTORY ENTRIES |
| # |
| # These entries expose all files under a given output directory as |
| # a single filegroup() using the glob() function. IMPORTANT: For |
| # correctness, only use these when it is 100% sure that the content |
| # of the source directory is re-created properly during incremental |
| # builds. These look like: |
| # |
| # name: (required) |
| # Bazel filegroup name. |
| # Type: string |
| # |
| # source_dir: (required) |
| # A source directory path, relative to the Ninja build output |
| # directory, which will contain all input files for the Bazel |
| # filegroup(). |
| # |
| # dest_dir: (required) |
| # A directory prefix for all input files, relative to the top of |
| # the generated workspace. This will be a symlink to source_dir, |
| # and the filegroup() will use a glob(["<dest_dir>/*"]) call to get all |
| # files in it. |
| # |
| # gn_label: (optional) |
| # GN label of target that defines this entry. |
| # Type: GN label string |
| # |
| # They should generate Bazel targets that look like: |
| # |
| # filegroup( |
| # name = "<name>", |
| # srcs = glob(["<dest_dir>/**]), |
| # ) |
| # |
| # Where <dest_dir> is a repository symlink that points to source_dir. |
| # |
| generated_file(target_name) { |
| forward_variables_from(invoker, [ "testonly" ]) |
| outputs = [ invoker.output ] |
| data_keys = [ "bazel_inputs" ] |
| walk_keys = [ "bazel_inputs_barrier" ] |
| deps = invoker.inputs_deps |
| output_conversion = "json" |
| } |
| } |