LoginSignup
6
5

More than 1 year has passed since last update.

初心者でも使えたビルドツールのBazel(C/C++編)

Posted at

始めに

 仕事で、ゼロからEdge 向けのAIの推論を行うアプリケーションを作ることになったので、Googleの社内でも使われているというBazelというビルドツールを使って、プロジェクトを作成したので、その時のノウハウをまとめておきます。
テストフレームワークにgtestを採用して、Bazelでビルドとテストの実行を行いました。

 最初は、使い慣れていてたくさんサンプルがあるmakeの方が使いやすいなぁと思っていたが、一度慣れると、bazelの方が簡単に書けるし、makeに詳しくない新しい開発メンバーもすぐに使えるようになっているので、bazelにしてよかったなぁという感じ。

Bazelとは

 Googleの社内でも使われているビルドとテストを行うツールで、Tensorflowやmediapipeなどでも使われています。
https://bazel.build/

コードを修正したときに、関係のあるファイルのみをビルドし直すので、ビルド時間も早く、複数言語にも対応、実行binaryやlibrary、テストコードでBUILDファイルのルールが異なり、ソースコードの目的も分かりやすい。

Bazelのインストール(Linux X86_64向け)

$ sudo apt install apt-transport-https curl gnupg
$ 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

用意するファイル

ソースコード以外にも、WORKSPACEというプロジェクトで一つのファイルと、BUILDというモジュールごとに用意するファイルの2種類がある。

フォルダとファイルの構成

