// 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 accessor) { super(reporter, options, out, skyframeExecutor, accessor); } @Override public String getName() { return "text"; } @Override public void processOutput(Iterable partialResult) throws IOException, InterruptedException { try { for (ConfiguredTargetValue configuredTargetValue : partialResult) { List 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 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)); } }