Bazel

Build ToolScalableParallelIncremental BuildLarge-scale Projects

Build Tool

Bazel

Overview

Bazel is a high-performance, scalable build and test tool developed by Google. Originally developed as Google's internal tool "Blaze" and open-sourced in 2015, it's designed for multi-language support (Java, C++, Python, Go, JavaScript, TypeScript, etc.) and large-scale projects. Bazel provides incremental builds, parallel processing, distributed builds, and cross-platform support. It's adopted by major open-source projects like TensorFlow, Angular, and Envoy, with proven track record in enterprise environments.

Details

Key Features

  • Fast Builds: Significant time reduction through incremental builds and parallelization
  • Scalability: Handles codebases with millions of lines of code
  • Reproducible Builds: Consistent build results in hermetic environments
  • Multi-language Support: Java, C++, Python, Go, JavaScript, Android, iOS, and more
  • Distributed Builds: Remote Build Execution (RBE) for cloud-distributed processing
  • Precise Dependency Tracking: Detailed dependency graph construction
  • Test Integration: Unified build and test management

Architecture

Builds dependency graphs from targets defined in BUILD.bazel files, efficiently rebuilding only changed targets. Guarantees high reproducibility through sandbox execution.

Ecosystem

Rich ecosystem including Bazel Central Registry (BCR), rules_* (official rule sets), Remote Build Execution, IDE integration (IntelliJ, VS Code), and CI/CD pipeline integration.

Pros and Cons

Pros

  • Ultra-fast Builds: Significant time reduction through incremental builds and parallelization
  • Scalability: Designed to handle massive projects
  • Reproducibility: Consistent build results independent of environment
  • Multi-language Support: Unified management of multiple languages with single tool
  • Distributed Builds: High-speed processing leveraging cloud resources
  • Detailed Dependency Management: Accurate and efficient dependency tracking
  • Enterprise Track Record: Large-scale operational experience at Google, Dropbox, Uber

Cons

  • High Learning Curve: Requires mastering unique concepts and configuration methods
  • Configuration Complexity: Complex initial setup and rule definitions
  • Tool Size: Larger installation size compared to other build tools
  • Ecosystem Dependencies: Reliance on Bazel-specific rules
  • Small Project Overhead: May have significant overhead for small projects
  • Debugging Difficulty: Troubleshooting abstracted build processes can be challenging

Reference Links

Usage Examples

Installation and Setup

# Ubuntu/Debian
curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor > bazel.gpg
sudo mv bazel.gpg /etc/apt/trusted.gpg.d/
echo "deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list
sudo apt update && sudo apt install bazel

# CentOS/RHEL/Fedora
sudo dnf copr enable vbatts/bazel
sudo dnf install bazel

# macOS (Homebrew)
brew install bazel

# Windows (Chocolatey)
choco install bazel

# Direct binary download
wget https://github.com/bazelbuild/bazel/releases/download/6.0.0/bazel-6.0.0-installer-linux-x86_64.sh
chmod +x bazel-6.0.0-installer-linux-x86_64.sh
./bazel-6.0.0-installer-linux-x86_64.sh --user

# Version check
bazel version

# Project initialization
bazel mod init my-project

Basic WORKSPACE.bazel and BUILD.bazel

# WORKSPACE.bazel (project root)
workspace(name = "my_project")

# Using Bazel Central Registry
bazel_dep(name = "rules_cc", version = "0.0.9")
bazel_dep(name = "rules_java", version = "7.1.0")
bazel_dep(name = "rules_python", version = "0.27.1")

# External dependencies (Maven)
bazel_dep(name = "rules_jvm_external", version = "5.3")
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
maven.install(
    artifacts = [
        "junit:junit:4.13.2",
        "com.google.guava:guava:31.1-jre",
        "org.apache.commons:commons-lang3:3.12.0",
    ],
    repositories = [
        "https://repo1.maven.org/maven2",
    ],
)
# BUILD.bazel (src/main/java directory)
load("@rules_java//java:defs.bzl", "java_binary", "java_library")

# Java library
java_library(
    name = "utils",
    srcs = ["Utils.java"],
    deps = [
        "@maven//:com_google_guava_guava",
        "@maven//:org_apache_commons_commons_lang3",
    ],
    visibility = ["//visibility:public"],
)