.
|-- BUILD
|-- README.md
|-- WORKSPACE
|-- gmock.BUILD
|-- opencv.BUILD
|-- sample_app
|   |-- BUILD
|   |-- lib
|   |   |-- BUILD
|   |   |-- sample_lib.cpp
|   |   `-- sample_lib.h
|   |-- sample_main.cpp
|   `-- test
|       |-- BUILD
|       `-- test_sample_app.cpp
`-- test_data
    |-- BUILD
    `-- sample_app.png

WORKSPACEファイルの作成

WORKSPACEというファイルは、プロジェクトのルートディレクトリの直下に置いておく必要がある。
ここには、基本的に外部のプロジェクト等を呼び出す際の記述を書く。
今回は、opencvやgtestの外部ライブラリを使用したので、以下のように記述しておく。

WORKSPACE
workspace(name = "sample_app")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository")
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")

new_git_repository(
    name = "googletest",
    build_file = "@//:gmock.BUILD",
    remote = "https://github.com/google/googletest",
    tag = "release-1.10.0",
)

new_local_repository(
    name = "opencv",
    path = "/usr",
    build_file = "opencv.BUILD",
)

WORKSPACEで、モジュール対してbuild_fileを指定すると、指定したBUILDファイルが呼び出されて、そちらでビルドされる。

 今回、googletest では WORKSPACEでgit のレポ名やブランチ名、タグ名などを指定して、build_fileにgmock.BUILDを指定している。
gmock.BUILDは以下のように記述する。

gmock.BUILD
cc_library(
    name = "gtest",
    srcs = [
        "googletest/src/gtest-all.cc",
        "googlemock/src/gmock-all.cc",
    ],
    hdrs = glob([
        "**/*.h",
        "googletest/src/*.cc",
        "googlemock/src/*.cc",
    ]),
    includes = [
        "googlemock",
        "googletest",
        "googletest/include",
        "googlemock/include",
    ],
    linkopts = ["-pthread"],
    visibility = ["//visibility:public"],
)

cc_library(
    name = "gtest_main",
    srcs = ["googlemock/src/gmock_main.cc"],
    linkopts = ["-pthread"],
    visibility = ["//visibility:public"],
    deps = [":gtest"],

 また、今回は、opencvはhost上にbuild済み(apt-get で取ってきた)のものを使うので、
opencv.BUILDにhost上の/usr/lib/x86_64-linux-gnu以下にある、opencvのlibraryを取り込む記述をしておく。
WORKSPACEの方で、path = "/usr" と書いているのでopencv.BUILDの方は/usr以下のパスで記述しておくこと。

opencv.BUILD
cc_library(
    name = "opencv",
    srcs = glob(["lib/x86_64-linux-gnu/libopencv_core.so*",
                 "lib/x86_64-linux-gnu/libopencv_highgui.so*",
                 "lib/x86_64-linux-gnu/libopencv_imgcodecs.so*",
                 "lib/x86_64-linux-gnu/libopencv_imgproc.so*",
                 "lib/x86_64-linux-gnu/libopencv_video.so*",
                 "lib/x86_64-linux-gnu/libopencv_videoio.so*",
                 "lib/x86_64-linux-gnu/blas/libblas.so*",
                 "lib/x86_64-linux-gnu/lapack/liblapack.so*",
                 ]),
    hdrs = glob(["include/opencv2/**/*.h*"]),
    includes = ["include/opencv2"],
    visibility = ["//visibility:public"],
    linkstatic = 1,
)

BUILDファイルの作成

では、実際にソースコードの方のBUILDファイルには、以下のような感じで記載する。
以下を見て、binaryをビルドするならcc_binary、 libraryを作りたいならcc_library、テストコードを書くならcc_test という構文を使う。
https://docs.bazel.build/versions/main/be/c-cpp.html

cc_binaryであれば、

  • binaryの名前(name = "sample_app.bin")
  • ビルドしたいファイル(srcs = "sample_main.cpp")
  • linkするlibrary(dep = "@opencv//:opencv")
  • -D で定義したいマクロ(deps = "ENABLE_DEBUG")
  • makeに渡すオプション(copts = "-g")
  • ソースコード以外のファイル(data = "test_data:sample_app.png")

という感じ書いていく。

sample_app/BUILD
cc_binary(
    name = "sample_app.bin",
    srcs = ["sample_main.cpp"],
    deps = ["//sample_app/lib:lib_sample_app",
            "@opencv//:opencv"],
    defines = ["ENABLE_DEBUG"],
    copts = ["-Isample_app/lib/",
             "-fPIC", "-g"],
    data = ["//test_data:sample_app.png"],
)

Libraryを作るときは、以下のようにcc_libraryを使って記述する。
cc_binaryをと異なるところは、visibility を追加する必要がある。

sample_app/lib/BUILD
cc_library(
    name = "lib_sample_app",
    srcs = ["sample_lib.cpp"],
    hdrs = ["sample_lib.h"],
    defines = ["ENABLE_DEBUG"],
    visibility = ["//visibility:public"],
    copts = ["-fPIC", "-g"],
)

ソースコードとは別に、テスト用に使うData もあると思うので、そういった物は、exports_filesというものを使って定義する。

test_data/BUILD
exports_files("sample_app.png")

 以下は、gtestで書いたテストコードをビルドするときのBUILDファイル。

sample_app/test/BUILD
cc_test(
    name = "test_sample_app",
    srcs = ["test_sample_app.cpp"],
    deps = ["//sample_app/lib:lib_sample_app",
           "@googletest//:gtest_main",
    copts = ["-Isample_app/lib/"]
)

ソースコードのビルドと実行

実際にビルドとテストを行うときは、bazel build というコマンドを使ってBUILDファイルの置いてある場所とname を指定する。
ビルド後は、bazel-bin以下の実行ファイルを実行すればよい。

bazel build sample_app:sample_app.bin
./bazel-bin/sample_app/sample_app.bin

テストコードのビルドと実行

テストコードの実行はビルドと実行を一緒に行ってくれるbazel run を使う

bazel run sample_app/test:test_sample_app

それ以外のTips

  • BAZEL_BUILD_FLAGSには--config debugを追加すると、debug buildになる。

  • --config にパラメータを指定して、.bazelrc に設定した内容も呼び出せる。

以下のようにbuild:XXXX と.bazelrc に書いておいて、bazel build するときに--config xxx を付けると、.bazelrc の内容で反映される。
tensorflow や mediapip のgithub を見れば、.bazelrc のサンプルを見ることができる。

.bazelrc
build:debug -c dbg
build:debug --copt="-g"
build:debug --strip="never"

build:lsan --copt -fsanitize=leak
build:lsan --copt -O0
build:lsan --linkopt -fsanitize=leak

build:msan --copt -fsanitize=memory
build:msan --copt -O0
build:msan --linkopt -fsanitize=memory

サンプルコード置き場

上で説明してきたサンプルコードは以下に置いてある。
https://github.com/daiki0321/bazel_sample_app

6
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5