Bazel チュートリアル: C++ プロジェクトをビルドする

問題を報告 ソースを表示

はじめに

Bazel を初めて使用する場合は、ここで紹介する情報が役に立つでしょう。この最初のビルドのチュートリアルでは、Bazel の使用方法を簡単に説明します。このチュートリアルでは、Bazel のコンテキストで使用される主な用語を定義し、Bazel ワークフローの基本について説明します。まず、必要なツールを基にして、複雑性が増している 3 つのプロジェクトをビルドして実行し、複雑化する仕組みと理由を学びます。

Bazel は多言語ビルドをサポートするビルドシステムですが、このチュートリアルでは C++ プロジェクトを例として使用し、ほとんどの言語に適用される一般的なガイドラインとフローを示します。

推定完了時間: 30 分。

前提条件

まだインストールしていない場合は、まず Bazel をインストールします。このチュートリアルではソース管理に Git を使用します。そのため、最適な結果を得るには Git もインストールしてください。

次に、選択したコマンドライン ツールで次のコマンドを実行して、Bazel の GitHub リポジトリからサンプル プロジェクトを取得します。

git clone https://github.com/bazelbuild/examples

このチュートリアルのサンプル プロジェクトは、examples/cpp-tutorial ディレクトリにあります。

その構成は次のとおりです。

examples
└── cpp-tutorial
    ├──stage1
    │  ├── main
    │  │   ├── BUILD
    │  │   └── hello-world.cc
    │  └── WORKSPACE
    ├──stage2
    │  ├── main
    │  │   ├── BUILD
    │  │   ├── hello-world.cc
    │  │   ├── hello-greet.cc
    │  │   └── hello-greet.h
    │  └── WORKSPACE
    └──stage3
       ├── main
       │   ├── BUILD
       │   ├── hello-world.cc
       │   ├── hello-greet.cc
       │   └── hello-greet.h
       ├── lib
       │   ├── BUILD
       │   ├── hello-time.cc
       │   └── hello-time.h
       └── WORKSPACE

3 つのファイルセットがあり、それぞれがこのチュートリアルの 1 つのステージを表しています。最初のステージでは、単一のパッケージ内に単一のターゲットをビルドします。2 番目のステージでは、単一のパッケージからバイナリとライブラリの両方をビルドします。3 番目の最終ステージでは、複数のパッケージを含むプロジェクトをビルドし、複数のターゲットでプロジェクトをビルドします。

まとめ: はじめに

Bazel(および Git)をインストールして、このチュートリアルのリポジトリのクローンを作成することで、Bazel を使用した最初のビルドの基礎を築きました。次のセクションに進んで用語を定義し、ワークスペースをセットアップします。

はじめに

ワークスペースをセットアップする

プロジェクトをビルドするには、その前にワークスペースを設定する必要があります。ワークスペースは、プロジェクトのソースファイルと Bazel のビルド出力を格納するディレクトリです。また、次の重要なファイルも含まれます。

  • WORKSPACE file 。ディレクトリとそのコンテンツを Bazel ワークスペースとして識別し、プロジェクトのディレクトリ構造のルートにあります。
  • 1 つ以上の BUILD files 。プロジェクトのさまざまな部分のビルド方法を Bazel に指定します。BUILD ファイルを含むワークスペース内のディレクトリはパッケージです。(パッケージの詳細については、このチュートリアルの後半で説明します)。

今後のプロジェクトで、ディレクトリを Bazel ワークスペースとして指定するには、そのディレクトリに WORKSPACE という名前の空のファイルを作成します。このチュートリアルでは、各ステージに WORKSPACE ファイルがあらかじめ用意されています。

: Bazel でプロジェクトをビルドする場合、すべての入力は同じワークスペースに存在する必要があります。リンクされていない限り、異なるワークスペースに存在するファイルは互いに独立しています。ワークスペースのルールについて詳しくは、こちらのガイドをご覧ください。

BUILD ファイルについて

BUILD ファイルには、Bazel に関するさまざまな種類の手順が含まれています。各 BUILD ファイルには、一連の手順として少なくとも 1 つのルールが必要です。このルールは、実行可能なバイナリやライブラリなどの目的の出力のビルド方法を Bazel に指示します。BUILD ファイル内のビルドルールの各インスタンスはターゲットと呼ばれ、ソースファイルと依存関係の特定のセットを参照します。ターゲットは他のターゲットを指すこともできます。