# Java application
java_binary(
    name = "main",
    srcs = ["Main.java"],
    main_class = "com.example.Main",
    deps = [
        ":utils",
        "@maven//:com_google_guava_guava",
    ],
)

# Test
java_test(
    name = "utils_test",
    srcs = ["UtilsTest.java"],
    test_class = "com.example.UtilsTest",
    deps = [
        ":utils",
        "@maven//:junit_junit",
    ],
)

C++ Project Configuration

# BUILD.bazel (for C++ project)
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")

# C++ library
cc_library(
    name = "math_utils",
    srcs = ["math_utils.cpp"],
    hdrs = ["math_utils.h"],
    visibility = ["//visibility:public"],
)

cc_library(
    name = "string_utils",
    srcs = ["string_utils.cpp"],
    hdrs = ["string_utils.h"],
    deps = [":math_utils"],
    visibility = ["//visibility:public"],
)

# C++ binary
cc_binary(
    name = "main",
    srcs = ["main.cpp"],
    deps = [
        ":math_utils",
        ":string_utils",
    ],
)

# C++ test (using Google Test)
cc_test(
    name = "math_utils_test",
    srcs = ["math_utils_test.cpp"],
    deps = [
        ":math_utils",
        "@com_google_googletest//:gtest_main",
    ],
)

# Google Test dependency (add to WORKSPACE.bazel)
# bazel_dep(name = "googletest", version = "1.14.0.bcr.1")

Python Project Configuration

# BUILD.bazel (for Python)
load("@rules_python//python:defs.bzl", "py_binary", "py_library", "py_test")

# Python library
py_library(
    name = "calculator",
    srcs = ["calculator.py"],
    visibility = ["//visibility:public"],
)

py_library(
    name = "utils",
    srcs = ["utils.py"],
    deps = [":calculator"],
    visibility = ["//visibility:public"],
)

# Python binary
py_binary(
    name = "main",
    srcs = ["main.py"],
    main = "main.py",
    deps = [
        ":calculator",
        ":utils",
    ],
)

# Python test
py_test(
    name = "calculator_test",
    srcs = ["calculator_test.py"],
    main = "calculator_test.py",
    deps = [":calculator"],
)

# External Python packages (add to WORKSPACE.bazel)
# bazel_dep(name = "rules_python", version = "0.27.1")
# pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip")
# pip.parse(
#     hub_name = "pip",
#     python_version = "3.11",
#     requirements_lock = "//:requirements_lock.txt",
# )

Multi-language Project

# BUILD.bazel (root directory)
# Multi-language project integration

# Java web API
java_binary(
    name = "api_server",
    srcs = glob(["src/main/java/**/*.java"]),
    main_class = "com.example.ApiServer",
    deps = [
        "@maven//:org_springframework_spring_boot_starter_web",
        "@maven//:org_springframework_boot_spring_boot_starter",
    ],
)

# Python data processing
py_binary(
    name = "data_processor",
    srcs = ["scripts/data_processor.py"],
    deps = [
        "@pip//numpy",
        "@pip//pandas",
    ],
)

# C++ high-performance computing
cc_binary(
    name = "compute_engine",
    srcs = glob(["src/cpp/**/*.cpp"]),
    hdrs = glob(["src/cpp/**/*.h"]),
    deps = [
        "@eigen//:eigen3",
        "@boost//:system",
    ],
)

# JavaScript/TypeScript frontend
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
nodejs_binary(
    name = "frontend",
    data = [
        "package.json",
        "@npm//react",
        "@npm//react-dom",
    ] + glob(["src/frontend/**/*"]),
    entry_point = "src/frontend/index.js",
)

# Integrated test suite
test_suite(
    name = "all_tests",
    tests = [
        "//src/main/java:utils_test",
        "//scripts:data_processor_test",
        "//src/cpp:compute_engine_test",
        "//src/frontend:frontend_test",
    ],
)

Advanced Configuration and Custom Rules

# BUILD.bazel (advanced configuration example)

# Custom rule definition
load("//tools:custom_rules.bzl", "custom_binary")

# Platform-specific builds
config_setting(
    name = "linux_x86_64",
    constraint_values = [
        "@platforms//os:linux",
        "@platforms//cpu:x86_64",
    ],
)

