blob: 6d3781d0cb1ad190d1cad5998c5ee4119d7de70d [file] [log] [blame] [view]
Googlere7634412017-06-07 14:52:50 -04001---
2layout: documentation
3title: Common C++ Build Use Cases
4---
5
6Introduction to Bazel: Common C++ Build Use Cases
7==========
8
9Here you will find some of the most common use cases for building C++ projects
10with Bazel. If you have not done so already, get started with building C++
11projects with Bazel by completing the tutorial
Googler032aab22017-09-14 16:49:56 +020012[Introduction to Bazel: Build a C++ Project](tutorial/cpp.html).
Googlere7634412017-06-07 14:52:50 -040013
Googler100a0782017-06-29 18:21:34 +020014## Contents
15
16* [Including multiple files in a target](#including-multiple-files-in-a-target)
17* [Using transitive includes](#using-transitive-includes)
18* [Adding include paths](#adding-include-paths)
19* [Including external libraries](#including-external-libraries)
20* [Writing and running C++ tests](#writing-and-running-c-tests)
21* [Adding dependencies on precompiled libraries](#adding-dependencies-on-precompiled-libraries)
Googlere7634412017-06-07 14:52:50 -040022
23## Including multiple files in a target
24
25You can include multiple files in a single target with
dzc205125b2017-06-26 11:01:47 +020026[glob](../be/functions.html#glob).
Googlere7634412017-06-07 14:52:50 -040027For example:
28
29```python
30cc_library(
31 name = "build-all-the-files",
steplea6b2a1b2018-08-10 07:03:27 -070032 srcs = glob(["*.cc"]),
Googlere7634412017-06-07 14:52:50 -040033 hdrs = glob(["*.h"]),
34)
35```
36
37With this target, Bazel will build all the `.cc` and `.h` files it finds in the
38same directory as the `BUILD` file that contains this target (excluding
39subdirectories).
40
41## Using transitive includes
42
43If a file includes a header, then the file's rule should depend on that header's
44library. Conversely, only direct dependencies need to be specified as
45dependencies. For example, suppose `sandwich.h` includes `bread.h` and
46`bread.h` includes `flour.h`. `sandwich.h` doesn't include `flour.h` (who wants
47flour in their sandwich?), so the `BUILD` file would look like this:
48
49```python
50cc_library(
51 name = "sandwich",
52 srcs = ["sandwich.cc"],
53 hdrs = ["sandwich.h"],
54 deps = [":bread"],
55)
56
57cc_library(
58 name = "bread",
59 srcs = ["bread.cc"],
60 hdrs = ["bread.h"],
61 deps = [":flour"],
62)
63
64cc_library(
65 name = "flour",
66 srcs = ["flour.cc"],
67 hdrs = ["flour.h"],
68)
69```
70
71Here, the `sandwich` library depends on the `bread` library, which depends
72on the `flour` library.
73
74## Adding include paths
75
76Sometimes you cannot (or do not want to) root include paths at the workspace
77root. Existing libraries might already have an include directory that doesn't
78match its path in your workspace. For example, suppose you have the following
79directory structure:
80
81```
82└── my-project
aolivas7cbe1fb2018-03-09 05:13:17 -080083 ├── legacy
Googlere7634412017-06-07 14:52:50 -040084 │   └── some_lib
85 │   ├── BUILD
86 │   ├── include
87 │   │   └── some_lib.h
88 │   └── some_lib.cc
89 └── WORKSPACE
90```
91
92Bazel will expect `some_lib.h` to be included as
aolivas7cbe1fb2018-03-09 05:13:17 -080093`legacy/some_lib/include/some_lib.h`, but suppose `some_lib.cc` includes
Daniel Martn61191f42019-10-17 02:54:56 -070094`"some_lib.h"`. To make that include path valid,
95`legacy/some_lib/BUILD` will need to specify that the `some_lib/include`
Googlere7634412017-06-07 14:52:50 -040096directory is an include directory:
97
98```python
99cc_library(
100 name = "some_lib",
101 srcs = ["some_lib.cc"],
aolivas7cbe1fb2018-03-09 05:13:17 -0800102 hdrs = ["include/some_lib.h"],
103 copts = ["-Ilegacy/some_lib/include"],
Googlere7634412017-06-07 14:52:50 -0400104)
105```
106
107This is especially useful for external dependencies, as their header files
108must otherwise be included with a `/` prefix.
109
110## Including external libraries
111
112Suppose you are using [Google Test](https://github.com/google/googletest). You
Klaus Aehlige7008872019-04-24 08:51:46 -0700113can use one of the repository functions in the `WORKSPACE` file to
Googlere7634412017-06-07 14:52:50 -0400114download Google Test and make it available in your repository:
115
116```python
Klaus Aehlige7008872019-04-24 08:51:46 -0700117load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
118
119http_archive(
Googlere7634412017-06-07 14:52:50 -0400120 name = "gtest",
121 url = "https://github.com/google/googletest/archive/release-1.7.0.zip",
122 sha256 = "b58cb7547a28b2c718d1e38aee18a3659c9e3ff52440297e965f5edffe34b6d0",
Klaus Aehlige7008872019-04-24 08:51:46 -0700123 build_file = "@//:gtest.BUILD",
Googlere7634412017-06-07 14:52:50 -0400124)
125```
126
Klaus Aehlige7008872019-04-24 08:51:46 -0700127**NOTE:** If the destination already contains a `BUILD` file, you can leave
128out the `build_file` attribute.
Googlere7634412017-06-07 14:52:50 -0400129
130Then create `gtest.BUILD`, a `BUILD` file used to compile Google Test.
131Google Test has several "special" requirements that make its `cc_library` rule
132more complicated:
133
134* `googletest-release-1.7.0/src/gtest-all.cc` `#include`s all of the other
135 files in `googletest-release-1.7.0/src/`, so we need to exclude it from the
136 compile or we'll get link errors for duplicate symbols.
137
138* It uses header files that are relative to the
139`googletest-release-1.7.0/include/` directory (`"gtest/gtest.h"`), so we must
140add that directory to the include paths.
141
142* It needs to link in `pthread`, so we add that as a `linkopt`.
143
144The final rule therefore looks like this:
145
146```python
147cc_library(
148 name = "main",
149 srcs = glob(
150 ["googletest-release-1.7.0/src/*.cc"],
151 exclude = ["googletest-release-1.7.0/src/gtest-all.cc"]
152 ),
153 hdrs = glob([
154 "googletest-release-1.7.0/include/**/*.h",
155 "googletest-release-1.7.0/src/*.h"
156 ]),
157 copts = [
158 "-Iexternal/gtest/googletest-release-1.7.0/include"
159 ],
160 linkopts = ["-pthread"],
161 visibility = ["//visibility:public"],
162)
163```
164
165This is somewhat messy: everything is prefixed with `googletest-release-1.7.0`
Klaus Aehlige7008872019-04-24 08:51:46 -0700166as a byproduct of the archive's structure. You can make `http_archive` strip
Googlere7634412017-06-07 14:52:50 -0400167this prefix by adding the `strip_prefix` attribute:
168
169```python
Klaus Aehlige7008872019-04-24 08:51:46 -0700170load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
171
172http_archive(
Googlere7634412017-06-07 14:52:50 -0400173 name = "gtest",
174 url = "https://github.com/google/googletest/archive/release-1.7.0.zip",
175 sha256 = "b58cb7547a28b2c718d1e38aee18a3659c9e3ff52440297e965f5edffe34b6d0",
Emmanuel Gohf1520f72019-09-03 10:45:45 -0700176 build_file = "@//:gtest.BUILD",
Googlere7634412017-06-07 14:52:50 -0400177 strip_prefix = "googletest-release-1.7.0",
178)
179```
180
181Then `gtest.BUILD` would look like this:
182
183```python
184cc_library(
185 name = "main",
186 srcs = glob(
187 ["src/*.cc"],
188 exclude = ["src/gtest-all.cc"]
189 ),
190 hdrs = glob([
191 "include/**/*.h",
192 "src/*.h"
193 ]),
194 copts = ["-Iexternal/gtest/include"],
195 linkopts = ["-pthread"],
196 visibility = ["//visibility:public"],
197)
198```
199
200Now `cc_` rules can depend on `@gtest//:main`.
201
202## Writing and running C++ tests
203
204For example, we could create a test `./test/hello-test.cc` such as:
205
206```cpp
207#include "gtest/gtest.h"
208#include "lib/hello-greet.h"
209
210TEST(HelloTest, GetGreet) {
211 EXPECT_EQ(get_greet("Bazel"), "Hello Bazel");
212}
213```
214
215Then create `./test/BUILD` file for your tests:
216
217```python
218cc_test(
219 name = "hello-test",
220 srcs = ["hello-test.cc"],
221 copts = ["-Iexternal/gtest/include"],
222 deps = [
223 "@gtest//:main",
??2ea81ac2019-03-26 14:53:44 -0700224 "//main:hello-greet",
Googlere7634412017-06-07 14:52:50 -0400225 ],
226)
227```
228
229Note that in order to make `hello-greet` visible to `hello-test`, we have to add
??2ea81ac2019-03-26 14:53:44 -0700230`"//test:__pkg__",` to the `visibility` attribute in `./main/BUILD`.
Googlere7634412017-06-07 14:52:50 -0400231
232Now you can use `bazel test` to run the test.
233
234```
235bazel test test:hello-test
236```
237
238This produces the following output:
239
240```
241INFO: Found 1 test target...
242Target //test:hello-test up-to-date:
243 bazel-bin/test/hello-test
244INFO: Elapsed time: 4.497s, Critical Path: 2.53s
245//test:hello-test PASSED in 0.3s
246
247Executed 1 out of 1 tests: 1 test passes.
248```
249
250
251## Adding dependencies on precompiled libraries
252
253If you want to use a library of which you only have a compiled version (for
254example, headers and a `.so` file) wrap it in a `cc_library` rule:
255
256```python
257cc_library(
258 name = "mylib",
259 srcs = ["mylib.so"],
260 hdrs = ["mylib.h"],
261)
262```
263
264This way, other C++ targets in your workspace can depend on this rule.