20
20
import com .google .common .base .Preconditions ;
21
21
import com .google .common .base .Splitter ;
22
22
import com .google .devtools .build .lib .buildtool .BuildRequestOptions ;
23
+ import com .google .devtools .build .lib .cmdline .RepositoryMapping ;
24
+ import com .google .devtools .build .lib .cmdline .RepositoryName ;
25
+ import com .google .devtools .build .lib .cmdline .TargetPattern ;
26
+ import com .google .devtools .build .lib .packages .LabelPrinter ;
27
+ import com .google .devtools .build .lib .packages .Target ;
28
+ import com .google .devtools .build .lib .packages .semantics .BuildLanguageOptions ;
23
29
import com .google .devtools .build .lib .profiler .Profiler ;
24
30
import com .google .devtools .build .lib .profiler .SilentCloseable ;
31
+ import com .google .devtools .build .lib .query2 .common .AbstractBlazeQueryEnvironment ;
32
+ import com .google .devtools .build .lib .query2 .common .UniverseScope ;
33
+ import com .google .devtools .build .lib .query2 .engine .QueryEnvironment ;
34
+ import com .google .devtools .build .lib .query2 .engine .QueryEnvironment .Setting ;
35
+ import com .google .devtools .build .lib .query2 .engine .QueryEvalResult ;
36
+ import com .google .devtools .build .lib .query2 .engine .QueryException ;
37
+ import com .google .devtools .build .lib .query2 .engine .QueryExpression ;
38
+ import com .google .devtools .build .lib .query2 .engine .QuerySyntaxException ;
39
+ import com .google .devtools .build .lib .query2 .engine .ThreadSafeOutputFormatterCallback ;
40
+ import com .google .devtools .build .lib .query2 .query .output .QueryOptions ;
25
41
import com .google .devtools .build .lib .runtime .CommandEnvironment ;
42
+ import com .google .devtools .build .lib .runtime .LoadingPhaseThreadsOption ;
26
43
import com .google .devtools .build .lib .runtime .ProjectFileSupport ;
27
44
import com .google .devtools .build .lib .runtime .events .InputFileEvent ;
45
+ import com .google .devtools .build .lib .skyframe .RepositoryMappingValue .RepositoryMappingResolutionException ;
28
46
import com .google .devtools .build .lib .server .FailureDetails .FailureDetail ;
29
47
import com .google .devtools .build .lib .server .FailureDetails .TargetPatterns ;
30
48
import com .google .devtools .build .lib .vfs .FileSystemUtils ;
31
49
import com .google .devtools .build .lib .vfs .Path ;
32
50
import com .google .devtools .common .options .OptionsParsingResult ;
33
51
import java .io .IOException ;
52
+ import java .util .ArrayList ;
53
+ import java .util .LinkedHashSet ;
34
54
import java .util .List ;
55
+ import java .util .Set ;
35
56
import java .util .function .Predicate ;
57
+ import net .starlark .java .eval .StarlarkSemantics ;
36
58
37
59
/** Provides support for reading target patterns from a file or the command-line. */
38
60
public final class TargetPatternsHelper {
@@ -42,20 +64,58 @@ public final class TargetPatternsHelper {
42
64
private TargetPatternsHelper () {}
43
65
44
66
/**
45
- * Reads a list of target patterns, either from the command-line residue or by reading newline
46
- * delimited target patterns from the --target_pattern_file flag. If --target_pattern_file is
47
- * specified and options contain a residue, or if the file cannot be read, throws {@link
48
- * TargetPatternsHelperException}.
67
+ * Reads a list of target patterns, either from the command-line residue, by reading newline
68
+ * delimited target patterns from the --target_pattern_file flag, or from --query/--query_file.
69
+ * If multiple options are specified, throws {@link TargetPatternsHelperException}.
70
+ *
71
+ * @return A list of target patterns.
49
72
*/
50
73
public static List <String > readFrom (CommandEnvironment env , OptionsParsingResult options )
51
74
throws TargetPatternsHelperException {
52
75
List <String > targets = options .getResidue ();
53
76
BuildRequestOptions buildRequestOptions = options .getOptions (BuildRequestOptions .class );
54
- if (!targets .isEmpty () && !buildRequestOptions .targetPatternFile .isEmpty ()) {
77
+
78
+ int optionCount = 0 ;
79
+ if (!targets .isEmpty ()) optionCount ++;
80
+ if (!buildRequestOptions .targetPatternFile .isEmpty ()) optionCount ++;
81
+ if (!buildRequestOptions .query .isEmpty ()) optionCount ++;
82
+ if (!buildRequestOptions .queryFile .isEmpty ()) optionCount ++;
83
+ if (optionCount > 1 ) {
55
84
throw new TargetPatternsHelperException (
56
- "Command-line target pattern and --target_pattern_file cannot both be specified" ,
85
+ "Only one of command-line target patterns, --target_pattern_file, --query, "
86
+ + "or --query_file may be specified" ,
57
87
TargetPatterns .Code .TARGET_PATTERN_FILE_WITH_COMMAND_LINE_PATTERN );
58
- } else if (!buildRequestOptions .targetPatternFile .isEmpty ()) {
88
+ }
89
+
90
+ if (!buildRequestOptions .query .isEmpty ()) {
91
+ try {
92
+ return executeQuery (env , buildRequestOptions .query , options );
93
+ } catch (QueryException | InterruptedException | IOException e ) {
94
+ throw new TargetPatternsHelperException (
95
+ "Error executing query: " + e .getMessage (),
96
+ TargetPatterns .Code .TARGET_PATTERNS_UNKNOWN );
97
+ }
98
+ } else if (!buildRequestOptions .queryFile .isEmpty ()) {
99
+ Path queryFilePath = env .getWorkingDirectory ().getRelative (buildRequestOptions .queryFile );
100
+ try {
101
+ env .getEventBus ()
102
+ .post (
103
+ InputFileEvent .create (
104
+ /* type= */ "query_file" , queryFilePath .getFileSize ()));
105
+ String queryExpression = FileSystemUtils .readContent (queryFilePath , ISO_8859_1 ).trim ();
106
+ return executeQuery (env , queryExpression , options );
107
+ } catch (IOException e ) {
108
+ throw new TargetPatternsHelperException (
109
+ "I/O error reading from " + queryFilePath .getPathString () + ": " + e .getMessage (),
110
+ TargetPatterns .Code .TARGET_PATTERN_FILE_READ_FAILURE );
111
+ } catch (QueryException | InterruptedException e ) {
112
+ throw new TargetPatternsHelperException (
113
+ "Error executing query from file: " + e .getMessage (),
114
+ TargetPatterns .Code .TARGET_PATTERNS_UNKNOWN );
115
+ }
116
+ }
117
+
118
+ if (!buildRequestOptions .targetPatternFile .isEmpty ()) {
59
119
// Works for absolute or relative file.
60
120
Path residuePath =
61
121
env .getWorkingDirectory ().getRelative (buildRequestOptions .targetPatternFile );
@@ -100,4 +160,64 @@ public FailureDetail getFailureDetail() {
100
160
.build ();
101
161
}
102
162
}
163
+
164
+ /** Executes a query and returns the resulting target patterns. */
165
+ private static List <String > executeQuery (
166
+ CommandEnvironment env , String queryExpression , OptionsParsingResult options )
167
+ throws QueryException , InterruptedException , IOException , TargetPatternsHelperException {
168
+ try {
169
+ LoadingPhaseThreadsOption threadsOption = options .getOptions (LoadingPhaseThreadsOption .class );
170
+ RepositoryMapping repoMapping =
171
+ env .getSkyframeExecutor ()
172
+ .getMainRepoMapping (false , threadsOption .threads , env .getReporter ());
173
+ TargetPattern .Parser mainRepoTargetParser =
174
+ new TargetPattern .Parser (env .getRelativeWorkingDirectory (), RepositoryName .MAIN , repoMapping );
175
+
176
+ StarlarkSemantics starlarkSemantics =
177
+ options .getOptions (BuildLanguageOptions .class ).toStarlarkSemantics ();
178
+ LabelPrinter labelPrinter =
179
+ new QueryOptions ().getLabelPrinter (starlarkSemantics , mainRepoTargetParser .getRepoMapping ());
180
+
181
+ AbstractBlazeQueryEnvironment <Target > queryEnv =
182
+ QueryEnvironmentBasedCommand .newQueryEnvironment (
183
+ env ,
184
+ /* keepGoing=*/ false ,
185
+ /* orderedResults= */ false ,
186
+ UniverseScope .EMPTY ,
187
+ threadsOption .threads ,
188
+ Set .of (),
189
+ /* useGraphlessQuery= */ true ,
190
+ mainRepoTargetParser ,
191
+ labelPrinter );
192
+
193
+ QueryExpression expr = QueryExpression .parse (queryExpression , queryEnv );
194
+ Set <String > targetPatterns = new LinkedHashSet <>();
195
+ ThreadSafeOutputFormatterCallback <Target > callback =
196
+ new ThreadSafeOutputFormatterCallback <Target >() {
197
+ @ Override
198
+ public void processOutput (Iterable <Target > partialResult ) {
199
+ for (Target target : partialResult ) {
200
+ targetPatterns .add (target .getLabel ().toString ());
201
+ }
202
+ }
203
+ };
204
+
205
+ QueryEvalResult result = queryEnv .evaluateQuery (expr , callback );
206
+ if (!result .getSuccess ()) {
207
+ throw new TargetPatternsHelperException ("Query evaluation failed" ,
208
+ TargetPatterns .Code .TARGET_PATTERNS_UNKNOWN );
209
+ }
210
+
211
+ return new ArrayList <>(targetPatterns );
212
+ } catch (InterruptedException e ) {
213
+ throw new TargetPatternsHelperException ("Query interrupted" ,
214
+ TargetPatterns .Code .TARGET_PATTERNS_UNKNOWN );
215
+ } catch (RepositoryMappingResolutionException e ) {
216
+ throw new TargetPatternsHelperException (e .getMessage (),
217
+ TargetPatterns .Code .TARGET_PATTERNS_UNKNOWN );
218
+ } catch (QuerySyntaxException e ) {
219
+ throw new TargetPatternsHelperException ("Query syntax error: " + e .getMessage (),
220
+ TargetPatterns .Code .TARGET_PATTERNS_UNKNOWN );
221
+ }
222
+ }
103
223
}
0 commit comments