config_setting(
    name = "darwin_arm64",
    constraint_values = [
        "@platforms//os:macos",
        "@platforms//cpu:arm64",
    ],
)

# Conditional compilation
cc_binary(
    name = "platform_specific",
    srcs = ["main.cpp"],
    deps = select({
        ":linux_x86_64": [":linux_deps"],
        ":darwin_arm64": [":macos_deps"],
        "//conditions:default": [":default_deps"],
    }),
)

# Docker image generation
load("@io_bazel_rules_docker//container:container.bzl", "container_image")
container_image(
    name = "app_image",
    base = "@java_base//image",
    files = [":api_server_deploy.jar"],
    cmd = ["java", "-jar", "/api_server_deploy.jar"],
)

# Profiling and metrics
cc_binary(
    name = "profiled_binary",
    srcs = ["main.cpp"],
    copts = ["-fprofile-generate"],
    linkopts = ["-fprofile-generate"],
    deps = [":core_lib"],
)

Build and Test Execution

# Basic build commands
bazel build //src/main/java:main          # Build specific target
bazel build //...                         # Build all targets
bazel build //src/...                     # Build all under package

# Test execution
bazel test //src/main/java:utils_test     # Run specific test
bazel test //...                          # Run all tests
bazel test //src/... --test_output=all    # Tests with detailed output

# Execution
bazel run //src/main/java:main            # Run binary
bazel run //src/main/java:main -- --arg1 value1  # Run with arguments

# Cleanup
bazel clean                               # Clear build cache
bazel clean --expunge                     # Delete all data

# Parallel builds
bazel build //... --jobs=8                # Build with 8 parallel jobs
bazel build //... --local_cpu_resources=4 # Limit CPU usage

# Remote build configuration
bazel build //... --remote_executor=grpcs://remote-execution-server:443

Performance Optimization Configuration

# .bazelrc file (project root)

# Basic optimization
build --compilation_mode=opt
build --copt=-O3
build --copt=-DNDEBUG

# Parallel processing settings
build --jobs=auto
build --local_cpu_resources=HOST_CPUS-1
build --local_ram_resources=HOST_RAM*0.8

# Cache settings
build --disk_cache=/tmp/bazel-disk-cache
build --repository_cache=/tmp/bazel-repo-cache

# Incremental build optimization
build --watchfs
build --experimental_split_coverage_postprocessing
build --experimental_strict_action_env

# Debug configuration
build:debug --compilation_mode=dbg
build:debug --copt=-g
build:debug --strip=never

# Test configuration
test --test_output=errors
test --test_summary=detailed
test --test_verbose_timeout_warnings

# Remote build configuration
build:remote --remote_executor=grpcs://remote-execution-server:443
build:remote --remote_cache=grpcs://remote-cache-server:443
build:remote --google_default_credentials

# Platform-specific settings
build:linux --platforms=@io_bazel_rules_go//go/toolchain:linux_amd64
build:macos --platforms=@io_bazel_rules_go//go/toolchain:darwin_amd64
build:windows --platforms=@io_bazel_rules_go//go/toolchain:windows_amd64

CI/CD Integration Example

# .github/workflows/bazel.yml
name: Bazel Build and Test

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Mount bazel cache
      uses: actions/cache@v3
      with:
        path: |
          ~/.cache/bazel
        key: bazel-${{ runner.os }}-${{ hashFiles('**/WORKSPACE.bazel', '**/*.bzl') }}
        restore-keys: |
          bazel-${{ runner.os }}-

    - name: Install Bazel
      run: |
        curl -LO "https://github.com/bazelbuild/bazel/releases/download/6.0.0/bazel-6.0.0-linux-x86_64"
        chmod +x bazel-6.0.0-linux-x86_64
        sudo mv bazel-6.0.0-linux-x86_64 /usr/local/bin/bazel

    - name: Build
      run: bazel build //...

    - name: Test
      run: bazel test //... --test_output=all

    - name: Coverage
      run: bazel coverage //... --combined_report=lcov

    - name: Upload coverage
      uses: codecov/codecov-action@v3
      with:
        files: ./bazel-out/_coverage/_coverage_report.dat