The Role of BUILD.gn in Chromium's Module Dependencies

In the massive codebase of the Chromium web browser, maintaining architectural integrity across thousands of modules is a critical challenge. This article provides an overview of how BUILD.gn files—the configuration files for Chromium's meta-build system, GN (Generate Ninja)—do much more than just define how code is compiled. Specifically, it examines how these files act as architectural gatekeepers by enforcing strict target visibility rules and executing automated C++ header dependency checks, ensuring that modules only interact in explicitly approved ways.

Understanding BUILD.gn in Chromium

Chromium uses GN to generate build files for Ninja, the actual build system. Every module or directory in Chromium contains a BUILD.gn file that defines build targets, such as executables, shared libraries, or source sets. Because C++ lacks a native module system in the standards historically used by Chromium, the project relies on BUILD.gn files to define module boundaries, encapsulate internal logic, and control how different parts of the browser communicate.

Enforcing Visibility Rules

By default, any target defined in a BUILD.gn file is public, meaning any other target in the Chromium repository can depend on it. To prevent tightly coupled "spaghetti code" and enforce encapsulation, BUILD.gn provides the visibility attribute.

The visibility attribute is a list of label patterns that explicitly defines which other targets are allowed to depend on the current target. If Target A attempts to add Target B to its dependencies, GN checks Target B's visibility list. If Target A is not on that list, the build configuration step fails immediately.

Common ways Chromium uses visibility include:

  • File-Private Targets: Setting visibility = [ ":*" ] restricts a target so that only other targets within the exact same BUILD.gn file can depend on it. This is commonly used for internal helper libraries or split targets.
  • Directory-Level Encapsulation: Setting visibility = [ "//content/browser/*" ] allows any target within the //content/browser/ directory tree to use the target, while blocking access from the rest of the browser, such as //chrome/ or //ui/.
  • Strict Allowlisting: A target can specify exact targets, such as visibility = [ "//chrome/browser:browser_process" ], ensuring only one specific module in the entire codebase can utilize it.

Through these rules, component owners can expose a clean public API target while hiding the messy implementation targets behind strict visibility walls.

Header Dependency Checks

In C++, it is easy for a developer to write an #include directive for a header file that happens to be available on the compiler's include path, without actually declaring a build dependency on the module that owns that header. This leads to fragile builds that break when compilation order changes, as well as violations of layered architecture.

BUILD.gn files prevent this through header dependency checking, which is executed via the gn check command (and run automatically on Chromium's continuous integration servers).

Controlling Header Exposure with public

Within a BUILD.gn target, source files are listed in the sources array. By default, if a header file (.h) is listed in sources, GN considers it publicly includable by any direct dependent.

However, to enforce stricter boundaries, a target can define a public array alongside sources.

  • Headers listed in public are the official API of the module.
  • Headers listed only in sources become private to that target.

If an external module attempts to #include a header that is in sources but not in public, gn check will throw an error, even if the external module properly depends on the target.

Validating the Include Graph

When gn check runs, it parses the C++ source code to find every #include statement and cross-references them against the dependency graph defined in the BUILD.gn files. For an #include to be valid, two conditions must be met:

  1. The target containing the #include must have a declared dependency path to the target that owns the header.
  2. The included header must be marked as public in the owning target.

The Role of deps vs. public_deps

Header checks rely heavily on the distinction between deps and public_deps in BUILD.gn files.

  • deps (Private Dependencies): If Target A lists Target B in its deps, Target A can include Target B's public headers in its own .cc files. However, if Target C depends on Target A, Target C cannot include Target B's headers. The dependency is not transitive.
  • public_deps (Public Dependencies): If Target A includes one of Target B's headers inside Target A's own public header file, Target A must list Target B in public_deps. This tells GN to forward Target B's public headers to anyone who depends on Target A.

If a developer uses deps instead of public_deps when exposing a dependency's types in a public header, gn check will detect the missing transitive link and fail the build.

Handling Exceptions

In rare cases, legacy code or platform-specific configurations cannot strictly adhere to GN's header rules. BUILD.gn provides two main escape hatches for these scenarios:

  • check_includes = false: Added to a target in BUILD.gn to completely disable header checking for that specific target.
  • // nogncheck: A comment placed on the same line as an #include directive in a C++ file, instructing GN to ignore that specific inclusion during verification (often used for conditional includes guarded by preprocessor macros that GN cannot evaluate).

By combining visibility rules with public header definitions and gn check, BUILD.gn files transform Chromium's build system into an active static analysis tool that preserves modularity at scale.