Skip to content

Commit 791e1f7

Browse files
keithl-stripecopybara-github
authored andcommitted
Add bazel query --output_file option, which writes query results directly to a file
This is a proposed fix for #24293 This speeds up a fully warm `bazel query ...` by 23.7%, reducing wall time from 1m49s to 1m23s ``` $ time bazel query '...' --output=streamed_proto > queryoutput4.streamedproto real 1m48.768s user 0m27.410s sys 0m19.646s $ time bazel query '...' --output=streamed_proto --output_file=queryoutput5.streamedproto real 1m22.920s user 0m0.045s sys 0m0.016s ``` _💁‍♂️ Note: when combined with #24305, total wall time is 37s, an overall reduction of 66%._ Closes #24298. PiperOrigin-RevId: 700583890 Change-Id: Ic13f0611aca60c2ce8641e72a0fcfc330f13c803
1 parent c79749a commit 791e1f7

File tree

9 files changed

+111
-9
lines changed

9 files changed

+111
-9
lines changed

scripts/bash_completion_test.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ test_build_options() {
452452

453453
test_query_options() {
454454
assert_expansion 'query --out' \
455-
'query --output='
455+
'query --output'
456456

457457
# Basic label expansion works for query, too.
458458
make_packages

src/main/java/com/google/devtools/build/lib/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ java_library(
403403
"//src/main/java/com/google/devtools/build/lib/query2",
404404
"//src/main/java/com/google/devtools/build/lib/query2:aquery-utils",
405405
"//src/main/java/com/google/devtools/build/lib/query2/common:cquery-node",
406+
"//src/main/java/com/google/devtools/build/lib/query2/common:options",
406407
"//src/main/java/com/google/devtools/build/lib/query2/engine",
407408
"//src/main/java/com/google/devtools/build/lib/query2/query/output",
408409
"//src/main/java/com/google/devtools/build/lib/rules:repository/repository_function",

src/main/java/com/google/devtools/build/lib/buildtool/AqueryProcessor.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,16 @@ public AqueryProcessor(
6767
actionFilters = buildActionFilters(queryExpression);
6868
}
6969

70+
@Override
71+
protected AqueryOptions getQueryOptions(CommandEnvironment env) {
72+
return env.getOptions().getOptions(AqueryOptions.class);
73+
}
74+
7075
/** Outputs the current action graph from Skyframe. */
7176
public BlazeCommandResult dumpActionGraphFromSkyframe(CommandEnvironment env) {
77+
AqueryOptions aqueryOptions = getQueryOptions(env);
7278
try (QueryRuntimeHelper queryRuntimeHelper =
73-
env.getRuntime().getQueryRuntimeHelperFactory().create(env)) {
74-
AqueryOptions aqueryOptions = env.getOptions().getOptions(AqueryOptions.class);
79+
env.getRuntime().getQueryRuntimeHelperFactory().create(env, aqueryOptions)) {
7580

7681
PrintStream printStream =
7782
queryRuntimeHelper.getOutputStreamForQueryOutput() == null

src/main/java/com/google/devtools/build/lib/buildtool/CqueryProcessor.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.google.devtools.build.lib.cmdline.TargetPattern;
2020
import com.google.devtools.build.lib.packages.semantics.BuildLanguageOptions;
2121
import com.google.devtools.build.lib.query2.PostAnalysisQueryEnvironment.TopLevelConfigurations;
22+
import com.google.devtools.build.lib.query2.common.CommonQueryOptions;
2223
import com.google.devtools.build.lib.query2.common.CqueryNode;
2324
import com.google.devtools.build.lib.query2.cquery.ConfiguredTargetQueryEnvironment;
2425
import com.google.devtools.build.lib.query2.cquery.CqueryOptions;
@@ -36,6 +37,11 @@ public CqueryProcessor(
3637
super(queryExpression, mainRepoTargetParser);
3738
}
3839

40+
@Override
41+
protected CommonQueryOptions getQueryOptions(CommandEnvironment env) {
42+
return env.getOptions().getOptions(CqueryOptions.class);
43+
}
44+
3945
@Override
4046
protected ConfiguredTargetQueryEnvironment getQueryEnvironment(
4147
BuildRequest request,

src/main/java/com/google/devtools/build/lib/buildtool/PostAnalysisQueryProcessor.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.google.devtools.build.lib.query2.NamedThreadSafeOutputFormatterCallback;
2727
import com.google.devtools.build.lib.query2.PostAnalysisQueryEnvironment;
2828
import com.google.devtools.build.lib.query2.PostAnalysisQueryEnvironment.TopLevelConfigurations;
29+
import com.google.devtools.build.lib.query2.common.CommonQueryOptions;
2930
import com.google.devtools.build.lib.query2.engine.QueryEvalResult;
3031
import com.google.devtools.build.lib.query2.engine.QueryException;
3132
import com.google.devtools.build.lib.query2.engine.QueryExpression;
@@ -90,7 +91,7 @@ public void process(
9091
}
9192

9293
try (QueryRuntimeHelper queryRuntimeHelper =
93-
env.getRuntime().getQueryRuntimeHelperFactory().create(env)) {
94+
env.getRuntime().getQueryRuntimeHelperFactory().create(env, getQueryOptions(env))) {
9495
doPostAnalysisQuery(
9596
request,
9697
env,
@@ -131,6 +132,8 @@ public void process(
131132
}
132133
}
133134

135+
protected abstract CommonQueryOptions getQueryOptions(CommandEnvironment env);
136+
134137
protected abstract PostAnalysisQueryEnvironment<T> getQueryEnvironment(
135138
BuildRequest request,
136139
CommandEnvironment env,

src/main/java/com/google/devtools/build/lib/query2/common/CommonQueryOptions.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,10 @@ public AspectResolutionModeConverter() {
373373
+ "applicable to --output=graph.")
374374
public boolean graphFactored;
375375

376+
///////////////////////////////////////////////////////////
377+
// INPUT / OUTPUT OPTIONS //
378+
///////////////////////////////////////////////////////////
379+
376380
@Option(
377381
name = "query_file",
378382
defaultValue = "",
@@ -382,4 +386,15 @@ public AspectResolutionModeConverter() {
382386
"If set, query will read the query from the file named here, rather than on the command "
383387
+ "line. It is an error to specify a file here as well as a command-line query.")
384388
public String queryFile;
389+
390+
@Option(
391+
name = "output_file",
392+
defaultValue = "",
393+
documentationCategory = OptionDocumentationCategory.QUERY,
394+
effectTags = {OptionEffectTag.TERMINAL_OUTPUT},
395+
help =
396+
"When specified, query results will be written directly to this file, and nothing will be"
397+
+ " printed to Bazel's standard output stream (stdout). In benchmarks, this is"
398+
+ " generally faster than <code>bazel query &gt; file</code>.")
399+
public String outputFile;
385400
}

src/main/java/com/google/devtools/build/lib/runtime/QueryRuntimeHelper.java

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,14 @@
1515

1616
import com.google.common.annotations.VisibleForTesting;
1717
import com.google.common.base.Preconditions;
18+
import com.google.common.base.Strings;
19+
import com.google.devtools.build.lib.query2.common.CommonQueryOptions;
1820
import com.google.devtools.build.lib.server.FailureDetails;
1921
import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
2022
import com.google.devtools.build.lib.server.FailureDetails.Query;
2123
import com.google.devtools.build.lib.server.FailureDetails.Query.Code;
24+
import com.google.devtools.build.lib.vfs.Path;
25+
import java.io.IOException;
2226
import java.io.OutputStream;
2327

2428
/**
@@ -47,14 +51,17 @@ public interface QueryRuntimeHelper extends AutoCloseable {
4751

4852
/** Factory for {@link QueryRuntimeHelper} instances. */
4953
interface Factory {
50-
QueryRuntimeHelper create(CommandEnvironment env) throws QueryRuntimeHelperException;
54+
QueryRuntimeHelper create(CommandEnvironment env, CommonQueryOptions options)
55+
throws QueryRuntimeHelperException;
5156
}
5257

5358
/**
5459
* A {@link Factory} for {@link StdoutQueryRuntimeHelper} instances that simply wrap the given
5560
* {@link CommandEnvironment} instance's stdout.
5661
*
5762
* <p>This is intended to be the default {@link Factory}.
63+
*
64+
* <p>If {@code --output_file} is set, the stdout is redirected to the defined path value instead.
5865
*/
5966
class StdoutQueryRuntimeHelperFactory implements Factory {
6067
public static final StdoutQueryRuntimeHelperFactory INSTANCE =
@@ -63,8 +70,14 @@ class StdoutQueryRuntimeHelperFactory implements Factory {
6370
private StdoutQueryRuntimeHelperFactory() {}
6471

6572
@Override
66-
public QueryRuntimeHelper create(CommandEnvironment env) {
67-
return createInternal(env.getReporter().getOutErr().getOutputStream());
73+
public QueryRuntimeHelper create(CommandEnvironment env, CommonQueryOptions options)
74+
throws QueryRuntimeHelperException {
75+
if (Strings.isNullOrEmpty(options.outputFile)) {
76+
return createInternal(env.getReporter().getOutErr().getOutputStream());
77+
} else {
78+
return FileQueryRuntimeHelper.create(
79+
env.getWorkingDirectory().getRelative(options.outputFile));
80+
}
6881
}
6982

7083
public QueryRuntimeHelper createInternal(OutputStream stdoutOutputStream) {
@@ -91,6 +104,49 @@ public void afterQueryOutputIsWritten() {}
91104
@Override
92105
public void close() {}
93106
}
107+
108+
/**
109+
* A {@link QueryRuntimeHelper} that wraps a {@link java.io.FileOutputStream} instead of writing
110+
* to standard out, for improved performance.
111+
*/
112+
public static class FileQueryRuntimeHelper implements QueryRuntimeHelper {
113+
private final Path path;
114+
private final OutputStream out;
115+
116+
private FileQueryRuntimeHelper(Path path) throws IOException {
117+
this.path = path;
118+
this.out = path.getOutputStream();
119+
}
120+
121+
public static FileQueryRuntimeHelper create(Path path) throws QueryRuntimeHelperException {
122+
try {
123+
return new FileQueryRuntimeHelper(path);
124+
} catch (IOException e) {
125+
throw new QueryRuntimeHelperException(
126+
"Could not open query output file " + path.getPathString(),
127+
Code.QUERY_OUTPUT_WRITE_FAILURE,
128+
e);
129+
}
130+
}
131+
132+
@Override
133+
public OutputStream getOutputStreamForQueryOutput() {
134+
return out;
135+
}
136+
137+
@Override
138+
public void afterQueryOutputIsWritten() {}
139+
140+
@Override
141+
public void close() throws QueryRuntimeHelperException {
142+
try {
143+
out.close();
144+
} catch (IOException e) {
145+
throw new QueryRuntimeHelperException(
146+
"Could not close query output file " + path, Code.QUERY_OUTPUT_WRITE_FAILURE, e);
147+
}
148+
}
149+
}
94150
}
95151

96152
/** Describes what went wrong in {@link QueryRuntimeHelper}. */

src/main/java/com/google/devtools/build/lib/runtime/commands/QueryEnvironmentBasedCommand.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ private BlazeCommandResult execInternal(CommandEnvironment env, OptionsParsingRe
167167
.getLabelPrinter(starlarkSemantics, mainRepoTargetParser.getRepoMapping());
168168

169169
try (QueryRuntimeHelper queryRuntimeHelper =
170-
env.getRuntime().getQueryRuntimeHelperFactory().create(env)) {
170+
env.getRuntime().getQueryRuntimeHelperFactory().create(env, queryOptions)) {
171171
Either<BlazeCommandResult, QueryEvalResult> result;
172172
try (AbstractBlazeQueryEnvironment<Target> queryEnv =
173173
newQueryEnvironment(

src/test/shell/integration/bazel_query_test.sh

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ fi
4242
source "$(rlocation "io_bazel/src/test/shell/integration_test_setup.sh")" \
4343
|| { echo "integration_test_setup.sh not found!" >&2; exit 1; }
4444

45-
# `uname` returns the current platform, e.g "MSYS_NT-10.0" or "Linux".
45+
# `uname` returns the current platform, e.g. "MSYS_NT-10.0" or "Linux".
4646
# `tr` converts all upper case letters to lower case.
4747
# `case` matches the result if the `uname | tr` expression to string prefixes
4848
# that use the same wildcards as names do in Bash, i.e. "msys*" matches strings
@@ -82,6 +82,22 @@ EOF
8282
expect_log "//peach:harken"
8383
}
8484

85+
function test_output_to_file() {
86+
rm -rf peach
87+
mkdir -p peach
88+
cat > peach/BUILD <<EOF
89+
sh_library(name='brighton', deps=[':harken'])
90+
sh_library(name='harken')
91+
EOF
92+
93+
bazel query 'deps(//peach:brighton)' --output_file=$TEST_log > $TEST_TMPDIR/query_stdout
94+
95+
expect_log "//peach:brighton"
96+
expect_log "//peach:harken"
97+
98+
assert_equals "" "$(<$TEST_TMPDIR/query_stdout)"
99+
}
100+
85101
function test_invalid_query_fails_parsing() {
86102
bazel query 'deps("--bad_target_name_from_bad_script")' >& "$TEST_log" \
87103
&& fail "Expected failure"

0 commit comments

Comments
 (0)