Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 1 | --- |
| 2 | layout: documentation |
| 3 | title: Common C++ Build Use Cases |
| 4 | --- |
| 5 | |
Googler | 1403a16 | 2021-01-22 13:42:50 -0800 | [diff] [blame] | 6 | # Common C++ Build Use Cases |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 7 | |
| 8 | Here you will find some of the most common use cases for building C++ projects |
| 9 | with Bazel. If you have not done so already, get started with building C++ |
| 10 | projects with Bazel by completing the tutorial |
Googler | 032aab2 | 2017-09-14 16:49:56 +0200 | [diff] [blame] | 11 | [Introduction to Bazel: Build a C++ Project](tutorial/cpp.html). |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 12 | |
Googler | ab19d44 | 2021-04-21 14:38:23 -0700 | [diff] [blame] | 13 | For information on cc_library and hdrs header files, see |
| 14 | <a href="be/c-cpp.html#cc_library">cc_library</a>. |
| 15 | |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 16 | ## Including multiple files in a target |
| 17 | |
| 18 | You can include multiple files in a single target with |
dzc | 205125b | 2017-06-26 11:01:47 +0200 | [diff] [blame] | 19 | [glob](../be/functions.html#glob). |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 20 | For example: |
| 21 | |
| 22 | ```python |
| 23 | cc_library( |
| 24 | name = "build-all-the-files", |
steple | a6b2a1b | 2018-08-10 07:03:27 -0700 | [diff] [blame] | 25 | srcs = glob(["*.cc"]), |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 26 | hdrs = glob(["*.h"]), |
| 27 | ) |
| 28 | ``` |
| 29 | |
| 30 | With this target, Bazel will build all the `.cc` and `.h` files it finds in the |
| 31 | same directory as the `BUILD` file that contains this target (excluding |
| 32 | subdirectories). |
| 33 | |
| 34 | ## Using transitive includes |
| 35 | |
| 36 | If a file includes a header, then the file's rule should depend on that header's |
Googler | 25a3c7d | 2021-01-22 18:38:49 +0100 | [diff] [blame] | 37 | library. Conversely, only direct dependencies need to be specified as |
| 38 | dependencies. For example, suppose `sandwich.h` includes `bread.h` and |
| 39 | `bread.h` includes `flour.h`. `sandwich.h` doesn't include `flour.h` (who wants |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 40 | flour in their sandwich?), so the `BUILD` file would look like this: |
| 41 | |
| 42 | ```python |
| 43 | cc_library( |
| 44 | name = "sandwich", |
| 45 | srcs = ["sandwich.cc"], |
| 46 | hdrs = ["sandwich.h"], |
| 47 | deps = [":bread"], |
| 48 | ) |
| 49 | |
| 50 | cc_library( |
| 51 | name = "bread", |
| 52 | srcs = ["bread.cc"], |
| 53 | hdrs = ["bread.h"], |
| 54 | deps = [":flour"], |
| 55 | ) |
| 56 | |
| 57 | cc_library( |
| 58 | name = "flour", |
| 59 | srcs = ["flour.cc"], |
| 60 | hdrs = ["flour.h"], |
| 61 | ) |
| 62 | ``` |
| 63 | |
| 64 | Here, the `sandwich` library depends on the `bread` library, which depends |
| 65 | on the `flour` library. |
| 66 | |
| 67 | ## Adding include paths |
| 68 | |
| 69 | Sometimes you cannot (or do not want to) root include paths at the workspace |
| 70 | root. Existing libraries might already have an include directory that doesn't |
Googler | 25a3c7d | 2021-01-22 18:38:49 +0100 | [diff] [blame] | 71 | match its path in your workspace. For example, suppose you have the following |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 72 | directory structure: |
| 73 | |
| 74 | ``` |
| 75 | └── my-project |
aolivas | 7cbe1fb | 2018-03-09 05:13:17 -0800 | [diff] [blame] | 76 | ├── legacy |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 77 | │ └── some_lib |
| 78 | │ ├── BUILD |
| 79 | │ ├── include |
| 80 | │ │ └── some_lib.h |
| 81 | │ └── some_lib.cc |
| 82 | └── WORKSPACE |
| 83 | ``` |
| 84 | |
| 85 | Bazel will expect `some_lib.h` to be included as |
aolivas | 7cbe1fb | 2018-03-09 05:13:17 -0800 | [diff] [blame] | 86 | `legacy/some_lib/include/some_lib.h`, but suppose `some_lib.cc` includes |
Googler | 25a3c7d | 2021-01-22 18:38:49 +0100 | [diff] [blame] | 87 | `"some_lib.h"`. To make that include path valid, |
Daniel Martn | 61191f4 | 2019-10-17 02:54:56 -0700 | [diff] [blame] | 88 | `legacy/some_lib/BUILD` will need to specify that the `some_lib/include` |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 89 | directory is an include directory: |
| 90 | |
| 91 | ```python |
| 92 | cc_library( |
| 93 | name = "some_lib", |
| 94 | srcs = ["some_lib.cc"], |
aolivas | 7cbe1fb | 2018-03-09 05:13:17 -0800 | [diff] [blame] | 95 | hdrs = ["include/some_lib.h"], |
| 96 | copts = ["-Ilegacy/some_lib/include"], |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 97 | ) |
| 98 | ``` |
| 99 | |
| 100 | This is especially useful for external dependencies, as their header files |
| 101 | must otherwise be included with a `/` prefix. |
| 102 | |
| 103 | ## Including external libraries |
| 104 | |
| 105 | Suppose you are using [Google Test](https://github.com/google/googletest). You |
Klaus Aehlig | e700887 | 2019-04-24 08:51:46 -0700 | [diff] [blame] | 106 | can use one of the repository functions in the `WORKSPACE` file to |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 107 | download Google Test and make it available in your repository: |
| 108 | |
| 109 | ```python |
Klaus Aehlig | e700887 | 2019-04-24 08:51:46 -0700 | [diff] [blame] | 110 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") |
| 111 | |
| 112 | http_archive( |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 113 | name = "gtest", |
ranjanih | 15cfe70 | 2021-03-10 14:15:28 -0800 | [diff] [blame] | 114 | url = "https://github.com/google/googletest/archive/release-1.10.0.zip", |
| 115 | sha256 = "94c634d499558a76fa649edb13721dce6e98fb1e7018dfaeba3cd7a083945e91", |
Klaus Aehlig | e700887 | 2019-04-24 08:51:46 -0700 | [diff] [blame] | 116 | build_file = "@//:gtest.BUILD", |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 117 | ) |
| 118 | ``` |
| 119 | |
Klaus Aehlig | e700887 | 2019-04-24 08:51:46 -0700 | [diff] [blame] | 120 | **NOTE:** If the destination already contains a `BUILD` file, you can leave |
| 121 | out the `build_file` attribute. |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 122 | |
| 123 | Then create `gtest.BUILD`, a `BUILD` file used to compile Google Test. |
| 124 | Google Test has several "special" requirements that make its `cc_library` rule |
| 125 | more complicated: |
| 126 | |
ranjanih | 15cfe70 | 2021-03-10 14:15:28 -0800 | [diff] [blame] | 127 | * `googletest-release-1.10.0/src/gtest-all.cc` `#include`s all other |
| 128 | files in `googletest-release-1.10.0/src/`: exclude it from the |
Googler | 480d83e | 2021-02-26 17:24:10 -0800 | [diff] [blame] | 129 | compile to prevent link errors for duplicate symbols. |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 130 | |
| 131 | * It uses header files that are relative to the |
ranjanih | 15cfe70 | 2021-03-10 14:15:28 -0800 | [diff] [blame] | 132 | `googletest-release-1.10.0/include/` directory (`"gtest/gtest.h"`), so you must |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 133 | add that directory to the include paths. |
| 134 | |
Googler | 480d83e | 2021-02-26 17:24:10 -0800 | [diff] [blame] | 135 | * It needs to link in `pthread`, so add that as a `linkopt`. |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 136 | |
| 137 | The final rule therefore looks like this: |
| 138 | |
| 139 | ```python |
| 140 | cc_library( |
| 141 | name = "main", |
| 142 | srcs = glob( |
ranjanih | 15cfe70 | 2021-03-10 14:15:28 -0800 | [diff] [blame] | 143 | ["googletest-release-1.10.0/src/*.cc"], |
| 144 | exclude = ["googletest-release-1.10.0/src/gtest-all.cc"] |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 145 | ), |
| 146 | hdrs = glob([ |
ranjanih | 15cfe70 | 2021-03-10 14:15:28 -0800 | [diff] [blame] | 147 | "googletest-release-1.10.0/include/**/*.h", |
| 148 | "googletest-release-1.10.0/src/*.h" |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 149 | ]), |
| 150 | copts = [ |
ranjanih | 15cfe70 | 2021-03-10 14:15:28 -0800 | [diff] [blame] | 151 | "-Iexternal/gtest/googletest-release-1.10.0/include", |
| 152 | "-Iexternal/gtest/googletest-release-1.10.0" |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 153 | ], |
| 154 | linkopts = ["-pthread"], |
| 155 | visibility = ["//visibility:public"], |
| 156 | ) |
| 157 | ``` |
| 158 | |
ranjanih | 15cfe70 | 2021-03-10 14:15:28 -0800 | [diff] [blame] | 159 | This is somewhat messy: everything is prefixed with `googletest-release-1.10.0` |
Klaus Aehlig | e700887 | 2019-04-24 08:51:46 -0700 | [diff] [blame] | 160 | as a byproduct of the archive's structure. You can make `http_archive` strip |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 161 | this prefix by adding the `strip_prefix` attribute: |
| 162 | |
| 163 | ```python |
Klaus Aehlig | e700887 | 2019-04-24 08:51:46 -0700 | [diff] [blame] | 164 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") |
| 165 | |
| 166 | http_archive( |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 167 | name = "gtest", |
ranjanih | 15cfe70 | 2021-03-10 14:15:28 -0800 | [diff] [blame] | 168 | url = "https://github.com/google/googletest/archive/release-1.10.0.zip", |
| 169 | sha256 = "94c634d499558a76fa649edb13721dce6e98fb1e7018dfaeba3cd7a083945e91", |
Emmanuel Goh | f1520f7 | 2019-09-03 10:45:45 -0700 | [diff] [blame] | 170 | build_file = "@//:gtest.BUILD", |
ranjanih | 15cfe70 | 2021-03-10 14:15:28 -0800 | [diff] [blame] | 171 | strip_prefix = "googletest-release-1.10.0", |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 172 | ) |
| 173 | ``` |
| 174 | |
| 175 | Then `gtest.BUILD` would look like this: |
| 176 | |
| 177 | ```python |
| 178 | cc_library( |
| 179 | name = "main", |
| 180 | srcs = glob( |
| 181 | ["src/*.cc"], |
| 182 | exclude = ["src/gtest-all.cc"] |
| 183 | ), |
| 184 | hdrs = glob([ |
| 185 | "include/**/*.h", |
| 186 | "src/*.h" |
| 187 | ]), |
| 188 | copts = ["-Iexternal/gtest/include"], |
| 189 | linkopts = ["-pthread"], |
| 190 | visibility = ["//visibility:public"], |
| 191 | ) |
| 192 | ``` |
| 193 | |
| 194 | Now `cc_` rules can depend on `@gtest//:main`. |
| 195 | |
| 196 | ## Writing and running C++ tests |
| 197 | |
Googler | 480d83e | 2021-02-26 17:24:10 -0800 | [diff] [blame] | 198 | For example, you could create a test `./test/hello-test.cc`, such as: |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 199 | |
| 200 | ```cpp |
| 201 | #include "gtest/gtest.h" |
ranjanih | 36d69ae | 2021-04-22 06:18:09 -0700 | [diff] [blame^] | 202 | #include "main/hello-greet.h" |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 203 | |
| 204 | TEST(HelloTest, GetGreet) { |
| 205 | EXPECT_EQ(get_greet("Bazel"), "Hello Bazel"); |
| 206 | } |
| 207 | ``` |
| 208 | |
| 209 | Then create `./test/BUILD` file for your tests: |
| 210 | |
| 211 | ```python |
| 212 | cc_test( |
| 213 | name = "hello-test", |
| 214 | srcs = ["hello-test.cc"], |
| 215 | copts = ["-Iexternal/gtest/include"], |
| 216 | deps = [ |
| 217 | "@gtest//:main", |
?? | 2ea81ac | 2019-03-26 14:53:44 -0700 | [diff] [blame] | 218 | "//main:hello-greet", |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 219 | ], |
| 220 | ) |
| 221 | ``` |
| 222 | |
Googler | 480d83e | 2021-02-26 17:24:10 -0800 | [diff] [blame] | 223 | To make `hello-greet` visible to `hello-test`, you must add |
?? | 2ea81ac | 2019-03-26 14:53:44 -0700 | [diff] [blame] | 224 | `"//test:__pkg__",` to the `visibility` attribute in `./main/BUILD`. |
Googler | e763441 | 2017-06-07 14:52:50 -0400 | [diff] [blame] | 225 | |
| 226 | Now you can use `bazel test` to run the test. |
| 227 | |
| 228 | ``` |
| 229 | bazel test test:hello-test |
| 230 | ``` |
| 231 | |
| 232 | This produces the following output: |
| 233 | |
| 234 | ``` |
| 235 | INFO: Found 1 test target... |
| 236 | Target //test:hello-test up-to-date: |
| 237 | bazel-bin/test/hello-test |
| 238 | INFO: Elapsed time: 4.497s, Critical Path: 2.53s |
| 239 | //test:hello-test PASSED in 0.3s |
| 240 | |
| 241 | Executed 1 out of 1 tests: 1 test passes. |
| 242 | ``` |
| 243 | |
| 244 | |
| 245 | ## Adding dependencies on precompiled libraries |
| 246 | |
| 247 | If you want to use a library of which you only have a compiled version (for |
| 248 | example, headers and a `.so` file) wrap it in a `cc_library` rule: |
| 249 | |
| 250 | ```python |
| 251 | cc_library( |
| 252 | name = "mylib", |
| 253 | srcs = ["mylib.so"], |
| 254 | hdrs = ["mylib.h"], |
| 255 | ) |
| 256 | ``` |
| 257 | |
| 258 | This way, other C++ targets in your workspace can depend on this rule. |