@@ -267,31 +267,39 @@ public static Map<String, BuildOptions> validate(
267
267
Map <PackageValue .Key , PackageValue > buildSettingPackages ,
268
268
Map <String , BuildOptions > toOptions )
269
269
throws TransitionException {
270
- // Collect settings changed during this transition and their types. This includes settings that
271
- // were only used as inputs as to the transition and thus had their default values added to the
272
- // fromOptions, which in case of a no-op transition directly end up in toOptions.
273
- Map <Label , Rule > changedSettingToRule = Maps .newHashMap ();
270
+ // Collect settings that are inputs or outputs of the transition together with their types.
271
+ // Output setting values will be validated and removed if set to their default.
272
+ Map <Label , Rule > inputAndOutputSettingsToRule = Maps .newHashMap ();
273
+ // Collect settings that were only used as inputs to the transition and thus possibly had their
274
+ // default values added to the fromOptions. They will be removed if set to ther default, but
275
+ // should not be validated.
276
+ Set <Label > inputOnlySettings = Sets .newHashSet ();
274
277
root .visit (
275
278
(StarlarkTransitionVisitor )
276
279
transition -> {
277
- ImmutableSet <Label > changedSettings =
280
+ ImmutableSet <Label > inputAndOutputSettings =
278
281
getRelevantStarlarkSettingsFromTransition (
279
282
transition , Settings .INPUTS_AND_OUTPUTS );
280
- for (Label setting : changedSettings ) {
281
- changedSettingToRule .put (
283
+ ImmutableSet <Label > outputSettings =
284
+ getRelevantStarlarkSettingsFromTransition (transition , Settings .OUTPUTS );
285
+ for (Label setting : inputAndOutputSettings ) {
286
+ inputAndOutputSettingsToRule .put (
282
287
setting , getActual (buildSettingPackages , setting ).getAssociatedRule ());
288
+ if (!outputSettings .contains (setting )) {
289
+ inputOnlySettings .add (setting );
290
+ }
283
291
}
284
292
});
285
293
286
- // Return early if no starlark settings were affected .
287
- if (changedSettingToRule .isEmpty ()) {
294
+ // Return early if the transition has neither inputs nor outputs (rare) .
295
+ if (inputAndOutputSettingsToRule .isEmpty ()) {
288
296
return toOptions ;
289
297
}
290
298
291
299
ImmutableMap .Builder <Label , Label > aliasToActualBuilder = new ImmutableMap .Builder <>();
292
- for (Map .Entry <Label , Rule > changedSettingWithRule : changedSettingToRule .entrySet ()) {
293
- Label setting = changedSettingWithRule .getKey ();
294
- Rule rule = changedSettingWithRule .getValue ();
300
+ for (Map .Entry <Label , Rule > settingWithRule : inputAndOutputSettingsToRule .entrySet ()) {
301
+ Label setting = settingWithRule .getKey ();
302
+ Rule rule = settingWithRule .getValue ();
295
303
if (!rule .getLabel ().equals (setting )) {
296
304
aliasToActualBuilder .put (setting , rule .getLabel ());
297
305
}
@@ -307,69 +315,28 @@ public static Map<String, BuildOptions> validate(
307
315
BuildOptions .Builder cleanedOptions = null ;
308
316
// Clean up aliased values.
309
317
BuildOptions options = unalias (entry .getValue (), aliasToActual );
310
- for (Map .Entry <Label , Rule > changedSettingWithRule : changedSettingToRule .entrySet ()) {
318
+ for (Map .Entry <Label , Rule > settingWithRule : inputAndOutputSettingsToRule .entrySet ()) {
311
319
// If the build setting was referenced in the transition via an alias, this is that alias
312
- Label maybeAliasSetting = changedSettingWithRule .getKey ();
313
- Rule rule = changedSettingWithRule .getValue ();
314
- // If the build setting was *not* referenced in the transition by an alias, this is the same
315
- // value as {@code maybeAliasSetting} above.
320
+ Label maybeAliasSetting = settingWithRule .getKey ();
321
+ Rule rule = settingWithRule .getValue ();
316
322
Label actualSetting = rule .getLabel ();
317
- Object newValue = options .getStarlarkOptions ().get (actualSetting );
318
- // TODO(b/154132845): fix NPE occasionally observed here.
319
- Preconditions .checkState (
320
- newValue != null ,
321
- "Error while attempting to validate new values from starlark"
322
- + " transition(s) with the outputs %s. Post-transition configuration should include"
323
- + " '%s' but only includes starlark options: %s. If you run into this error"
324
- +
" please ping b/154132845 or email [email protected] ." ,
325
- changedSettingToRule .keySet (),
326
- actualSetting ,
327
- options .getStarlarkOptions ().keySet ());
328
- boolean allowsMultiple = rule .getRuleClassObject ().getBuildSetting ().allowsMultiple ();
329
- if (allowsMultiple ) {
330
- // if this setting allows multiple settings
331
- if (!(newValue instanceof List )) {
332
- throw new TransitionException (
333
- String .format (
334
- "'%s' allows multiple values and must be set"
335
- + " in transition using a starlark list instead of single value '%s'" ,
336
- actualSetting , newValue ));
337
- }
338
- List <?> rawNewValueAsList = (List <?>) newValue ;
339
- List <Object > convertedValue = new ArrayList <>();
340
- Type <?> type = rule .getRuleClassObject ().getBuildSetting ().getType ();
341
- for (Object value : rawNewValueAsList ) {
342
- try {
343
- convertedValue .add (type .convert (value , maybeAliasSetting ));
344
- } catch (ConversionException e ) {
345
- throw new TransitionException (e );
346
- }
347
- }
348
- if (convertedValue .equals (
349
- ImmutableList .of (rule .getAttr (STARLARK_BUILD_SETTING_DEFAULT_ATTR_NAME )))) {
350
- if (cleanedOptions == null ) {
351
- cleanedOptions = options .toBuilder ();
352
- }
353
- cleanedOptions .removeStarlarkOption (rule .getLabel ());
354
- }
355
- } else {
356
- // if this setting does not allow multiple settings
357
- Object convertedValue ;
358
- try {
359
- convertedValue =
360
- rule .getRuleClassObject ()
361
- .getBuildSetting ()
362
- .getType ()
363
- .convert (newValue , maybeAliasSetting );
364
- } catch (ConversionException e ) {
365
- throw new TransitionException (e );
366
- }
367
- if (convertedValue .equals (rule .getAttr (STARLARK_BUILD_SETTING_DEFAULT_ATTR_NAME ))) {
368
- if (cleanedOptions == null ) {
369
- cleanedOptions = options .toBuilder ();
370
- }
371
- cleanedOptions .removeStarlarkOption (rule .getLabel ());
323
+ // Input-only settings may have had their literal default value added to the BuildOptions
324
+ // so that the transition can read them. We have to remove these explicitly set value here
325
+ // to preserve the invariant that Starlark settings at default values are not explicitly set
326
+ // in the BuildOptions.
327
+ final boolean isInputOnlySettingAtDefault =
328
+ inputOnlySettings .contains (maybeAliasSetting )
329
+ && rule .getAttr (STARLARK_BUILD_SETTING_DEFAULT_ATTR_NAME )
330
+ .equals (options .getStarlarkOptions ().get (actualSetting ));
331
+ // For output settings, the raw value returned by the transition first has to be validated
332
+ // and converted to the proper type before it can be compared to the default value.
333
+ if (isInputOnlySettingAtDefault
334
+ || validateAndCheckIfAtDefault (
335
+ rule , options , maybeAliasSetting , inputAndOutputSettingsToRule .keySet ())) {
336
+ if (cleanedOptions == null ) {
337
+ cleanedOptions = options .toBuilder ();
372
338
}
339
+ cleanedOptions .removeStarlarkOption (rule .getLabel ());
373
340
}
374
341
}
375
342
// Keep the same instance if we didn't do anything to maintain reference equality later on.
@@ -381,6 +348,81 @@ public static Map<String, BuildOptions> validate(
381
348
return cleanedOptionMap .buildOrThrow ();
382
349
}
383
350
351
+ /**
352
+ * Validate the value of a particular build setting after a transition has been applied.
353
+ *
354
+ * @param buildSettingRule the build setting to validate.
355
+ * @param options the {@link BuildOptions} reflecting the post-transition configuration.
356
+ * @param maybeAliasSetting the label used to refer to the build setting in the transition,
357
+ * possibly an alias. This is only used for error messages.
358
+ * @param inputAndOutputSettings the transition input and output settings. This is only used for
359
+ * error messages.
360
+ * @return {@code true} if and only if the setting is set to its default value after the
361
+ * transition.
362
+ * @throws TransitionException if the value returned by the transition for this setting has an
363
+ * invalid type.
364
+ */
365
+ private static boolean validateAndCheckIfAtDefault (
366
+ Rule buildSettingRule ,
367
+ BuildOptions options ,
368
+ Label maybeAliasSetting ,
369
+ Set <Label > inputAndOutputSettings )
370
+ throws TransitionException {
371
+ // If the build setting was *not* referenced in the transition by an alias, this is the same
372
+ // value as {@code maybeAliasSetting}.
373
+ Label actualSetting = buildSettingRule .getLabel ();
374
+ Object newValue = options .getStarlarkOptions ().get (actualSetting );
375
+ // TODO(b/154132845): fix NPE occasionally observed here.
376
+ Preconditions .checkState (
377
+ newValue != null ,
378
+ "Error while attempting to validate new values from starlark"
379
+ + " transition(s) with the inputs and outputs %s. Post-transition configuration should"
380
+ + " include '%s' but only includes starlark options: %s. If you run into this error"
381
+ +
" please ping b/154132845 or email [email protected] ." ,
382
+ inputAndOutputSettings ,
383
+ actualSetting ,
384
+ options .getStarlarkOptions ().keySet ());
385
+ boolean allowsMultiple =
386
+ buildSettingRule .getRuleClassObject ().getBuildSetting ().allowsMultiple ();
387
+ if (allowsMultiple ) {
388
+ // if this setting allows multiple settings
389
+ if (!(newValue instanceof List )) {
390
+ throw new TransitionException (
391
+ String .format (
392
+ "'%s' allows multiple values and must be set"
393
+ + " in transition using a starlark list instead of single value '%s'" ,
394
+ actualSetting , newValue ));
395
+ }
396
+ List <?> rawNewValueAsList = (List <?>) newValue ;
397
+ List <Object > convertedValue = new ArrayList <>();
398
+ Type <?> type = buildSettingRule .getRuleClassObject ().getBuildSetting ().getType ();
399
+ for (Object value : rawNewValueAsList ) {
400
+ try {
401
+ convertedValue .add (type .convert (value , maybeAliasSetting ));
402
+ } catch (ConversionException e ) {
403
+ throw new TransitionException (e );
404
+ }
405
+ }
406
+ return convertedValue .equals (
407
+ ImmutableList .of (buildSettingRule .getAttr (STARLARK_BUILD_SETTING_DEFAULT_ATTR_NAME )));
408
+ } else {
409
+ // if this setting does not allow multiple settings
410
+ Object convertedValue ;
411
+ try {
412
+ convertedValue =
413
+ buildSettingRule
414
+ .getRuleClassObject ()
415
+ .getBuildSetting ()
416
+ .getType ()
417
+ .convert (newValue , maybeAliasSetting );
418
+ } catch (ConversionException e ) {
419
+ throw new TransitionException (e );
420
+ }
421
+ return convertedValue .equals (
422
+ buildSettingRule .getAttr (STARLARK_BUILD_SETTING_DEFAULT_ATTR_NAME ));
423
+ }
424
+ }
425
+
384
426
/*
385
427
* Resolve aliased build setting issues
386
428
*
0 commit comments