Bazel

ビルドツールスケーラブル並列増分ビルド大規模プロジェクト

ビルドツール

Bazel

概要

Bazelは、Google社で開発された高性能でスケーラブルなビルド・テストツールです。元々はGoogleの内部ツール「Blaze」として開発され、2015年にオープンソース化されました。多言語対応(Java、C++、Python、Go、JavaScript、TypeScript等)と大規模プロジェクトに特化した設計が特徴で、増分ビルド、並列処理、分散ビルド、クロスプラットフォーム対応を提供します。TensorFlow、Angular、Envoy等の大規模オープンソースプロジェクトで採用され、エンタープライズ環境での実績も豊富です。

詳細

主要機能

  • 高速ビルド: 増分ビルドと並列処理による大幅な時間短縮
  • スケーラビリティ: 数百万行のコードベースに対応
  • 再現可能ビルド: ハーマライズされた環境での一貫したビルド結果
  • 多言語サポート: Java、C++、Python、Go、JavaScript、Android、iOS等
  • 分散ビルド: Remote Build Execution (RBE)によるクラウド分散処理
  • 詳細な依存関係追跡: 精密な依存関係グラフ構築
  • テスト統合: ビルドとテストの統一管理

アーキテクチャ

BUILD.bazelファイルで定義されたターゲットから依存関係グラフを構築し、変更されたターゲットのみを効率的に再ビルド。Sandbox環境での実行により高い再現性を保証。

エコシステム

Bazel Central Registry (BCR)、rules_*(公式ルールセット)、Remote Build Execution、IDE統合(IntelliJ、VS Code)、CI/CDパイプライン統合が充実。

メリット・デメリット

メリット

  • 超高速ビルド: 増分ビルドと並列処理により大幅な時間短縮
  • スケーラビリティ: 巨大プロジェクトに対応する設計
  • 再現性: 環境に依存しない一貫したビルド結果
  • 多言語対応: 単一ツールで複数言語を統一管理
  • 分散ビルド: クラウドリソースを活用した高速処理
  • 詳細な依存関係管理: 正確で効率的な依存関係追跡
  • 企業での実績: Google、Dropbox、Uber等での大規模運用実績

デメリット

  • 学習コストの高さ: 独特の概念と設定方法の習得が必要
  • 設定の複雑さ: 初期設定とルール定義が複雑
  • ツールサイズ: 他のビルドツールより大きなインストールサイズ
  • エコシステム依存: Bazel専用ルールへの依存
  • 小規模プロジェクト: オーバーヘッドが大きい場合も
  • デバッグの困難さ: 抽象化されたビルドプロセスの問題追跡

参考ページ

書き方の例

インストールとセットアップ

# 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

# バイナリ直接ダウンロード
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

# バージョン確認
bazel version

# プロジェクト初期化
bazel mod init my-project

基本的なWORKSPACE.bazelとBUILD.bazel

# WORKSPACE.bazel (プロジェクトルート)
workspace(name = "my_project")

# 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")

# 外部依存関係(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 ディレクトリ)
load("@rules_java//java:defs.bzl", "java_binary", "java_library")

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

# Javaアプリケーション
java_binary(
    name = "main",
    srcs = ["Main.java"],
    main_class = "com.example.Main",
    deps = [
        ":utils",
        "@maven//:com_google_guava_guava",
    ],
)

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

C++プロジェクトの設定

# BUILD.bazel (C++プロジェクト用)
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")

# C++ライブラリ
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++バイナリ
cc_binary(
    name = "main",
    srcs = ["main.cpp"],
    deps = [
        ":math_utils",
        ":string_utils",
    ],
)

# C++テスト(Google Test使用)
cc_test(
    name = "math_utils_test",
    srcs = ["math_utils_test.cpp"],
    deps = [
        ":math_utils",
        "@com_google_googletest//:gtest_main",
    ],
)

# Google Test依存関係(WORKSPACE.bazel に追加)
# bazel_dep(name = "googletest", version = "1.14.0.bcr.1")

Pythonプロジェクトの設定

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

# Pythonライブラリ
py_library(
    name = "calculator",
    srcs = ["calculator.py"],
    visibility = ["//visibility:public"],
)

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

# Pythonバイナリ
py_binary(
    name = "main",
    srcs = ["main.py"],
    main = "main.py",
    deps = [
        ":calculator",
        ":utils",
    ],
)

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

# 外部Pythonパッケージ(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",
# )

マルチ言語プロジェクト

# BUILD.bazel (ルートディレクトリ)
# マルチ言語プロジェクトの統合

# 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",
)

# 統合テストスイート
test_suite(
    name = "all_tests",
    tests = [
        "//src/main/java:utils_test",
        "//scripts:data_processor_test",
        "//src/cpp:compute_engine_test",
        "//src/frontend:frontend_test",
    ],
)

高度な設定とカスタムルール

# BUILD.bazel (高度な設定例)

# カスタムルール定義
load("//tools:custom_rules.bzl", "custom_binary")

# プラットフォーム固有ビルド
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",
    ],
)

# 条件付きコンパイル
cc_binary(
    name = "platform_specific",
    srcs = ["main.cpp"],
    deps = select({
        ":linux_x86_64": [":linux_deps"],
        ":darwin_arm64": [":macos_deps"],
        "//conditions:default": [":default_deps"],
    }),
)

# Docker イメージ生成
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"],
)

# プロファイリングとメトリクス
cc_binary(
    name = "profiled_binary",
    srcs = ["main.cpp"],
    copts = ["-fprofile-generate"],
    linkopts = ["-fprofile-generate"],
    deps = [":core_lib"],
)

ビルドとテストの実行

# 基本的なビルドコマンド
bazel build //src/main/java:main          # 特定ターゲットのビルド
bazel build //...                         # 全ターゲットのビルド
bazel build //src/...                     # パッケージ以下の全ビルド

# テスト実行
bazel test //src/main/java:utils_test     # 特定テストの実行
bazel test //...                          # 全テストの実行
bazel test //src/... --test_output=all    # 詳細出力付きテスト

# 実行
bazel run //src/main/java:main            # バイナリの実行
bazel run //src/main/java:main -- --arg1 value1  # 引数付き実行

# クリーンアップ
bazel clean                               # ビルドキャッシュクリア
bazel clean --expunge                     # 全データ削除

# 並列ビルド
bazel build //... --jobs=8                # 8並列でビルド
bazel build //... --local_cpu_resources=4 # CPU使用数制限

# リモートビルド設定
bazel build //... --remote_executor=grpcs://remote-execution-server:443

パフォーマンス最適化設定

# .bazelrc ファイル(プロジェクトルート)

# 基本最適化
build --compilation_mode=opt
build --copt=-O3
build --copt=-DNDEBUG

# 並列処理設定
build --jobs=auto
build --local_cpu_resources=HOST_CPUS-1
build --local_ram_resources=HOST_RAM*0.8

# キャッシュ設定
build --disk_cache=/tmp/bazel-disk-cache
build --repository_cache=/tmp/bazel-repo-cache

# 増分ビルド最適化
build --watchfs
build --experimental_split_coverage_postprocessing
build --experimental_strict_action_env

# デバッグ用設定
build:debug --compilation_mode=dbg
build:debug --copt=-g
build:debug --strip=never

# テスト設定
test --test_output=errors
test --test_summary=detailed
test --test_verbose_timeout_warnings

# リモートビルド設定
build:remote --remote_executor=grpcs://remote-execution-server:443
build:remote --remote_cache=grpcs://remote-cache-server:443
build:remote --google_default_credentials

# プラットフォーム別設定
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統合例

# .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