Skip to content

Commit

Permalink
Support --output=commands flag for aquery
Browse files Browse the repository at this point in the history
This output format prints a list of build commands with one command
per line, similar to `ninja -t commands`. This is frequently useful
when debugging build issues.

Fixes #22389.
  • Loading branch information
pcc committed Jan 16, 2025
1 parent c570f97 commit 6400279
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 5 deletions.
5 changes: 4 additions & 1 deletion site/en/query/aquery.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,15 @@ available during a build.

### Aquery options {:#aquery-options}

#### `--output=(text|summary|proto|jsonproto|textproto), default=text` {:#output}
#### `--output=(text|summary|commands|proto|jsonproto|textproto), default=text` {:#output}

The default output format (`text`) is human-readable,
use `proto`, `textproto`, or `jsonproto` for machine-readable format.
The proto message is `analysis.ActionGraphContainer`.

The `commands` output format prints a list of build commands with
one command per line.

#### `--include_commandline, default=true` {:#include-commandline}

Includes the content of the action command lines in the output (potentially large).
Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/google/devtools/build/lib/query2/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ java_library(
"//src/main/java/com/google/devtools/build/lib/util",
"//src/main/java/com/google/devtools/build/lib/util:command",
"//src/main/java/com/google/devtools/build/lib/util:detailed_exit_code",
"//src/main/java/com/google/devtools/build/lib/util:script_util",
"//src/main/java/com/google/devtools/build/lib/util:shell_escaper",
"//src/main/java/com/google/devtools/build/lib/util:string_encoding",
"//src/main/java/com/google/devtools/build/lib/vfs",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,21 @@ public ConfiguredTargetValueAccessor getAccessor() {
AqueryOutputHandler.OutputType.JSON,
actionFilters),
new ActionGraphTextOutputFormatterCallback(
eventHandler, aqueryOptions, out, accessor, actionFilters, getLabelPrinter()),
eventHandler,
aqueryOptions,
out,
accessor,
ActionGraphTextOutputFormatterCallback.OutputType.TEXT,
actionFilters,
getLabelPrinter()),
new ActionGraphTextOutputFormatterCallback(
eventHandler,
aqueryOptions,
out,
accessor,
ActionGraphTextOutputFormatterCallback.OutputType.COMMANDS,
actionFilters,
getLabelPrinter()),
new ActionGraphSummaryOutputFormatterCallback(
eventHandler, aqueryOptions, out, accessor, actionFilters));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import com.google.devtools.build.lib.skyframe.RuleConfiguredTargetValue;
import com.google.devtools.build.lib.util.CommandDescriptionForm;
import com.google.devtools.build.lib.util.CommandFailureUtils;
import com.google.devtools.build.lib.util.ScriptUtil;
import com.google.devtools.build.lib.util.ShellEscaper;
import java.io.IOException;
import java.io.OutputStream;
Expand All @@ -58,8 +59,19 @@

/** Output callback for aquery, prints human readable output. */
class ActionGraphTextOutputFormatterCallback extends AqueryThreadsafeCallback {
public enum OutputType {
TEXT("text"),
COMMANDS("commands");

final String formatName;

OutputType(String formatName) {
this.formatName = formatName;
}
}

private final ActionKeyContext actionKeyContext = new ActionKeyContext();
private final OutputType outputType;
private final AqueryActionFilter actionFilters;
private final LabelPrinter labelPrinter;
private Map<String, String> paramFileNameToContentMap;
Expand All @@ -69,16 +81,18 @@ class ActionGraphTextOutputFormatterCallback extends AqueryThreadsafeCallback {
AqueryOptions options,
OutputStream out,
TargetAccessor<ConfiguredTargetValue> accessor,
OutputType outputType,
AqueryActionFilter actionFilters,
LabelPrinter labelPrinter) {
super(eventHandler, options, out, accessor);
this.outputType = outputType;
this.actionFilters = actionFilters;
this.labelPrinter = labelPrinter;
}

@Override
public String getName() {
return "text";
return outputType.formatName;
}

@Override
Expand Down Expand Up @@ -128,8 +142,17 @@ private void writeAction(ActionAnalysisMetadata action, PrintStream printStream)
return;
}

ActionOwner actionOwner = action.getOwner();
StringBuilder stringBuilder = new StringBuilder();
switch (outputType) {
case TEXT -> writeText(action, stringBuilder);
case COMMANDS -> writeCommand(action, stringBuilder);
}
printStream.write(stringBuilder.toString().getBytes(UTF_8));
}

private void writeText(ActionAnalysisMetadata action, StringBuilder stringBuilder)
throws IOException, CommandLineExpansionException, InterruptedException, EvalException {
ActionOwner actionOwner = action.getOwner();
stringBuilder
.append(action.prettyPrint())
.append('\n')
Expand Down Expand Up @@ -338,8 +361,27 @@ private void writeAction(ActionAnalysisMetadata action, PrintStream printStream)
}

stringBuilder.append('\n');
}

printStream.write(stringBuilder.toString().getBytes(UTF_8));
private void writeCommand(ActionAnalysisMetadata action, StringBuilder stringBuilder)
throws IOException, CommandLineExpansionException, InterruptedException, EvalException {
if (!(action instanceof CommandAction)) {
return;
}

boolean first = true;
for (String arg :
((CommandAction) action)
.getArguments().stream()
.map(a -> internalToEscapedUnicode(a))
.collect(toImmutableList())) {
if (!first) {
stringBuilder.append(' ');
}
ScriptUtil.emitCommandElement(stringBuilder, arg, first);
first = false;
}
stringBuilder.append('\n');
}

/** Lazy initialization of paramFileNameToContentMap. */
Expand Down
19 changes: 19 additions & 0 deletions src/test/shell/integration/aquery_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,25 @@ EOF
assert_not_contains "echo unused" output
}

function test_basic_aquery_commands() {
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=commands "//$pkg:bar" > output 2> "$TEST_log" \
|| fail "Expected success"
cat output >> "$TEST_log"
assert_contains "echo unused" output
}

function test_basic_aquery_proto() {
local pkg="${FUNCNAME[0]}"
mkdir -p "$pkg" || fail "mkdir -p $pkg"
Expand Down

0 comments on commit 6400279

Please sign in to comment.