RELNOTES[NEW]: bazel info: Allow to specify multiple keys.
If you specify more than one key, then the value will prefixed with the
key.
Closes #11591.
PiperOrigin-RevId: 316845962
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 282ff8e..7ee7ea3 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
@@ -18,6 +18,7 @@
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.analysis.NoBuildEvent;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
@@ -80,6 +81,7 @@
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
+import java.util.stream.Collectors;
/**
* Implementation of 'blaze info'.
@@ -213,38 +215,37 @@
"Blaze info does not support starlark options. Ignoring options: "
+ removedStarlarkOptions));
}
- if (residue.size() > 1) {
- String message = "at most one key may be specified";
- env.getReporter().handle(Event.error(message));
- return createFailureResult(
- message, ExitCode.COMMAND_LINE_ERROR, FailureDetails.InfoCommand.Code.TOO_MANY_KEYS);
- }
- String key = residue.size() == 1 ? residue.get(0) : null;
env.getEventBus().post(new NoBuildEvent());
- if (key != null) { // print just the value for the specified key:
- byte[] value;
- if (items.containsKey(key)) {
- value = items.get(key).get(configurationSupplier, env);
- } else {
- String message = "unknown key: '" + key + "'";
+ if (!residue.isEmpty()) {
+ ImmutableSet.Builder<String> unknownKeysBuilder = ImmutableSet.builder();
+ for (String key : residue) {
+ byte[] value;
+ if (items.containsKey(key)) {
+ try (SilentCloseable c = Profiler.instance().profile(key + ".infoItem")) {
+ value = items.get(key).get(configurationSupplier, env);
+ if (residue.size() > 1) {
+ outErr.getOutputStream().write((key + ": ").getBytes(StandardCharsets.UTF_8));
+ }
+ outErr.getOutputStream().write(value);
+ }
+ } else {
+ unknownKeysBuilder.add(key);
+ }
+ }
+ ImmutableSet<String> unknownKeys = unknownKeysBuilder.build();
+ if (!unknownKeys.isEmpty()) {
+ String message =
+ "unknown key(s): "
+ + unknownKeys.stream()
+ .map(key -> "'" + key + "'")
+ .collect(Collectors.joining(", "));
env.getReporter().handle(Event.error(message));
return createFailureResult(
message,
ExitCode.COMMAND_LINE_ERROR,
FailureDetails.InfoCommand.Code.KEY_NOT_RECOGNIZED);
}
- try {
- outErr.getOutputStream().write(value);
- outErr.getOutputStream().flush();
- } catch (IOException e) {
- String message = "Cannot write info block: " + e.getMessage();
- env.getReporter().handle(Event.error(message));
- return createFailureResult(
- message,
- ExitCode.ANALYSIS_FAILURE,
- FailureDetails.InfoCommand.Code.INFO_BLOCK_WRITE_FAILURE);
- }
} else { // print them all
configurationSupplier.get(); // We'll need this later anyway
for (InfoItem infoItem : items.values()) {
@@ -258,6 +259,7 @@
}
}
}
+ outErr.getOutputStream().flush();
} catch (AbruptExitException e) {
return BlazeCommandResult.detailedExitCode(e.getDetailedExitCode());
} catch (AbruptExitRuntimeException e) {
diff --git a/src/test/shell/integration/BUILD b/src/test/shell/integration/BUILD
index 382f31a..bdb9d63 100644
--- a/src/test/shell/integration/BUILD
+++ b/src/test/shell/integration/BUILD
@@ -693,6 +693,15 @@
],
)
+sh_test(
+ name = "info_test",
+ srcs = ["info_test.sh"],
+ data = [
+ ":test-deps",
+ "@bazel_tools//tools/bash/runfiles",
+ ],
+)
+
########################################################################
# Test suites.
diff --git a/src/test/shell/integration/info_test.sh b/src/test/shell/integration/info_test.sh
new file mode 100755
index 0000000..d19dfdc
--- /dev/null
+++ b/src/test/shell/integration/info_test.sh
@@ -0,0 +1,101 @@
+#!/bin/bash
+#
+# Copyright 2020 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.
+#
+# An end-to-end test that Bazel info command reasonable output.
+
+# --- begin runfiles.bash initialization ---
+set -euo pipefail
+if [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
+ if [[ -f "$0.runfiles_manifest" ]]; then
+ export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest"
+ elif [[ -f "$0.runfiles/MANIFEST" ]]; then
+ export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST"
+ elif [[ -f "$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
+ export RUNFILES_DIR="$0.runfiles"
+ fi
+fi
+if [[ -f "${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
+ source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash"
+elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
+ source "$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \
+ "$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)"
+else
+ echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash"
+ exit 1
+fi
+# --- end runfiles.bash initialization ---
+
+source "$(rlocation "io_bazel/src/test/shell/integration_test_setup.sh")" \
+ || { echo "integration_test_setup.sh not found!" >&2; exit 1; }
+
+
+#### TESTS #############################################################
+
+function test_info() {
+ bazel info >$TEST_log \
+ || fail "${PRODUCT_NAME} info failed"
+
+ # Test some arbitrary keys.
+ expect_log 'max-heap-size: [0-9]*MB'
+ expect_log 'server_pid: [0-9]*'
+ expect_log 'command_log: .*/command\.log'
+ expect_log 'release: development version'
+
+ # Make sure that hidden keys are not shown.
+ expect_not_log 'used-heap-size-after-gc'
+ expect_not_log 'starlark-semantics'
+}
+
+function test_server_pid() {
+ bazel info server_pid >$TEST_log \
+ || fail "${PRODUCT_NAME} info failed"
+ expect_log '[0-9]*'
+}
+
+function test_used_heap_size_after_gc() {
+ bazel info used-heap-size-after-gc >$TEST_log \
+ || fail "${PRODUCT_NAME} info failed"
+ expect_log '[0-9]*MB'
+}
+
+function test_starlark_semantics() {
+ bazel info starlark-semantics >$TEST_log \
+ || fail "${PRODUCT_NAME} info failed"
+ expect_log 'StarlarkSemantics{.*}'
+}
+
+function test_multiple_keys() {
+ bazel info release used-heap-size gc-count >$TEST_log \
+ || fail "${PRODUCT_NAME} info failed"
+ expect_log 'release: development version'
+ expect_log 'used-heap-size: [0-9]*MB'
+ expect_log 'gc-count: [0-9]*'
+}
+
+function test_multiple_keys_wrong_keys() {
+ bazel info command_log foo used-heap-size-after-gc bar gc-count foo &>$TEST_log \
+ && fail "expected ${PRODUCT_NAME} info to fail with unknown keys"
+
+ # First test the valid keys.
+ expect_log 'command_log: .*/command\.log'
+ expect_log 'used-heap-size-after-gc: [0-9]*MB'
+ expect_log 'gc-count: [0-9]*'
+
+ # Then the error message.
+ expect_log "ERROR: unknown key(s): 'foo', 'bar'"
+}
+
+run_suite "Integration tests for ${PRODUCT_NAME} info."