[9.4.0] Add `external_directory` info item (#31170) (#32222) `bazel info external_directory` prints the directory under which external repositories are fetched (currently `$(bazel info output_base)/external`). If users don't have to hardcode the `external/` string, we could change it to `bazel-external/` at some point in the future. No - [x] I have added tests for the new use cases (if any). - [ ] I have updated the documentation (if applicable). RELNOTES: Added `bazel info external_directory`, which prints the directory under which external repositories are fetched. Closes #31170 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/bazel/pull/31170 from fmeum:claude/external-root-info-item-d3h3bs 8dc0c3f7b0419d431c63476c4cd92804ac46dca8 PiperOrigin-RevId: 1002378183 Change-Id: I816de546dc6486af8dc90b3d9cc4662e97160c02 <!-- Thank you for contributing to Bazel! Please read the contribution guidelines: https://bazel.build/contribute and guidelines for AI-assisted contributions: https://bazel.build/contribute/ai-assisted-contributions --> ### Description <!-- Please provide a brief summary of the changes in this PR. --> ### Motivation <!-- Why is this change important? Does it fix a specific bug or add a new feature? If this PR fixes an existing issue, please link it here (e.g. "Fixes #1234"). --> ### Build API Changes <!-- Does this PR affect the Build API? (e.g. Starlark API, providers, command-line flags, native rules) If yes, please answer the following: 1. Has this been discussed in a design doc or issue? (Please link it) 2. Is the change backward compatible? 3. If it's a breaking change, what is the migration plan? --> No ### Checklist - [ ] I have added tests for the new use cases (if any). - [ ] I have updated the documentation (if applicable). ### Release Notes <!-- If this is a new feature, please add 'RELNOTES[NEW]: <description>' here. If this is a breaking change, please add 'RELNOTES[INC]: <reason>' here. If this change should be mentioned in release notes, please add 'RELNOTES: <reason>' here. --> RELNOTES: None Commit https://github.com/bazelbuild/bazel/commit/263a69f54f4f1b6583199b0a7955ba1e58f7d54d Co-authored-by: Fabian Meumertzheim <fabian@meumertzhe.im>
diff --git a/docs/external/overview.mdx b/docs/external/overview.mdx index 2a06cc6c..0c9b023 100644 --- a/docs/external/overview.mdx +++ b/docs/external/overview.mdx
@@ -212,14 +212,14 @@ ### Directory layout {:#directory-layout} -After being fetched, the repo can be found in the subdirectory `external` in the -[output base](/remote/output-directories), under its canonical name. +After being fetched, the repo can be found in the external directory, which is +printed by `bazel info external_directory`, under its canonical name. You can run the following command to see the contents of the repo with the canonical name `canonical_name`: ```posix-terminal -ls $(bazel info output_base)/external/{{ '<var>' }} canonical_name {{ '</var>' }} +ls $(bazel info external_directory)/{{ '<var>' }} canonical_name {{ '</var>' }} ``` ### REPO.bazel file {:#repo.bazel}
diff --git a/docs/external/vendor.mdx b/docs/external/vendor.mdx index 60b094d..dfc1896 100644 --- a/docs/external/vendor.mdx +++ b/docs/external/vendor.mdx
@@ -140,7 +140,7 @@ ## Understand how vendor mode works {:#how-vendor-mode-works} Bazel fetches external dependencies of a project under `$(bazel info -output_base)/external`. Vendoring external dependencies means moving out +external_directory)`. Vendoring external dependencies means moving out relevant files and directories to the given vendor directory and use the vendored source for later builds. @@ -151,7 +151,7 @@ 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 -a symlink to it under `$(bazel info output_base)/external` instead of actually +a symlink to it under `$(bazel info external_directory)` instead of actually running the repository rule. Otherwise, a warning is printed and Bazel will fallback to fetching the latest version of the repo. @@ -175,16 +175,15 @@ 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. + external_directory)`. 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 + path under `$(bazel info external_directory)` to a relative path under `<vendor_dir>/bazel-external`. For example, if the original symlink is ```none -<vendor_dir>/repo_foo+/link => $(bazel info output_base)/external/repo_bar+/file +<vendor_dir>/repo_foo+/link => $(bazel info external_directory)/repo_bar+/file ``` It will be rewritten to @@ -196,7 +195,7 @@ where ```none -<vendor_dir>/bazel-external => $(bazel info output_base)/external # This might be new if output base is changed +<vendor_dir>/bazel-external => $(bazel info external_directory) # This might be new if output base is changed ``` Since `<vendor_dir>/bazel-external` is generated by Bazel automatically, it's @@ -206,8 +205,8 @@ after the vendored source is moved to another location or the bazel output base is changed. -Note: symlinks that point to an absolute path outside of $(bazel info -output_base)/external are not rewritten. Therefore, it could still break +Note: symlinks that point to an absolute path outside of `$(bazel info +external_directory)` are not rewritten. Therefore, it could still break cross-machine compatibility. Note: On Windows, vendoring symlinks only works with
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/InfoCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/InfoCommand.java index 20937b6..1a1f7c6 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/InfoCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/InfoCommand.java
@@ -44,6 +44,7 @@ import com.google.devtools.build.lib.runtime.commands.info.DefaultPackagePathInfoItem; import com.google.devtools.build.lib.runtime.commands.info.DefaultsPackageInfoItem; import com.google.devtools.build.lib.runtime.commands.info.ExecutionRootInfoItem; +import com.google.devtools.build.lib.runtime.commands.info.ExternalDirectoryInfoItem; import com.google.devtools.build.lib.runtime.commands.info.GcCountInfoItem; import com.google.devtools.build.lib.runtime.commands.info.GcTimeInfoItem; import com.google.devtools.build.lib.runtime.commands.info.InfoItemHandler; @@ -305,6 +306,7 @@ new InstallMd5InfoItem(), new OutputBaseInfoItem(productName), new ExecutionRootInfoItem(), + new ExternalDirectoryInfoItem(), new OutputPathInfoItem(), new ClientEnv(), new BlazeBinInfoItem(productName),
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/info/ExternalDirectoryInfoItem.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/info/ExternalDirectoryInfoItem.java new file mode 100644 index 0000000..6ba0a7b --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/info/ExternalDirectoryInfoItem.java
@@ -0,0 +1,44 @@ +// Copyright 2026 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.devtools.build.lib.runtime.commands.info; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Supplier; +import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue; +import com.google.devtools.build.lib.cmdline.LabelConstants; +import com.google.devtools.build.lib.runtime.CommandEnvironment; +import com.google.devtools.build.lib.runtime.InfoItem; + +/** Info item for the external_directory, under which external repositories are fetched. */ +public final class ExternalDirectoryInfoItem extends InfoItem { + public ExternalDirectoryInfoItem() { + super( + "external_directory", + "The directory under which external repositories are fetched, one subdirectory per" + + " canonical repository name.", + false); + } + + @Override + public byte[] get( + Supplier<BuildConfigurationValue> configurationSupplier, CommandEnvironment env) { + checkNotNull(env); + return print( + env.getDirectories() + .getOutputBase() + .getRelative(LabelConstants.EXTERNAL_REPOSITORY_LOCATION)); + } +}
diff --git a/src/test/shell/integration/info_test.sh b/src/test/shell/integration/info_test.sh index fb1a883..adb54ad 100755 --- a/src/test/shell/integration/info_test.sh +++ b/src/test/shell/integration/info_test.sh
@@ -65,6 +65,13 @@ expect_log '[0-9]*' } +function test_external_directory() { + bazel info external_directory >$TEST_log \ + || fail "${PRODUCT_NAME} info failed" + local -r output_base="$(bazel info output_base 2>/dev/null)" + expect_log "^${output_base}/external\$" +} + function test_used_heap_size_after_gc() { bazel info used-heap-size-after-gc >$TEST_log \ || fail "${PRODUCT_NAME} info failed"