diff options
Diffstat (limited to 'site/docs/tutorial/cpp.md')
-rw-r--r-- | site/docs/tutorial/cpp.md | 360 |
1 files changed, 2 insertions, 358 deletions
diff --git a/site/docs/tutorial/cpp.md b/site/docs/tutorial/cpp.md index 3d6effbd68..6ec737f93d 100644 --- a/site/docs/tutorial/cpp.md +++ b/site/docs/tutorial/cpp.md @@ -1,360 +1,4 @@ --- -layout: documentation -title: Build C++ +layout: redirect +redirect: docs/tutorial/cpp.html --- - -Build C++ -========= - -You can use Bazel to build your C++ application. In this tutorial you'll learn how to: - -* Build your first C++ target -* Use external libraries -* Write and run C++ tests -* Use precompiled libraries - -## Setting up your workspace - -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 %} - -## Creating source files - -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' -#include "lib/hello-greet.h" -#include "main/hello-time.h" -#include <iostream> -#include <string> - -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' -#include "main/hello-time.h" -#include <ctime> -#include <iostream> - -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' -#ifndef LIB_HELLO_GREET_H_ -#define LIB_HELLO_GREET_H_ - -#include <string> - -std::string get_greet(const std::string &thing); - -#endif -EOF -$ cat > lib/hello-greet.cc <<'EOF' -#include "lib/hello-greet.h" -#include <string> - -std::string get_greet(const std::string& who) { - return "Hello " + who; -} -EOF -{% endhighlight %} - -## Adding BUILD files - -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 %} - -Note that `visibility = ["//main:__pkg__"]` indicates `hello-greet` is visible from `main/BUILD`. -Then we'd create the following `main/BUILD` file: - -{% highlight python %} -cc_library( - name = "hello-time", - srcs = ["hello-time.cc"], - hdrs = ["hello-time.h"], -) - -cc_binary( - name = "hello-world", - srcs = ["hello-world.cc"], - deps = [ - ":hello-time", - "//lib:hello-greet", - ], -) -{% endhighlight %} - -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`. - -Now you are ready to build your hello world C++ binary: - -{% highlight bash %} -$ bazel build main:hello-world -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 -$ ./bazel-bin/main/hello-world -Hello world -Thu Jun 23 18:51:46 2016 -$ ./bazel-bin/main/hello-world Bazel -Hello Bazel -Thu Jun 23 18:52:10 2016 -{% endhighlight %} - -Congratulations, you've just built your first Bazel target! - -## 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: - -```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) 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: - -``` -└── 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 an `external/[repository-name]/` prefix. - -## Including external libraries - -Suppose you are using [Google Test](https://code.google.com/p/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://googletest.googlecode.com/files/gtest-1.7.0.zip", - sha256 = "247ca18dd83f53deb1328be17e4b1be31514cedfc1e3424f672bf11fd7e0d60d", - build_file = "gtest.BUILD", -) -``` - -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: - -* `gtest-1.7.0/src/gtest-all.cc` `#include`s all of the other files in - `gtest-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 relative to the `gtest-1.7.0/include/` directory - (`"gtest/gtest.h"`), so we must add that directory the include paths. -* It needs to link in pthread, so we add that as a `linkopt`. - -The final rule looks like this: - -```python -cc_library( - name = "main", - srcs = glob( - ["gtest-1.7.0/src/*.cc"], - exclude = ["gtest-1.7.0/src/gtest-all.cc"] - ), - hdrs = glob([ - "gtest-1.7.0/include/**/*.h", - "gtest-1.7.0/src/*.h" - ]), - copts = [ - "-Iexternal/gtest/gtest-1.7.0/include" - ], - linkopts = ["-pthread"], - visibility = ["//visibility:public"], -) -``` - -This is somewhat messy: everything is prefixed with gtest-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://googletest.googlecode.com/files/gtest-1.7.0.zip", - sha256 = "247ca18dd83f53deb1328be17e4b1be31514cedfc1e3424f672bf11fd7e0d60d", - build_file = "gtest.BUILD", - strip_prefix = "gtest-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 `//external: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(FactorialTest, Negative) { - 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 in order to make `hello-greet` visible to `hello-test`, we have to add `"//test:__pkg__",` to `visibility` attribute in `./lib/BUILD`. - -Now you can use `bazel test` to run the test. - -{% highlight bash %} -$ bazel test test:hello-test -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. -{% endhighlight %} - - -## Adding dependencies on precompiled libraries - -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"], -) -``` - -Then other C++ targets in your workspace can depend on this rule. |