Skip to content

Commit 1644cea

Browse files
gregestrencopybara-github
authored andcommitted
PROJECT.scl: support "my_scl_config": ["--config=foo"]
### Notes - Only supports `--configs` defined in global rc files, as determined by `GlobalRcUtils` - We should only support `--config`s that only set `BuildOptions` flags. I left that part as a TODO for this change. - This is not meant to be permanent. We ideally want the reverse: `--scl_config` is the source of truth and `--config`s can set `--scl_config`. The current approach supports practical migration since bazerlcs are already sources of truth, and we don't want to further *fork* sources of truth. ### Implementation details I originally tried creating a dedicated `OptionsParser`, parsing `--config=foo`, then reading the results. I eventually realized this fights Bazel's option parsing model, which expects `--config=foo` parses once, at the beginning of a build, with access to core runtime objects. `OptionsParser` doesn't even handle `--config`. `BlazeOptionHandler` and `ConfigExpander` process it and feed results into `OptionsParser`. That logic has strange expectations that are hard to replicate. IMO the current approach is much cleaner. I made a point to hide all implementation details from any code not in .lib.runtime`. There's no need to export that complexity. PiperOrigin-RevId: 709145745 Change-Id: I1cc946bc83b273491a148855b6ec427cf6b3c236
1 parent cf94101 commit 1644cea

File tree

22 files changed

+431
-51
lines changed

22 files changed

+431
-51
lines changed

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,18 @@ java_library(
281281
],
282282
)
283283

