visibility vs template expansion

24 views
Skip to first unread message

Łukasz Anforowicz

unread,
Feb 10, 2026, 3:32:09 PMFeb 10
to gn-dev
Hello @gn-dev,

Context: some GN `template`s will expand into **multiple** targets.  For example, `rust_static_library("foo")` will expand into a `group("foo")` and a bag of interdependent other targets (e.g. `rust_library("foo_main_target")`, `foo_clippy`, `foo_cxx_generated`,  etc.).  It is quite common for `deps` specified for `rust_static_library("foo")` to be needed by more than 1 target - let me share 2 examples example:
  • A native library (used via FFI) needs to be 1) linked as a transitive dependency of `foo_main_target` and 2) included from `.rs.h` and/or `.rs.cc` files generated by `cxx` FFI tool run underneath `foo_cxx_generated`
  • A `build.rs`-generated `gen/.../auto_generated_source.rs` may need to be `include!`d from one of sources of "foo" when 1) building `foo_main_target` using `rustc`, 2) linting `foo_clippy` using `clippy-driver`, 3) generating C++ bindings in `foo_cpp_api_from_rust` target using Crubit
Problem: setting `visibility = [":foo"]` may result in a dependency being visible only to `group("foo")` and being invisible the other, internal targets that `rust_static_library("foo")` expands into.

A workaround that Chrome Rust team has been recommending is to set `visibility = [":*"]` instead of `visibility = [":foo"]`.  This is a somewhat broader visibility, but the affected target's visibility still remains restricted to a given directory - using the target from another directory would require changing it's `visibility` which would presumably would require `OWNERS` approval for `BUILD.gn` changes.  QUESTION: Is this a reasonable guidance?

In theory GN could:
  1. internally track the origin of a target  - e.g. it could track that `action_foreach("foo_cxx_generated_gen")` got expanded from `rust_cxx("foo_cxx_generated")` which got expanded from `rust_target("foo")` which got expanded from `rust_static_library("foo")`.
  2. compare `visibility` not just to a given target name (e.g. to `":foo_cxx_generated_gen"`) but to the whole expansion set (e.g. also compare `visibility` to `":foo_cxx_generated"` and `":foo"`).
QUESTION: Does the above look like a reasonable feature request for GN?  What is the best way to discuss and track this proposal?  Should I open a new bug for this in https://gn.issues.chromium.org/home?

QUESTION: Are there other potential ways forward?  (e.g. additional wildcard support in `visibility` declarations/lists)

Best regards,

Lukasz

Andrew Grieve

unread,
Feb 11, 2026, 9:44:51 AMFeb 11
to Łukasz Anforowicz, gn-dev
Android templates certainly have this same problem. I documented it here to work-around it by having inner templates not apply visibility, but it does seem like a shortcoming. I generally found visibility to be more of a hindrance than helpful in practice (especially since we have DEPS files already), so I actually don't mind that its shortcomings discourage its use :P.

David Turner

unread,
Feb 11, 2026, 10:04:34 AMFeb 11
to Andrew Grieve, Łukasz Anforowicz, gn-dev
I think tracking the expansions would be too costly, and it requires a new data structure because non-final template instantiations simply do not exist in the resolved build graph, which is what visibility checks use to perform their checks (and this happens very late in the `gn gen` process when most information about templates and GN build files has been discarded).

An alternative would for the top-level group() target to *grant* its label to its dependencies for visibility purposes. For example with a new boolean attribute, such as `grant_visibility_to_deps = true`. That would allow the visibility checks to detect this case appropriately. Wdyt?

To unsubscribe from this group and stop receiving emails from it, send an email to gn-dev+un...@chromium.org.

Łukasz Anforowicz

unread,
Feb 11, 2026, 11:45:25 AMFeb 11
to David Turner, Andrew Grieve, gn-dev
On Wed, Feb 11, 2026 at 7:04 AM David Turner <di...@google.com> wrote:
I think tracking the expansions would be too costly, and it requires a new data structure because non-final template instantiations simply do not exist in the resolved build graph, which is what visibility checks use to perform their checks (and this happens very late in the `gn gen` process when most information about templates and GN build files has been discarded).

An alternative would for the top-level group() target to *grant* its label to its dependencies for visibility purposes. For example with a new boolean attribute, such as `grant_visibility_to_deps = true`. That would allow the visibility checks to detect this case appropriately. Wdyt?