cpp-tutorial/stage1/main ディレクトリの BUILD ファイルを見てみましょう。

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
)

この例では、hello-world ターゲットが Bazel の組み込み cc_binary rule をインスタンス化します。このルールは、依存関係のない hello-world.cc ソースファイルから自己完結型の実行可能バイナリをビルドするよう Bazel に指示します。

まとめ: はじめに

これで、いくつかの主要な用語と、このプロジェクトと Bazel に関する一般的な意味を理解できたと思います。次のセクションでは、プロジェクトのステージ 1 をビルドしてテストします。

ステージ 1: 単一のターゲット、単一のパッケージ

プロジェクトの最初の部分を構築する段階に入ります。参考までに、プロジェクトのステージ 1 セクションの構造は次のとおりです。

examples
└── cpp-tutorial
    └──stage1
       ├── main
       │   ├── BUILD
       │   └── hello-world.cc
       └── WORKSPACE

次のコマンドを実行して cpp-tutorial/stage1 ディレクトリに移動します。

cd cpp-tutorial/stage1

続いて、次のコマンドを実行します。

bazel build //main:hello-world

ターゲット ラベルの //main: の部分は、ワークスペースのルートからの BUILD ファイルの場所であり、hello-worldBUILD ファイルのターゲット名です。

Bazel では、次のようなコードが生成されます。

INFO: Found 1 target...
Target //main:hello-world up-to-date:
  bazel-bin/main/hello-world
INFO: Elapsed time: 2.267s, Critical Path: 0.25s

初めての Bazel ターゲットをビルドできました。Bazel は、ビルド出力をワークスペースのルートの bazel-bin ディレクトリに配置します。

新しく作成したバイナリをテストします。

bazel-bin/main/hello-world

これにより、「Hello world」メッセージが出力されます。

ステージ 1 の依存関係グラフを次に示します。

hello-world の依存関係グラフには、1 つのターゲットと 1 つのソースファイルが表示されます。

まとめ: ステージ 1

最初のビルドを完了したら、ビルドの構造に関する基本概念を理解できたと思います。次のステージでは、別のターゲットを追加して複雑さを増します。

ステージ 2: 複数のビルド ターゲット

小規模なプロジェクトでは単一のターゲットで十分ですが、大規模なプロジェクトを複数のターゲットとパッケージに分割することもできます。これにより、増分ビルド(Bazel による変更点のみの再ビルド)を高速化できます。また、プロジェクトの複数の部分を一度にビルドすることで、ビルドを高速化できます。チュートリアルのこのステージではターゲットを追加し、次のステップではパッケージを追加します。

これは、ステージ 2 で使用するディレクトリです。

    ├──stage2
    │  ├── main
    │  │   ├── BUILD
    │  │   ├── hello-world.cc
    │  │   ├── hello-greet.cc
    │  │   └── hello-greet.h
    │  └── WORKSPACE

以下に、cpp-tutorial/stage2/main ディレクトリの BUILD ファイルを示します。

cc_library(
    name = "hello-greet",
    srcs = ["hello-greet.cc"],
    hdrs = ["hello-greet.h"],
)

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
    deps = [
        ":hello-greet",
    ],
)

この BUILD ファイルを使用して、Bazel は最初に hello-greet ライブラリ(Bazel の組み込み cc_library rule を使用)をビルドし、次に hello-world バイナリをビルドします。hello-world ターゲットの deps 属性は、hello-world バイナリのビルドに hello-greet ライブラリが必要であることを Bazel に知らせます。

この新しいバージョンのプロジェクトをビルドする前に、次のコマンドを実行してディレクトリを変更し、cpp-tutorial/stage2 ディレクトリに切り替える必要があります。

cd ../stage2

これで、以下の使い慣れたコマンドを使用して、新しいバイナリをビルドできるようになりました。

bazel build //main:hello-world

繰り返しになりますが、Bazel は次のような内容を生成します。