284+
java_library(
285+
name = "runtime/config_flag_definitions",
286+
srcs = [
287+
"runtime/ConfigFlagDefinitions.java",
288+
],
289+
deps = [
290+
"//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec",
291+
"//src/main/java/com/google/devtools/common/options",
292+
"//third_party:guava",
293+
],
294+
)
295+
284296
java_library(
285297
name = "runtime",
286298
srcs = glob(
@@ -295,6 +307,7 @@ java_library(
295307
"runtime/BlazeCommandResult.java",
296308
"runtime/CommandDispatcher.java",
297309
"runtime/CommandLinePathFactory.java",
310+
"runtime/ConfigFlagDefinitions.java",
298311
"runtime/GcThrashingDetector.java",
299312
"runtime/KeepGoingOption.java",
300313
"runtime/LoadingPhaseThreadsOption.java",
@@ -313,6 +326,7 @@ java_library(
313326
":runtime/blaze_command_result",
314327
":runtime/command_dispatcher",
315328
":runtime/command_line_path_factory",
329+
":runtime/config_flag_definitions",
316330
":runtime/memory_pressure",
317331
":runtime/test_summary_options",
318332
":starlark_options_parser",

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,7 @@ java_library(
615615
":config/core_options",
616616
":config/invalid_configuration_exception",
617617
":project_resolution_exception",
618+
"//src/main/java/com/google/devtools/build/lib:runtime/config_flag_definitions",
618619
"//src/main/java/com/google/devtools/build/lib/cmdline",
619620
"//src/main/java/com/google/devtools/build/lib/events",
620621
"//src/main/java/com/google/devtools/build/lib/skyframe:project_files_lookup_value",

src/main/java/com/google/devtools/build/lib/analysis/Project.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import com.google.devtools.build.lib.cmdline.Label;
3333
import com.google.devtools.build.lib.events.Event;
3434
import com.google.devtools.build.lib.events.ExtendedEventHandler;
35+
import com.google.devtools.build.lib.runtime.ConfigFlagDefinitions;
3536
import com.google.devtools.build.lib.server.FailureDetails.BuildConfiguration.Code;
3637
import com.google.devtools.build.lib.skyframe.ProjectFilesLookupValue;
3738
import com.google.devtools.build.lib.skyframe.ProjectValue;
@@ -277,6 +278,7 @@ public static FlagSetValue modifyBuildOptionsWithFlagSets(
277278
Label projectFile,
278279
BuildOptions targetOptions,
279280
ImmutableMap<String, String> userOptions,
281+
ConfigFlagDefinitions configFlagDefinitions,
280282
boolean enforceCanonicalConfigs,
281283
ExtendedEventHandler eventHandler,
282284
SkyframeExecutor skyframeExecutor)
@@ -288,6 +290,7 @@ public static FlagSetValue modifyBuildOptionsWithFlagSets(
288290
targetOptions.get(CoreOptions.class).sclConfig,
289291
targetOptions,
290292
userOptions,
293+
configFlagDefinitions,
291294
enforceCanonicalConfigs);
292295

293296
EvaluationResult<SkyValue> result =

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ static ProjectEvaluationResult evaluateProjectFile(
280280
BuildTool.applySclConfigs(
281281
buildOptions,
282282
userOptions,
283+
env.getConfigFlagDefinitions(),
283284
projectFile,
284285
request.getBuildOptions().enforceProjectConfigs,
285286
env.getSkyframeExecutor(),

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
import com.google.devtools.build.lib.runtime.BlazeRuntime;
7676
import com.google.devtools.build.lib.runtime.CommandEnvironment;
7777
import com.google.devtools.build.lib.runtime.CommandLineEvent.CanonicalCommandLineEvent;
78+
import com.google.devtools.build.lib.runtime.ConfigFlagDefinitions;
7879
import com.google.devtools.build.lib.server.FailureDetails.ActionQuery;
7980
import com.google.devtools.build.lib.server.FailureDetails.BuildConfiguration.Code;
8081
import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
@@ -1016,6 +1017,7 @@ public static PathFragmentPrefixTrie getWorkingSetMatcherForSkyfocus(
10161017
public static ImmutableSet<String> applySclConfigs(
10171018
BuildOptions buildOptionsBeforeFlagSets,
10181019
ImmutableMap<String, String> userOptions,
1020+
ConfigFlagDefinitions configFlagDefinitions,
10191021
Label projectFile,
10201022
boolean enforceCanonicalConfigs,
10211023
SkyframeExecutor skyframeExecutor,
@@ -1027,6 +1029,7 @@ public static ImmutableSet<String> applySclConfigs(
10271029
projectFile,
10281030
buildOptionsBeforeFlagSets,
10291031
userOptions,
1032+
configFlagDefinitions,
10301033
enforceCanonicalConfigs,
10311034
eventHandler,
10321035
skyframeExecutor);

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import com.google.devtools.build.lib.profiler.Profiler;
5050
import com.google.devtools.build.lib.profiler.ProfilerTask;
5151
import com.google.devtools.build.lib.profiler.SilentCloseable;
52+
import com.google.devtools.build.lib.runtime.BlazeOptionHandler.DetailedParseResults;
5253
import com.google.devtools.build.lib.runtime.InstrumentationOutputFactory.DestinationRelativeTo;
5354
import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy;
5455
import com.google.devtools.build.lib.server.FailureDetails;
@@ -339,11 +340,12 @@ private BlazeCommandResult execExclusively(
339340
BlazeOptionHandler optionHandler =
340341
new BlazeOptionHandler(
341342
runtime, workspace, command, commandAnnotation, optionsParser, invocationPolicy);
342-
DetailedExitCode earlyExitCode =
343-
optionHandler.parseOptions(
343+
DetailedParseResults parseResults =
344+
optionHandler.parseOptionsAndGetConfigDefinitions(
344345
args,
345346
storedEventHandler,
346347
/* invocationPolicyFlagListBuilder= */ ImmutableList.builder());
348+
DetailedExitCode earlyExitCode = parseResults.detailedExitCode();
347349
OptionsParsingResult options = optionHandler.getOptionsResult();
348350

349351
// The initCommand call also records the start time for the timestamp granularity monitor.
@@ -359,7 +361,8 @@ private BlazeCommandResult execExclusively(
359361
commandExtensions,
360362
this::setShutdownReason,
361363
commandExtensionReporter,
362-
attemptNumber);
364+
attemptNumber,
365+
parseResults.configFlagDefinitions());
363366

364367
if (!attemptedCommandIds.isEmpty()) {
365368
if (attemptedCommandIds.contains(env.getCommandId())) {

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

Lines changed: 80 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.google.common.base.Preconditions;
2121
import com.google.common.collect.ArrayListMultimap;
2222
import com.google.common.collect.ImmutableList;
23+
import com.google.common.collect.ImmutableListMultimap;
2324
import com.google.common.collect.ImmutableSet;
2425
import com.google.common.collect.Iterables;
2526
import com.google.common.collect.ListMultimap;
@@ -29,6 +30,7 @@
2930
import com.google.devtools.build.lib.events.EventHandler;
3031
import com.google.devtools.build.lib.events.ExtendedEventHandler;
3132
import com.google.devtools.build.lib.packages.Target;
33+
import com.google.devtools.build.lib.runtime.ConfigFlagDefinitions.ConfigDefinition;
3234
import com.google.devtools.build.lib.runtime.StarlarkOptionsParser.BuildSettingLoader;
3335
import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy;
3436
import com.google.devtools.build.lib.server.FailureDetails;
@@ -245,7 +247,13 @@ void parseRcOptions(
245247
}
246248
}
247249

248-
private void parseArgsAndConfigs(List<String> args, ExtendedEventHandler eventHandler)
250+
/**
251+
* Returns a map from rc file definitions to the options they define and which rc files defined.
252+
* them. For example, "build:asan" keys a {@code --config=asan} definition and "build" keys
253+
* options that apply to all build commands.
254+
*/
255+
private ListMultimap<String, RcChunkOfArgs> parseArgsAndConfigs(
256+
List<String> args, ExtendedEventHandler eventHandler)
249257
throws OptionsParsingException, InterruptedException, AbruptExitException {
250258
Path workspaceDirectory = workspace.getWorkspace();
251259
// TODO(ulfjack): The working directory is passed by the client as part of CommonCommandOptions,
@@ -308,6 +316,7 @@ private void parseArgsAndConfigs(List<String> args, ExtendedEventHandler eventHa
308316
}
309317

310318
expandConfigOptions(eventHandler, commandToRcArgs);
319+
return commandToRcArgs;
311320
}
312321

313322
/**
@@ -423,6 +432,10 @@ DetailedExitCode parseStarlarkOptions(CommandEnvironment env) {
423432
return DetailedExitCode.success();
424433
}
425434

435+
/** Detailed parsing results: exit code and {@code --config=foo} definitions. */
436+
static record DetailedParseResults(
437+
DetailedExitCode detailedExitCode, ConfigFlagDefinitions configFlagDefinitions) {}
438+
426439
/**
427440
* Parses the options, taking care not to generate any output to outErr, return, or throw an
428441
* exception.
@@ -433,29 +446,51 @@ DetailedExitCode parseOptions(
433446
List<String> args,
434447
ExtendedEventHandler eventHandler,
435448
ImmutableList.Builder<OptionAndRawValue> invocationPolicyFlagListBuilder) {
436-
DetailedExitCode result =
437-
parseOptionsInternal(args, eventHandler, invocationPolicyFlagListBuilder);
438-
if (!result.isSuccess()) {
449+
DetailedParseResults result =
450+
parseOptionsInternal(
451+
args, eventHandler, invocationPolicyFlagListBuilder, /* getConfigDefinitions= */ false);
452+
if (!result.detailedExitCode.isSuccess()) {
439453
optionsParser.setError();
440454
}
441-
return result;
455+
return result.detailedExitCode;
442456
}
443457

444-
private DetailedExitCode parseOptionsInternal(
458+
/**
459+
* {@link #parseOptions} variation that also returns {@code --config=foo} definitions. Callers can
460+
* use this to determine which flags {@code --config=foo} sets.
461+
*/
462+
DetailedParseResults parseOptionsAndGetConfigDefinitions(
445463
List<String> args,
446464
ExtendedEventHandler eventHandler,
447465
ImmutableList.Builder<OptionAndRawValue> invocationPolicyFlagListBuilder) {
466+
DetailedParseResults result =
467+
parseOptionsInternal(
468+
args, eventHandler, invocationPolicyFlagListBuilder, /* getConfigDefinitions= */ true);
469+
if (!result.detailedExitCode.isSuccess()) {
470+
optionsParser.setError();
471+
}
472+
return result;
473+
}
474+
475+
private DetailedParseResults parseOptionsInternal(
476+
List<String> args,
477+
ExtendedEventHandler eventHandler,
478+
ImmutableList.Builder<OptionAndRawValue> invocationPolicyFlagListBuilder,
479+
boolean getConfigDefinitions) {
448480
// The initialization code here was carefully written to parse the options early before we call
449481
// into the BlazeModule APIs, which means we must not generate any output to outErr, return, or
450482
// throw an exception. All the events happening here are instead stored in a temporary event
451483
// handler, and later replayed.
452484
DetailedExitCode earlyExitCode = checkCwdInWorkspace(eventHandler);
453485
if (!earlyExitCode.isSuccess()) {
454-
return earlyExitCode;
486+
return new DetailedParseResults(
487+
earlyExitCode, new ConfigFlagDefinitions(ImmutableListMultimap.of()));
455488
}
456489

490+
ListMultimap<String, RcChunkOfArgs> rcDefinitions = ImmutableListMultimap.of();
491+
DetailedExitCode exitCode;
457492
try {
458-
parseArgsAndConfigs(args, eventHandler);
493+
rcDefinitions = parseArgsAndConfigs(args, eventHandler);
459494
// Allow the command to edit the options.
460495
command.editOptions(optionsParser);
461496
// Merge the invocation policy that is user-supplied, from the command line, and any
@@ -485,22 +520,49 @@ private DetailedExitCode parseOptionsInternal(
485520
for (String warning : commonOptions.deprecationWarnings) {
486521
eventHandler.handle(Event.warn(warning));
487522
}
523+
exitCode = DetailedExitCode.success();
488524
} catch (OptionsParsingException e) {
489525
String logMessage = "Error parsing options";
490526
logger.atInfo().withCause(e).log("%s", logMessage);
491-
return processOptionsParsingException(
492-
eventHandler, e, logMessage, Code.OPTIONS_PARSE_FAILURE);
527+
exitCode =
528+
processOptionsParsingException(eventHandler, e, logMessage, Code.OPTIONS_PARSE_FAILURE);
493529
} catch (InterruptedException e) {
494-
return DetailedExitCode.of(
495-
FailureDetail.newBuilder()
496-
.setInterrupted(
497-
FailureDetails.Interrupted.newBuilder()
498-
.setCode(FailureDetails.Interrupted.Code.INTERRUPTED))
499-
.build());
530+
exitCode =
531+
DetailedExitCode.of(
532+
FailureDetail.newBuilder()
533+
.setInterrupted(
534+
FailureDetails.Interrupted.newBuilder()
535+
.setCode(FailureDetails.Interrupted.Code.INTERRUPTED))
536+
.build());
500537
} catch (AbruptExitException e) {
501-
return e.getDetailedExitCode();
538+
exitCode = e.getDetailedExitCode();
502539
}
503-
return DetailedExitCode.success();
540+
541+
if (!getConfigDefinitions) {
542+
return new DetailedParseResults(
543+
exitCode, new ConfigFlagDefinitions(ImmutableListMultimap.of()));
544+
}
545+
// Transforms all rc definitions into valid --config definitions for this command. For example,
546+
// "build:asan" is valid for a build command but not "test:asan". Rc definitions like "build"
547+
// aren't included because those aren't --config definitions.
548+
var validConfigDefs = new ImmutableListMultimap.Builder<String, ConfigDefinition>();
549+
List<String> matchingRcCommands = getCommandNamesToParse(commandAnnotation);
550+
for (var entry : rcDefinitions.entries()) {
551+
String rcKey = entry.getKey();
552+
int firstColon = rcKey.indexOf(":");
553+
if (firstColon == -1) {
554+
continue;
555+
}
556+
String cmd = rcKey.substring(0, firstColon);
557+
String configName = rcKey.substring(firstColon + 1);
558+
if (matchingRcCommands.contains(cmd)) {
559+
validConfigDefs.put(
560+
configName,
561+
new ConfigFlagDefinitions.ConfigDefinition(
562+
ImmutableList.copyOf(entry.getValue().getArgs()), entry.getValue().getRcFile()));
563+
}
564+
}
565+
return new DetailedParseResults(exitCode, new ConfigFlagDefinitions(validConfigDefs.build()));
504566
}
505567

506568
/**

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,8 @@ public CommandEnvironment initCommand(
233233
List<Any> commandExtensions,
234234
Consumer<String> shutdownReasonConsumer,
235235
CommandExtensionReporter commandExtensionReporter,
236-
int attemptNumber) {
236+
int attemptNumber,
237+
ConfigFlagDefinitions configFlagDefinitions) {
237238
quiescingExecutors.resetParameters(options);
238239
CommandEnvironment env =
239240
new CommandEnvironment(
@@ -253,7 +254,8 @@ public CommandEnvironment initCommand(
253254
commandExtensions,
254255
shutdownReasonConsumer,
255256
commandExtensionReporter,
256-
attemptNumber);
257+
attemptNumber,
258+
configFlagDefinitions);
257259
skyframeExecutor.setClientEnv(env.getClientEnv());
258260
BuildRequestOptions buildRequestOptions = options.getOptions(BuildRequestOptions.class);
259261
if (buildRequestOptions != null && !buildRequestOptions.useActionCache) {

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ public class CommandEnvironment {
100100
private final BlazeRuntime runtime;
101101
private final BlazeWorkspace workspace;
102102
private final BlazeDirectories directories;
103+
private final ConfigFlagDefinitions configFlagDefinitions;
103104

104105
private final UUID commandId; // Unique identifier for the command being run
105106
private final String buildRequestId; // Unique identifier for the build being run
@@ -223,7 +224,8 @@ public void exit(AbruptExitException exception) {
223224
List<Any> commandExtensions,
224225
Consumer<String> shutdownReasonConsumer,
225226
CommandExtensionReporter commandExtensionReporter,
226-
int attemptNumber) {
227+
int attemptNumber,
228+
ConfigFlagDefinitions configFlagDefinitions) {
227229
checkArgument(attemptNumber >= 1);
228230

229231
this.runtime = runtime;
@@ -243,6 +245,8 @@ public void exit(AbruptExitException exception) {
243245
this.blazeModuleEnvironment = new BlazeModuleEnvironment();
244246
this.timestampGranularityMonitor = new TimestampGranularityMonitor(runtime.getClock());
245247
this.attemptNumber = attemptNumber;
248+
this.configFlagDefinitions = configFlagDefinitions;
249+
246250
// Record the command's starting time again, for use by
247251
// TimestampGranularityMonitor.waitForTimestampGranularity().
248252
// This should be done as close as possible to the start of
@@ -462,6 +466,11 @@ public OptionsParsingResult getOptions() {
462466
return options;
463467
}
464468

469+
/** {@code --config} definitions for this invocation. */
470+
public ConfigFlagDefinitions getConfigFlagDefinitions() {
471+
return configFlagDefinitions;
472+
}
473+
465474
public InvocationPolicy getInvocationPolicy() {
466475
return invocationPolicy;
467476
}

0 commit comments

Comments
 (0)