kchodorow | 2214797 | 2017-05-01 20:08:25 +0200 | [diff] [blame] | 1 | --- |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 2 | layout: documentation |
| 3 | title: Best practices |
kchodorow | 2214797 | 2017-05-01 20:08:25 +0200 | [diff] [blame] | 4 | --- |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 5 | |
| 6 | # Best practices for Bazel |
| 7 | |
| 8 | This document assumes that you are familiar with Bazel and provides advice on structuring your |
| 9 | projects to take full advantage of Bazel's features. |
| 10 | |
| 11 | The overall goals are: |
| 12 | |
| 13 | - To use fine-grained dependencies to allow parallelism and incrementality. |
| 14 | - To keep dependencies well-encapsulated. |
| 15 | - To make code well-structured and testable. |
| 16 | - To create a build configuration that is easy to understand and maintain. |
| 17 | |
| 18 | These guidelines are not requirements: few projects will be able to adhere to all of them. As the |
| 19 | man page for lint says, "A special reward will be presented to the first person to produce a real |
| 20 | program that produces no errors with strict checking." However, incorporating as many of these |
| 21 | principles as possible should make a project more readable, less error-prone, and faster to build. |
| 22 | |
| 23 | This document uses the requirement levels described in |
| 24 | [this RFC](https://www.ietf.org/rfc/rfc2119.txt). |
| 25 | |
| 26 | ## Contents |
| 27 | |
| 28 | - [General structure](#general-structure) |
| 29 | - [Running builds and tests](#running-builds-and-tests) |
| 30 | - [Third party dependencies](#third-party-dependencies) |
| 31 | - [Depending on binaries](#depending-on-binaries) |
| 32 | - [Versioning](#versioning) |
| 33 | - [.bazelrc](#bazelrc) |
| 34 | - [Packages](#packages) |
| 35 | - [BUILD files](#build-files) |
| 36 | - [BUILD file style guide](#build-file-style-guide) |
| 37 | - [Formatting](#formatting) |
| 38 | - [References to targets in the current package](#references-to-targets-in-the-current-package) |
| 39 | - [Target naming](#target-naming) |
| 40 | - [Visibility](#visibility) |
| 41 | - [Dependencies](#dependencies) |
| 42 | - [Globs](#globs) |
laurentlb | 978780d | 2017-07-19 19:19:21 +0200 | [diff] [blame] | 43 | - [.bzl files](#bzl-files) |
| 44 | - [.bzl files style guide](#bzl-files-style-guide) |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 45 | - [Packaging rules](#packaging-rules) |
| 46 | - [Rule choice](#rule-choice) |
| 47 | - [WORKSPACE files](#workspace-files) |
| 48 | - [Repository rules](#repository-rules) |
| 49 | - [Custom BUILD files](#custom-build-files) |
| 50 | - [Skylark repository rules](#skylark-repository-rules) |
| 51 | - [Java](#java) |
| 52 | - [Directory structure](#directory-structure) |
| 53 | - [BUILD files](#build-files) |
| 54 | - [C++](#c) |
| 55 | - [BUILD files](#build-files) |
| 56 | - [Include paths](#include-paths) |
| 57 | - [Protos](#protos) |
| 58 | - [Recommended Code Organization](#recommended-code-organization) |
| 59 | |
| 60 | # General structure |
| 61 | |
| 62 | ## Running builds and tests |
| 63 | |
| 64 | A project should always be able to run `bazel build //...` and `bazel test //...` successfully on |
| 65 | its stable branch. Targets that are necessary but do not build under certain circumstances (e.g., |
| 66 | require specific build flags, do not build on a certain platform, require license agreements) |
| 67 | should be tagged as specifically as possible (e.g., "`requires-osx`"). This tagging allows |
| 68 | targets to be filtered at a more fine-grained level than the "manual" tag and allows someone |
| 69 | inspecting the BUILD file to understand what a target's restrictions are. |
| 70 | |
| 71 | ## Third party dependencies |
| 72 | |
| 73 | Prefer declaring third party dependencies as remote repositories in the WORKSPACE file. If it's |
| 74 | necessary to check third party dependencies into your repository, put them in a directory called |
| 75 | `third_party/` under your workspace directory. Note that all BUILD files in `third_party/` must |
dzc | 5596d3b | 2017-06-07 21:51:52 -0400 | [diff] [blame] | 76 | include [license](https://docs.bazel.build/be/functions.html#licenses) |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 77 | declarations. |
| 78 | |
| 79 | ## Depending on binaries |
| 80 | |
| 81 | Everything should be built from source whenever possible. Generally this means that, instead of |
| 82 | depending on a library `some-library.so`, you'd create a BUILD file and build `some-library.so` |
| 83 | from its sources, then depend on that target. |
| 84 | |
| 85 | Building from source prevents a build from using a library that was built with incompatible flags |
| 86 | or a different architecture. There are also some features like coverage, static analysis, or |
| 87 | dynamic analysis that will only work on the source. |
| 88 | |
| 89 | ## Versioning |
| 90 | |
| 91 | Prefer building all code from head whenever possible. When versions must be used, avoid including |
| 92 | the version in the target name (e.g., `//guava`, not `//guava-20.0`). This naming makes the library |
| 93 | easier to update (only one target needs to be updated). It is also more resilient to diamond |
| 94 | dependency issues: if one library depends on `guava-19.0` and one depends on `guava-20.0`, you |
| 95 | could end up with a library that tries to depend on two different versions. If you created a |
| 96 | misleading alias to point both targets to one guava library, then the BUILD files are misleading. |
| 97 | |
| 98 | ## `.bazelrc` |
| 99 | |
| 100 | For project-specific options, use the configuration file `_your-workspace_/tools/bazel.rc`. |
| 101 | |
| 102 | For options that you **do not** want to check into source control, create the configuration file |
| 103 | `_your-workspace_/.bazelrc` and add `.bazelrc` to your `.gitignore`. Note that this file has a |
| 104 | different name than the file above (`bazel.rc` vs `.bazelrc`). |
| 105 | |
| 106 | ## Packages |
| 107 | |
| 108 | Every directory that contains buildable files should be a package. If a BUILD file refers to files |
| 109 | in subdirectories (e.g., `srcs = ["a/b/C.java"]`) it is a sign that a BUILD file should be added to |
| 110 | that subdirectory. The longer this structure exists, the more likely circular dependencies will be |
| 111 | inadvertently created, a target's scope will creep, and an increasing number of reverse |
| 112 | dependencies will have to be updated. |
| 113 | |
| 114 | # BUILD files |
| 115 | |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 116 | See the [BUILD file style |
dzc | 5596d3b | 2017-06-07 21:51:52 -0400 | [diff] [blame] | 117 | guide](https://docs.bazel.build/skylark/build-style.html). |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 118 | |
laurentlb | 978780d | 2017-07-19 19:19:21 +0200 | [diff] [blame] | 119 | # .bzl files |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 120 | |
laurentlb | 978780d | 2017-07-19 19:19:21 +0200 | [diff] [blame] | 121 | ## .bzl files style guide |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 122 | |
| 123 | See the [Style guide for .bzl |
dzc | 5596d3b | 2017-06-07 21:51:52 -0400 | [diff] [blame] | 124 | files](https://docs.bazel.build/skylark/bzl-style.html) for Skylark rule guidelines. |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 125 | |
| 126 | ## Packaging rules |
| 127 | |
dzc | 5596d3b | 2017-06-07 21:51:52 -0400 | [diff] [blame] | 128 | See [Packaging rules](https://docs.bazel.build/skylark/deploying.html) for advice |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 129 | on how to structure and where to put new Skylark rules. |
| 130 | |
| 131 | ## Rule choice |
| 132 | |
| 133 | When using a language for which Bazel has built-in rules (e.g., C++), prefer using these rules to |
| 134 | writing your own in Skylark. These rules are documented in the [build |
dzc | 5596d3b | 2017-06-07 21:51:52 -0400 | [diff] [blame] | 135 | encyclopedia](https://docs.bazel.build/be/overview.html). |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 136 | |
| 137 | # WORKSPACE files |
| 138 | |
| 139 | ## Repository rules |
| 140 | |
| 141 | Prefer `http_archive` and `new_http_archive` to `git_repository`, `new_git_repository`, and |
| 142 | `maven_jar`. |
| 143 | |
| 144 | `git_repository` depends on jGit, which has several unpleasant bugs, and `maven_jar` uses Maven's |
| 145 | internal API, which generally works but is less optimized for Bazel than `http_archive`'s |
| 146 | downloader logic. Track the following issues filed to remediate these problems: |
| 147 | |
| 148 | - [Use `http_archive` as `git_repository`'s |
| 149 | backend.](https://github.com/bazelbuild/bazel/issues/2147) |
| 150 | - [Improve `maven_jar`'s backend.](https://github.com/bazelbuild/bazel/issues/1752) |
| 151 | |
| 152 | Do not use `bind()`. See "[Consider removing |
| 153 | bind](https://github.com/bazelbuild/bazel/issues/1952)" for a long discussion of its issues and |
| 154 | alternatives. |
| 155 | |
| 156 | ## Custom BUILD files |
| 157 | |
| 158 | When using a `new_` repository rule, prefer to specify `build_file_content`, not `build_file`. |
| 159 | |
| 160 | ## Skylark repository rules |
| 161 | |
| 162 | A Skylark repository rule should generally be responsible for: |
| 163 | |
| 164 | - Detecting system settings and writing them to files. |
| 165 | - Finding resources elsewhere on the system. |
| 166 | - Downloading resources from URLs. |
| 167 | - Generating or symlinking BUILD files into the external repository directory. |
| 168 | |
| 169 | Avoid using `repository_ctx.execute` when possible. For example, when using a non-Bazel C++ |
| 170 | library that has a build using Make, it is preferable to use `respository_ctx.download()` and then |
| 171 | write a BUILD file that builds it, instead of running `ctx.execute(["make"])`. |
| 172 | |
| 173 | # Java |
| 174 | |
| 175 | ## Directory structure |
| 176 | |
| 177 | Prefer Maven's standard directory layout (sources under `src/main/java`, tests under |
| 178 | `src/test/java`). |
| 179 | |
| 180 | ## BUILD files |
| 181 | |
| 182 | Use one BUILD file per package containing Java sources. Every BUILD file should contain one |
| 183 | `java_library` rule that looks like this: |
| 184 | |
| 185 | ```python |
| 186 | java_library( |
| 187 | name = "directory-name", |
| 188 | srcs = glob(["*.java"]), |
| 189 | deps = [...], |
| 190 | ) |
| 191 | ``` |
| 192 | |
| 193 | The name of the library should be the name of the directory containing the BUILD file. The sources |
| 194 | should be a non-recursive glob of all Java files in the directory. |
| 195 | |
| 196 | Tests should be in a matching directory under `src/test` and depend on this library. |
| 197 | |
| 198 | # C++ |
| 199 | |
| 200 | ## BUILD files |
| 201 | |
| 202 | Each BUILD file should contain one `cc_library` rule target per compilation unit in the directory. |
| 203 | C++ libraries should be as fine-grained as possible to provide as much incrementality as possible. |
| 204 | |
| 205 | If there is a single source file in `srcs`, the library should be named based on that C++ file's |
| 206 | name. This library should contain a C++ file(s), any matching header file(s), and the library's |
| 207 | direct dependencies. For example, |
| 208 | |
| 209 | ```python |
| 210 | cc_library( |
| 211 | name = "mylib", |
| 212 | srcs = ["mylib.cc"], |
| 213 | hdrs = ["mylib.h"], |
| 214 | deps = [":lower-level-lib"] |
| 215 | ) |
| 216 | ``` |
| 217 | |
| 218 | There should be one `cc_test` rule target per `cc_library` target in the file. The `cc_test`'s |
| 219 | source should be a file named `[libname]_test.cc`. For example, a test for the target above might |
| 220 | look like: |
| 221 | |
| 222 | ``` |
| 223 | cc_test( |
| 224 | name = "mylib_test", |
| 225 | srcs = ["mylib_test.cc"], |
| 226 | deps = [":mylib"] |
| 227 | ) |
| 228 | ``` |
| 229 | |
| 230 | ## Include paths |
| 231 | |
| 232 | All include paths should be relative to the workspace directory. Use `includes` only if a public |
| 233 | header needs to be widely used at a non-workspace-relative path (for legacy or `third_party` code). |
| 234 | Otherwise, prefer to use the `copts` attribute, not the `includes` attribute. |
| 235 | |
| 236 | Using `cc_inc_library` is discouraged, prefer `copts` or `includes`. |
| 237 | See [the design document](https://docs.google.com/document/d/18qUWh0uUiJBv6ZOySvp6DEV0NjVnBoEy-r-ZHa9cmhU/edit#heading=h.kmep1cl5ym9k) |
| 238 | on C++ include directories for reasoning. |
| 239 | |
| 240 | # Protos |
| 241 | |
| 242 | ## Recommended Code Organization |
| 243 | |
| 244 | - One `proto_library` rule per `.proto` file. |
| 245 | - A file named `foo.proto` will be in a rule named `foo_proto`, which is located in the same |
| 246 | package. |
| 247 | - A `[language]_proto_library` that wraps a `proto_library` named `foo_proto` should be called |
| 248 | `foo_[language]_proto`, and be located in the same package. |