Features
Explicit Dependency Management
What is Explicit Dependency Management?
In Bazel, Explicit Dependency Management means that you must clearly and explicitly list all the dependencies that your build targets need. Unlike some other build systems where dependencies might be inferred automatically, Bazel requires you to specify each dependency directly. The dependency graph in Bazel must always be a directed acyclic graph which means no cycles are allowed.
Basic Idea
Clear Declaration: You must list every file, library, or target your build depends on in your build configuration files. This ensures that Bazel knows exactly what each target needs to build correctly.
No Implicit Dependencies: There is no automatic assumption about what dependencies a target might need based on file locations or other heuristics. Everything must be explicitly stated.
Example Scenario
Let’s say you’re working on a project with a library and an application that depends on that library. Here’s how you’d manage dependencies in Bazel:
Project Structure:
lessCopy codemy_project/ ├── //app │ ├── BUILD.bazel │ └── main.py └── //lib ├── BUILD.bazel └── utils.py
Targets and Dependencies:
//lib:utils
- A target that builds a library with utility functions.//app:main
- A target that builds an application and depends on the utility functions.
Setting Up Explicit Dependencies:
//lib/BUILD.bazel
py_library( name = "utils", srcs = ["utils.py"], )
//app/BUILD.bazel
py_binary( name = "main", srcs = ["main.py"], deps = ["//lib:utils"], # Explicitly declare the dependency on //lib:utils )
Explanation
Explicit Declaration:
In the
//lib/BUILD.bazel
file, thepy_library
target//lib:utils
is defined with its source files. This target represents the library with utility functions.In the
//app/BUILD.bazel
file, thepy_binary
target//app:main
is defined as a Python binary. Thedeps
attribute explicitly lists//lib:utils
as a dependency. This means that//app:main
requires//lib:utils
to build.
No Implicit Dependencies:
Bazel doesn’t guess or infer that
//app:main
might need//lib:utils
based on file paths or other heuristics. You must explicitly state this dependency in thedeps
attribute.
Benefits
Predictability: By explicitly declaring all dependencies, you ensure that your build is predictable and reproducible. You know exactly what each target depends on.
Clarity: It makes the build configuration clear and understandable. Anyone looking at the build files can see all the dependencies listed without guessing.
Error Prevention: It reduces the risk of build errors due to missing dependencies. Since everything is declared explicitly, Bazel will catch any missing dependencies and prompt you to fix them.
Comparison to Implicit Dependencies
Implicit Dependencies: In some build systems, dependencies might be inferred based on file locations or naming conventions. This can lead to unexpected issues if the system makes incorrect assumptions.
Explicit Dependencies (Bazel): Bazel’s requirement for explicit declarations avoids such issues. You specify exactly what each target needs, which makes the build process more reliable.
Summary
Explicit Dependency Management in Bazel means you must clearly list every dependency your build targets require. This approach ensures that your build process is predictable, clear, and free from implicit assumptions about what dependencies are needed. By specifying dependencies directly in your build configuration files, you avoid potential issues and make your project easier to manage.
Advanced Visibility Features
What is Visibility in Bazel?
In Bazel, visibility is a feature that allows you to control which parts of your code can access or depend on specific build targets. This helps you manage and restrict how different parts of your project interact with each other, improving modularity and reducing the risk of unintended dependencies.
Basic Idea
Visibility Control: You can limit which packages or targets are allowed to depend on a particular target. This way, you can keep certain parts of your codebase hidden from others and only expose what's necessary.
Example Scenario
Imagine you have a project with multiple modules:
Project Structure:
my_project/
├── //frontend
│ ├── BUILD.bazel
│ └── main.py
├── //backend
│ ├── BUILD.bazel
│ └── service.py
└── //shared
├── BUILD.bazel
└── utils.py
Targets and Visibility:
//frontend:main
- A target that builds the frontend module.//backend:service
- A target that builds the backend module.//shared:utils
- A target that builds some shared utility functions.
Setting Up Visibility:
You want to allow only the
frontend
module to use the utilities fromshared
, but you don’t want thebackend
module to have access to these utilities.
Here’s how you can set it up in Bazel:
//shared/BUILD.bazel
pythonCopy codepy_library(
name = "utils",
srcs = ["utils.py"],
visibility = ["//frontend:__pkg__"], # Only the frontend module can use this target
)
//frontend/BUILD.bazel
pythonCopy codepy_binary(
name = "main",
srcs = ["main.py"],
deps = ["//shared:utils"], # Can depend on utils
)
//backend/BUILD.bazel
pythonCopy codepy_binary(
name = "service",
srcs = ["service.py"],
# No dependency on //shared:utils, as it's not listed here
)
Explanation
Visibility Declaration:
In the
//shared/BUILD.bazel
file, thevisibility
attribute for//shared:utils
is set to["//frontend:__pkg__"]
. This means that only targets in thefrontend
package can see and depend on the//shared:utils
target.
Controlled Access:
The
frontend
module can use theutils
library because it is explicitly allowed by the visibility setting.The
backend
module cannot access theutils
library because it’s not listed in the visibility setting. This prevents unintended dependencies and keeps thebackend
module isolated from theshared
utilities.
Benefits
Modularity: By controlling visibility, you can better organize your project and enforce modular design. Each part of the project only has access to the dependencies it needs.
Maintainability: It reduces the risk of changes in one part of the project affecting others unintentionally, making the project easier to maintain and refactor.
Summary
Bazel’s visibility feature lets you control which parts of your project can access specific build targets. By using visibility, you can limit dependencies and keep different parts of your codebase modular and isolated, similar to how you might use package visibility in Java or namespaces in C++.
TODO:
Remote Build Execution and Caching
Build Dependency Analysis
Fast, Correct Builds (and Tests)
Last updated