Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 19 additions & 19 deletions docs/external/vendor.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ keywords: product:Bazel,Bzlmod,vendor
{# disableFinding("repo") #}

---
title: 'Vendor Mode'
---

## title: 'Vendor Mode'

Vendor mode is a feature that lets you create a local copy of
external dependencies. This is useful for offline builds, or when you want to
Expand Down Expand Up @@ -96,9 +96,9 @@ bazel vendor --vendor_dir=vendor_src

Note that vendoring all dependencies has a few **disadvantages**:

- Fetching all repos, including those introduced transitively, can be time-consuming.
- The vendor directory can become very large.
- Some repos may fail to fetch if they are not compatible with the current platform or environment.
- Fetching all repos, including those introduced transitively, can be time-consuming.
- The vendor directory can become very large.
- Some repos may fail to fetch if they are not compatible with the current platform or environment.

Therefore, consider vendoring for specific targets first.

Expand All @@ -125,12 +125,12 @@ pin("@@bazel_skylib+")

With this configuration

- Both repos will be excluded from subsequent vendor commands.
- Repo `bazel_skylib` will be overridden to the source located under the
vendor directory.
- The user can safely modify the vendored source of `bazel_skylib`.
- To re-vendor `bazel_skylib`, the user has to disable the pin statement
first.
- Both repos will be excluded from subsequent vendor commands.
- Repo `bazel_skylib` will be overridden to the source located under the
vendor directory.
- The user can safely modify the vendored source of `bazel_skylib`.
- To re-vendor `bazel_skylib`, the user has to disable the pin statement
first.

Note: Repository rules with
[`local`](/rules/lib/globals/bzl#repository_rule.local) or
Expand All @@ -146,8 +146,8 @@ vendored source for later builds.

The content being vendored includes:

- The repo directory
- The repo marker file
- The repo directory
- The repo marker file

During a build, if the vendored marker file is up-to-date or the repo is
pinned in the VENDOR.bazel file, then Bazel uses the vendored source by creating
Expand All @@ -174,12 +174,12 @@ External repositories may contain symlinks pointing to other files or
directories. To make sure symlinks work correctly, Bazel uses the following
strategy to rewrite symlinks in the vendored source:

- Create a symlink `<vendor_dir>/bazel-external` that points to `$(bazel info
output_base)/external`. It is refreshed by every Bazel command
automatically.
- For the vendored source, rewrite all symlinks that originally point to a
path under `$(bazel info output_base)/external` to a relative path under
`<vendor_dir>/bazel-external`.
- Create a symlink `<vendor_dir>/bazel-external` that points to `$(bazel info
output_base)/external`. It is refreshed by every Bazel command
automatically.
- For the vendored source, rewrite all symlinks that originally point to a
path under `$(bazel info output_base)/external` to a relative path under
`<vendor_dir>/bazel-external`.

For example, if the original symlink is

Expand Down
16 changes: 16 additions & 0 deletions site/en/external/vendor.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,22 @@ Note that vendoring all dependencies has a few **disadvantages**:

Therefore, consider vendoring for specific targets first.

### Vendor tools for Bazel subcommands {:#vendor-tools-for-subcommands}

Some Bazel subcommands (such as `bazel mod tidy`) have implicit tool
dependencies that are not reachable from user build targets, so they are
**not** included by `bazel vendor //...`. To vendor those tools as well, add
the `@bazel_tools//tools:tools_for_bazel_subcommands` filegroup to your
vendor invocation:

```none
bazel vendor //... @bazel_tools//tools:tools_for_bazel_subcommands
```

This is required if you plan to run commands like `bazel mod tidy` in an
offline or hermetic environment (for example with `--vendor_dir` and
`--nofetch`).

## Configure vendor mode with VENDOR.bazel {:#configure-vendor-mode}

You can control how given repos are handled with the VENDOR.bazel file located
Expand Down
61 changes: 61 additions & 0 deletions src/test/py/bazel/bzlmod/bazel_vendor_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -874,6 +874,67 @@ def testVendorAliasTarget(self):
# Regression test for https://github.com/bazelbuild/bazel/issues/23300
self.RunBazel(['vendor', '//foo/...', '--vendor_dir=vendor'])

def testVendorToolsForBazelSubcommands(self):
# Regression test for https://github.com/bazelbuild/bazel/issues/29222:
# `bazel vendor //...` alone doesn't pull in tools needed by Bazel
# subcommands (e.g. buildozer for `bazel mod tidy`). Users must explicitly
# vendor @bazel_tools//tools:tools_for_bazel_subcommands for those
# subcommands to work under `--nofetch`.
self.ScratchFile(
'MODULE.bazel',
[
'ext = use_extension("//:extension.bzl", "ext")',
'use_repo(ext, "dep", "indirect_dep")',
],
)
self.ScratchFile('BUILD.bazel')
self.ScratchFile(
'extension.bzl',
[
'def _repo_impl(ctx):',
' ctx.file("WORKSPACE")',
' ctx.file("BUILD", "filegroup(name=\'lala\')")',
'repo_rule = repository_rule(implementation=_repo_impl)',
'',
'def _ext_impl(ctx):',
' repo_rule(name="dep")',
' repo_rule(name="missing_dep")',
' repo_rule(name="indirect_dep")',
' return ctx.extension_metadata(',
' root_module_direct_deps=["dep", "missing_dep"],',
' root_module_direct_dev_deps=[],',
' )',
'',
'ext = module_extension(implementation=_ext_impl)',
],
)

# Vendor the main target set plus the tools filegroup so that
# `bazel mod tidy` (which invokes buildozer) can run offline.
self.RunBazel([
'vendor',
'--vendor_dir=vendor',
'//...',
'@bazel_tools//tools:tools_for_bazel_subcommands',
])

# Run `bazel mod tidy` under `--nofetch`. Without the filegroup being
# vendored above, this would fail because buildozer can't be fetched.
self.RunBazel([
'mod',
'tidy',
'--vendor_dir=vendor',
'--nofetch',
])

# Verify that mod tidy actually rewrote MODULE.bazel based on the
# extension's root_module_direct_deps metadata.
with open(self.Path('MODULE.bazel'), 'r', encoding='utf-8') as f:
contents = f.read()
self.assertIn('"dep"', contents)
self.assertIn('"missing_dep"', contents)
self.assertNotIn('"indirect_dep"', contents)


if __name__ == '__main__':
absltest.main()
18 changes: 18 additions & 0 deletions tools/BUILD.tools
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,21 @@ filegroup(
"//tools/test:bzl_srcs",
],
)

# Tools that Bazel subcommands (e.g. `bazel mod tidy`) depend on implicitly,
# but that aren't in the transitive closure of user build targets. Users who
# need to run these subcommands offline (e.g. with `--vendor_dir` and
# `--nofetch`) should include this target in their `bazel vendor` invocation:
#
# bazel vendor //... @bazel_tools//tools:tools_for_bazel_subcommands
#
# See https://github.com/bazelbuild/bazel/issues/29222.
filegroup(
name = "tools_for_bazel_subcommands",
srcs = [
# Required by `bazel mod tidy`. Keep in sync with the label resolved
# in src/main/java/com/google/devtools/build/lib/bazel/bzlmod/
# BazelModTidyFunction.java (search for "buildozer.exe").
"@buildozer_binary//:buildozer.exe",
],
)
Loading