diff options
author | Googler <noreply@google.com> | 2017-06-07 14:52:50 -0400 |
---|---|---|
committer | John Cater <jcater@google.com> | 2017-06-08 10:52:38 -0400 |
commit | e763441482b98d4b4fc2d5e81e8939aed68a536c (patch) | |
tree | 971184abf3c2b219c622cac451772ded8e4fcb37 /site/docs | |
parent | b343cda7ebf327f947479ac1c204a2b2ac766748 (diff) |
Update the C++ tutorial to match the format of the Java tutorial and to use a sample project hosted on GitHub.
This depends on pull request #23 in bazelbuild/examples which uploads the C++ sample project.
Staged at:
[]
[]
PiperOrigin-RevId: 158297121
Diffstat (limited to 'site/docs')
-rw-r--r-- | site/docs/tutorial/cpp-use-cases.md | 254 | ||||
-rw-r--r-- | site/docs/tutorial/cpp.md | 572 |
2 files changed, 534 insertions, 292 deletions
diff --git a/site/docs/tutorial/cpp-use-cases.md b/site/docs/tutorial/cpp-use-cases.md new file mode 100644 index 0000000000..1c3af2ca60 --- /dev/null +++ b/site/docs/tutorial/cpp-use-cases.md @@ -0,0 +1,254 @@ +--- +layout: documentation +title: Common C++ Build Use Cases +--- + +Introduction to Bazel: Common C++ Build Use Cases +========== + +Here you will find some of the most common use cases for building C++ projects +with Bazel. If you have not done so already, get started with building C++ +projects with Bazel by completing the tutorial +[Introduction to Bazel: Build a C++ Project](cpp.md). + +[TOC] + +## Including multiple files in a target + +You can include multiple files in a single target with +[glob](https://bazel.build/versions/master/docs/be/functions.html#glob). +For example: + +```python +cc_library( + name = "build-all-the-files", + srcs = glob(["*.cc"]) + hdrs = glob(["*.h"]), +) +``` + +With this target, Bazel will build all the `.cc` and `.h` files it finds in the +same directory as the `BUILD` file that contains this target (excluding +subdirectories). + +## Using transitive includes + +If a file includes a header, then the file's rule should depend on that header's +library. Conversely, only direct dependencies need to be specified as +dependencies. For example, suppose `sandwich.h` includes `bread.h` and +`bread.h` includes `flour.h`. `sandwich.h` doesn't include `flour.h` (who wants +flour in their sandwich?), so the `BUILD` file would look like this: + +```python +cc_library( + name = "sandwich", + srcs = ["sandwich.cc"], + hdrs = ["sandwich.h"], + deps = [":bread"], +) + +cc_library( + name = "bread", + srcs = ["bread.cc"], + hdrs = ["bread.h"], + deps = [":flour"], +) + +cc_library( + name = "flour", + srcs = ["flour.cc"], + hdrs = ["flour.h"], +) +``` + +Here, the `sandwich` library depends on the `bread` library, which depends +on the `flour` library. + +## Adding include paths + +Sometimes you cannot (or do not want to) root include paths at the workspace +root. Existing libraries might already have an include directory that doesn't +match its path in your workspace. For example, suppose you have the following +directory structure: + +``` +└── my-project + ├── third_party + │ └── some_lib + │ ├── BUILD + │ ├── include + │ │ └── some_lib.h + │ └── some_lib.cc + └── WORKSPACE +``` + +Bazel will expect `some_lib.h` to be included as +`third_party/some_lib/include/some_lib.h`, but suppose `some_lib.cc` includes +`"include/some_lib.h"`. To make that include path valid, +`third_party/some_lib/BUILD` will need to specify that the `some_lib/` +directory is an include directory: + +```python +cc_library( + name = "some_lib", + srcs = ["some_lib.cc"], + hdrs = ["some_lib.h"], + copts = ["-Ithird_party/some_lib"], +) +``` + +This is especially useful for external dependencies, as their header files +must otherwise be included with a `/` prefix. + +## Including external libraries + +Suppose you are using [Google Test](https://github.com/google/googletest). You +can use one of the `new_` repository functions in the `WORKSPACE` file to +download Google Test and make it available in your repository: + +```python +new_http_archive( + name = "gtest", + url = "https://github.com/google/googletest/archive/release-1.7.0.zip", + sha256 = "b58cb7547a28b2c718d1e38aee18a3659c9e3ff52440297e965f5edffe34b6d0", + build_file = "gtest.BUILD", +) +``` + +**NOTE:** If the destination already contains a `BUILD` file, you can use one of +the `non-new_` functions. + +Then create `gtest.BUILD`, a `BUILD` file used to compile Google Test. +Google Test has several "special" requirements that make its `cc_library` rule +more complicated: + +* `googletest-release-1.7.0/src/gtest-all.cc` `#include`s all of the other + files in `googletest-release-1.7.0/src/`, so we need to exclude it from the + compile or we'll get link errors for duplicate symbols. + +* It uses header files that are relative to the +`googletest-release-1.7.0/include/` directory (`"gtest/gtest.h"`), so we must +add that directory to the include paths. + +* It needs to link in `pthread`, so we add that as a `linkopt`. + +The final rule therefore looks like this: + +```python +cc_library( + name = "main", + srcs = glob( + ["googletest-release-1.7.0/src/*.cc"], + exclude = ["googletest-release-1.7.0/src/gtest-all.cc"] + ), + hdrs = glob([ + "googletest-release-1.7.0/include/**/*.h", + "googletest-release-1.7.0/src/*.h" + ]), + copts = [ + "-Iexternal/gtest/googletest-release-1.7.0/include" + ], + linkopts = ["-pthread"], + visibility = ["//visibility:public"], +) +``` + +This is somewhat messy: everything is prefixed with `googletest-release-1.7.0` +as a byproduct of the archive's structure. You can make `new_http_archive` strip +this prefix by adding the `strip_prefix` attribute: + +```python +new_http_archive( + name = "gtest", + url = "https://github.com/google/googletest/archive/release-1.7.0.zip", + sha256 = "b58cb7547a28b2c718d1e38aee18a3659c9e3ff52440297e965f5edffe34b6d0", + build_file = "gtest.BUILD", + strip_prefix = "googletest-release-1.7.0", +) +``` + +Then `gtest.BUILD` would look like this: + +```python +cc_library( + name = "main", + srcs = glob( + ["src/*.cc"], + exclude = ["src/gtest-all.cc"] + ), + hdrs = glob([ + "include/**/*.h", + "src/*.h" + ]), + copts = ["-Iexternal/gtest/include"], + linkopts = ["-pthread"], + visibility = ["//visibility:public"], +) +``` + +Now `cc_` rules can depend on `@gtest//:main`. + +## Writing and running C++ tests + +For example, we could create a test `./test/hello-test.cc` such as: + +```cpp +#include "gtest/gtest.h" +#include "lib/hello-greet.h" + +TEST(HelloTest, GetGreet) { + EXPECT_EQ(get_greet("Bazel"), "Hello Bazel"); +} +``` + +Then create `./test/BUILD` file for your tests: + +```python +cc_test( + name = "hello-test", + srcs = ["hello-test.cc"], + copts = ["-Iexternal/gtest/include"], + deps = [ + "@gtest//:main", + "//lib:hello-greet", + ], +) +``` + +Note that in order to make `hello-greet` visible to `hello-test`, we have to add +`"//test:__pkg__",` to the `visibility` attribute in `./lib/BUILD`. + +Now you can use `bazel test` to run the test. + +``` +bazel test test:hello-test +``` + +This produces the following output: + +``` +INFO: Found 1 test target... +Target //test:hello-test up-to-date: + bazel-bin/test/hello-test +INFO: Elapsed time: 4.497s, Critical Path: 2.53s +//test:hello-test PASSED in 0.3s + +Executed 1 out of 1 tests: 1 test passes. +``` + + +## Adding dependencies on precompiled libraries + +If you want to use a library of which you only have a compiled version (for +example, headers and a `.so` file) wrap it in a `cc_library` rule: + +```python +cc_library( + name = "mylib", + srcs = ["mylib.so"], + hdrs = ["mylib.h"], +) +``` + +This way, other C++ targets in your workspace can depend on this rule. + diff --git a/site/docs/tutorial/cpp.md b/site/docs/tutorial/cpp.md index 23362010ef..dbf90f5487 100644 --- a/site/docs/tutorial/cpp.md +++ b/site/docs/tutorial/cpp.md @@ -1,395 +1,383 @@ --- layout: documentation -title: Build C++ +title: Build Tutorial - C++ --- -Build C++ -========= +Introduction to Bazel: Build a C++ Project +========== -You can use Bazel to build your C++ application. In this tutorial you'll learn how to: +In this tutorial, you'll learn the basics of building C++ applications with +Bazel. You will set up your workspace and build a simple C++ project that +illustrates key Bazel concepts, such as targets and `BUILD` files. After +completing this tutorial, take a look at +[Common C++ Build Use Cases](cpp-use-cases.md) for information on more advanced +concepts such as writing and running C++ tests. -* Build your first C++ target -* Use external libraries -* Write and run C++ tests -* Use precompiled libraries +Estimated completion time: 30 minutes. -## Setting up your workspace +## What you'll learn -Suppose that you have an existing project in a directory, say, -`~/gitroot/my-project/`. Create an empty file at -`~/gitroot/my-project/WORKSPACE` to show Bazel where your project's root is. -We are going to create a small hello world project with the following directory structure: -{% highlight bash %} -└── my-project - ├── lib - │ ├── BUILD - │ ├── hello-greet.cc - │ └── hello-greet.h - ├── main - │ ├── BUILD - │ ├── hello-time.cc - │ ├── hello-time.h - │ └── hello-world.cc - └── WORKSPACE -{% endhighlight %} +In this tutorial you'll learn how to: -## Creating source files +* Build a target +* Visualize the project's dependencies +* Split the project into multiple targets and packages +* Control target visibility across packages +* Reference targets through labels -Using the following commands to create the necessary source files: -{% highlight bash %} -# If you're not already there, move to your workspace directory. -cd ~/gitroot/my-project -mkdir ./main -cat > main/hello-world.cc <<'EOF' +## Before you begin -#include "lib/hello-greet.h" -#include "main/hello-time.h" -#include <iostream> -#include <string> +To prepare for the tutorial, first [Install Bazel](/docs/install.md) if +you don't have it installed already. Then, retrieve the sample project from +Bazel's GitHub repository: -int main(int argc, char** argv) { - std::string who = "world"; - if (argc > 1) { - who = argv[1]; - } - std::cout << get_greet(who) <<std::endl; - print_localtime(); - return 0; -} -EOF - -cat > main/hello-time.h <<'EOF' - -#ifndef MAIN_HELLO_TIME_H_ -#define MAIN_HELLO_TIME_H_ - -void print_localtime(); - -#endif -EOF - -cat > main/hello-time.cc <<'EOF' +``` +git clone https://github.com/bazelbuild/examples/ +``` -#include "main/hello-time.h" -#include <ctime> -#include <iostream> +The sample project for this tutorial is in the `examples/cpp-tutorial` directory +and is structured as follows: -void print_localtime() { - std::time_t result = std::time(nullptr); - std::cout << std::asctime(std::localtime(&result)); -} -EOF -mkdir ./lib -cat > lib/hello-greet.h <<'EOF' +``` +examples +└── cpp-tutorial + ├──stage1 + │ └── main + │ ├── BUILD + │ ├── hello-world.cc + │ └── WORKSPACE + ├──stage2 + │ ├── main + │ │ ├── BUILD + │ │ ├── hello-world.cc + │ │ ├── hello-greet.cc + │ │ ├── hello-greet.h + │ └── WORKSPACE + └──stage3 + ├── main + │ ├── BUILD + │ ├── hello-world.cc + │ ├── hello-greet.cc + │ └── hello-greet.h + ├── lib + │ ├── BUILD + │ ├── hello-time.cc + │ └── hello-time.h + └── WORKSPACE +``` -#ifndef LIB_HELLO_GREET_H_ -#define LIB_HELLO_GREET_H_ +As you can see, there are three sets of files, each set representing a stage in +this tutorial. In the first stage, you will build a single target residing in a +single package. In the second stage, you will split your project into multiple +targets but keep it in a single package. In the third and final stage, you will +split your project into multiple packages and build it with multiple targets. -#include <string> +## Build with Bazel -std::string get_greet(const std::string &thing); +### Set up the workspace -#endif -EOF +Before you can build a project, you need to set up its workspace. A workspace is +a directory that holds your project's source files and Bazel's build outputs. It +also contains files that Bazel recognizes as special: -cat > lib/hello-greet.cc <<'EOF' +* The `WORKSPACE` file, which identifies the directory and its contents as a + Bazel workspace and lives at the root of the project's directory structure, -#include "lib/hello-greet.h" -#include <string> +* One or more `BUILD` files, which tell Bazel how to build different parts of + the project. (A directory within the workspace that contains a `BUILD` file + is a *package*. You will learn about packages later in this tutorial.) -std::string get_greet(const std::string& who) { - return "Hello " + who; -} -EOF -{% endhighlight %} +To designate a directory as a Bazel workspace, create an empty file named +`WORKSPACE` in that directory. -## Adding BUILD files +When Bazel builds the project, all inputs and dependencies must be in the same +workspace. Files residing in different workspaces are independent of one +another unless linked, which is beyond the scope of this tutorial. -As you can see from the source code, `main/hello-world.cc` needs to include both `lib/hello-greet.h` and `main/hello-time.h`. -First we create `lib/BUILD` for hello-greet.cc: - -{% highlight python %} -cc_library( - name = "hello-greet", - srcs = ["hello-greet.cc"], - hdrs = ["hello-greet.h"], - visibility = ["//main:__pkg__"], -) -{% endhighlight %} +### Understand the BUILD file -Note that `visibility = ["//main:__pkg__"]` indicates `hello-greet` is visible from `main/BUILD`. -Then we'd create the following `main/BUILD` file: +A `BUILD` file contains several different types of instructions for Bazel. +The most important type is the *build rule*, which tells Bazel how to build the +desired outputs, such as executable binaries or libraries. Each instance +of a build rule in the `BUILD` file is called a *target* and points to a +specific set of source files and dependencies. A target can also point to other +targets. -{% highlight python %} -cc_library( - name = "hello-time", - srcs = ["hello-time.cc"], - hdrs = ["hello-time.h"], -) +Take a look at the `BUILD` file in the `cpp-tutorial/stage1/main` directory: +``` cc_binary( name = "hello-world", srcs = ["hello-world.cc"], - deps = [ - ":hello-time", - "//lib:hello-greet", - ], ) -{% endhighlight %} +``` + +In our example, the `hello-world` target instantiates Bazel's built-in +[`cc_binary` rule](docs/be/c-cpp.html#cc_binary). The rule tells Bazel to build +a self-contained executable binary from the `hello-world.cc` source file with no +dependencies. -Note when depending on a target in the same package, we can just use `:hello-time`. -When the target is in other package, a full path from root should be used, like `//lib:hello-greet`. +The attributes in the target explicitly state its dependencies and options. +While the `name` attribute is mandatory, many are optional. For example, in the +`hello-greet` target, `name` is self-explanatory, and `srcs` specifies the +source file(s) from which Bazel builds the target. -Now you are ready to build your hello world C++ binary: +### Build the project -{% highlight bash %} -bazel build main:hello-world -{% endhighlight %} +Let's build your sample project. Change into the `cpp-tutorial/stage1` directory +and run the following command: + +``` +bazel build //main:hello-world +``` -This produces the following output: +Notice the target label - the `//main:` part is the location of our `BUILD` +file relative to the root of the workspace, and `hello-world` is what we named +that target in the `BUILD` file. (You will learn about target labels in more +detail at the end of this tutorial.) -{% highlight bash %} +Bazel produces output similar to the following: + +``` INFO: Found 1 target... Target //main:hello-world up-to-date: bazel-bin/main/hello-world -INFO: Elapsed time: 2.869s, Critical Path: 1.00s -{% endhighlight %} +INFO: Elapsed time: 2.267s, Critical Path: 0.25s +``` -{% highlight bash %} -./bazel-bin/main/hello-world -{% endhighlight %} +Congratulations, you just built your first Bazel target! Bazel places build +outputs in the `bazel-bin` directory at the root of the workspace. Browse +through its contents to get an idea for Bazel's output structure. -This produces the following output: +Now test your freshly built binary: -{% highlight bash %} -Hello world -Thu Jun 23 18:51:46 2016 -{% endhighlight %} +```shell +bazel-bin/main/hello-world +``` -{% highlight bash %} -./bazel-bin/main/hello-world Bazel -{% endhighlight %} +### Review the dependency graph -This produces the following output: +A successful build has all of its dependencies explicitly stated in the `BUILD` +file. Bazel uses those statements to create the project's dependency graph, +which enables accurate incremental builds. -{% highlight bash %} -Hello Bazel -Thu Jun 23 18:52:10 2016 -{% endhighlight %} +Let's visualize our sample project's dependencies. First, generate a text +representation of the dependency graph (run the command at the workspace root): -Congratulations, you've just built your first Bazel target! +``` +bazel query --nohost_deps --noimplicit_deps 'deps(//main:hello-world)' \ + --output graph +``` -## Transitive includes +The above command tells Bazel to look for all dependencies for the target +`//main:hello-world` (excluding host and implicit dependencies) and format the +output as a graph. -If a file includes a header, then the file's rule should depend on that header's -library. Conversely, only direct dependencies need to be specified as -dependencies. For example, suppose `sandwich.h` includes `bread.h` and -`bread.h` includes `flour.h`. `sandwich.h` doesn't include `flour.h` (who wants -flour in their sandwich?), so the BUILD file would look like: +Then, paste the text into [GraphViz](http://www.webgraphviz.com/). -```python -cc_library( - name = "sandwich", - srcs = ["sandwich.cc"], - hdrs = ["sandwich.h"], - deps = [":bread"], -) +As you can see, the first stage of the sample project has a single target that +that builds a single source file with no additional dependencies: + +![Dependency graph for 'hello-world'](/assets/cpp-tutorial-stage1.png) + +Now that you have set up your workspace, built your project, and examined its +dependencies, let's add some complexity. + +## Refine your Bazel build + +While a single target is sufficient for small projects, you may want to split +larger projects into multiple targets and packages to allow for fast incremental +builds (that is, only rebuild what's changed) and to speed up your builds by +building multiple parts of a project at once. + +### Specify multiple build targets +Let's split our sample project build into two targets. Take a look at the +`BUILD` file in the `cpp-tutorial/stage2/main` directory: + +``` cc_library( - name = "bread", - srcs = ["bread.cc"], - hdrs = ["bread.h"], - deps = [":flour"], + name = "hello-greet", + srcs = ["hello-greet.cc"], + hdrs = ["hello-greet.h"], ) -cc_library( - name = "flour", - srcs = ["flour.cc"], - hdrs = ["flour.h"], +cc_binary( + name = "hello-world", + srcs = ["hello-world.cc"], + deps = [ + ":hello-greet", + ], ) ``` -Here, the `sandwich` library depends on the `bread` library, which depends -on the `flour` library. - -## Adding include paths +With this `BUILD` file, Bazel first builds the `hello-greet` library +(using Bazel's built-in [`cc_library` rule](docs/be/c-cpp.html#cc_library), +then the `hello-world` binary. The `deps` attribute in the `hello-world` target +tells Bazel that the `hello-greet` library is required to build the `hello-world` +binary. -Sometimes you cannot (or do not want to) base include paths at the workspace -root. Existing libraries might already have a include directory that doesn't -match its path in your workspace. For example, suppose you have the following -directory structure: +Let's build this new version of our project. Change into the +`cpp-tutorial/stage2` directory and run the following command: ``` -└── my-project - ├── third_party - │ └── some_lib - │ ├── BUILD - │ ├── include - │ │ └── some_lib.h - │ └── some_lib.cc - └── WORKSPACE +bazel build //main:hello-world ``` -Bazel will expect `some_lib.h` to be included as -`third_party/some_lib/include/some_lib.h`, but suppose `some_lib.cc` includes -`"include/some_lib.h"`. To make that include path valid, -`third_party/some_lib/BUILD` will need to specify that the `some_lib/` -directory is an include directory: +Bazel produces output similar to the following: -```python -cc_library( - name = "some_lib", - srcs = ["some_lib.cc"], - hdrs = ["some_lib.h"], - copts = ["-Ithird_party/some_lib"], -) +``` +INFO: Found 1 target... +Target //main:hello-world up-to-date: + bazel-bin/main/hello-world +INFO: Elapsed time: 2.399s, Critical Path: 0.30s ``` -This is especially useful for external dependencies, as their header files -must otherwise be included with an `external/[repository-name]/` prefix. +Now test your freshly built binary: -## Including external libraries +``` +bazel-bin/main/hello-world +``` -Suppose you are using [Google Test](https://github.com/google/googletest). You -can use one of the `new_` repository functions in the `WORKSPACE` file to -download Google Test and make it available in your repository: +If you now modify `hello-greet.cc` and rebuild the project, Bazel will +only recompile that file. -```python -new_http_archive( - name = "gtest", - url = "https://github.com/google/googletest/archive/release-1.7.0.zip", - sha256 = "b58cb7547a28b2c718d1e38aee18a3659c9e3ff52440297e965f5edffe34b6d0", - build_file = "gtest.BUILD", -) -``` +Looking at the dependency graph, you can see that `hello-world` depends on the +same inputs as it did before, but the structure of the build is different: -Then create `gtest.BUILD`, a BUILD file to use to compile Google Test. -Google Test has several "special" requirements that make its `cc_library` rule -more complicated: +![Dependency graph for 'hello-world'](/assets/cpp-tutorial-stage2.png) -* `googletest-release-1.7.0/src/gtest-all.cc` `#include`s all of the other files in - `googletest-release-1.7.0/src/`, so we need to exclude it from the compile or we'll get - link errors for duplicate symbols. -* It uses header files that are relative to the `googletest-release-1.7.0/include/` directory - (`"gtest/gtest.h"`), so we must add that directory to the include paths. -* It needs to link in pthread, so we add that as a `linkopt`. +You've now built the project with two targets. The `hello-world` target builds +one source file and depends on one other target (`//main:hello-greet`), which +builds two additional source files. -The final rule looks like this: +### Use multiple packages + +Let’s now split the project into multiple packages. Take a look at the contents +of the `cpp-tutorial/stage3` directory: -```python -cc_library( - name = "main", - srcs = glob( - ["googletest-release-1.7.0/src/*.cc"], - exclude = ["googletest-release-1.7.0/src/gtest-all.cc"] - ), - hdrs = glob([ - "googletest-release-1.7.0/include/**/*.h", - "googletest-release-1.7.0/src/*.h" - ]), - copts = [ - "-Iexternal/gtest/googletest-release-1.7.0/include" - ], - linkopts = ["-pthread"], - visibility = ["//visibility:public"], -) ``` +└──stage3 + ├── main + │ ├── BUILD + │ ├── hello-world.cc + │ ├── hello-greet.cc + │ └── hello-greet.h + ├── lib + │ ├── BUILD + │ ├── hello-time.cc + │ └── hello-time.h + └── WORKSPACE +``` +Notice that we now have two sub-directories, and each contains a `BUILD` file. +Therefore, to Bazel, the workspace now contains two packages, `lib` and `main`. -This is somewhat messy: everything is prefixed with googletest-release-1.7.0 as a byproduct -of the archive's structure. You can make `new_http_archive` strip this prefix by -adding the `strip_prefix` attribute: +Take a look at the `lib/BUILD` file: ```python -new_http_archive( - name = "gtest", - url = "https://github.com/google/googletest/archive/release-1.7.0.zip", - sha256 = "b58cb7547a28b2c718d1e38aee18a3659c9e3ff52440297e965f5edffe34b6d0", - build_file = "gtest.BUILD", - strip_prefix = "googletest-release-1.7.0", +cc_library( + name = "hello-time", + srcs = ["hello-time.cc"], + hdrs = ["hello-time.h"], + visibility = ["//main:__pkg__"], ) ``` -Then `gtest.BUILD` would look like this: +And at the `main/BUILD` file: ```python cc_library( - name = "main", - srcs = glob( - ["src/*.cc"], - exclude = ["src/gtest-all.cc"] - ), - hdrs = glob([ - "include/**/*.h", - "src/*.h" - ]), - copts = ["-Iexternal/gtest/include"], - linkopts = ["-pthread"], - visibility = ["//visibility:public"], + name = "hello-greet", + srcs = ["hello-greet.cc"], + hdrs = ["hello-greet.h"], +) + +cc_binary( + name = "hello-world", + srcs = ["hello-world.cc"], + deps = [ + ":hello-greet", + "//lib:hello-time", + ], ) ``` -Now `cc_` rules can depend on `@gtest//:main`. +As you can see, the `hello-world` target in the `main` package depends on the +`hello-time` target in the `lib` package (hence the target label +`//lib:hello-time`) - Bazel knows this through the `deps` attribute. Take a look +at the dependency graph: -## Writing and running C++ tests +![Dependency graph for 'hello-world'](/assets/cpp-tutorial-stage3.png) -For example, we could create a test `./test/hello-test.cc` such as: +Notice that for the build to succeed, we make the `//lib:hello-time` target in +`lib/BUILD` explicitly visible to targets in `main/BUILD` using the `visibility` +attribute. This is because by default targets are only visible to other targets +in the same `BUILD` file. (Bazel uses target visibility to prevent issues such +as libraries containing implementation details leaking into public APIs.) -```cpp -#include "gtest/gtest.h" -#include "lib/hello-greet.h" +Let's build this final version of our project. Change into the +`cpp-tutorial/stage3` directory and run the following command: -TEST(HelloTest, GetGreet) { - EXPECT_EQ(get_greet("Bazel"), "Hello Bazel"); -} +``` +bazel build //main:hello-world ``` -Then create `./test/BUILD` file for your tests: +Bazel produces output similar to the following: -```python -cc_test( - name = "hello-test", - srcs = ["hello-test.cc"], - copts = ["-Iexternal/gtest/include"], - deps = [ - "@gtest//:main", - "//lib:hello-greet", - ], -) +``` +INFO: Found 1 target... +Target //main:hello-world up-to-date: + bazel-bin/main/hello-world +INFO: Elapsed time: 0.167s, Critical Path: 0.00s ``` -Note in order to make `hello-greet` visible to `hello-test`, we have to add `"//test:__pkg__",` to `visibility` attribute in `./lib/BUILD`. +Now test the freshly built binary: -Now you can use `bazel test` to run the test. +``` +bazel-bin/main/hello-world +``` -{% highlight bash %} -bazel test test:hello-test -{% endhighlight %} +You've now built the project as two packages with three targets and understand +the dependencies between them. -This produces the following output: +## Use labels to reference targets -{% highlight bash %} -INFO: Found 1 test target... -Target //test:hello-test up-to-date: - bazel-bin/test/hello-test -INFO: Elapsed time: 4.497s, Critical Path: 2.53s -//test:hello-test PASSED in 0.3s +In `BUILD` files and at the command line, Bazel uses *labels* to reference +targets - for example, `//main:hello-world` or `//lib:hello-time`. Their syntax +is: -Executed 1 out of 1 tests: 1 test passes. -{% endhighlight %} +``` +//path/to/package:target-name +``` +If the target is a rule target, then `path/to/package` is the path to the +directory containing the `BUILD` file, and `target-name` is what you named the +target in the `BUILD` file (the `name` attribute). If the target is a file +target, then `path/to/package` is the path to the root of the package, and +`target-name` is the name of the target file, including its full path. -## Adding dependencies on precompiled libraries +When referencing targets within the same package, you can skip the package path +and just use `//:target-name`. When referencing targets within the same `BUILD` +file, you can even skip the `//` workspace root identifier and just use +`:target-name`. -If you want to use a library that you only have a compiled version of (e.g., -headers and a .so) wrap it in a `cc_library` rule: -```python -cc_library( - name = "mylib", - srcs = ["mylib.so"], - hdrs = ["mylib.h"], -) -``` +## Further reading + +Congratulations! You now know the basics of building a C++ project with Bazel. +Next, read up on the most common [C++ build use cases](cpp-use-cases.md). Then, +check out the following: + +* [External Dependencies](https://bazel.build/versions/master/docs/external.html) + to learn more about working with local and remote repositories. + +* The [Build Encyclopedia](/docs/be/overview.html) to learn more about Bazel. + +* The [Java build tutorial](/docs/tutorial/java.md) to get started with + building Java applications with Bazel. + +* The [mobile application tutorial](/docs/tutorial/app.md) to get started with + building mobile applications for Android and iOS with Bazel. -Then other C++ targets in your workspace can depend on this rule. +Happy building! |