Bazel build system

Bazel build system

Bazel (bazel.build) is an open-source build and test tool (similar to Make, Maven, and Gradle) designed to suppoort multiple languages and large codebases.

Spacetime uses Bazel as build system for the public repositories containing APIs, SDKs and code samples.

Understanding Bazel can help you building and testing the provided libraries and examples using the provided build setup. You can also choose to use Bazel in your own projects, but that is not required to use our APIs.

Benefits of Bazel

  1. Consistency: Bazel ensures that builds are consistent across different environments, reducing the chances of environment-specific issues.
  2. Scalability: Bazel is designed to handle large codebases and complex build processes, making it suitable for enterprise-level applications.
  3. Efficiency: Bazel’s caching and parallel execution capabilities speed up build and test times, leading to faster development cycles.
  4. Extensibility: Bazel supports multiple languages and platforms, allowing for easy integration with your existing projects and workflows.

What is Bazelisk?

Bazelisk is a user-friendly launcher for Bazel that automatically downloads and installs the correct version of Bazel specified in your project. It helps ensure that all developers and CI systems use the same version of Bazel, making the build environment more consistent and predictable. By including Bazelisk with our main repository, we simplify the setup process, so you don’t need to manually install Bazel.

Key Concepts

  • BUILD file: The file where build instructions are defined. It contains rules and targets.
  • Target: A buildable unit such as binary, library, or test.
  • Label: A unique identifier for a target, formatted as //package:target.
  • Rule: Defines how to build a specific type of target, like cc_binary or java_binary.

How We Use Bazel

SpaceTime integrates Bazel to manage the build and testing processes. Bazel helps ensure that our codebase remains consistent and reliable, even as it grows. By using Bazel, we can;

  • Automate Builds: Bazel automates the build process, ensuring that all dependencies are correctly managed and up-to-date.
  • Run Tests Efficiently: Bazel provides robust testing capabilities, allowing us to run tests quickly and reliably.
  • Manage Dependencies: Bazel handles external dependencies seamlessly, making it easier to integrate third-party libraries.

Running Bazel Commands

Since Bazelisk is included with our main repository, you don’t need to install Bazel separately. Here are some basic commands to get you started.

Build a Target bazel build //path/to/package:target_name

Run a Target bazel run //path/to/package:target_name

NOTE: Only some targets can be run. Usually, but not only, targets created by rules such as *_binary. In particular, *_test and *_library cannot be run.

Test a Target bazel test //path/to/package:target_name

NOTE: Only some targets can be tested. Usually, these are targets created by rules such as *_test.

Query Targets bazel query //path/to/package:target_name This command allows you to query the dependency graph, providing information about the available targets in your workspace. It’s a powerful tool for exploring and understanding the structure of your project.

NOTE: You can refer to multiple targets at once using syntax such as //path/…, which includes all targets within the specified path and subdirectories. Additionally, if the name of the target is the same as the package, you can skip specifying the target name, such as using //abc instead of //abc:abc. For example, bazel build //… is useful for checking that all targets are building correctly.

Example Project

Below is an example of a BUILD file that defines a library, a test, and a binary. This example will help you understand how dependencies are linked across targets.

BUILD File

client.py

# Define a C++ library
cc_library(
    name = "my_library",
    srcs = ["my_library.cpp"],
    hdrs = ["my_library.h"],
)

# Define a test that depends on the library
cc_test(
    name = "my_library_test",
    srcs = ["my_library_test.cpp"],
    deps = [":my_library"],
)

# Define a binary that depends on the library
cc_binary(
    name = "my_application",
    srcs = ["main.cpp"],
    deps = [":my_library"],
)

Explanation

  1. Library Definition: The cc_library rule defines a library named my_library with it’s source (my_library.cpp) and header (my_library.h) files.
  2. Test Definition: The cc_test rule defines a test named my_library_test which depends on my_library. This means that my_library_test will link against my_library when it’s built.
  3. Binary Definition: the cc_binary rule defines a binary named my_application which also depends on my_library. This means that my_application will link against my_library when it’s built.

Running the Example

  • To build the library: bazel build //path/to/package:my_library
  • To run the test: bazel test //path/to/package:my_library_test
  • To build and run the application: bazel run //path/to/package:my_application

Best Practices

  • Keep BUILD Files Simple: Avoid logic in BUILD files.
  • Use Consistent Naming Conventions: This helps with readability and maintainability.
  • Leverage Bazel’s Caching: Bazel caches build results to speed up incremental builds. Avoid unnecessary clean commands.
  • Organize Your Workspace: Structure your workspace logically to make navigation easier.

Troubleshooting

  • Build Fails Due to Missing Dependencies: Ensure all dependencies are correctly specified in BUILD and WORKSPACE files.
  • Inconsistent Builds: Run bazel clean to reset the build environment.
  • Performance Issues: Check for unnecessary rebuilds and optimize your BUILD files.

Learn more