fwe | acae1cd | 2022-02-17 09:45:38 -0800 | [diff] [blame] | 1 | Project: /_project.yaml |
| 2 | Book: /_book.yaml |
| 3 | |
| 4 | # Working with External Dependencies |
| 5 | |
| 6 | Bazel can depend on targets from other projects. Dependencies from these other |
| 7 | projects are called _external dependencies_. |
| 8 | |
| 9 | Note: Bazel 5.0 and newer has a new external dependency system, codenamed |
| 10 | "Bzlmod", which renders a lot of the content on this page obsolete. See [Bzlmod |
| 11 | user guide](/docs/bzlmod) for more information. |
| 12 | |
| 13 | The `WORKSPACE` file (or `WORKSPACE.bazel` file) in the |
| 14 | [workspace directory](/concepts/build-ref#workspace) |
| 15 | tells Bazel how to get other projects' sources. These other projects can |
| 16 | contain one or more `BUILD` files with their own targets. `BUILD` files within |
| 17 | the main project can depend on these external targets by using their name from |
| 18 | the `WORKSPACE` file. |
| 19 | |
| 20 | For example, suppose there are two projects on a system: |
| 21 | |
| 22 | ``` |
| 23 | / |
| 24 | home/ |
| 25 | user/ |
| 26 | project1/ |
| 27 | WORKSPACE |
| 28 | BUILD |
| 29 | srcs/ |
| 30 | ... |
| 31 | project2/ |
| 32 | WORKSPACE |
| 33 | BUILD |
| 34 | my-libs/ |
| 35 | ``` |
| 36 | |
| 37 | If `project1` wanted to depend on a target, `:foo`, defined in |
| 38 | `/home/user/project2/BUILD`, it could specify that a repository named |
| 39 | `project2` could be found at `/home/user/project2`. Then targets in |
| 40 | `/home/user/project1/BUILD` could depend on `@project2//:foo`. |
| 41 | |
| 42 | The `WORKSPACE` file allows users to depend on targets from other parts of the |
| 43 | filesystem or downloaded from the internet. It uses the same syntax as `BUILD` |
| 44 | files, but allows a different set of rules called _repository rules_ (sometimes |
| 45 | also known as _workspace rules_). Bazel comes with a few [built-in repository |
| 46 | rules](/reference/be/workspace) and a set of [embedded Starlark repository |
| 47 | rules](/rules/lib/repo/index). Users can also write [custom repository |
| 48 | rules](/rules/repository_rules) to get more complex behavior. |
| 49 | |
| 50 | ## Supported types of external dependencies {:#types} |
| 51 | |
| 52 | A few basic types of external dependencies can be used: |
| 53 | |
| 54 | - [Dependencies on other Bazel projects](#bazel-projects) |
| 55 | - [Dependencies on non-Bazel projects](#non-bazel-projects) |
| 56 | - [Dependencies on external packages](#external-packages) |
| 57 | |
| 58 | ### Depending on other Bazel projects {:#bazel-projects} |
| 59 | |
| 60 | If you want to use targets from a second Bazel project, you can |
| 61 | use |
| 62 | [`local_repository`](/reference/be/workspace#local_repository), |
| 63 | [`git_repository`](/rules/lib/repo/git#git_repository) |
| 64 | or [`http_archive`](/rules/lib/repo/http#http_archive) |
| 65 | to symlink it from the local filesystem, reference a git repository or download |
| 66 | it (respectively). |
| 67 | |
| 68 | For example, suppose you are working on a project, `my-project/`, and you want |
| 69 | to depend on targets from your coworker's project, `coworkers-project/`. Both |
| 70 | projects use Bazel, so you can add your coworker's project as an external |
| 71 | dependency and then use any targets your coworker has defined from your own |
| 72 | BUILD files. You would add the following to `my_project/WORKSPACE`: |
| 73 | |
| 74 | ```python |
| 75 | local_repository( |
| 76 | name = "coworkers_project", |
| 77 | path = "/path/to/coworkers-project", |
| 78 | ) |
| 79 | ``` |
| 80 | |
| 81 | If your coworker has a target `//foo:bar`, your project can refer to it as |
| 82 | `@coworkers_project//foo:bar`. External project names must be |
| 83 | [valid workspace names](/rules/lib/globals#workspace). |
| 84 | |
| 85 | ### Depending on non-Bazel projects {:#non-bazel-projects} |
| 86 | |
| 87 | Rules prefixed with `new_`, such as |
| 88 | [`new_local_repository`](/reference/be/workspace#new_local_repository), |
| 89 | allow you to create targets from projects that do not use Bazel. |
| 90 | |
| 91 | For example, suppose you are working on a project, `my-project/`, and you want |
| 92 | to depend on your coworker's project, `coworkers-project/`. Your coworker's |
| 93 | project uses `make` to build, but you'd like to depend on one of the .so files |
| 94 | it generates. To do so, add the following to `my_project/WORKSPACE`: |
| 95 | |
| 96 | ```python |
| 97 | new_local_repository( |
| 98 | name = "coworkers_project", |
| 99 | path = "/path/to/coworkers-project", |
| 100 | build_file = "coworker.BUILD", |
| 101 | ) |
| 102 | ``` |
| 103 | |
| 104 | `build_file` specifies a `BUILD` file to overlay on the existing project, for |
| 105 | example: |
| 106 | |
| 107 | ```python |
| 108 | cc_library( |
| 109 | name = "some-lib", |
| 110 | srcs = glob(["**"]), |
| 111 | visibility = ["//visibility:public"], |
| 112 | ) |
| 113 | ``` |
| 114 | |
| 115 | You can then depend on `@coworkers_project//:some-lib` from your project's |
| 116 | `BUILD` files. |
| 117 | |
| 118 | ### Depending on external packages {:#external-packages} |
| 119 | |
| 120 | #### Maven artifacts and repositories {:#maven-repositories} |
| 121 | |
| 122 | Use the ruleset [`rules_jvm_external`](https://github.com/bazelbuild/rules_jvm_external){: .external} |
| 123 | to download artifacts from Maven repositories and make them available as Java |
| 124 | dependencies. |
| 125 | |
| 126 | ## Fetching dependencies {:#fetching-dependencies} |
| 127 | |
| 128 | By default, external dependencies are fetched as needed during `bazel build`. If |
| 129 | you would like to prefetch the dependencies needed for a specific set of targets, use |
| 130 | [`bazel fetch`](/reference/command-line-reference#commands). |
| 131 | To unconditionally fetch all external dependencies, use |
| 132 | [`bazel sync`](/reference/command-line-reference#commands). |
| 133 | As fetched repositories are [stored in the output base](#layout), fetching |
| 134 | happens per workspace. |
| 135 | |
| 136 | ## Shadowing dependencies {:#shadowing-dependencies} |
| 137 | |
| 138 | Whenever possible, it is recommended to have a single version policy in your |
| 139 | project. This is required for dependencies that you compile against and end up |
| 140 | in your final binary. But for cases where this isn't true, it is possible to |
| 141 | shadow dependencies. Consider the following scenario: |
| 142 | |
| 143 | myproject/WORKSPACE |
| 144 | |
| 145 | ```python |
| 146 | workspace(name = "myproject") |
| 147 | |
| 148 | local_repository( |
| 149 | name = "A", |
| 150 | path = "../A", |
| 151 | ) |
| 152 | local_repository( |
| 153 | name = "B", |
| 154 | path = "../B", |
| 155 | ) |
| 156 | ``` |
| 157 | |
| 158 | A/WORKSPACE |
| 159 | |
| 160 | ```python |
| 161 | workspace(name = "A") |
| 162 | |
| 163 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") |
| 164 | http_archive( |
| 165 | name = "testrunner", |
| 166 | urls = ["https://github.com/testrunner/v1.zip"], |
| 167 | sha256 = "...", |
| 168 | ) |
| 169 | ``` |
| 170 | |
| 171 | B/WORKSPACE |
| 172 | |
| 173 | ```python |
| 174 | workspace(name = "B") |
| 175 | |
| 176 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") |
| 177 | http_archive( |
| 178 | name = "testrunner", |
| 179 | urls = ["https://github.com/testrunner/v2.zip"], |
| 180 | sha256 = "..." |
| 181 | ) |
| 182 | ``` |
| 183 | |
| 184 | Both dependencies `A` and `B` depend on `testrunner`, but they depend on |
| 185 | different versions of `testrunner`. There is no reason for these test runners to |
| 186 | not peacefully coexist within `myproject`, however they will clash with each |
| 187 | other since they have the same name. To declare both dependencies, |
| 188 | update myproject/WORKSPACE: |
| 189 | |
| 190 | ```python |
| 191 | workspace(name = "myproject") |
| 192 | |
| 193 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") |
| 194 | http_archive( |
| 195 | name = "testrunner-v1", |
| 196 | urls = ["https://github.com/testrunner/v1.zip"], |
| 197 | sha256 = "..." |
| 198 | ) |
| 199 | http_archive( |
| 200 | name = "testrunner-v2", |
| 201 | urls = ["https://github.com/testrunner/v2.zip"], |
| 202 | sha256 = "..." |
| 203 | ) |
| 204 | local_repository( |
| 205 | name = "A", |
| 206 | path = "../A", |
| 207 | repo_mapping = {"@testrunner" : "@testrunner-v1"} |
| 208 | ) |
| 209 | local_repository( |
| 210 | name = "B", |
| 211 | path = "../B", |
| 212 | repo_mapping = {"@testrunner" : "@testrunner-v2"} |
| 213 | ) |
| 214 | ``` |
| 215 | |
| 216 | This mechanism can also be used to join diamonds. For example if `A` and `B` |
| 217 | had the same dependency but call it by different names, those dependencies can |
| 218 | be joined in myproject/WORKSPACE. |
| 219 | |
| 220 | ## Overriding repositories from the command line {:#overriding-repositories} |
| 221 | |
| 222 | To override a declared repository with a local repository from the command line, |
| 223 | use the |
| 224 | [`--override_repository`](/reference/command-line-reference#flag--override_repository) |
| 225 | flag. Using this flag changes the contents of external repositories without |
| 226 | changing your source code. |
| 227 | |
| 228 | For example, to override `@foo` to the local directory `/path/to/local/foo`, |
| 229 | pass the `--override_repository=foo=/path/to/local/foo` flag. |
| 230 | |
| 231 | Some of the use cases include: |
| 232 | |
| 233 | * Debugging issues. For example, you can override a `http_archive` repository |
| 234 | to a local directory where you can make changes more easily. |
| 235 | * Vendoring. If you are in an environment where you cannot make network calls, |
| 236 | override the network-based repository rules to point to local directories |
| 237 | instead. |
| 238 | |
| 239 | ## Using proxies {:#using-proxies} |
| 240 | |
| 241 | Bazel will pick up proxy addresses from the `HTTPS_PROXY` and `HTTP_PROXY` |
| 242 | environment variables and use these to download HTTP/HTTPS files (if specified). |
| 243 | |
| 244 | ## Support for IPv6 {:#support-for-ipv6} |
| 245 | |
| 246 | On IPv6-only machines, Bazel will be able to download dependencies with |
| 247 | no changes. On dual-stack IPv4/IPv6 machines, however, Bazel follows the same |
| 248 | convention as Java: if IPv4 is enabled, IPv4 is preferred. In some situations, |
| 249 | for example when IPv4 network is unable to resolve/reach external addresses, |
| 250 | this can cause `Network unreachable` exceptions and build failures. |
| 251 | In these cases, you can override Bazel's behavior to prefer IPv6 |
| 252 | by using [`java.net.preferIPv6Addresses=true` system property](https://docs.oracle.com/javase/8/docs/api/java/net/doc-files/net-properties.html){: .external}. |
| 253 | Specifically: |
| 254 | |
| 255 | * Use `--host_jvm_args=-Djava.net.preferIPv6Addresses=true` |
fwe | 6c2bd4a | 2022-02-18 10:47:46 -0800 | [diff] [blame] | 256 | [startup option](/docs/user-manual#startup-options), |
fwe | acae1cd | 2022-02-17 09:45:38 -0800 | [diff] [blame] | 257 | for example by adding the following line in your |
| 258 | [`.bazelrc` file](/docs/bazelrc): |
| 259 | |
| 260 | `startup --host_jvm_args=-Djava.net.preferIPv6Addresses=true` |
| 261 | |
| 262 | * If you are running Java build targets that need to connect to the internet |
| 263 | as well (integration tests sometimes needs that), also use |
| 264 | `--jvmopt=-Djava.net.preferIPv6Addresses=true` |
fwe | 6c2bd4a | 2022-02-18 10:47:46 -0800 | [diff] [blame] | 265 | [tool flag](/docs/user-manual#jvmopt), for example by having the |
fwe | acae1cd | 2022-02-17 09:45:38 -0800 | [diff] [blame] | 266 | following line in your [`.bazelrc` file](/docs/bazelrc): |
| 267 | |
| 268 | `build --jvmopt=-Djava.net.preferIPv6Addresses` |
| 269 | |
| 270 | * If you are using |
| 271 | [rules_jvm_external](https://github.com/bazelbuild/rules_jvm_external){: .external}, |
| 272 | for example, for dependency version resolution, also add |
| 273 | `-Djava.net.preferIPv6Addresses=true` to the `COURSIER_OPTS` |
| 274 | environment variable to [provide JVM options for Coursier](https://github.com/bazelbuild/rules_jvm_external#provide-jvm-options-for-coursier-with-coursier_opts){: .external} |
| 275 | |
| 276 | ## Transitive dependencies {:#transitive-dependencies} |
| 277 | |
| 278 | Bazel only reads dependencies listed in your `WORKSPACE` file. If your project |
| 279 | (`A`) depends on another project (`B`) which lists a dependency on a third |
| 280 | project (`C`) in its `WORKSPACE` file, you'll have to add both `B` |
| 281 | and `C` to your project's `WORKSPACE` file. This requirement can balloon the |
| 282 | `WORKSPACE` file size, but limits the chances of having one library |
| 283 | include `C` at version 1.0 and another include `C` at 2.0. |
| 284 | |
| 285 | ## Caching of external dependencies {:#caching-external-dependencies} |
| 286 | |
| 287 | By default, Bazel will only re-download external dependencies if their |
| 288 | definition changes. Changes to files referenced in the definition (such as patches |
| 289 | or `BUILD` files) are also taken into account by bazel. |
| 290 | |
| 291 | To force a re-download, use `bazel sync`. |
| 292 | |
| 293 | ## Layout {:#layout} |
| 294 | |
| 295 | External dependencies are all downloaded to a directory under the subdirectory |
| 296 | `external` in the [output base](/docs/output_directories). In case of a |
| 297 | [local repository](/reference/be/workspace#local_repository), a symlink is created |
| 298 | there instead of creating a new directory. |
| 299 | You can see the `external` directory by running: |
| 300 | |
| 301 | ```posix-terminal |
| 302 | ls $(bazel info output_base)/external |
| 303 | ``` |
| 304 | |
| 305 | Note that running `bazel clean` will not actually delete the external |
| 306 | directory. To remove all external artifacts, use `bazel clean --expunge`. |
| 307 | |
| 308 | ## Offline builds {:#offline-builds} |
| 309 | |
| 310 | It is sometimes desirable or necessary to run a build in an offline fashion. For |
| 311 | simple use cases, such as traveling on an airplane, |
| 312 | [prefetching](#fetching-dependencies) the needed |
| 313 | repositories with `bazel fetch` or `bazel sync` can be enough; moreover, the |
| 314 | using the option `--nofetch`, fetching of further repositories can be disabled |
| 315 | during the build. |
| 316 | |
| 317 | For true offline builds, where the providing of the needed files is to be done |
| 318 | by an entity different from bazel, bazel supports the option |
| 319 | `--distdir`. Whenever a repository rule asks bazel to fetch a file via |
| 320 | [`ctx.download`](/rules/lib/repository_ctx#download) or |
| 321 | [`ctx.download_and_extract`](/rules/lib/repository_ctx#download_and_extract) |
| 322 | and provides a hash sum of the file |
| 323 | needed, bazel will first look into the directories specified by that option for |
| 324 | a file matching the basename of the first URL provided, and use that local copy |
| 325 | if the hash matches. |
| 326 | |
| 327 | Bazel itself uses this technique to bootstrap offline from the [distribution |
fwe | 6c2bd4a | 2022-02-18 10:47:46 -0800 | [diff] [blame] | 328 | artifact](https://github.com/bazelbuild/bazel-website/blob/master/designs/_posts/2016-10-11-distribution-artifact.md). |
fwe | acae1cd | 2022-02-17 09:45:38 -0800 | [diff] [blame] | 329 | It does so by [collecting all the needed external |
| 330 | dependencies](https://github.com/bazelbuild/bazel/blob/5cfa0303d6ac3b5bd031ff60272ce80a704af8c2/WORKSPACE#L116){: .external} |
| 331 | in an internal |
| 332 | [`distdir_tar`](https://github.com/bazelbuild/bazel/blob/5cfa0303d6ac3b5bd031ff60272ce80a704af8c2/distdir.bzl#L44){: .external}. |
| 333 | |
| 334 | However, bazel allows the execution of arbitrary commands in repository rules, |
| 335 | without knowing if they call out to the network. Therefore, bazel has no option |
| 336 | to enforce builds being fully offline. So testing if a build works correctly |
| 337 | offline requires external blocking of the network, as bazel does in its |
| 338 | bootstrap test. |
| 339 | |
| 340 | ## Best practices {:#best-practices} |
| 341 | |
| 342 | ### Repository rules {:#repository-rules} |
| 343 | |
| 344 | A repository rule should generally be responsible for: |
| 345 | |
| 346 | - Detecting system settings and writing them to files. |
| 347 | - Finding resources elsewhere on the system. |
| 348 | - Downloading resources from URLs. |
| 349 | - Generating or symlinking BUILD files into the external repository directory. |
| 350 | |
| 351 | Avoid using `repository_ctx.execute` when possible. For example, when using a non-Bazel C++ |
| 352 | library that has a build using Make, it is preferable to use `repository_ctx.download()` and then |
| 353 | write a BUILD file that builds it, instead of running `ctx.execute(["make"])`. |
| 354 | |
| 355 | Prefer [`http_archive`](/rules/lib/repo/http#http_archive) to `git_repository` and |
| 356 | `new_git_repository`. The reasons are: |
| 357 | |
| 358 | * Git repository rules depend on system `git(1)` whereas the HTTP downloader is built |
| 359 | into Bazel and has no system dependencies. |
| 360 | * `http_archive` supports a list of `urls` as mirrors, and `git_repository` supports only |
| 361 | a single `remote`. |
| 362 | * `http_archive` works with the [repository cache](/docs/build#repository-cache), but not |
| 363 | `git_repository`. See |
| 364 | [#5116](https://github.com/bazelbuild/bazel/issues/5116){: .external} for more information. |
| 365 | |
| 366 | Do not use `bind()`. See "[Consider removing |
| 367 | bind](https://github.com/bazelbuild/bazel/issues/1952){: .external}" for a long |
| 368 | discussion of its issues and alternatives. |