blob: 0d83a56f203792255342203fc12384219898979f [file] [log] [blame] [view]
fweacae1cd2022-02-17 09:45:38 -08001Project: /_project.yaml
2Book: /_book.yaml
3
4# Common C++ Build Use Cases
5
Googler3b9ed6e2022-11-08 02:19:42 -08006{% include "_buttons.html" %}
7
fweacae1cd2022-02-17 09:45:38 -08008Here you will find some of the most common use cases for building C++ projects
9with Bazel. If you have not done so already, get started with building C++
10projects with Bazel by completing the tutorial
Googler0c4cf502022-12-19 13:43:43 -080011[Introduction to Bazel: Build a C++ Project](/start/cpp).
fweacae1cd2022-02-17 09:45:38 -080012
13For information on cc_library and hdrs header files, see
14<a href="/reference/be/c-cpp#cc_library">cc_library</a>.
15
16## Including multiple files in a target {:#multiple-files-target}
17
18You can include multiple files in a single target with
19<a href="/reference/be/functions#glob">glob</a>.
20For example:
21
22```python
23cc_library(
24 name = "build-all-the-files",
25 srcs = glob(["*.cc"]),
26 hdrs = glob(["*.h"]),
27)
28```
29
30With this target, Bazel will build all the `.cc` and `.h` files it finds in the
31same directory as the `BUILD` file that contains this target (excluding
32subdirectories).
33
34## Using transitive includes {:#transitive-includes}
35
36If a file includes a header, then any rule with that file as a source (that is,
37having that file in the `srcs`, `hdrs`, or `textual_hdrs` attribute) should
38depend on the included header's library rule. Conversely, only direct
39dependencies need to be specified as dependencies. For example, suppose
40`sandwich.h` includes `bread.h` and `bread.h` includes `flour.h`. `sandwich.h`
41doesn't include `flour.h` (who wants flour in their sandwich?), so the `BUILD`
42file would look like this:
43
44```python
45cc_library(
46 name = "sandwich",
47 srcs = ["sandwich.cc"],
48 hdrs = ["sandwich.h"],
49 deps = [":bread"],
50)
51
52cc_library(
53 name = "bread",
54 srcs = ["bread.cc"],
55 hdrs = ["bread.h"],
56 deps = [":flour"],
57)
58
59cc_library(
60 name = "flour",
61 srcs = ["flour.cc"],
62 hdrs = ["flour.h"],
63)
64```
65
66Here, the `sandwich` library depends on the `bread` library, which depends
67on the `flour` library.
68
69## Adding include paths {:#add-include-paths}
70
71Sometimes you cannot (or do not want to) root include paths at the workspace
72root. Existing libraries might already have an include directory that doesn't
73match its path in your workspace. For example, suppose you have the following
74directory structure:
75
76```
77└── my-project
78 ├── legacy
79 │   └── some_lib
80 │   ├── BUILD
81 │   ├── include
82 │   │   └── some_lib.h
83 │   └── some_lib.cc
84 └── WORKSPACE
85```
86
87Bazel will expect `some_lib.h` to be included as
88`legacy/some_lib/include/some_lib.h`, but suppose `some_lib.cc` includes
89`"some_lib.h"`. To make that include path valid,
90`legacy/some_lib/BUILD` will need to specify that the `some_lib/include`
91directory is an include directory:
92
93```python
94cc_library(
95 name = "some_lib",
96 srcs = ["some_lib.cc"],
97 hdrs = ["include/some_lib.h"],
98 copts = ["-Ilegacy/some_lib/include"],
99)
100```
101
102This is especially useful for external dependencies, as their header files
103must otherwise be included with a `/` prefix.
104
shingt91239852023-11-28 10:48:01 -0800105## Include external libraries {:#include-external-libraries}
fweacae1cd2022-02-17 09:45:38 -0800106
shingt91239852023-11-28 10:48:01 -0800107Suppose you are using [Google Test](https://github.com/google/googletest)
108{: .external}.
fweacae1cd2022-02-17 09:45:38 -0800109You can use one of the repository functions in the `WORKSPACE` file to
110download Google Test and make it available in your repository:
111
112```python
113load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
114
115http_archive(
116 name = "gtest",
117 url = "https://github.com/google/googletest/archive/release-1.10.0.zip",
118 sha256 = "94c634d499558a76fa649edb13721dce6e98fb1e7018dfaeba3cd7a083945e91",
119 build_file = "@//:gtest.BUILD",
120)
121```
122
123Note: If the destination already contains a `BUILD` file, you can leave
124out the `build_file` attribute.
125
126Then create `gtest.BUILD`, a `BUILD` file used to compile Google Test.
127Google Test has several "special" requirements that make its `cc_library` rule
128more complicated:
129
shingt91239852023-11-28 10:48:01 -0800130* `googletest-release-1.10.0/googletest/src/gtest-all.cc` `#include`s all other
131 files in `googletest-release-1.10.0/googletest/src/`: exclude it from the
fweacae1cd2022-02-17 09:45:38 -0800132 compile to prevent link errors for duplicate symbols.
133
134* It uses header files that are relative to the
shingt91239852023-11-28 10:48:01 -0800135`googletest-release-1.10.0/googletest/include/` directory (`"gtest/gtest.h"`),
136 so you must add that directory to the include paths.
fweacae1cd2022-02-17 09:45:38 -0800137
138* It needs to link in `pthread`, so add that as a `linkopt`.
139
140The final rule therefore looks like this:
141
142```python
143cc_library(
144 name = "main",
145 srcs = glob(
shingt91239852023-11-28 10:48:01 -0800146 ["googletest-release-1.10.0/googletest/src/*.cc"],
147 exclude = ["googletest-release-1.10.0/googletest/src/gtest-all.cc"]
fweacae1cd2022-02-17 09:45:38 -0800148 ),
149 hdrs = glob([
shingt91239852023-11-28 10:48:01 -0800150 "googletest-release-1.10.0/googletest/include/**/*.h",
151 "googletest-release-1.10.0/googletest/src/*.h"
fweacae1cd2022-02-17 09:45:38 -0800152 ]),
153 copts = [
shingt91239852023-11-28 10:48:01 -0800154 "-Iexternal/gtest/googletest-release-1.10.0/googletest/include",
155 "-Iexternal/gtest/googletest-release-1.10.0/googletest"
fweacae1cd2022-02-17 09:45:38 -0800156 ],
157 linkopts = ["-pthread"],
158 visibility = ["//visibility:public"],
159)
160```
161
162This is somewhat messy: everything is prefixed with `googletest-release-1.10.0`
163as a byproduct of the archive's structure. You can make `http_archive` strip
164this prefix by adding the `strip_prefix` attribute:
165
166```python
167load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
168
169http_archive(
170 name = "gtest",
171 url = "https://github.com/google/googletest/archive/release-1.10.0.zip",
172 sha256 = "94c634d499558a76fa649edb13721dce6e98fb1e7018dfaeba3cd7a083945e91",
173 build_file = "@//:gtest.BUILD",
174 strip_prefix = "googletest-release-1.10.0",
175)
176```
177
178Then `gtest.BUILD` would look like this:
179
180```python
181cc_library(
182 name = "main",
183 srcs = glob(
shingt91239852023-11-28 10:48:01 -0800184 ["googletest/src/*.cc"],
185 exclude = ["googletest/src/gtest-all.cc"]
fweacae1cd2022-02-17 09:45:38 -0800186 ),
187 hdrs = glob([
shingt91239852023-11-28 10:48:01 -0800188 "googletest/include/**/*.h",
189 "googletest/src/*.h"
fweacae1cd2022-02-17 09:45:38 -0800190 ]),
shingt91239852023-11-28 10:48:01 -0800191 copts = [
192 "-Iexternal/gtest/googletest/include",
193 "-Iexternal/gtest/googletest",
194 ],
fweacae1cd2022-02-17 09:45:38 -0800195 linkopts = ["-pthread"],
196 visibility = ["//visibility:public"],
197)
198```
199
200Now `cc_` rules can depend on `@gtest//:main`.
201
202## Writing and running C++ tests {:#run-c-tests}
203
204For example, you could create a test `./test/hello-test.cc`, such as:
205
206```cpp
207#include "gtest/gtest.h"
208#include "main/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"],
shingt91239852023-11-28 10:48:01 -0800221 copts = [
222 "-Iexternal/gtest/googletest/include",
223 "-Iexternal/gtest/googletest",
224 ],
fweacae1cd2022-02-17 09:45:38 -0800225 deps = [
226 "@gtest//:main",
227 "//main:hello-greet",
228 ],
229)
230```
231
232To make `hello-greet` visible to `hello-test`, you must add
233`"//test:__pkg__",` to the `visibility` attribute in `./main/BUILD`.
234
235Now you can use `bazel test` to run the test.
236
237```
238bazel test test:hello-test
239```
240
241This produces the following output:
242
243```
244INFO: Found 1 test target...
245Target //test:hello-test up-to-date:
246 bazel-bin/test/hello-test
247INFO: Elapsed time: 4.497s, Critical Path: 2.53s
248//test:hello-test PASSED in 0.3s
249
250Executed 1 out of 1 tests: 1 test passes.
251```
252
253
254## Adding dependencies on precompiled libraries {:#precompiled-libraries}
255
256If you want to use a library of which you only have a compiled version (for
257example, headers and a `.so` file) wrap it in a `cc_library` rule:
258
259```python
260cc_library(
261 name = "mylib",
262 srcs = ["mylib.so"],
263 hdrs = ["mylib.h"],
264)
265```
266
267This way, other C++ targets in your workspace can depend on this rule.