Add text output to aquery. RELNOTES[NEW]: The aquery command now supports --output=text. PiperOrigin-RevId: 207096607
diff --git a/src/main/java/com/google/devtools/build/lib/query2/ActionGraphQueryEnvironment.java b/src/main/java/com/google/devtools/build/lib/query2/ActionGraphQueryEnvironment.java index e3dd353..47509b5 100644 --- a/src/main/java/com/google/devtools/build/lib/query2/ActionGraphQueryEnvironment.java +++ b/src/main/java/com/google/devtools/build/lib/query2/ActionGraphQueryEnvironment.java
@@ -140,6 +140,8 @@ OutputStream out = reporter.getOutErr().getOutputStream(); return ImmutableList.of( new ActionGraphProtoOutputFormatterCallback( + reporter, aqueryOptions, out, skyframeExecutor, accessor), + new ActionGraphTextOutputFormatterCallback( reporter, aqueryOptions, out, skyframeExecutor, accessor)); }
diff --git a/src/main/java/com/google/devtools/build/lib/query2/ActionGraphTextOutputFormatterCallback.java b/src/main/java/com/google/devtools/build/lib/query2/ActionGraphTextOutputFormatterCallback.java new file mode 100644 index 0000000..23be8bd --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/query2/ActionGraphTextOutputFormatterCallback.java
@@ -0,0 +1,155 @@ +// Copyright 2018 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.query2; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Streams; +import com.google.devtools.build.lib.actions.ActionAnalysisMetadata; +import com.google.devtools.build.lib.actions.ActionExecutionMetadata; +import com.google.devtools.build.lib.actions.ActionKeyContext; +import com.google.devtools.build.lib.actions.ActionOwner; +import com.google.devtools.build.lib.actions.CommandLineExpansionException; +import com.google.devtools.build.lib.analysis.actions.SpawnAction; +import com.google.devtools.build.lib.buildeventstream.BuildEvent; +import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos; +import com.google.devtools.build.lib.events.Reporter; +import com.google.devtools.build.lib.query2.engine.QueryEnvironment.TargetAccessor; +import com.google.devtools.build.lib.query2.output.AqueryOptions; +import com.google.devtools.build.lib.skyframe.ConfiguredTargetValue; +import com.google.devtools.build.lib.skyframe.SkyframeExecutor; +import com.google.devtools.build.lib.util.CommandDescriptionForm; +import com.google.devtools.build.lib.util.CommandFailureUtils; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.List; +import java.util.stream.Collectors; + +/** Output callback for aquery, prints human readable output. */ +public class ActionGraphTextOutputFormatterCallback extends AqueryThreadsafeCallback { + + private final ActionKeyContext actionKeyContext = new ActionKeyContext(); + + ActionGraphTextOutputFormatterCallback( + Reporter reporter, + AqueryOptions options, + OutputStream out, + SkyframeExecutor skyframeExecutor, + TargetAccessor<ConfiguredTargetValue> accessor) { + super(reporter, options, out, skyframeExecutor, accessor); + } + + @Override + public String getName() { + return "text"; + } + + @Override + public void processOutput(Iterable<ConfiguredTargetValue> partialResult) + throws IOException, InterruptedException { + try { + for (ConfiguredTargetValue configuredTargetValue : partialResult) { + List<ActionAnalysisMetadata> actions = configuredTargetValue.getActions(); + for (ActionAnalysisMetadata action : actions) { + writeAction(action, printStream); + } + } + } catch (CommandLineExpansionException e) { + throw new IOException(e.getMessage()); + } + } + + private void writeAction(ActionAnalysisMetadata action, PrintStream printStream) + throws IOException, CommandLineExpansionException { + ActionOwner actionOwner = action.getOwner(); + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder + .append(action.prettyPrint()) + .append('\n') + .append(" Mnemonic: ") + .append(action.getMnemonic()) + .append('\n'); + + if (actionOwner != null) { + BuildEvent configuration = actionOwner.getConfiguration(); + BuildEventStreamProtos.Configuration configProto = + configuration.asStreamProto(/*context=*/ null).getConfiguration(); + stringBuilder + .append(" Owner: ") + .append(actionOwner.getLabel().toString()) + .append('\n') + .append(" Configuration: ") + .append(configProto.getMnemonic()) + .append('\n'); + } + + if (action instanceof ActionExecutionMetadata) { + ActionExecutionMetadata actionExecutionMetadata = (ActionExecutionMetadata) action; + stringBuilder + .append(" ActionKey: ") + .append(actionExecutionMetadata.getKey(actionKeyContext)) + .append('\n'); + } + + stringBuilder + .append(" Inputs: [") + .append( + Streams.stream(action.getInputs()) + .map(input -> input.getExecPathString()) + .sorted() + .collect(Collectors.joining(", "))) + .append("]\n") + .append(" Outputs: [") + .append( + Streams.stream(action.getOutputs()) + .map(input -> input.getExecPathString()) + .sorted() + .collect(Collectors.joining(", "))) + .append("]\n"); + + if (action instanceof SpawnAction) { + SpawnAction spawnAction = (SpawnAction) action; + // TODO(twerth): This handles the fixed environment. We probably want to output the inherited + // environment as well. + ImmutableMap<String, String> fixedEnvironment = spawnAction.getEnvironment().getFixedEnv(); + stringBuilder + .append(" Environment: [") + .append( + Streams.stream(fixedEnvironment.entrySet()) + .map( + environmentVariable -> + environmentVariable.getKey() + "=" + environmentVariable.getValue()) + .sorted() + .collect(Collectors.joining(", "))) + .append("]\n") + + // TODO(twerth): Add option to only optionally include the command line. + .append(" Command Line: ") + .append( + CommandFailureUtils.describeCommand( + CommandDescriptionForm.COMPLETE, + /* prettyPrintArgs= */ true, + spawnAction.getArguments(), + /* environment= */ null, + /* cwd= */ null)) + .append("\n"); + } + + stringBuilder.append('\n'); + + printStream.write(stringBuilder.toString().getBytes(UTF_8)); + } +}
diff --git a/src/main/java/com/google/devtools/build/lib/query2/BUILD b/src/main/java/com/google/devtools/build/lib/query2/BUILD index b3ded13..3ccf9fb 100644 --- a/src/main/java/com/google/devtools/build/lib/query2/BUILD +++ b/src/main/java/com/google/devtools/build/lib/query2/BUILD
@@ -27,6 +27,8 @@ "//src/main/java/com/google/devtools/build/lib:packages-internal", "//src/main/java/com/google/devtools/build/lib:util", "//src/main/java/com/google/devtools/build/lib/actions", + "//src/main/java/com/google/devtools/build/lib/buildeventstream", + "//src/main/java/com/google/devtools/build/lib/buildeventstream/proto:build_event_stream_java_proto", "//src/main/java/com/google/devtools/build/lib/causes", "//src/main/java/com/google/devtools/build/lib/collect/compacthashset", "//src/main/java/com/google/devtools/build/lib/collect/nestedset",
diff --git a/src/test/shell/integration/aquery_test.sh b/src/test/shell/integration/aquery_test.sh index 735967a..e21f0c4 100755 --- a/src/test/shell/integration/aquery_test.sh +++ b/src/test/shell/integration/aquery_test.sh
@@ -45,9 +45,39 @@ assert_contains "//$pkg:foo" output assert_not_contains "//$pkg:bar" output - bazel aquery "deps(//$pkg:foo)" > output 2> "$TEST_log" || fail "Expected success" + bazel aquery "deps(//$pkg:foo)" > output 2> "$TEST_log" \ + || fail "Expected success" assert_contains "//$pkg:foo" output assert_contains "//$pkg:bar" output } +function test_aquery_text() { + local pkg="${FUNCNAME[0]}" + mkdir -p "$pkg" || fail "mkdir -p $pkg" + cat > "$pkg/BUILD" <<'EOF' +genrule( + name = "bar", + srcs = ["dummy.txt"], + outs = ["bar_out.txt"], + cmd = "echo unused > $(OUTS)", +) +EOF + echo "hello aquery" > "$pkg/in.txt" + + bazel aquery --output=text "//$pkg:bar" > output 2> "$TEST_log" \ + || fail "Expected success" + cat output >> "$TEST_log" + assert_contains "action 'Executing genrule //$pkg:bar'" output + assert_contains "Mnemonic: Genrule" output + assert_contains "Owner: //$pkg:bar" output + assert_contains "Configuration: .*-fastbuild" output + # Only check that the inputs/outputs/command line/environment exist, but not + # their actual contents since that would be too much. + assert_contains "Inputs: \[" output + assert_contains "Outputs: \[" output + assert_contains "Command Line: (" output + assert_contains "Environment: \[" output + +} + run_suite "${PRODUCT_NAME} action graph query tests"