INFO: Found 1 target...
Target //main:hello-world up-to-date:
  bazel-bin/main/hello-world
INFO: Elapsed time: 2.399s, Critical Path: 0.30s

これで、新たにビルドしたバイナリをテストできるようになりました。これにより、別の「Hello world」が返されます。

bazel-bin/main/hello-world

hello-greet.cc を変更してプロジェクトを再ビルドすると、Bazel はそのファイルのみを再コンパイルします。

依存関係グラフを見ると、hello-worldhello-greet という追加の入力に依存していることがわかります。

「hello-world」の依存関係グラフには、ファイルを変更した後の依存関係の変更が表示されます。

まとめ: ステージ 2

これで、2 つのターゲットを持つプロジェクトをビルドできました。hello-world ターゲットは 1 つのソースファイルをビルドし、もう 1 つのターゲット(//main:hello-greet)に依存します。このターゲットは 2 つの追加ソースファイルをビルドします。次のセクションでは、さらに一歩進んで別のパッケージを追加します。

ステージ 3: 複数のパッケージ

この次のステージで、ウォッチフェイスの追加機能レイヤがもう 1 つ追加され、複数のパッケージを持つプロジェクトがビルドされます。cpp-tutorial/stage3 ディレクトリの構造と内容を以下に示します。

└──stage3
   ├── main
   │   ├── BUILD
   │   ├── hello-world.cc
   │   ├── hello-greet.cc
   │   └── hello-greet.h
   ├── lib
   │   ├── BUILD
   │   ├── hello-time.cc
   │   └── hello-time.h
   └── WORKSPACE

サブディレクトリが 2 つあり、それぞれに BUILD ファイルが含まれています。そのため、Bazel では、ワークスペースに libmain の 2 つのパッケージが含まれるようになりました。

lib/BUILD ファイルを確認します。

cc_library(
    name = "hello-time",
    srcs = ["hello-time.cc"],
    hdrs = ["hello-time.h"],
    visibility = ["//main:__pkg__"],
)

main/BUILD ファイルの場合:

cc_library(
    name = "hello-greet",
    srcs = ["hello-greet.cc"],
    hdrs = ["hello-greet.h"],
)

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
    deps = [
        ":hello-greet",
        "//lib:hello-time",
    ],
)

メイン パッケージの hello-world ターゲットは、lib パッケージの hello-time ターゲットに依存します(したがってターゲット ラベル //lib:hello-time になります)。Bazel はこれを deps 属性を通じて認識します。これは、依存関係グラフに反映されています。

「hello-world」の依存関係グラフには、メイン パッケージ内のターゲットが「lib」パッケージ内のターゲットにどのように依存しているかが示されます。

ビルドを成功させるには、Visibility 属性を使用して、lib/BUILD//lib:hello-time ターゲットを main/BUILD のターゲットから明示的に参照できるようにします。これは、デフォルトでは、ターゲットは同じ BUILD ファイル内の他のターゲットにのみ表示されるためです。Bazel は、ターゲットの可視性を使用して、実装の詳細を含むライブラリが公開 API に漏洩するなどの問題を防ぎます。

プロジェクトの最終バージョンをビルドします。次のコマンドを実行して cpp-tutorial/stage3 ディレクトリに切り替えます。

cd  ../stage3

もう一度次のコマンドを実行します。

bazel build //main:hello-world

Bazel では、次のようなコードが生成されます。

INFO: Found 1 target...
Target //main:hello-world up-to-date:
  bazel-bin/main/hello-world
INFO: Elapsed time: 0.167s, Critical Path: 0.00s

このチュートリアルの最後のバイナリをテストして、最後の Hello world メッセージを取得します。

bazel-bin/main/hello-world

まとめ: ステージ 3

これで、プロジェクトを 3 つのターゲットを含む 2 つのパッケージとしてビルドし、それらの間の依存関係を理解しました。これにより、今後のプロジェクトを Bazel でビルドできるようになります。次のセクションでは、Bazel に関する作業の続きについて説明します。

次のステップ

Bazel を使用した最初の基本的なビルドが完了しましたが、これはまだ始まりにすぎません。Bazel による学習を続行するには、以下のリソースをご覧ください。

ご利用をお待ちしております。