blob: 07f42f458fa18f638a6129890d0cdfd72e6c697a [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",
32 srcs = glob(["*.cc"])
33 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
83 ├── third_party
84 │   └── 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
93`third_party/some_lib/include/some_lib.h`, but suppose `some_lib.cc` includes
94`"include/some_lib.h"`. To make that include path valid,
95`third_party/some_lib/BUILD` will need to specify that the `some_lib/`
96directory is an include directory:
97
98```python
99cc_library(
100 name = "some_lib",
101 srcs = ["some_lib.cc"],
102 hdrs = ["some_lib.h"],
103 copts = ["-Ithird_party/some_lib"],
104)
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
113can use one of the `new_` repository functions in the `WORKSPACE` file to
114download Google Test and make it available in your repository:
115
116```python
117new_http_archive(
118 name = "gtest",
119 url = "https://github.com/google/googletest/archive/release-1.7.0.zip",
120 sha256 = "b58cb7547a28b2c718d1e38aee18a3659c9e3ff52440297e965f5edffe34b6d0",
121 build_file = "gtest.BUILD",
122)
123```
124
125**NOTE:** If the destination already contains a `BUILD` file, you can use one of
126the `non-new_` functions.
127
128Then create `gtest.BUILD`, a `BUILD` file used to compile Google Test.
129Google Test has several "special" requirements that make its `cc_library` rule
130more complicated:
131
132* `googletest-release-1.7.0/src/gtest-all.cc` `#include`s all of the other
133 files in `googletest-release-1.7.0/src/`, so we need to exclude it from the
134 compile or we'll get link errors for duplicate symbols.
135
136* It uses header files that are relative to the
137`googletest-release-1.7.0/include/` directory (`"gtest/gtest.h"`), so we must
138add that directory to the include paths.
139
140* It needs to link in `pthread`, so we add that as a `linkopt`.
141
142The final rule therefore looks like this:
143
144```python
145cc_library(
146 name = "main",
147 srcs = glob(
148 ["googletest-release-1.7.0/src/*.cc"],
149 exclude = ["googletest-release-1.7.0/src/gtest-all.cc"]
150 ),
151 hdrs = glob([
152 "googletest-release-1.7.0/include/**/*.h",
153 "googletest-release-1.7.0/src/*.h"
154 ]),
155 copts = [
156 "-Iexternal/gtest/googletest-release-1.7.0/include"
157 ],
158 linkopts = ["-pthread"],
159 visibility = ["//visibility:public"],
160)
161```
162
163This is somewhat messy: everything is prefixed with `googletest-release-1.7.0`
164as a byproduct of the archive's structure. You can make `new_http_archive` strip
165this prefix by adding the `strip_prefix` attribute:
166
167```python
168new_http_archive(
169 name = "gtest",
170 url = "https://github.com/google/googletest/archive/release-1.7.0.zip",
171 sha256 = "b58cb7547a28b2c718d1e38aee18a3659c9e3ff52440297e965f5edffe34b6d0",
172 build_file = "gtest.BUILD",
173 strip_prefix = "googletest-release-1.7.0",
174)
175```
176
177Then `gtest.BUILD` would look like this:
178
179```python
180cc_library(
181 name = "main",
182 srcs = glob(
183 ["src/*.cc"],
184 exclude = ["src/gtest-all.cc"]
185 ),
186 hdrs = glob([
187 "include/**/*.h",
188 "src/*.h"
189 ]),
190 copts = ["-Iexternal/gtest/include"],
191 linkopts = ["-pthread"],
192 visibility = ["//visibility:public"],
193)
194```
195
196Now `cc_` rules can depend on `@gtest//:main`.
197
198## Writing and running C++ tests
199
200For example, we could create a test `./test/hello-test.cc` such as:
201
202```cpp
203#include "gtest/gtest.h"
204#include "lib/hello-greet.h"
205
206TEST(HelloTest, GetGreet) {
207 EXPECT_EQ(get_greet("Bazel"), "Hello Bazel");
208}
209```
210
211Then create `./test/BUILD` file for your tests:
212
213```python
214cc_test(
215 name = "hello-test",
216 srcs = ["hello-test.cc"],
217 copts = ["-Iexternal/gtest/include"],
218 deps = [
219 "@gtest//:main",
220 "//lib:hello-greet",
221 ],
222)
223```
224
225Note that in order to make `hello-greet` visible to `hello-test`, we have to add
226`"//test:__pkg__",` to the `visibility` attribute in `./lib/BUILD`.
227
228Now you can use `bazel test` to run the test.
229
230```
231bazel test test:hello-test
232```
233
234This produces the following output:
235
236```
237INFO: Found 1 test target...
238Target //test:hello-test up-to-date:
239 bazel-bin/test/hello-test
240INFO: Elapsed time: 4.497s, Critical Path: 2.53s
241//test:hello-test PASSED in 0.3s
242
243Executed 1 out of 1 tests: 1 test passes.
244```
245
246
247## Adding dependencies on precompiled libraries
248
249If you want to use a library of which you only have a compiled version (for
250example, headers and a `.so` file) wrap it in a `cc_library` rule:
251
252```python
253cc_library(
254 name = "mylib",
255 srcs = ["mylib.so"],
256 hdrs = ["mylib.h"],
257)
258```
259
260This way, other C++ targets in your workspace can depend on this rule.
261