Skip to content

Commit 557a7e7

Browse files
fmeumtwigg
and
twigg
authored
Fixes for the Starlark transition hash computation (#14251)
* Move transitionDirectoryNameFragment calculation to BuildConfigurationValue As per discussion in b/203470434, transitionDirectoryNameFragment should completely depend on the current values of the (rest of) the BuildOptions class. Thus, it is far better to have this always computed from BuildOptions when building a BuildConfigurationValue than rely on users keeping it consistent. (Other results of BuildConfigurationValue itself are themselves wholly computed from BuildOptions so placement there is a natural fit.) This naturally fixes the exec transition forgetting to update transitionDirectoryNameFragment. This fixes and subsumes #13464 and #13915, respectively. This is related to #14023 PiperOrigin-RevId: 407913175 * Properly account for StarlarkOptions at their default (=null) when calculating ST-hash Previously, the hash calculation was skipping including StarlarkOptions that happened to be at their default values. This is wrong since those values may still be in "affected by Starlark transition" (because either the commandline set them and the Starlark transition reset them to their Starlark defaults thus still requiring a hash change OR the commandline did not set them but a series of Starlark transitions did an default->B->default anyways causing the Starlark option to still be 'stuck' in "affected by Starlark transition"). Resolves #14239 PiperOrigin-RevId: 408701552 Co-authored-by: twigg <[email protected]>
1 parent c500e7a commit 557a7e7

File tree

8 files changed

+178
-74
lines changed

8 files changed

+178
-74
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -1526,6 +1526,7 @@ java_library(
15261526
":config/run_under",
15271527
":config/transitive_option_details",
15281528
":platform_options",
1529+
":starlark/function_transition_util",
15291530
"//src/main/java/com/google/devtools/build/lib/actions",
15301531
"//src/main/java/com/google/devtools/build/lib/actions:artifacts",
15311532
"//src/main/java/com/google/devtools/build/lib/buildeventstream",

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

+19-2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import com.google.devtools.build.lib.analysis.BlazeDirectories;
2929
import com.google.devtools.build.lib.analysis.PlatformOptions;
3030
import com.google.devtools.build.lib.analysis.config.OutputDirectories.InvalidMnemonicException;
31+
import com.google.devtools.build.lib.analysis.starlark.FunctionTransitionUtil;
3132
import com.google.devtools.build.lib.buildeventstream.BuildEventIdUtil;
3233
import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos;
3334
import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildEventId;
@@ -67,7 +68,7 @@
6768
*
6869
* <p>Instances of BuildConfiguration are canonical:
6970
*
70-
* <pre>c1.equals(c2) <=> c1==c2.</pre>
71+
* <pre>{@code c1.equals(c2) <=> c1==c2.}</pre>
7172
*/
7273
// TODO(janakr): If overhead of fragments class names is too high, add constructor that just takes
7374
// fragments and gets names from them.
@@ -107,6 +108,14 @@ public interface ActionEnvironmentProvider {
107108
private final BuildOptions buildOptions;
108109
private final CoreOptions options;
109110

111+
/**
112+
* If non-empty, this is appended to output directories as ST-[transitionDirectoryNameFragment].
113+
* The value is a hash of BuildOptions that have been affected by a Starlark transition.
114+
*
115+
* <p>See b/203470434 or #14023 for more information and planned behavior changes.
116+
*/
117+
private final String transitionDirectoryNameFragment;
118+
110119
private final ImmutableMap<String, String> commandLineBuildVariables;
111120

112121
private final int hashCode; // We can precompute the hash code as all its inputs are immutable.
@@ -189,14 +198,17 @@ public BuildConfiguration(
189198
if (buildOptions.contains(PlatformOptions.class)) {
190199
platformOptions = buildOptions.get(PlatformOptions.class);
191200
}
201+
this.transitionDirectoryNameFragment =
202+
FunctionTransitionUtil.computeOutputDirectoryNameFragment(buildOptions);
192203
this.outputDirectories =
193204
new OutputDirectories(
194205
directories,
195206
options,
196207
platformOptions,
197208
this.fragments,
198209
mainRepositoryName,
199-
siblingRepositoryLayout);
210+
siblingRepositoryLayout,
211+
transitionDirectoryNameFragment);
200212
this.mainRepositoryName = mainRepositoryName;
201213
this.siblingRepositoryLayout = siblingRepositoryLayout;
202214

@@ -403,6 +415,11 @@ public String getMnemonic() {
403415
return outputDirectories.getMnemonic();
404416
}
405417

418+
@VisibleForTesting
419+
public String getTransitionDirectoryNameFragment() {
420+
return transitionDirectoryNameFragment;
421+
}
422+
406423
@Override
407424
public String toString() {
408425
return checksum();

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

-17
Original file line numberDiff line numberDiff line change
@@ -240,22 +240,6 @@ public class CoreOptions extends FragmentOptions implements Cloneable {
240240
+ "'fastbuild', 'dbg', 'opt'.")
241241
public CompilationMode hostCompilationMode;
242242

243-
/**
244-
* This option is used by starlark transitions to add a distinguishing element to the output
245-
* directory name, in order to avoid name clashing.
246-
*/
247-
@Option(
248-
name = "transition directory name fragment",
249-
defaultValue = "null",
250-
documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
251-
effectTags = {
252-
OptionEffectTag.LOSES_INCREMENTAL_STATE,
253-
OptionEffectTag.AFFECTS_OUTPUTS,
254-
OptionEffectTag.LOADING_AND_ANALYSIS
255-
},
256-
metadataTags = {OptionMetadataTag.INTERNAL})
257-
public String transitionDirectoryNameFragment;
258-
259243
@Option(
260244
name = "experimental_enable_aspect_hints",
261245
defaultValue = "false",
@@ -839,7 +823,6 @@ public IncludeConfigFragmentsEnumConverter() {
839823
public FragmentOptions getHost() {
840824
CoreOptions host = (CoreOptions) getDefault();
841825

842-
host.transitionDirectoryNameFragment = transitionDirectoryNameFragment;
843826
host.affectedByStarlarkTransition = affectedByStarlarkTransition;
844827
host.compilationMode = hostCompilationMode;
845828
host.isHost = true;

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

+7-6
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,12 @@ public ArtifactRoot getRoot(
147147
@Nullable PlatformOptions platformOptions,
148148
ImmutableSortedMap<Class<? extends Fragment>, Fragment> fragments,
149149
RepositoryName mainRepositoryName,
150-
boolean siblingRepositoryLayout)
150+
boolean siblingRepositoryLayout,
151+
String transitionDirectoryNameFragment)
151152
throws InvalidMnemonicException {
152153
this.directories = directories;
153-
this.mnemonic = buildMnemonic(options, platformOptions, fragments);
154+
this.mnemonic =
155+
buildMnemonic(options, platformOptions, fragments, transitionDirectoryNameFragment);
154156
this.outputDirName = options.isHost ? "host" : mnemonic;
155157

156158
this.outputDirectory =
@@ -207,7 +209,8 @@ private static void validateMnemonicPart(String part, String errorTemplate, Obje
207209
private static String buildMnemonic(
208210
CoreOptions options,
209211
@Nullable PlatformOptions platformOptions,
210-
ImmutableSortedMap<Class<? extends Fragment>, Fragment> fragments)
212+
ImmutableSortedMap<Class<? extends Fragment>, Fragment> fragments,
213+
String transitionDirectoryNameFragment)
211214
throws InvalidMnemonicException {
212215
// See explanation at declaration for outputRoots.
213216
List<String> nameParts = new ArrayList<>();
@@ -230,9 +233,7 @@ private static String buildMnemonic(
230233

231234
// Add the transition suffix.
232235
addMnemonicPart(
233-
nameParts,
234-
options.transitionDirectoryNameFragment,
235-
"Transition directory name fragment '%s'");
236+
nameParts, transitionDirectoryNameFragment, "Transition directory name fragment '%s'");
236237

237238
// Join all the parts.
238239
String mnemonic = nameParts.stream().filter(not(Strings::isNullOrEmpty)).collect(joining("-"));

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

+27-25
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
package com.google.devtools.build.lib.analysis.starlark;
1616

17+
import static com.google.common.collect.ImmutableSet.toImmutableSet;
1718
import static com.google.devtools.build.lib.analysis.config.StarlarkDefinedConfigTransition.COMMAND_LINE_OPTION_PREFIX;
1819
import static com.google.devtools.build.lib.analysis.config.transitions.ConfigurationTransition.PATCH_TRANSITION_KEY;
1920
import static java.util.stream.Collectors.joining;
@@ -57,7 +58,7 @@
5758
* Utility class for common work done across {@link StarlarkAttributeTransitionProvider} and {@link
5859
* StarlarkRuleTransitionProvider}.
5960
*/
60-
public class FunctionTransitionUtil {
61+
public final class FunctionTransitionUtil {
6162

6263
// The length of the hash of the config tacked onto the end of the output path.
6364
// Limited for ergonomics and MAX_PATH reasons.
@@ -165,7 +166,7 @@ static ImmutableMap<String, OptionInfo> buildOptionInfo(BuildOptions buildOption
165166
ImmutableSet<Class<? extends FragmentOptions>> optionClasses =
166167
buildOptions.getNativeOptions().stream()
167168
.map(FragmentOptions::getClass)
168-
.collect(ImmutableSet.toImmutableSet());
169+
.collect(toImmutableSet());
169170

170171
for (Class<? extends FragmentOptions> optionClass : optionClasses) {
171172
ImmutableList<OptionDefinition> optionDefinitions =
@@ -394,7 +395,8 @@ private static BuildOptions applyTransition(
394395
convertedNewValues.add("//command_line_option:evaluating for analysis test");
395396
toOptions.get(CoreOptions.class).evaluatingForAnalysisTest = true;
396397
}
397-
updateOutputDirectoryNameFragment(convertedNewValues, optionInfoMap, toOptions);
398+
399+
updateAffectedByStarlarkTransition(toOptions.get(CoreOptions.class), convertedNewValues);
398400
return toOptions;
399401
}
400402

@@ -404,27 +406,20 @@ private static BuildOptions applyTransition(
404406
* the build by Starlark transitions. Options only set on command line are not affecting the
405407
* computation.
406408
*
407-
* @param changedOptions the names of all options changed by this transition in label form e.g.
408-
* "//command_line_option:cpu" for native options and "//myapp:foo" for starlark options.
409-
* @param optionInfoMap a map of all native options (name -> OptionInfo) present in {@code
410-
* toOptions}.
411-
* @param toOptions the newly transitioned {@link BuildOptions} for which we need to updated
412-
* {@code transitionDirectoryNameFragment} and {@code affectedByStarlarkTransition}.
409+
* @param toOptions the {@link BuildOptions} to use to calculate which we need to compute {@code
410+
* transitionDirectoryNameFragment}.
413411
*/
414412
// TODO(bazel-team): This hashes different forms of equivalent values differently though they
415413
// should be the same configuration. Starlark transitions are flexible about the values they
416414
// take (e.g. bool-typed options can take 0/1, True/False, "0"/"1", or "True"/"False") which
417415
// makes it so that two configurations that are the same in value may hash differently.
418-
private static void updateOutputDirectoryNameFragment(
419-
Set<String> changedOptions, Map<String, OptionInfo> optionInfoMap, BuildOptions toOptions) {
420-
// Return without doing anything if this transition hasn't changed any option values.
421-
if (changedOptions.isEmpty()) {
422-
return;
423-
}
424-
416+
public static String computeOutputDirectoryNameFragment(BuildOptions toOptions) {
425417
CoreOptions buildConfigOptions = toOptions.get(CoreOptions.class);
426-
427-
updateAffectedByStarlarkTransition(buildConfigOptions, changedOptions);
418+
if (buildConfigOptions.affectedByStarlarkTransition.isEmpty()) {
419+
return "";
420+
}
421+
// TODO(blaze-configurability-team): A mild performance optimization would have this be global.
422+
Map<String, OptionInfo> optionInfoMap = buildOptionInfo(toOptions);
428423

429424
TreeMap<String, Object> toHash = new TreeMap<>();
430425
for (String optionName : buildConfigOptions.affectedByStarlarkTransition) {
@@ -445,19 +440,21 @@ private static void updateOutputDirectoryNameFragment(
445440
toHash.put(optionName, value);
446441
} else {
447442
Object value = toOptions.getStarlarkOptions().get(Label.parseAbsoluteUnchecked(optionName));
448-
if (value != null) {
449-
toHash.put(optionName, value);
450-
}
443+
toHash.put(optionName, value);
451444
}
452445
}
453446

454447
ImmutableList.Builder<String> hashStrs = ImmutableList.builderWithExpectedSize(toHash.size());
455448
for (Map.Entry<String, Object> singleOptionAndValue : toHash.entrySet()) {
456-
String toAdd = singleOptionAndValue.getKey() + "=" + singleOptionAndValue.getValue();
457-
hashStrs.add(toAdd);
449+
Object value = singleOptionAndValue.getValue();
450+
if (value != null) {
451+
hashStrs.add(singleOptionAndValue.getKey() + "=" + value);
452+
} else {
453+
// Avoid using =null to different from value being the non-null String "null"
454+
hashStrs.add(singleOptionAndValue.getKey() + "@null");
455+
}
458456
}
459-
buildConfigOptions.transitionDirectoryNameFragment =
460-
transitionDirectoryNameFragment(hashStrs.build());
457+
return transitionDirectoryNameFragment(hashStrs.build());
461458
}
462459

463460
/**
@@ -466,6 +463,9 @@ private static void updateOutputDirectoryNameFragment(
466463
*/
467464
private static void updateAffectedByStarlarkTransition(
468465
CoreOptions buildConfigOptions, Set<String> changedOptions) {
466+
if (changedOptions.isEmpty()) {
467+
return;
468+
}
469469
Set<String> mutableCopyToUpdate =
470470
new TreeSet<>(buildConfigOptions.affectedByStarlarkTransition);
471471
mutableCopyToUpdate.addAll(changedOptions);
@@ -503,4 +503,6 @@ OptionDefinition getDefinition() {
503503
return definition;
504504
}
505505
}
506+
507+
private FunctionTransitionUtil() {}
506508
}

0 commit comments

Comments
 (0)