Yeah, I think that would also work.  I think that (similarily to the original proposal) this would also mean that each final/real target can have a set of multiple "visibility labels" (rather than just the real, `$target_name`-based one).

`grant_visibility_to_deps` is one way to add to the set of "visibility labels" of other targets.  For maximum flexibility you may want to directly support specifying which targets should inherit your label for visibility checks:

template("foobar") {
  _impl_detail_target_a = "${target_name}_a"
  _impl_detail_target_b = "${target_name}_b"
  group($target_name) {
    forward_variables_from(invoker, [ "data_deps", "deps" ])
    grant_visibility_to = [
      ":${_impl_detail_target_a}",
      ":${_impl_detail_target_b}",
    ]
    # ...
  }
}

The explicit targets lists helps 1) in case you don't want to propagate `visbility` to some of `deps`, or 2) when you want to propagate `visibility` to non-`deps` - e.g. `validations` are a special kind of a dependency that GN doesn't treat as a real dependency in some contexts.


On Wed, Feb 11, 2026 at 3:44 PM Andrew Grieve <agr...@chromium.org> wrote:
Android templates certainly have this same problem. I documented it here to work-around it by having inner templates not apply visibility, but it does seem like a shortcoming. I generally found visibility to be more of a hindrance than helpful in practice (especially since we have DEPS files already), so I actually don't mind that its shortcomings discourage its use :P.

DEPS only work for C/C++ `#include`s, but don't work for Java, Rust, etc., right?

`visibility` in Blaze/Blaze seems to work fairly well for google3 AFAIK.  Maybe this is because Bazel doesn't support granting visibility to a specific target, only to a directory, a tree of subdirectories, or a package group.

Andrew Grieve

unread,
Feb 11, 2026, 12:06:18 PMFeb 11
to Łukasz Anforowicz, David Turner, Andrew Grieve, gn-dev
On Wed, Feb 11, 2026 at 11:45 AM Łukasz Anforowicz <luk...@chromium.org> wrote:


On Wed, Feb 11, 2026 at 7:04 AM David Turner <di...@google.com> wrote:
I think tracking the expansions would be too costly, and it requires a new data structure because non-final template instantiations simply do not exist in the resolved build graph, which is what visibility checks use to perform their checks (and this happens very late in the `gn gen` process when most information about templates and GN build files has been discarded).

An alternative would for the top-level group() target to *grant* its label to its dependencies for visibility purposes. For example with a new boolean attribute, such as `grant_visibility_to_deps = true`. That would allow the visibility checks to detect this case appropriately. Wdyt?

Yeah, I think that would also work.  I think that (similarily to the original proposal) this would also mean that each final/real target can have a set of multiple "visibility labels" (rather than just the real, `$target_name`-based one).

`grant_visibility_to_deps` is one way to add to the set of "visibility labels" of other targets.  For maximum flexibility you may want to directly support specifying which targets should inherit your label for visibility checks:

template("foobar") {
  _impl_detail_target_a = "${target_name}_a"
  _impl_detail_target_b = "${target_name}_b"
  group($target_name) {
    forward_variables_from(invoker, [ "data_deps", "deps" ])
    grant_visibility_to = [
      ":${_impl_detail_target_a}",
      ":${_impl_detail_target_b}",
    ]
    # ...
  }
}

The explicit targets lists helps 1) in case you don't want to propagate `visbility` to some of `deps`, or 2) when you want to propagate `visibility` to non-`deps` - e.g. `validations` are a special kind of a dependency that GN doesn't treat as a real dependency in some contexts.


On Wed, Feb 11, 2026 at 3:44 PM Andrew Grieve <agr...@chromium.org> wrote:
Android templates certainly have this same problem. I documented it here to work-around it by having inner templates not apply visibility, but it does seem like a shortcoming. I generally found visibility to be more of a hindrance than helpful in practice (especially since we have DEPS files already), so I actually don't mind that its shortcomings discourage its use :P.

DEPS only work for C/C++ `#include`s, but don't work for Java, Rust, etc., right?
It works for Java as well. 
 

`visibility` in Blaze/Blaze seems to work fairly well for google3 AFAIK.  Maybe this is because Bazel doesn't support granting visibility to a specific target, only to a directory, a tree of subdirectories, or a package group.

I'd guess experiences vary, but I've found it largely just leads to visibility lists needing to be updated without much thought. I do think there are times where it's useful (e.g. expert review required), but using it by default leads to busywork.
Reply all
Reply to author
Forward
0 new messages