blob: 4fd38ba73088c279b9078fd3a8913debceb24478 [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 = [
Yuchen Daidde1dbe2020-02-21 06:20:25 -0800158 "-Iexternal/gtest/googletest-release-1.7.0/include",
159 "-Iexternal/gtest/googletest-release-1.7.0"
Googlere7634412017-06-07 14:52:50 -0400160 ],
161 linkopts = ["-pthread"],
162 visibility = ["//visibility:public"],
163)
164```
165
166This is somewhat messy: everything is prefixed with `googletest-release-1.7.0`
Klaus Aehlige7008872019-04-24 08:51:46 -0700167as a byproduct of the archive's structure. You can make `http_archive` strip
Googlere7634412017-06-07 14:52:50 -0400168this prefix by adding the `strip_prefix` attribute:
169
170```python
Klaus Aehlige7008872019-04-24 08:51:46 -0700171load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
172
173http_archive(
Googlere7634412017-06-07 14:52:50 -0400174 name = "gtest",
175 url = "https://github.com/google/googletest/archive/release-1.7.0.zip",
176 sha256 = "b58cb7547a28b2c718d1e38aee18a3659c9e3ff52440297e965f5edffe34b6d0",
Emmanuel Gohf1520f72019-09-03 10:45:45 -0700177 build_file = "@//:gtest.BUILD",
Googlere7634412017-06-07 14:52:50 -0400178 strip_prefix = "googletest-release-1.7.0",
179)
180```
181
182Then `gtest.BUILD` would look like this:
183
184```python
185cc_library(
186 name = "main",
187 srcs = glob(
188 ["src/*.cc"],
189 exclude = ["src/gtest-all.cc"]
190 ),
191 hdrs = glob([
192 "include/**/*.h",
193 "src/*.h"
194 ]),
195 copts = ["-Iexternal/gtest/include"],
196 linkopts = ["-pthread"],
197 visibility = ["//visibility:public"],
198)
199```
200
201Now `cc_` rules can depend on `@gtest//:main`.
202
203## Writing and running C++ tests
204
205For example, we could create a test `./test/hello-test.cc` such as:
206
207```cpp
208#include "gtest/gtest.h"
209#include "lib/hello-greet.h"
210
211TEST(HelloTest, GetGreet) {
212 EXPECT_EQ(get_greet("Bazel"), "Hello Bazel");
213}
214```
215
216Then create `./test/BUILD` file for your tests:
217
218```python
219cc_test(
220 name = "hello-test",
221 srcs = ["hello-test.cc"],
222 copts = ["-Iexternal/gtest/include"],
223 deps = [
224 "@gtest//:main",
??2ea81ac2019-03-26 14:53:44 -0700225 "//main:hello-greet",
Googlere7634412017-06-07 14:52:50 -0400226 ],
227)
228```
229
230Note that in order to make `hello-greet` visible to `hello-test`, we have to add
??2ea81ac2019-03-26 14:53:44 -0700231`"//test:__pkg__",` to the `visibility` attribute in `./main/BUILD`.
Googlere7634412017-06-07 14:52:50 -0400232
233Now you can use `bazel test` to run the test.
234
235```
236bazel test test:hello-test
237```
238
239This produces the following output:
240
241```
242INFO: Found 1 test target...
243Target //test:hello-test up-to-date:
244 bazel-bin/test/hello-test
245INFO: Elapsed time: 4.497s, Critical Path: 2.53s
246//test:hello-test PASSED in 0.3s
247
248Executed 1 out of 1 tests: 1 test passes.
249```
250
251
252## Adding dependencies on precompiled libraries
253
254If you want to use a library of which you only have a compiled version (for
255example, headers and a `.so` file) wrap it in a `cc_library` rule:
256
257```python
258cc_library(
259 name = "mylib",
260 srcs = ["mylib.so"],
261 hdrs = ["mylib.h"],
262)
263```
264
265This way, other C++ targets in your workspace can depend on this rule.