Skip to content

Commit 51c90c7

Browse files
twiggcopybara-github
authored andcommitted
Add --experimental_exec_configuration_distinguisher
This work is cleaved off from the --experimental_output_directory_naming_scheme work (see #14023) and is meant as a less-exotic (and more quickly deployable) way to fix-up potential action conflicts due to insufficient configuration distinguishing in or around the exec transition. The following modes are added: legacy: Keep current behavior of only setting platform_suffix to exec-%X where %X is a hash of the current execution platform. fullHash: Set platform_suffix to exec-%X where %X is a hash of the entire post-configuration transition (although, with platform_suffix emptied, to prevent self-referential hashing headaches). diffToAffected: Set platform_suffix to exec (only for ease in reading output paths) and update `affected by Starlark transition` with all changed options. This effectively treats the exec transition like a Starlark transition. off: Do not touch platform_suffix or do any other work inside the execution transition to ensure distinguished configurations. This is expected to be used with --experimental_output_directory_naming_scheme as part of removing these local configuration distinguishers in favor of a global system. PiperOrigin-RevId: 433839227
1 parent 3e6327c commit 51c90c7

File tree

7 files changed

+242
-7
lines changed

7 files changed

+242
-7
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,6 +1688,7 @@ java_library(
16881688
":config/transitions/patch_transition",
16891689
":config/transitions/transition_factory",
16901690
":platform_options",
1691+
":starlark/function_transition_util",
16911692
"//src/main/java/com/google/devtools/build/lib/cmdline",
16921693
"//src/main/java/com/google/devtools/build/lib/events",
16931694
"//src/main/java/com/google/devtools/build/lib/packages",

src/main/java/com/google/devtools/build/lib/analysis/config/CoreOptions.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,45 @@ public String getTypeDescription() {
287287
metadataTags = {OptionMetadataTag.INTERNAL})
288288
public List<String> affectedByStarlarkTransition;
289289

290+
/** Values for the --experimental_exec_configuration_distinguisher options * */
291+
public enum ExecConfigurationDistinguisherScheme {
292+
/** Use hash of selected execution platform for platform_suffix. * */
293+
LEGACY,
294+
/** Do not touch platform_suffix or do anything else. * */
295+
OFF,
296+
/** Use hash of entire configuration (with platform_suffix="") for platform_suffix. * */
297+
FULL_HASH,
298+
/** Set platform_suffix to "exec", instead update `affected by starlark transition` * */
299+
DIFF_TO_AFFECTED
300+
}
301+
302+
/** Converter for the {@code --experimental_exec_configuration_distinguisher} options. */
303+
public static class ExecConfigurationDistinguisherSchemeConverter
304+
extends EnumConverter<ExecConfigurationDistinguisherScheme> {
305+
public ExecConfigurationDistinguisherSchemeConverter() {
306+
super(
307+
ExecConfigurationDistinguisherScheme.class,
308+
"Exec transition configuration distinguisher scheme");
309+
}
310+
}
311+
312+
@Option(
313+
name = "experimental_exec_configuration_distinguisher",
314+
defaultValue = "legacy",
315+
converter = ExecConfigurationDistinguisherSchemeConverter.class,
316+
documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
317+
effectTags = {OptionEffectTag.AFFECTS_OUTPUTS},
318+
metadataTags = {OptionMetadataTag.EXPERIMENTAL},
319+
help =
320+
"Please only use this flag as part of a suggested migration or testing strategy due to"
321+
+ " potential for action conflicts. Controls how the execution transition changes the"
322+
+ " platform_suffix flag. In legacy mode, sets it to a hash of the execution"
323+
+ " platform. In fullhash mode, sets it to a hash of the entire configuration. In off"
324+
+ " mode, does not touch it.")
325+
public ExecConfigurationDistinguisherScheme execConfigurationDistinguisherScheme;
326+
327+
/* At the moment, EXPLICIT_IN_OUTPUT_PATH is not being set here because platform_suffix
328+
* is being used as a configuration distinguisher for the exec transition. */
290329
@Option(
291330
name = "platform_suffix",
292331
defaultValue = "null",
@@ -858,6 +897,7 @@ public FragmentOptions getHost() {
858897
host.compilationMode = hostCompilationMode;
859898
host.isHost = true;
860899
host.isExec = false;
900+
host.execConfigurationDistinguisherScheme = execConfigurationDistinguisherScheme;
861901
host.outputPathsMode = outputPathsMode;
862902
host.enableRunfiles = enableRunfiles;
863903
host.executionInfoModifier = executionInfoModifier;

src/main/java/com/google/devtools/build/lib/analysis/config/ExecutionTransitionFactory.java

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.google.devtools.build.lib.analysis.PlatformOptions;
2424
import com.google.devtools.build.lib.analysis.config.transitions.PatchTransition;
2525
import com.google.devtools.build.lib.analysis.config.transitions.TransitionFactory;
26+
import com.google.devtools.build.lib.analysis.starlark.FunctionTransitionUtil;
2627
import com.google.devtools.build.lib.cmdline.Label;
2728
import com.google.devtools.build.lib.events.EventHandler;
2829
import com.google.devtools.build.lib.packages.AttributeTransitionData;
@@ -127,8 +128,6 @@ private static BuildOptions transitionImpl(BuildOptionsView options, Label execu
127128
CoreOptions coreOptions = checkNotNull(execOptions.get(CoreOptions.class));
128129
coreOptions.isHost = false;
129130
coreOptions.isExec = true;
130-
coreOptions.platformSuffix =
131-
String.format("exec-%X", executionPlatform.getCanonicalForm().hashCode());
132131
// Disable extra actions
133132
coreOptions.actionListeners = null;
134133

@@ -138,20 +137,57 @@ private static BuildOptions transitionImpl(BuildOptionsView options, Label execu
138137
platformOptions.platforms = ImmutableList.of(executionPlatform);
139138
}
140139

141-
BuildOptions result = execOptions.underlying();
142140
// Remove any FeatureFlags that were set.
143141
ImmutableList<Label> featureFlags =
144142
execOptions.underlying().getStarlarkOptions().entrySet().stream()
145143
.filter(entry -> entry.getValue() instanceof FeatureFlagValue)
146144
.map(Map.Entry::getKey)
147145
.collect(toImmutableList());
146+
147+
BuildOptions result = execOptions.underlying();
148148
if (!featureFlags.isEmpty()) {
149149
BuildOptions.Builder resultBuilder = result.toBuilder();
150150
featureFlags.forEach(resultBuilder::removeStarlarkOption);
151151
result = resultBuilder.build();
152152
}
153153

154-
// Finally, re-apply the platform mappings in case anything was changed.
154+
// Finally, set the configuration distinguisher, platform_suffix, according to the
155+
// selected scheme.
156+
157+
// The conditional use of a Builder above may have replaced result and underlying options
158+
// with a clone so must refresh it.
159+
coreOptions = result.get(CoreOptions.class);
160+
// TODO(blaze-configurability-team): These updates probably requires a bit too much knowledge
161+
// of exactly how the immutable state and mutable state of BuildOptions is interacting.
162+
// Might be good to have an option to wipeout that state rather than cloning so much.
163+
switch (coreOptions.execConfigurationDistinguisherScheme) {
164+
case LEGACY:
165+
coreOptions.platformSuffix =
166+
String.format("exec-%X", executionPlatform.getCanonicalForm().hashCode());
167+
break;
168+
case FULL_HASH:
169+
coreOptions.platformSuffix = "";
170+
// execOptions creation above made a clone, which will have a fresh hashCode
171+
int fullHash = result.hashCode();
172+
coreOptions.platformSuffix = String.format("exec-%X", fullHash);
173+
// Previous call to hashCode irreparably locked in state so must clone to refresh since
174+
// options mutated after that
175+
result = result.clone();
176+
break;
177+
case DIFF_TO_AFFECTED:
178+
// Setting platform_suffix here should not be necessary for correctness but
179+
// done for user clarity.
180+
coreOptions.platformSuffix = "exec";
181+
ImmutableSet<String> diff =
182+
FunctionTransitionUtil.getAffectedByStarlarkTransitionViaDiff(
183+
result, options.underlying());
184+
FunctionTransitionUtil.updateAffectedByStarlarkTransition(coreOptions, diff);
185+
// Previous call to diff irreparably locked in state so must clone to refresh.
186+
result = result.clone();
187+
break;
188+
default:
189+
// else if OFF do nothing
190+
}
155191

156192
return result;
157193
}

src/main/java/com/google/devtools/build/lib/analysis/starlark/FunctionTransitionUtil.java

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.google.common.collect.ImmutableMap;
2727
import com.google.common.collect.ImmutableSet;
2828
import com.google.common.collect.Sets;
29+
import com.google.common.collect.Streams;
2930
import com.google.devtools.build.lib.analysis.config.BuildOptions;
3031
import com.google.devtools.build.lib.analysis.config.CoreOptions;
3132
import com.google.devtools.build.lib.analysis.config.FragmentOptions;
@@ -49,6 +50,7 @@
4950
import java.util.Set;
5051
import java.util.TreeMap;
5152
import java.util.TreeSet;
53+
import java.util.stream.Stream;
5254
import javax.annotation.Nullable;
5355
import net.starlark.java.eval.Dict;
5456
import net.starlark.java.eval.EvalException;
@@ -432,9 +434,15 @@ public static String computeOutputDirectoryNameFragment(BuildOptions toOptions)
432434
String nativeOptionName = optionName.substring(COMMAND_LINE_OPTION_PREFIX.length());
433435
Object value;
434436
try {
437+
OptionInfo optionInfo = optionInfoMap.get(nativeOptionName);
438+
if (optionInfo == null) {
439+
// This can occur if toOptions has been trimmed but the supplied chosen native options
440+
// includes that trimmed options.
441+
// (e.g. legacy naming mode, using --trim_test_configuration and --test_arg transition).
442+
continue;
443+
}
435444
value =
436-
optionInfoMap
437-
.get(nativeOptionName)
445+
optionInfo
438446
.getDefinition()
439447
.getField()
440448
.get(toOptions.get(optionInfoMap.get(nativeOptionName).getOptionClass()));
@@ -462,6 +470,26 @@ public static String computeOutputDirectoryNameFragment(BuildOptions toOptions)
462470
return transitionDirectoryNameFragment(hashStrs.build());
463471
}
464472

473+
public static ImmutableSet<String> getAffectedByStarlarkTransitionViaDiff(
474+
BuildOptions toOptions, BuildOptions baselineOptions) {
475+
if (toOptions.equals(baselineOptions)) {
476+
return ImmutableSet.of();
477+
}
478+
479+
BuildOptions.OptionsDiff diff = BuildOptions.diff(toOptions, baselineOptions);
480+
Stream<String> diffNative =
481+
diff.getFirst().keySet().stream()
482+
.filter(
483+
optionDef ->
484+
!optionDef.hasOptionMetadataTag(OptionMetadataTag.EXPLICIT_IN_OUTPUT_PATH))
485+
.map(option -> COMMAND_LINE_OPTION_PREFIX + option.getOptionName());
486+
// Note: getChangedStarlarkOptions includes all changed options, added options and removed
487+
// options between baselineOptions and toOptions. This is necessary since there is no current
488+
// notion of trimming a Starlark option: 'null' or non-existent justs means set to default.
489+
Stream<String> diffStarlark = diff.getChangedStarlarkOptions().stream().map(Label::toString);
490+
return Streams.concat(diffNative, diffStarlark).collect(toImmutableSet());
491+
}
492+
465493
/**
466494
* Extend the global build config affectedByStarlarkTransition, by adding any new option names
467495
* from changedOptions

src/main/java/com/google/devtools/common/options/OptionDefinition.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
package com.google.devtools.common.options;
1616

17+
import static java.util.Arrays.stream;
1718
import static java.util.Comparator.comparing;
1819

1920
import com.google.common.collect.ImmutableList;
@@ -131,6 +132,11 @@ public OptionMetadataTag[] getOptionMetadataTags() {
131132
return optionAnnotation.metadataTags();
132133
}
133134

135+
/** {@link Option#metadataTags()} */
136+
public boolean hasOptionMetadataTag(OptionMetadataTag tag) {
137+
return stream(getOptionMetadataTags()).anyMatch(tag::equals);
138+
}
139+
134140
/** {@link Option#converter()} ()} */
135141
@SuppressWarnings({"rawtypes"})
136142
public Class<? extends Converter> getProvidedConverter() {

src/test/java/com/google/devtools/build/lib/analysis/config/ExecutionTransitionFactoryTest.java

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,127 @@ public void executionTransition_noExecPlatform()
9292
assertThat(result).isNotNull();
9393
assertThat(result).isEqualTo(options);
9494
}
95+
96+
@Test
97+
public void executionTransition_confDist_legacy()
98+
throws OptionsParsingException, InterruptedException {
99+
ExecutionTransitionFactory execTransitionFactory = ExecutionTransitionFactory.create();
100+
PatchTransition transition =
101+
execTransitionFactory.create(
102+
AttributeTransitionData.builder()
103+
.attributes(FakeAttributeMapper.empty())
104+
.executionPlatform(EXECUTION_PLATFORM)
105+
.build());
106+
107+
assertThat(transition).isNotNull();
108+
109+
// Apply the transition.
110+
BuildOptions options =
111+
BuildOptions.of(
112+
ImmutableList.of(CoreOptions.class, PlatformOptions.class),
113+
"--platforms=//platform:target",
114+
"--experimental_exec_configuration_distinguisher=legacy");
115+
116+
BuildOptions result =
117+
transition.patch(
118+
new BuildOptionsView(options, transition.requiresOptionFragments()),
119+
new StoredEventHandler());
120+
121+
assertThat(result.get(CoreOptions.class).affectedByStarlarkTransition).isEmpty();
122+
assertThat(result.get(CoreOptions.class).platformSuffix)
123+
.contains(String.format("%X", EXECUTION_PLATFORM.getCanonicalForm().hashCode()));
124+
}
125+
126+
@Test
127+
public void executionTransition_confDist_fullHash()
128+
throws OptionsParsingException, InterruptedException {
129+
ExecutionTransitionFactory execTransitionFactory = ExecutionTransitionFactory.create();
130+
PatchTransition transition =
131+
execTransitionFactory.create(
132+
AttributeTransitionData.builder()
133+
.attributes(FakeAttributeMapper.empty())
134+
.executionPlatform(EXECUTION_PLATFORM)
135+
.build());
136+
137+
assertThat(transition).isNotNull();
138+
139+
// Apply the transition.
140+
BuildOptions options =
141+
BuildOptions.of(
142+
ImmutableList.of(CoreOptions.class, PlatformOptions.class),
143+
"--platforms=//platform:target",
144+
"--experimental_exec_configuration_distinguisher=full_hash");
145+
146+
BuildOptions result =
147+
transition.patch(
148+
new BuildOptionsView(options, transition.requiresOptionFragments()),
149+
new StoredEventHandler());
150+
151+
BuildOptions mutableCopy = result.clone();
152+
mutableCopy.get(CoreOptions.class).platformSuffix = "";
153+
int fullHash = mutableCopy.hashCode();
154+
155+
assertThat(result.get(CoreOptions.class).affectedByStarlarkTransition).isEmpty();
156+
assertThat(result.get(CoreOptions.class).platformSuffix)
157+
.contains(String.format("%X", fullHash));
158+
}
159+
160+
@Test
161+
public void executionTransition_confDist_diffToAffected()
162+
throws OptionsParsingException, InterruptedException {
163+
ExecutionTransitionFactory execTransitionFactory = ExecutionTransitionFactory.create();
164+
PatchTransition transition =
165+
execTransitionFactory.create(
166+
AttributeTransitionData.builder()
167+
.attributes(FakeAttributeMapper.empty())
168+
.executionPlatform(EXECUTION_PLATFORM)
169+
.build());
170+
171+
assertThat(transition).isNotNull();
172+
173+
// Apply the transition.
174+
BuildOptions options =
175+
BuildOptions.of(
176+
ImmutableList.of(CoreOptions.class, PlatformOptions.class),
177+
"--platforms=//platform:target",
178+
"--experimental_exec_configuration_distinguisher=diff_to_affected");
179+
180+
BuildOptions result =
181+
transition.patch(
182+
new BuildOptionsView(options, transition.requiresOptionFragments()),
183+
new StoredEventHandler());
184+
185+
assertThat(result.get(CoreOptions.class).affectedByStarlarkTransition).isNotEmpty();
186+
assertThat(result.get(CoreOptions.class).platformSuffix).isEqualTo("exec");
187+
}
188+
189+
@Test
190+
public void executionTransition_confDist_off()
191+
throws OptionsParsingException, InterruptedException {
192+
ExecutionTransitionFactory execTransitionFactory = ExecutionTransitionFactory.create();
193+
PatchTransition transition =
194+
execTransitionFactory.create(
195+
AttributeTransitionData.builder()
196+
.attributes(FakeAttributeMapper.empty())
197+
.executionPlatform(EXECUTION_PLATFORM)
198+
.build());
199+
200+
assertThat(transition).isNotNull();
201+
202+
// Apply the transition.
203+
BuildOptions options =
204+
BuildOptions.of(
205+
ImmutableList.of(CoreOptions.class, PlatformOptions.class),
206+
"--platforms=//platform:target",
207+
"--experimental_exec_configuration_distinguisher=off");
208+
209+
BuildOptions result =
210+
transition.patch(
211+
new BuildOptionsView(options, transition.requiresOptionFragments()),
212+
new StoredEventHandler());
213+
214+
assertThat(result.get(CoreOptions.class).affectedByStarlarkTransition).isEmpty();
215+
assertThat(result.get(CoreOptions.class).platformSuffix)
216+
.isEqualTo(options.get(CoreOptions.class).platformSuffix);
217+
}
95218
}

src/test/java/com/google/devtools/build/lib/analysis/test/TestTrimmingTransitionTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ public void composeCommutativelyWithExecutionTransition()
114114
BuildOptions.of(
115115
ImmutableList.of(CoreOptions.class, PlatformOptions.class, TestOptions.class),
116116
"--platforms=//platform:target",
117-
"--trim_test_configuration");
117+
"--trim_test_configuration",
118+
"--experimental_exec_configuration_distinguisher=off");
118119

119120
EventHandler handler = new StoredEventHandler();
120121

0 commit comments

Comments
 (0)