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:

  1. Project Structure:

    lessCopy codemy_project/
    ├── //app
    │   ├── BUILD.bazel
    │   └── main.py
    └── //lib
        ├── BUILD.bazel
        └── utils.py
  2. 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.

  3. 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, the py_library target //lib:utils is defined with its source files. This target represents the library with utility functions.

    • In the //app/BUILD.bazel file, the py_binary target //app:main is defined as a Python binary. The depsattribute 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 the deps 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:

  1. Project Structure:

my_project/
├── //frontend
│   ├── BUILD.bazel
│   └── main.py
├── //backend
│   ├── BUILD.bazel
│   └── service.py
└── //shared
    ├── BUILD.bazel
    └── utils.py
  1. 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.

  1. Setting Up Visibility:

    • You want to allow only the frontend module to use the utilities from shared, but you don’t want the backend 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, the visibility attribute for //shared:utils is set to ["//frontend:__pkg__"]. This means that only targets in the frontend package can see and depend on the //shared:utils target.

  • Controlled Access:

    • The frontend module can use the utils library because it is explicitly allowed by the visibility setting.

    • The backend module cannot access the utils library because it’s not listed in the visibility setting. This prevents unintended dependencies and keeps the backend module isolated from the shared 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