Memoize computation of unloaded toolchain contexts and config conditions. PiperOrigin-RevId: 526430621 Change-Id: Ia2dd492e5a80488d55a4cc5646c8407b6c76d326
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BUILD b/src/main/java/com/google/devtools/build/lib/analysis/BUILD index d99d23e..54d398d 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/BUILD +++ b/src/main/java/com/google/devtools/build/lib/analysis/BUILD
@@ -9,6 +9,7 @@ name = "srcs", srcs = glob(["**"]) + [ "//src/main/java/com/google/devtools/build/lib/analysis/platform:srcs", + "//src/main/java/com/google/devtools/build/lib/analysis/producers:srcs", "//src/main/java/com/google/devtools/build/lib/analysis/starlark/annotations:srcs", "//src/main/java/com/google/devtools/build/lib/analysis/stringtemplate:srcs", ],
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/constraints/IncompatibleTargetChecker.java b/src/main/java/com/google/devtools/build/lib/analysis/constraints/IncompatibleTargetChecker.java index a740460..4030580 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/constraints/IncompatibleTargetChecker.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/constraints/IncompatibleTargetChecker.java
@@ -111,12 +111,14 @@ private final ResultSink sink; + private final StateMachine runAfter; + private final ImmutableList.Builder<ConstraintValueInfo> invalidConstraintValuesBuilder = new ImmutableList.Builder<>(); /** Sink for the output of this state machine. */ public interface ResultSink { - void accept(Optional<RuleConfiguredTargetValue> incompatibleTarget); + void acceptIncompatibleTarget(Optional<RuleConfiguredTargetValue> incompatibleTarget); void acceptValidationException(ValidationException e); } @@ -127,29 +129,31 @@ ConfigConditions configConditions, @Nullable PlatformInfo platformInfo, @Nullable NestedSetBuilder<Package> transitivePackages, - ResultSink sink) { + ResultSink sink, + StateMachine runAfter) { this.target = target; this.configuration = configuration; this.configConditions = configConditions; this.platformInfo = platformInfo; this.transitivePackages = transitivePackages; this.sink = sink; + this.runAfter = runAfter; } @Override public StateMachine step(Tasks tasks, ExtendedEventHandler listener) { Rule rule = target.getAssociatedRule(); if (rule == null || !rule.useToolchainResolution() || platformInfo == null) { - sink.accept(Optional.empty()); - return DONE; + sink.acceptIncompatibleTarget(Optional.empty()); + return runAfter; } // Retrieves the label list for the target_compatible_with attribute. ConfiguredAttributeMapper attrs = ConfiguredAttributeMapper.of(rule, configConditions.asProviders(), configuration); if (!attrs.has("target_compatible_with", BuildType.LABEL_LIST)) { - sink.accept(Optional.empty()); - return DONE; + sink.acceptIncompatibleTarget(Optional.empty()); + return runAfter; } // Resolves the constraint labels, checking for invalid configured attributes. @@ -158,7 +162,7 @@ targetCompatibleWith = attrs.getAndValidate("target_compatible_with", BuildType.LABEL_LIST); } catch (ValidationException e) { sink.acceptValidationException(e); - return DONE; + return runAfter; } for (Label label : targetCompatibleWith) { tasks.lookUp( @@ -180,21 +184,21 @@ private StateMachine processResult(Tasks tasks, ExtendedEventHandler listener) { var invalidConstraintValues = invalidConstraintValuesBuilder.build(); - if (invalidConstraintValues.isEmpty()) { - sink.accept(Optional.empty()); - return DONE; + if (!invalidConstraintValues.isEmpty()) { + sink.acceptIncompatibleTarget( + Optional.of( + createIncompatibleRuleConfiguredTarget( + target, + configuration, + configConditions, + IncompatiblePlatformProvider.incompatibleDueToConstraints( + platformInfo.label(), invalidConstraintValues), + target.getAssociatedRule().getRuleClass(), + transitivePackages))); + return runAfter; } - sink.accept( - Optional.of( - createIncompatibleRuleConfiguredTarget( - target, - configuration, - configConditions, - IncompatiblePlatformProvider.incompatibleDueToConstraints( - platformInfo.label(), invalidConstraintValues), - target.getAssociatedRule().getRuleClass(), - transitivePackages))); - return DONE; + sink.acceptIncompatibleTarget(Optional.empty()); + return runAfter; } }
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/producers/BUILD b/src/main/java/com/google/devtools/build/lib/analysis/producers/BUILD new file mode 100644 index 0000000..8eb4648 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/analysis/producers/BUILD
@@ -0,0 +1,54 @@ +load("@rules_java//java:defs.bzl", "java_library") + +package( + default_applicable_licenses = ["//:license"], + default_visibility = ["//src:__subpackages__"], +) + +filegroup( + name = "srcs", + srcs = glob(["**"]), + visibility = ["//src:__subpackages__"], +) + +java_library( + name = "producers", + srcs = glob(["*.java"]), + deps = [ + "//src/main/java/com/google/devtools/build/lib/analysis:config/build_configuration", + "//src/main/java/com/google/devtools/build/lib/analysis:config/config_conditions", + "//src/main/java/com/google/devtools/build/lib/analysis:config/config_matching_provider", + "//src/main/java/com/google/devtools/build/lib/analysis:configured_target", + "//src/main/java/com/google/devtools/build/lib/analysis:configured_target_value", + "//src/main/java/com/google/devtools/build/lib/analysis:constraints/incompatible_target_checker", + "//src/main/java/com/google/devtools/build/lib/analysis:exec_group_collection", + "//src/main/java/com/google/devtools/build/lib/analysis:platform_configuration", + "//src/main/java/com/google/devtools/build/lib/analysis:target_and_configuration", + "//src/main/java/com/google/devtools/build/lib/analysis:toolchain_collection", + "//src/main/java/com/google/devtools/build/lib/analysis/platform", + "//src/main/java/com/google/devtools/build/lib/analysis/platform:utils", + "//src/main/java/com/google/devtools/build/lib/causes", + "//src/main/java/com/google/devtools/build/lib/cmdline", + "//src/main/java/com/google/devtools/build/lib/collect/nestedset", + "//src/main/java/com/google/devtools/build/lib/events", + "//src/main/java/com/google/devtools/build/lib/packages", + "//src/main/java/com/google/devtools/build/lib/packages:configured_attribute_mapper", + "//src/main/java/com/google/devtools/build/lib/packages:exec_group", + "//src/main/java/com/google/devtools/build/lib/skyframe:configured_target_and_data", + "//src/main/java/com/google/devtools/build/lib/skyframe:configured_target_key", + "//src/main/java/com/google/devtools/build/lib/skyframe:configured_value_creation_exception", + "//src/main/java/com/google/devtools/build/lib/skyframe:no_matching_platform_exception", + "//src/main/java/com/google/devtools/build/lib/skyframe:package_value", + "//src/main/java/com/google/devtools/build/lib/skyframe:platform_lookup_util", + "//src/main/java/com/google/devtools/build/lib/skyframe:rule_configured_target_value", + "//src/main/java/com/google/devtools/build/lib/skyframe:toolchain_context_key", + "//src/main/java/com/google/devtools/build/lib/skyframe:toolchain_exception", + "//src/main/java/com/google/devtools/build/lib/skyframe:unloaded_toolchain_context", + "//src/main/java/com/google/devtools/build/lib/util:detailed_exit_code", + "//src/main/java/com/google/devtools/build/skyframe", + "//src/main/java/com/google/devtools/build/skyframe:skyframe-objects", + "//third_party:auto_value", + "//third_party:guava", + "//third_party:jsr305", + ], +)
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/producers/ConfigConditionsProducer.java b/src/main/java/com/google/devtools/build/lib/analysis/producers/ConfigConditionsProducer.java new file mode 100644 index 0000000..bb9c7db --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/analysis/producers/ConfigConditionsProducer.java
@@ -0,0 +1,188 @@ +// Copyright 2023 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.lib.analysis.producers; + +import com.google.common.collect.ImmutableMap; +import com.google.devtools.build.lib.analysis.TargetAndConfiguration; +import com.google.devtools.build.lib.analysis.config.ConfigConditions; +import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider; +import com.google.devtools.build.lib.analysis.platform.PlatformInfo; +import com.google.devtools.build.lib.cmdline.Label; +import com.google.devtools.build.lib.events.ExtendedEventHandler; +import com.google.devtools.build.lib.packages.BuildType; +import com.google.devtools.build.lib.packages.RawAttributeMapper; +import com.google.devtools.build.lib.packages.Rule; +import com.google.devtools.build.lib.packages.RuleClass; +import com.google.devtools.build.lib.packages.Target; +import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndData; +import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey; +import com.google.devtools.build.lib.skyframe.ConfiguredValueCreationException; +import com.google.devtools.build.lib.util.DetailedExitCode; +import com.google.devtools.build.lib.util.DetailedExitCode.DetailedExitCodeComparator; +import com.google.devtools.build.skyframe.state.StateMachine; +import java.util.List; +import javax.annotation.Nullable; + +/** Computes the targets that key the configurable attributes used by this rule. */ +final class ConfigConditionsProducer + implements StateMachine, ConfiguredTargetAndDataProducer.ResultSink { + interface ResultSink { + void acceptConfigConditions(ConfigConditions configConditions); + + void acceptConfigConditionsError(ConfiguredValueCreationException error); + } + + // -------------------- Input -------------------- + private final TargetAndConfiguration targetAndConfiguration; + @Nullable private final PlatformInfo targetPlatformInfo; + private final TransitiveDependencyState transitiveState; + + // -------------------- Output -------------------- + private final ResultSink sink; + + // -------------------- Sequencing -------------------- + private final StateMachine runAfter; + + // -------------------- Internal State -------------------- + @Nullable // Null if there are no config labels. + private final List<Label> configLabels; + @Nullable // Null if there are no config labels. + private final ConfiguredTargetAndData[] prerequisites; + @Nullable // Null if there are no dependency errors. + private DetailedExitCode mostImportantExitCode; + + ConfigConditionsProducer( + TargetAndConfiguration targetAndConfiguration, + @Nullable PlatformInfo targetPlatformInfo, + TransitiveDependencyState transitiveState, + ResultSink sink, + StateMachine runAfter) { + this.targetAndConfiguration = targetAndConfiguration; + this.targetPlatformInfo = targetPlatformInfo; + this.transitiveState = transitiveState; + this.sink = sink; + this.runAfter = runAfter; + + this.configLabels = computeConfigLabels(targetAndConfiguration.getTarget()); + this.prerequisites = + configLabels == null ? null : new ConfiguredTargetAndData[configLabels.size()]; + } + + @Override + public StateMachine step(Tasks tasks, ExtendedEventHandler listener) { + if (configLabels == null) { + sink.acceptConfigConditions(ConfigConditions.EMPTY); + return runAfter; + } + + // Collect the actual deps without a configuration transition (since by definition config + // conditions evaluate over the current target's configuration). If the dependency is + // (erroneously) something that needs the null configuration, its analysis will be + // short-circuited. That error will be reported later. + for (int i = 0; i < configLabels.size(); ++i) { + tasks.enqueue( + new ConfiguredTargetAndDataProducer( + ConfiguredTargetKey.builder() + .setLabel(configLabels.get(i)) + .setConfiguration(targetAndConfiguration.getConfiguration()) + .build(), + /* transitionKey= */ null, + transitiveState, + (ConfiguredTargetAndDataProducer.ResultSink) this, + i)); + } + return this::constructConfigConditions; + } + + @Override + public void acceptConfiguredTargetAndData(ConfiguredTargetAndData value, int index) { + prerequisites[index] = value; + } + + @Override + public void acceptConfiguredTargetAndDataError(ConfiguredValueCreationException error) { + DetailedExitCode newExitCode = error.getDetailedExitCode(); + mostImportantExitCode = + DetailedExitCodeComparator.chooseMoreImportantWithFirstIfTie( + newExitCode, mostImportantExitCode); + if (newExitCode.equals(mostImportantExitCode)) { + sink.acceptConfigConditionsError( + // The precise error is reported by the dependency that failed to load. + // TODO(gregce): beautify this error: https://github.com/bazelbuild/bazel/issues/11984. + new ConfiguredValueCreationException( + targetAndConfiguration, + "errors encountered resolving select() keys for " + + targetAndConfiguration.getLabel())); + } + } + + private StateMachine constructConfigConditions(Tasks tasks, ExtendedEventHandler listener) { + if (mostImportantExitCode != null) { + return runAfter; // There was a previous error. + } + + var asConfiguredTargets = new ImmutableMap.Builder<Label, ConfiguredTargetAndData>(); + var asConfigConditions = new ImmutableMap.Builder<Label, ConfigMatchingProvider>(); + for (int i = 0; i < configLabels.size(); ++i) { + var label = configLabels.get(i); + var prerequisite = prerequisites[i]; + asConfiguredTargets.put(label, prerequisite); + try { + asConfigConditions.put( + label, ConfigConditions.fromConfiguredTarget(prerequisite, targetPlatformInfo)); + } catch (ConfigConditions.InvalidConditionException e) { + var targetLabel = targetAndConfiguration.getLabel(); + String message = + String.format( + "%s is not a valid select() condition for %s.\n", + prerequisite.getTargetLabel(), targetLabel) + + String.format( + "To inspect the select(), run: bazel query --output=build %s.\n", targetLabel) + + "For more help, see https://bazel.build/reference/be/functions#select.\n\n"; + sink.acceptConfigConditionsError( + new ConfiguredValueCreationException(targetAndConfiguration, message)); + return runAfter; + } + } + sink.acceptConfigConditions( + ConfigConditions.create( + asConfiguredTargets.buildOrThrow(), asConfigConditions.buildOrThrow())); + return runAfter; + } + + /** + * Computes the config labels belonging to the given target. + * + * @return null if there were no config labels, implying a {@link ConfigConditions#EMPTY} result. + */ + @Nullable + private static List<Label> computeConfigLabels(Target target) { + if (!(target instanceof Rule)) { + return null; + } + + var attrs = RawAttributeMapper.of(((Rule) target)); + if (!attrs.has(RuleClass.CONFIG_SETTING_DEPS_ATTRIBUTE)) { + return null; + } + + // Collects the labels of the configured targets we need to resolve. + List<Label> configLabels = + attrs.get(RuleClass.CONFIG_SETTING_DEPS_ATTRIBUTE, BuildType.LABEL_LIST); + if (configLabels.isEmpty()) { + return null; + } + return configLabels; + } +}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/producers/ConfiguredTargetAndDataProducer.java b/src/main/java/com/google/devtools/build/lib/analysis/producers/ConfiguredTargetAndDataProducer.java new file mode 100644 index 0000000..e30a346 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/analysis/producers/ConfiguredTargetAndDataProducer.java
@@ -0,0 +1,164 @@ +// Copyright 2023 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.lib.analysis.producers; + +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.lib.analysis.ConfiguredTarget; +import com.google.devtools.build.lib.analysis.ConfiguredTargetValue; +import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue; +import com.google.devtools.build.lib.events.ExtendedEventHandler; +import com.google.devtools.build.lib.packages.NoSuchTargetException; +import com.google.devtools.build.lib.packages.Package; +import com.google.devtools.build.lib.packages.Target; +import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndData; +import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey; +import com.google.devtools.build.lib.skyframe.ConfiguredValueCreationException; +import com.google.devtools.build.lib.skyframe.PackageValue; +import com.google.devtools.build.skyframe.SkyValue; +import com.google.devtools.build.skyframe.state.StateMachine; +import java.util.function.Consumer; +import javax.annotation.Nullable; + +/** + * Determines {@link ConfiguredTargetAndData} from {@link ConfiguredTargetKey}. + * + * <p>The resulting package and configuration are based on the resulting {@link ConfiguredTarget} + * and may be different from what is in the key, for example, if there is an alias. + */ +final class ConfiguredTargetAndDataProducer + implements StateMachine, + Consumer<SkyValue>, + StateMachine.ValueOrExceptionSink<ConfiguredValueCreationException> { + /** Interface for accepting values produced by this class. */ + interface ResultSink { + void acceptConfiguredTargetAndData(ConfiguredTargetAndData value, int index); + + void acceptConfiguredTargetAndDataError(ConfiguredValueCreationException error); + } + + // -------------------- Input -------------------- + private final ConfiguredTargetKey key; + @Nullable // Null if no transition key is needed (patch transition or no-op split transition). + private final String transitionKey; + private final TransitiveDependencyState transitiveState; + + // -------------------- Output -------------------- + private final ResultSink sink; + private final int outputIndex; + + // -------------------- Internal State -------------------- + private ConfiguredTarget configuredTarget; + @Nullable // Null if the configured target key's configuration key is null. + private BuildConfigurationValue configurationValue; + private Package pkg; + + public ConfiguredTargetAndDataProducer( + ConfiguredTargetKey key, + @Nullable String transitionKey, + TransitiveDependencyState transitiveState, + ResultSink sink, + int outputIndex) { + this.key = key; + this.transitionKey = transitionKey; + this.transitiveState = transitiveState; + this.sink = sink; + this.outputIndex = outputIndex; + } + + @Override + public StateMachine step(Tasks tasks, ExtendedEventHandler listener) { + tasks.lookUp( + key, + ConfiguredValueCreationException.class, + (ValueOrExceptionSink<ConfiguredValueCreationException>) this); + return this::fetchConfigurationAndPackage; + } + + @Override + public void acceptValueOrException( + @Nullable SkyValue value, @Nullable ConfiguredValueCreationException error) { + if (value != null) { + var configuredTargetValue = (ConfiguredTargetValue) value; + this.configuredTarget = configuredTargetValue.getConfiguredTarget(); + transitiveState.updateTransitivePackages(configuredTargetValue); + return; + } + if (error != null) { + transitiveState.addTransitiveCauses(error.getRootCauses()); + sink.acceptConfiguredTargetAndDataError(error); + return; + } + throw new IllegalArgumentException("both value and error were null"); + } + + private StateMachine fetchConfigurationAndPackage(Tasks tasks, ExtendedEventHandler listener) { + if (configuredTarget == null) { + return DONE; // There was a previous error. + } + + var configurationKey = configuredTarget.getConfigurationKey(); + if (configurationKey != null) { + tasks.lookUp(configurationKey, (Consumer<SkyValue>) this); + } + + // An alternative to this is to optimistically fetch the package using the label of the + // configured target key. However, the actual package may differ when this is an + // AliasConfiguredTarget and would need to be refetched. + + // TODO(shahan): This lookup should be skipped when the ConfiguredTarget is fetched remotely. + var packageId = configuredTarget.getLabel().getPackageIdentifier(); + this.pkg = transitiveState.getDependencyPackage(packageId); + if (pkg == null) { + // In incremental builds, it is possible that the package won't be present in the cache. For + // example, suppose that a configured target A has two children B and C. If B is dirty, it + // causes A's re-evaluation, which causes this fetch to be performed for C. However, C has not + // been evaluated this build. + tasks.lookUp(PackageValue.key(packageId), (Consumer<SkyValue>) this); + } + + return this::constructResult; + } + + @Override + public void accept(SkyValue value) { + if (value instanceof BuildConfigurationValue) { + this.configurationValue = (BuildConfigurationValue) value; + return; + } + if (value instanceof PackageValue) { + this.pkg = ((PackageValue) value).getPackage(); + return; + } + throw new IllegalArgumentException("unexpected value: " + value); + } + + private StateMachine constructResult(Tasks tasks, ExtendedEventHandler listener) { + Target target; + try { + target = pkg.getTarget(configuredTarget.getLabel().getName()); + } catch (NoSuchTargetException e) { + // The package was fetched based on the label of the configured target. Since the configured + // target exists, it must have existed in the package when it was created. + throw new IllegalStateException("Target already verified for " + configuredTarget, e); + } + sink.acceptConfiguredTargetAndData( + new ConfiguredTargetAndData( + configuredTarget, + target, + configurationValue, + transitionKey == null ? ImmutableList.of() : ImmutableList.of(transitionKey)), + outputIndex); + return DONE; + } +}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/producers/DependencyContext.java b/src/main/java/com/google/devtools/build/lib/analysis/producers/DependencyContext.java new file mode 100644 index 0000000..c9ad0f2 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/analysis/producers/DependencyContext.java
@@ -0,0 +1,39 @@ +// Copyright 2023 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.lib.analysis.producers; + +import com.google.auto.value.AutoValue; +import com.google.devtools.build.lib.analysis.ToolchainCollection; +import com.google.devtools.build.lib.analysis.config.ConfigConditions; +import com.google.devtools.build.lib.skyframe.UnloadedToolchainContext; +import javax.annotation.Nullable; + +/** + * Groups together unloaded toolchain contexts and config conditions. + * + * <p>These are used together when computing dependencies. + */ +@AutoValue +public abstract class DependencyContext { + @Nullable + public abstract ToolchainCollection<UnloadedToolchainContext> unloadedToolchainContexts(); + + public abstract ConfigConditions configConditions(); + + static DependencyContext create( + @Nullable ToolchainCollection<UnloadedToolchainContext> unloadedToolchainContexts, + ConfigConditions configConditions) { + return new AutoValue_DependencyContext(unloadedToolchainContexts, configConditions); + } +}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/producers/DependencyContextError.java b/src/main/java/com/google/devtools/build/lib/analysis/producers/DependencyContextError.java new file mode 100644 index 0000000..13c6d3c --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/analysis/producers/DependencyContextError.java
@@ -0,0 +1,60 @@ +// Copyright 2023 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.lib.analysis.producers; + +import com.google.auto.value.AutoOneOf; +import com.google.devtools.build.lib.analysis.constraints.IncompatibleTargetChecker.IncompatibleTargetException; +import com.google.devtools.build.lib.packages.ConfiguredAttributeMapper.ValidationException; +import com.google.devtools.build.lib.skyframe.ConfiguredValueCreationException; +import com.google.devtools.build.lib.skyframe.ToolchainException; + +/** Tagged union of errors that can be encountered when creating the {@link DependencyContext}. */ +@AutoOneOf(DependencyContextError.Kind.class) +public abstract class DependencyContextError { + /** Tags for errors types that may occur. */ + public enum Kind { + TOOLCHAIN, + CONFIGURED_VALUE_CREATION, + INCOMPATIBLE_TARGET, + VALIDATION + } + + public abstract Kind kind(); + + public abstract ToolchainException toolchain(); + + public abstract ConfiguredValueCreationException configuredValueCreation(); + + /** This error is only possible for {@link DependencyContextProducerWithCompatibilityCheck}. */ + public abstract IncompatibleTargetException incompatibleTarget(); + + /** This error is only possible for {@link DependencyContextProducerWithCompatibilityCheck}. */ + public abstract ValidationException validation(); + + static DependencyContextError of(ToolchainException error) { + return AutoOneOf_DependencyContextError.toolchain(error); + } + + static DependencyContextError of(ConfiguredValueCreationException error) { + return AutoOneOf_DependencyContextError.configuredValueCreation(error); + } + + static DependencyContextError of(IncompatibleTargetException error) { + return AutoOneOf_DependencyContextError.incompatibleTarget(error); + } + + static DependencyContextError of(ValidationException error) { + return AutoOneOf_DependencyContextError.validation(error); + } +}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/producers/DependencyContextProducer.java b/src/main/java/com/google/devtools/build/lib/analysis/producers/DependencyContextProducer.java new file mode 100644 index 0000000..06b95fa --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/analysis/producers/DependencyContextProducer.java
@@ -0,0 +1,125 @@ +// Copyright 2023 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.lib.analysis.producers; + +import com.google.devtools.build.lib.analysis.TargetAndConfiguration; +import com.google.devtools.build.lib.analysis.ToolchainCollection; +import com.google.devtools.build.lib.analysis.config.ConfigConditions; +import com.google.devtools.build.lib.events.ExtendedEventHandler; +import com.google.devtools.build.lib.skyframe.ConfiguredValueCreationException; +import com.google.devtools.build.lib.skyframe.ToolchainException; +import com.google.devtools.build.lib.skyframe.UnloadedToolchainContext; +import com.google.devtools.build.skyframe.state.StateMachine; +import javax.annotation.Nullable; + +/** + * This class computes the unloaded toolchain context and {@link ConfigConditions}. + * + * <p>It uses {@link PlatformInfo} derived from the unloaded toolchain contexts to compute config + * conditions, creating a sequential dependency between the two. + */ +// TODO(b/278878321): unify this and DependencyContextProducerWithCompatibilityCheck. +public final class DependencyContextProducer + implements StateMachine, + UnloadedToolchainContextsProducer.ResultSink, + ConfigConditionsProducer.ResultSink { + /** + * Accepts results for both {@link DependencyContextProducer} and {@link + * DependencyContextProducerWithCompatibilityCheck}. + */ + public interface ResultSink { + void acceptDependencyContext(DependencyContext value); + + void acceptDependencyContextError(DependencyContextError error); + } + + // -------------------- Input -------------------- + private final UnloadedToolchainContextsInputs unloadedToolchainContextsInputs; + private final TargetAndConfiguration targetAndConfiguration; + private final TransitiveDependencyState transitiveState; + + // -------------------- Output -------------------- + private final ResultSink sink; + + // -------------------- Internal State -------------------- + @Nullable // Will be null if the target doesn't require toolchain resolution. + private ToolchainCollection<UnloadedToolchainContext> unloadedToolchainContexts; + private ConfigConditions configConditions; + boolean hasError = false; + + public DependencyContextProducer( + UnloadedToolchainContextsInputs unloadedToolchainContextsInputs, + TargetAndConfiguration targetAndConfiguration, + TransitiveDependencyState transitiveState, + ResultSink sink) { + this.unloadedToolchainContextsInputs = unloadedToolchainContextsInputs; + this.targetAndConfiguration = targetAndConfiguration; + this.transitiveState = transitiveState; + this.sink = sink; + } + + @Override + public StateMachine step(Tasks tasks, ExtendedEventHandler listener) { + return new UnloadedToolchainContextsProducer( + unloadedToolchainContextsInputs, + (UnloadedToolchainContextsProducer.ResultSink) this, + /* runAfter= */ this::computeConfigConditions); + } + + @Override + public void acceptUnloadedToolchainContexts( + @Nullable ToolchainCollection<UnloadedToolchainContext> unloadedToolchainContexts) { + this.unloadedToolchainContexts = unloadedToolchainContexts; + } + + @Override + public void acceptUnloadedToolchainContextsError(ToolchainException error) { + this.hasError = true; + sink.acceptDependencyContextError(DependencyContextError.of(error)); + } + + private StateMachine computeConfigConditions(Tasks tasks, ExtendedEventHandler listener) { + if (hasError) { + return DONE; + } + + return new ConfigConditionsProducer( + targetAndConfiguration, + unloadedToolchainContexts == null ? null : unloadedToolchainContexts.getTargetPlatform(), + transitiveState, + (ConfigConditionsProducer.ResultSink) this, + /* runAfter= */ this::constructResult); + } + + @Override + public void acceptConfigConditions(ConfigConditions configConditions) { + this.configConditions = configConditions; + } + + @Override + public void acceptConfigConditionsError(ConfiguredValueCreationException error) { + this.hasError = true; + sink.acceptDependencyContextError(DependencyContextError.of(error)); + } + + private StateMachine constructResult(Tasks tasks, ExtendedEventHandler listener) { + if (hasError) { + return DONE; + } + + sink.acceptDependencyContext( + DependencyContext.create(unloadedToolchainContexts, configConditions)); + return DONE; + } +}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/producers/DependencyContextProducerWithCompatibilityCheck.java b/src/main/java/com/google/devtools/build/lib/analysis/producers/DependencyContextProducerWithCompatibilityCheck.java new file mode 100644 index 0000000..ce3ae6e --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/analysis/producers/DependencyContextProducerWithCompatibilityCheck.java
@@ -0,0 +1,202 @@ +// Copyright 2023 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.lib.analysis.producers; + +import com.google.devtools.build.lib.analysis.PlatformConfiguration; +import com.google.devtools.build.lib.analysis.TargetAndConfiguration; +import com.google.devtools.build.lib.analysis.ToolchainCollection; +import com.google.devtools.build.lib.analysis.config.ConfigConditions; +import com.google.devtools.build.lib.analysis.constraints.IncompatibleTargetChecker.IncompatibleTargetException; +import com.google.devtools.build.lib.analysis.constraints.IncompatibleTargetChecker.IncompatibleTargetProducer; +import com.google.devtools.build.lib.analysis.platform.PlatformInfo; +import com.google.devtools.build.lib.events.ExtendedEventHandler; +import com.google.devtools.build.lib.packages.ConfiguredAttributeMapper.ValidationException; +import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey; +import com.google.devtools.build.lib.skyframe.ConfiguredValueCreationException; +import com.google.devtools.build.lib.skyframe.PlatformLookupUtil.InvalidPlatformException; +import com.google.devtools.build.lib.skyframe.RuleConfiguredTargetValue; +import com.google.devtools.build.lib.skyframe.ToolchainException; +import com.google.devtools.build.lib.skyframe.UnloadedToolchainContext; +import com.google.devtools.build.skyframe.state.StateMachine; +import java.util.Optional; +import javax.annotation.Nullable; + +/** + * Computes the {@link DependencyContext} while checking for platform compatibility. + * + * <p>See <a href="https://bazel.build/extending/platforms#skipping-incompatible-targets">Skipping + * Incompatible Targets</a> for more details on platform compatibility. + */ +// TODO(b/278878321): unify this and DependencyContextProducer. +public final class DependencyContextProducerWithCompatibilityCheck + implements StateMachine, + PlatformInfoProducer.ResultSink, + ConfigConditionsProducer.ResultSink, + IncompatibleTargetProducer.ResultSink, + UnloadedToolchainContextsProducer.ResultSink { + // -------------------- Input -------------------- + private final TargetAndConfiguration targetAndConfiguration; + private final UnloadedToolchainContextsInputs unloadedToolchainContextsInputs; + + private final TransitiveDependencyState transitiveState; + + // -------------------- Output -------------------- + private final DependencyContextProducer.ResultSink sink; + + // -------------------- Internal State -------------------- + private PlatformInfo targetPlatformInfo; + private ConfigConditions configConditions; + @Nullable // Will be null if the target doesn't require toolchain resolution. + private ToolchainCollection<UnloadedToolchainContext> unloadedToolchainContexts; + private boolean hasError = false; + + public DependencyContextProducerWithCompatibilityCheck( + TargetAndConfiguration targetAndConfiguration, + UnloadedToolchainContextsInputs unloadedToolchainContextsInputs, + TransitiveDependencyState transitiveState, + DependencyContextProducer.ResultSink sink) { + this.targetAndConfiguration = targetAndConfiguration; + this.unloadedToolchainContextsInputs = unloadedToolchainContextsInputs; + this.transitiveState = transitiveState; + this.sink = sink; + } + + @Override + public StateMachine step(Tasks tasks, ExtendedEventHandler listener) { + var defaultToolchainContextKey = unloadedToolchainContextsInputs.targetToolchainContextKey(); + if (defaultToolchainContextKey == null) { + // If `defaultToolchainContextKey` is null, there's no platform info, incompatibility check + // or toolchain resolution. Short-circuits and computes only the ConfigConditions. + return new ConfigConditionsProducer( + targetAndConfiguration, + /* targetPlatformInfo= */ null, + transitiveState, + (ConfigConditionsProducer.ResultSink) this, + /* runAfter= */ this::constructResult); + } + + // Non-null `defaultToolchainContextKey` guarantees that `platformConfiguration` is non-null. + var platformConfiguration = + targetAndConfiguration.getConfiguration().getFragment(PlatformConfiguration.class); + // Checks for incompatibility before toolchain resolution so that known missing + // toolchains mark the target incompatible instead of failing the build. + return new PlatformInfoProducer( + ConfiguredTargetKey.builder() + .setLabel(platformConfiguration.getTargetPlatform()) + .setConfigurationKey(defaultToolchainContextKey.configurationKey()) + .build(), + (PlatformInfoProducer.ResultSink) this, + /* runAfter= */ this::computeConfigConditions); + } + + @Override + public void acceptPlatformInfo(PlatformInfo info) { + this.targetPlatformInfo = info; + } + + @Override + public void acceptPlatformInfoError(InvalidPlatformException error) { + this.hasError = true; + sink.acceptDependencyContextError(DependencyContextError.of(error)); + } + + private StateMachine computeConfigConditions(Tasks tasks, ExtendedEventHandler listener) { + if (hasError) { + return DONE; + } + + return new ConfigConditionsProducer( + targetAndConfiguration, + targetPlatformInfo, + transitiveState, + (ConfigConditionsProducer.ResultSink) this, + /* runAfter= */ this::checkCompatibility); + } + + // -------------------- ConfigConditionsProducer.ResultSink -------------------- + @Override + public void acceptConfigConditions(ConfigConditions configConditions) { + this.configConditions = configConditions; + } + + @Override + public void acceptConfigConditionsError(ConfiguredValueCreationException error) { + this.hasError = true; + sink.acceptDependencyContextError(DependencyContextError.of(error)); + } + + private StateMachine checkCompatibility(Tasks tasks, ExtendedEventHandler listener) { + if (hasError) { + return DONE; + } + + return new IncompatibleTargetProducer( + targetAndConfiguration.getTarget(), + targetAndConfiguration.getConfiguration(), + configConditions, + targetPlatformInfo, + transitiveState.transitivePackages(), + (IncompatibleTargetProducer.ResultSink) this, + /* runAfter= */ this::computeUnloadedToolchainContexts); + } + + @Override + public void acceptIncompatibleTarget(Optional<RuleConfiguredTargetValue> incompatibleTarget) { + if (!incompatibleTarget.isEmpty()) { + this.hasError = true; + sink.acceptDependencyContextError( + DependencyContextError.of(new IncompatibleTargetException(incompatibleTarget.get()))); + } + } + + @Override + public void acceptValidationException(ValidationException e) { + this.hasError = true; + sink.acceptDependencyContextError(DependencyContextError.of(e)); + } + + private StateMachine computeUnloadedToolchainContexts( + Tasks tasks, ExtendedEventHandler listener) { + if (hasError) { + return DONE; + } + + return new UnloadedToolchainContextsProducer( + unloadedToolchainContextsInputs, + (UnloadedToolchainContextsProducer.ResultSink) this, + /* runAfter= */ this::constructResult); + } + + @Override + public void acceptUnloadedToolchainContexts( + @Nullable ToolchainCollection<UnloadedToolchainContext> unloadedToolchainContexts) { + this.unloadedToolchainContexts = unloadedToolchainContexts; + } + + @Override + public void acceptUnloadedToolchainContextsError(ToolchainException error) { + this.hasError = true; + sink.acceptDependencyContextError(DependencyContextError.of(error)); + } + + private StateMachine constructResult(Tasks tasks, ExtendedEventHandler listener) { + if (hasError) { + return DONE; + } + + sink.acceptDependencyContext( + DependencyContext.create(unloadedToolchainContexts, configConditions)); + return DONE; + } +}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/producers/PlatformInfoProducer.java b/src/main/java/com/google/devtools/build/lib/analysis/producers/PlatformInfoProducer.java new file mode 100644 index 0000000..e3179bf --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/analysis/producers/PlatformInfoProducer.java
@@ -0,0 +1,143 @@ +// Copyright 2023 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.lib.analysis.producers; + +import com.google.devtools.build.lib.analysis.ConfiguredTarget; +import com.google.devtools.build.lib.analysis.ConfiguredTargetValue; +import com.google.devtools.build.lib.analysis.platform.PlatformInfo; +import com.google.devtools.build.lib.analysis.platform.PlatformProviderUtils; +import com.google.devtools.build.lib.events.ExtendedEventHandler; +import com.google.devtools.build.lib.packages.NoSuchPackageException; +import com.google.devtools.build.lib.packages.NoSuchTargetException; +import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey; +import com.google.devtools.build.lib.skyframe.ConfiguredValueCreationException; +import com.google.devtools.build.lib.skyframe.PackageValue; +import com.google.devtools.build.lib.skyframe.PlatformLookupUtil; +import com.google.devtools.build.lib.skyframe.PlatformLookupUtil.InvalidPlatformException; +import com.google.devtools.build.skyframe.SkyValue; +import com.google.devtools.build.skyframe.state.StateMachine; +import javax.annotation.Nullable; + +/** + * Retrieves {@link PlatformInfo} for a given platform key. + * + * <p>This creates an explicit dependency on the {@link Package} to retrieve the associated target, + * so it is possible to verify that {@link PlatformInfo} is an advertised provider before + * constructing the {@link ConfiguredTarget}. + */ +final class PlatformInfoProducer + implements StateMachine, StateMachine.ValueOrExceptionSink<NoSuchPackageException> { + interface ResultSink { + void acceptPlatformInfo(PlatformInfo info); + + void acceptPlatformInfoError(InvalidPlatformException error); + } + + // -------------------- Input -------------------- + private final ConfiguredTargetKey platformKey; + + // -------------------- Output -------------------- + private final ResultSink sink; + + // -------------------- Sequencing -------------------- + private final StateMachine runAfter; + + // -------------------- Internal State -------------------- + private boolean passedValidation = false; + private ConfiguredTarget platform; + + PlatformInfoProducer(ConfiguredTargetKey platformKey, ResultSink sink, StateMachine runAfter) { + this.platformKey = platformKey; + this.sink = sink; + this.runAfter = runAfter; + } + + @Override + public StateMachine step(Tasks tasks, ExtendedEventHandler listener) { + // Loads the Package first to verify the Target. The ConfiguredTarget should not be loaded + // until after verification. See https://github.com/bazelbuild/bazel/pull/10307. + // + // In distributed analysis, these packages will be duplicated across shards. + tasks.lookUp( + PackageValue.key(platformKey.getLabel().getPackageIdentifier()), + NoSuchPackageException.class, + (StateMachine.ValueOrExceptionSink<NoSuchPackageException>) this); + return this::lookupPlatform; + } + + @Override + public void acceptValueOrException( + @Nullable SkyValue value, @Nullable NoSuchPackageException error) { + if (value != null) { + var pkg = ((PackageValue) value).getPackage(); + try { + var label = platformKey.getLabel(); + var target = pkg.getTarget(label.getName()); + if (!PlatformLookupUtil.hasPlatformInfo(target)) { + sink.acceptPlatformInfoError(new InvalidPlatformException(label)); // validation failure + return; + } + } catch (NoSuchTargetException e) { + sink.acceptPlatformInfoError(new InvalidPlatformException(e)); + return; + } + passedValidation = true; + return; + } + if (error != null) { + sink.acceptPlatformInfoError(new InvalidPlatformException(error)); + return; + } + throw new IllegalArgumentException("both value and error were null"); + } + + private StateMachine lookupPlatform(Tasks tasks, ExtendedEventHandler listener) { + if (!passedValidation) { + return runAfter; + } + + tasks.lookUp( + platformKey, ConfiguredValueCreationException.class, this::acceptPlatformValueOrError); + return this::retrievePlatformInfo; + } + + private void acceptPlatformValueOrError( + @Nullable SkyValue value, @Nullable ConfiguredValueCreationException error) { + if (value != null) { + var configuredTargetValue = (ConfiguredTargetValue) value; + this.platform = configuredTargetValue.getConfiguredTarget(); + return; + } + if (error != null) { + sink.acceptPlatformInfoError(new InvalidPlatformException(platformKey.getLabel(), error)); + return; + } + throw new IllegalArgumentException("both value and error were null"); + } + + private StateMachine retrievePlatformInfo(Tasks tasks, ExtendedEventHandler listener) { + if (platform == null) { + return runAfter; // An error occurred and was reported. + } + + PlatformInfo platformInfo = PlatformProviderUtils.platform(platform); + if (platformInfo == null) { + sink.acceptPlatformInfoError(new InvalidPlatformException(platform.getLabel())); + return runAfter; + } + + sink.acceptPlatformInfo(platformInfo); + return runAfter; + } +}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/producers/README.md b/src/main/java/com/google/devtools/build/lib/analysis/producers/README.md new file mode 100644 index 0000000..a3f0778 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/analysis/producers/README.md
@@ -0,0 +1,74 @@ +## Analysis Producers + +Analysis is driven by `StateMachine`[^documentation] implementations that +memoize computations across Skyframe restarts, and enable extremely efficient +concurrency. Concurrency is crucial in this context, as analysis involves +applying a series of processing steps to multiple dependencies. By employing +concurrency, these steps can proceed independently rather than being delayed by +stragglers. + +`StateMachines` serve as composable building blocks, and this document +illustrates how the analysis producers fit together. + +### `DependencyContextProducer` + +The `DependencyContextProducer` is used by `AspectFunction`. It first computes +the unloaded toolchain contexts, which as a side effect, computes +`PlatformInfo`, a prerequisite of `ConfigConditionsProducer`. + +```none + +---------------------------------+ + | DependencyContextProducer | + +---------------------------------+ + ^ ^ + | | + | | ++-----------------------------------+ +---------------------------------+ +| UnloadedToolchainContextsProducer | | ConfigConditionsProducer | ++-----------------------------------+ +---------------------------------+ + ^ + | + | + +---------------------------------+ + | ConfiguredTargetAndDataProducer | + +---------------------------------+ +``` + +### `DependencyContextProducerWithCompatibilityCheck` + +The `DependencyContextProducerWithCompatibilityCheck` is used by the +`ConfiguredTargetFunction`. Here, a check for incompatibility needs to happen +before the unloaded toolchain contexts are computed to avoid failing the build +due to incompatibility. So instead of obtaining `PlatformInfo` as a side effect +of computing the unloaded toolchain contexts, it is first computed directly +using `PlatformInfoProducer`. The `PlatformInfo` is then used to compute +`ConfigConditions` via the `ConfigConditionsProducer` and the resulting +`ConfigConditions` are used in the compatibility check. Only after the check +passes does it finally compute the unloaded toolchain contexts. + +```none ++-----------------------------------++----------------------------+ +| UnloadedToolchainContextsProducer || IncompatibleTargetProducer | ++-----------------------------------++----------------------------+ + | | + | | + v v ++-----------------------------------------------------------------+ +----------------------+ +| DependencyContextProducerWithCompatibilityCheck | <-- | PlatformInfoProducer | ++-----------------------------------------------------------------+ +----------------------+ + ^ + | + | ++-----------------------------------+ +| ConfigConditionsProducer | ++-----------------------------------+ + ^ + | + | ++-----------------------------------+ +| ConfiguredTargetAndDataProducer | ++-----------------------------------+ +``` + +[^documentation]: TODO(b/261521010): add link when documentation has been + checked in.
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/producers/TransitiveDependencyState.java b/src/main/java/com/google/devtools/build/lib/analysis/producers/TransitiveDependencyState.java new file mode 100644 index 0000000..7030cbb --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/analysis/producers/TransitiveDependencyState.java
@@ -0,0 +1,87 @@ +// Copyright 2023 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.lib.analysis.producers; + +import com.google.devtools.build.lib.analysis.ConfiguredTargetValue; +import com.google.devtools.build.lib.causes.Cause; +import com.google.devtools.build.lib.cmdline.PackageIdentifier; +import com.google.devtools.build.lib.collect.nestedset.NestedSet; +import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; +import com.google.devtools.build.lib.packages.Package; +import java.util.concurrent.ConcurrentHashMap; +import javax.annotation.Nullable; + +/** Tuple storing state associated with transitive dependencies. */ +public final class TransitiveDependencyState { + private final NestedSetBuilder<Cause> transitiveRootCauses; + + /** + * The set of packages transitively loaded for the value being built. + * + * <p>See {@link + * com.google.devtools.build.lib.analysis.ConfiguredObjectValue#getTransitivePackages}. + * + * <p>Non-null when transitive packages are tracked, determined by {@link + * com.google.devtools.build.lib.skyframe.SkyframeExecutor#shouldStoreTransitivePackagesInLoadingAndAnalysis}. + */ + @Nullable private final NestedSetBuilder<Package> transitivePackages; + + /** + * Contains packages that were previously computed. + * + * <p>It's valid to obtain packages of dependencies from this map instead of creating an edge in + * {@code Skyframe} due to the transitive dependency through the {@link ConfiguredTarget}. Note + * that this is compatible with bottom-up change pruning because {@link ConfiguredTargetValue} + * uses identity equals. + */ + @Nullable // TODO(b/261521010): make this non-null. + private final ConcurrentHashMap<PackageIdentifier, Package> prerequisitePackages; + + public TransitiveDependencyState( + NestedSetBuilder<Cause> transitiveRootCauses, + @Nullable NestedSetBuilder<Package> transitivePackages, + @Nullable ConcurrentHashMap<PackageIdentifier, Package> prerequisitePackages) { + this.transitiveRootCauses = transitiveRootCauses; + this.transitivePackages = transitivePackages; + this.prerequisitePackages = prerequisitePackages; + } + + public NestedSetBuilder<Package> transitivePackages() { + return transitivePackages; + } + + public void addTransitiveCauses(NestedSet<Cause> transitiveCauses) { + transitiveRootCauses.addTransitive(transitiveCauses); + } + + /** + * Adds to the set of transitive packages if tracked. + * + * <p>This is a no-op otherwise. + */ + public void updateTransitivePackages(ConfiguredTargetValue configuredTarget) { + if (transitivePackages == null) { + return; + } + transitivePackages.addTransitive(configuredTarget.getTransitivePackages()); + } + + @Nullable + public Package getDependencyPackage(PackageIdentifier packageId) { + if (prerequisitePackages == null) { + return null; + } + return prerequisitePackages.get(packageId); + } +}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/producers/UnloadedToolchainContextsInputs.java b/src/main/java/com/google/devtools/build/lib/analysis/producers/UnloadedToolchainContextsInputs.java new file mode 100644 index 0000000..90ce841 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/analysis/producers/UnloadedToolchainContextsInputs.java
@@ -0,0 +1,40 @@ +// Copyright 2023 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.lib.analysis.producers; + +import com.google.auto.value.AutoValue; +import com.google.common.collect.ImmutableMap; +import com.google.devtools.build.lib.analysis.ExecGroupCollection; +import com.google.devtools.build.lib.packages.ExecGroup; +import com.google.devtools.build.lib.skyframe.ToolchainContextKey; +import javax.annotation.Nullable; + +/** Collates inputs for the {@link UnloadedToolchainContextsProducer}. */ +@AutoValue +public abstract class UnloadedToolchainContextsInputs extends ExecGroupCollection.Builder { + @Nullable // Null if no toolchain resolution is required. + public abstract ToolchainContextKey targetToolchainContextKey(); + + public static UnloadedToolchainContextsInputs create( + ImmutableMap<String, ExecGroup> processedExecGroups, + @Nullable ToolchainContextKey targetToolchainContextKey) { + return new AutoValue_UnloadedToolchainContextsInputs( + processedExecGroups, targetToolchainContextKey); + } + + public static UnloadedToolchainContextsInputs empty() { + return new AutoValue_UnloadedToolchainContextsInputs( + ImmutableMap.of(), /* targetToolchainContextKey= */ null); + } +}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/producers/UnloadedToolchainContextsProducer.java b/src/main/java/com/google/devtools/build/lib/analysis/producers/UnloadedToolchainContextsProducer.java new file mode 100644 index 0000000..4d618ed --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/analysis/producers/UnloadedToolchainContextsProducer.java
@@ -0,0 +1,141 @@ +// Copyright 2023 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.lib.analysis.producers; + +import static com.google.devtools.build.lib.packages.ExecGroup.DEFAULT_EXEC_GROUP_NAME; + +import com.google.devtools.build.lib.analysis.ToolchainCollection; +import com.google.devtools.build.lib.events.ExtendedEventHandler; +import com.google.devtools.build.lib.packages.ExecGroup; +import com.google.devtools.build.lib.skyframe.NoMatchingPlatformException; +import com.google.devtools.build.lib.skyframe.ToolchainContextKey; +import com.google.devtools.build.lib.skyframe.ToolchainException; +import com.google.devtools.build.lib.skyframe.UnloadedToolchainContext; +import com.google.devtools.build.skyframe.SkyValue; +import com.google.devtools.build.skyframe.state.StateMachine; +import java.util.Map; +import javax.annotation.Nullable; + +final class UnloadedToolchainContextsProducer implements StateMachine { + interface ResultSink { + void acceptUnloadedToolchainContexts( + @Nullable ToolchainCollection<UnloadedToolchainContext> unloadedToolchainContexts); + + void acceptUnloadedToolchainContextsError(ToolchainException error); + } + + // -------------------- Input -------------------- + private final UnloadedToolchainContextsInputs unloadedToolchainContextsInputs; + + // -------------------- Output -------------------- + private final ResultSink sink; + + // -------------------- Sequencing -------------------- + private final StateMachine runAfter; + + // -------------------- Internal State -------------------- + private ToolchainCollection.Builder<UnloadedToolchainContext> toolchainContextsBuilder; + private boolean toolchainContextsHasError = false; + + UnloadedToolchainContextsProducer( + UnloadedToolchainContextsInputs unloadedToolchainContextsInputs, + ResultSink sink, + StateMachine runAfter) { + this.unloadedToolchainContextsInputs = unloadedToolchainContextsInputs; + this.sink = sink; + this.runAfter = runAfter; + } + + @Override + public StateMachine step(Tasks tasks, ExtendedEventHandler listener) { + var defaultToolchainContextKey = unloadedToolchainContextsInputs.targetToolchainContextKey(); + if (defaultToolchainContextKey == null) { + // Doesn't use toolchain resolution and short-circuits. + sink.acceptUnloadedToolchainContexts(null); + return runAfter; + } + + this.toolchainContextsBuilder = + ToolchainCollection.builderWithExpectedSize( + unloadedToolchainContextsInputs.execGroups().size() + 1); + + tasks.lookUp( + defaultToolchainContextKey, + ToolchainException.class, + new ToolchainContextLookupCallback(DEFAULT_EXEC_GROUP_NAME)); + + var keyBuilder = + ToolchainContextKey.key() + .configurationKey(defaultToolchainContextKey.configurationKey()) + .debugTarget(defaultToolchainContextKey.debugTarget()); + + for (Map.Entry<String, ExecGroup> entry : + unloadedToolchainContextsInputs.execGroups().entrySet()) { + var execGroup = entry.getValue(); + tasks.lookUp( + keyBuilder + .toolchainTypes(execGroup.toolchainTypes()) + .execConstraintLabels(execGroup.execCompatibleWith()) + .build(), + ToolchainException.class, + new ToolchainContextLookupCallback(entry.getKey())); + } + + return this::buildToolchainContexts; + } + + private class ToolchainContextLookupCallback + implements StateMachine.ValueOrExceptionSink<ToolchainException> { + private final String execGroupName; + + private ToolchainContextLookupCallback(String execGroupName) { + this.execGroupName = execGroupName; + } + + @Override + public void acceptValueOrException( + @Nullable SkyValue value, @Nullable ToolchainException error) { + if (value != null) { + var unloadedToolchainContext = (UnloadedToolchainContext) value; + var errorData = unloadedToolchainContext.errorData(); + if (errorData != null) { + handleError(new NoMatchingPlatformException(errorData)); + return; + } + toolchainContextsBuilder.addContext(execGroupName, unloadedToolchainContext); + return; + } + if (error != null) { + handleError(error); + return; + } + throw new IllegalArgumentException("both inputs were null"); + } + } + + private void handleError(ToolchainException error) { + if (!toolchainContextsHasError) { // Only propagates the first error. + toolchainContextsHasError = true; + sink.acceptUnloadedToolchainContextsError(error); + } + } + + private StateMachine buildToolchainContexts(Tasks tasks, ExtendedEventHandler listener) { + if (toolchainContextsHasError) { + return runAfter; + } + sink.acceptUnloadedToolchainContexts(toolchainContextsBuilder.build()); + return runAfter; + } +}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java index 8f59b09..445ec53 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java
@@ -53,6 +53,10 @@ import com.google.devtools.build.lib.analysis.config.transitions.ConfigurationTransition; import com.google.devtools.build.lib.analysis.config.transitions.NoTransition; import com.google.devtools.build.lib.analysis.configuredtargets.MergedConfiguredTarget; +import com.google.devtools.build.lib.analysis.producers.DependencyContext; +import com.google.devtools.build.lib.analysis.producers.DependencyContextProducer; +import com.google.devtools.build.lib.analysis.producers.TransitiveDependencyState; +import com.google.devtools.build.lib.analysis.producers.UnloadedToolchainContextsInputs; import com.google.devtools.build.lib.analysis.starlark.StarlarkTransition.TransitionException; import com.google.devtools.build.lib.bugreport.BugReport; import com.google.devtools.build.lib.causes.Cause; @@ -82,7 +86,6 @@ import com.google.devtools.build.lib.profiler.memory.CurrentRuleTracker; import com.google.devtools.build.lib.skyframe.AspectKeyCreator.AspectKey; import com.google.devtools.build.lib.skyframe.BzlLoadFunction.BzlLoadFailedException; -import com.google.devtools.build.lib.skyframe.PrerequisiteProducer.UnloadedToolchainContextsInputs; import com.google.devtools.build.lib.skyframe.SkyframeExecutor.BuildViewProvider; import com.google.devtools.build.lib.util.OrderedSetMultimap; import com.google.devtools.build.skyframe.SkyFunction; @@ -91,11 +94,11 @@ import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.SkyValue; import com.google.devtools.build.skyframe.SkyframeLookupResult; +import com.google.devtools.build.skyframe.state.Driver; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import javax.annotation.Nullable; import net.starlark.java.eval.StarlarkSemantics; @@ -206,7 +209,7 @@ } if (AliasProvider.isAlias(associatedTarget)) { - return createAliasAspect(env, targetAndConfiguration, aspect, key, associatedTarget); + return createAliasAspect(env, state, aspect, key, associatedTarget); } // If we get here, label should match original label, and therefore the target we looked up // above indeed corresponds to associatedTarget.getLabel(). @@ -278,33 +281,21 @@ } try { - var unloadedToolchainContextsInputs = - getUnloadedToolchainContextsInputs( - aspect.getDefinition(), key.getConfigurationKey(), configuration); - Optional<ToolchainCollection<UnloadedToolchainContext>> computedToolchainContexts = - PrerequisiteProducer.computeUnloadedToolchainContexts( - env, unloadedToolchainContextsInputs); - if (computedToolchainContexts == null) { + var dependencyContext = + getDependencyContext( + computeDependenciesState, + key, + aspect, + state.transitiveRootCauses, + state.transitivePackages, + env); + if (dependencyContext == null) { return null; } ToolchainCollection<UnloadedToolchainContext> unloadedToolchainContexts = - computedToolchainContexts.orElse(null); - - // Get the configuration targets that trigger this rule's configurable attributes. - ConfigConditions configConditions = - PrerequisiteProducer.computeConfigConditions( - env, - targetAndConfiguration, - state.transitivePackages, - unloadedToolchainContexts == null - ? null - : unloadedToolchainContexts.getTargetPlatform(), - state.transitiveRootCauses); - if (configConditions == null) { - // Those targets haven't yet been resolved. - return null; - } + dependencyContext.unloadedToolchainContexts(); + ConfigConditions configConditions = dependencyContext.configConditions(); OrderedSetMultimap<DependencyKind, ConfiguredTargetAndData> depValueMap; try { @@ -368,7 +359,7 @@ configuration, configConditions, toolchainContexts, - unloadedToolchainContextsInputs, + computeDependenciesState.execGroupCollectionBuilder, depValueMap, state.transitivePackages); } catch (DependencyEvaluationException e) { @@ -411,6 +402,59 @@ } } + /** Populates {@code state.execGroupCollection} as a side effect. */ + @Nullable // Null if a Skyframe restart is needed. + private static DependencyContext getDependencyContext( + PrerequisiteProducer.State state, + AspectKey key, + Aspect aspect, + NestedSetBuilder<Cause> transitiveRootCauses, + @Nullable NestedSetBuilder<Package> transitivePackages, + Environment env) + throws InterruptedException, ConfiguredValueCreationException, ToolchainException { + if (state.dependencyContext != null) { + return state.dependencyContext; + } + if (state.dependencyContextProducer == null) { + TargetAndConfiguration targetAndConfiguration = state.targetAndConfiguration; + UnloadedToolchainContextsInputs unloadedToolchainContextsInputs = + getUnloadedToolchainContextsInputs( + aspect.getDefinition(), + key.getConfigurationKey(), + targetAndConfiguration.getConfiguration()); + state.execGroupCollectionBuilder = unloadedToolchainContextsInputs; + state.dependencyContextProducer = + new Driver( + new DependencyContextProducer( + unloadedToolchainContextsInputs, + targetAndConfiguration, + new TransitiveDependencyState( + transitiveRootCauses, transitivePackages, /* prerequisitePackages= */ null), + (DependencyContextProducer.ResultSink) state)); + } + if (state.dependencyContextProducer.drive(env, env.getListener())) { + state.dependencyContextProducer = null; + } + + // During error bubbling, the state machine might not be done, but still emit an error. + var error = state.dependencyContextError; + if (error != null) { + switch (error.kind()) { + case TOOLCHAIN: + throw error.toolchain(); + case CONFIGURED_VALUE_CREATION: + throw error.configuredValueCreation(); + case INCOMPATIBLE_TARGET: + throw new IllegalStateException("Unexpected error: " + error.incompatibleTarget()); + case VALIDATION: + throw new IllegalStateException("Unexpected error: " + error.validation()); + } + throw new IllegalStateException("unreachable"); + } + + return state.dependencyContext; // Null if not yet done. + } + static BzlLoadValue.Key bzlLoadKeyForStarlarkAspect(StarlarkAspectClass starlarkAspectClass) { Label extensionLabel = starlarkAspectClass.getExtensionLabel(); return StarlarkBuiltinsValue.isBuiltinsRepo(extensionLabel.getRepository()) @@ -621,7 +665,7 @@ @Nullable private AspectValue createAliasAspect( Environment env, - TargetAndConfiguration originalTarget, + State state, Aspect aspect, AspectKey originalKey, ConfiguredTarget configuredTarget) @@ -636,36 +680,20 @@ storeTransitivePackages ? NestedSetBuilder.stableOrder() : null; NestedSetBuilder<Cause> transitiveRootCauses = NestedSetBuilder.stableOrder(); + TargetAndConfiguration originalTarget = state.computeDependenciesState.targetAndConfiguration; + // Compute the Dependency from the original target to aliasedLabel. Dependency dep; try { - var configuration = originalTarget.getConfiguration(); - // Determine what toolchains are needed by this target. - var unloadedToolchainContextsInputs = - getUnloadedToolchainContextsInputs( - aspect.getDefinition(), originalKey.getConfigurationKey(), configuration); - Optional<ToolchainCollection<UnloadedToolchainContext>> computedToolchainContexts = - PrerequisiteProducer.computeUnloadedToolchainContexts( - env, unloadedToolchainContextsInputs); - if (computedToolchainContexts == null) { - return null; - } - - ToolchainCollection<UnloadedToolchainContext> unloadedToolchainContexts = - computedToolchainContexts.orElse(null); - - // Get the configuration targets that trigger this rule's configurable attributes. - ConfigConditions configConditions = - PrerequisiteProducer.computeConfigConditions( - env, - originalTarget, - transitivePackages, - unloadedToolchainContexts == null - ? null - : unloadedToolchainContexts.getTargetPlatform(), - transitiveRootCauses); - if (configConditions == null) { - // Those targets haven't yet been resolved. + var dependencyContext = + getDependencyContext( + state.computeDependenciesState, + originalKey, + aspect, + state.transitiveRootCauses, + state.transitivePackages, + env); + if (dependencyContext == null) { return null; } @@ -675,24 +703,25 @@ } ConfigurationTransition transition = TransitionResolver.evaluateTransition( - configuration, + originalTarget.getConfiguration(), NoTransition.INSTANCE, aliasedTarget, ((ConfiguredRuleClassProvider) ruleClassProvider).getTrimmingTransitionFactory()); // Use ConfigurationResolver to apply any configuration transitions on the alias edge. - // This is a shortened/simplified variant of ConfiguredTargetFunction.computeDependencies + // This is a shortened/simplified variant of PrerequisiteProducer.computeDependencies // for just the one special attribute we care about here. DependencyKey depKey = DependencyKey.builder().setLabel(aliasedLabel).setTransition(transition).build(); DependencyKind depKind = DependencyKind.AttributeDependencyKind.forRule( getAttributeContainingAlias(originalTarget.getTarget())); + ConfigurationResolver resolver = new ConfigurationResolver( env, originalTarget, - configConditions.asProviders(), + dependencyContext.configConditions().asProviders(), buildViewProvider.getSkyframeBuildView().getStarlarkTransitionCache()); ImmutableList<Dependency> deps = resolver.resolveConfiguration(depKind, depKey, env.getListener());
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD index c635f15..f772ece 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD +++ b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
@@ -47,7 +47,6 @@ "HighWaterMarkLimiter.java", "LocalRepositoryLookupFunction.java", "PackageFunction.java", - "PlatformLookupUtil.java", "PlatformMappingFunction.java", "PrepareDepsOfPatternFunction.java", "PrerequisiteProducer.java", @@ -153,7 +152,6 @@ ":map_as_package_roots", ":metadata_consumer_for_metrics", ":no_matching_platform_data", - ":no_matching_platform_exception", ":node_dropping_inconsistency_receiver", ":package_error_function", ":package_error_message_function", @@ -164,6 +162,7 @@ ":package_roots_no_symlink_creation", ":package_value", ":pattern_expanding_error", + ":platform_lookup_util", ":precomputed_function", ":precomputed_value", ":prepare_deps_of_pattern_value", @@ -287,6 +286,7 @@ "//src/main/java/com/google/devtools/build/lib/analysis:workspace_status_action", "//src/main/java/com/google/devtools/build/lib/analysis/platform", "//src/main/java/com/google/devtools/build/lib/analysis/platform:utils", + "//src/main/java/com/google/devtools/build/lib/analysis/producers", "//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:common", "//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:exception", "//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:module_extension", @@ -312,8 +312,6 @@ "//src/main/java/com/google/devtools/build/lib/io:inconsistent_filesystem_exception", "//src/main/java/com/google/devtools/build/lib/io:process_package_directory_exception", "//src/main/java/com/google/devtools/build/lib/packages", - "//src/main/java/com/google/devtools/build/lib/packages:configured_attribute_mapper", - "//src/main/java/com/google/devtools/build/lib/packages:exec_group", "//src/main/java/com/google/devtools/build/lib/packages:globber", "//src/main/java/com/google/devtools/build/lib/packages:globber_utils", "//src/main/java/com/google/devtools/build/lib/packages/semantics", @@ -754,7 +752,6 @@ "//src/main/java/com/google/devtools/build/lib/actions:action_lookup_key", "//src/main/java/com/google/devtools/build/lib/analysis:config/build_configuration", "//src/main/java/com/google/devtools/build/lib/cmdline", - "//src/main/java/com/google/devtools/build/lib/concurrent", "//src/main/java/com/google/devtools/build/lib/packages", "//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec", "//src/main/java/com/google/devtools/build/skyframe:skyframe-objects", @@ -1157,7 +1154,6 @@ "//src/main/java/com/google/devtools/build/lib/analysis:config/build_configuration", "//src/main/java/com/google/devtools/build/lib/analysis:configured_target", "//src/main/java/com/google/devtools/build/lib/cmdline", - "//src/main/java/com/google/devtools/build/lib/concurrent", "//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec", "//src/main/java/com/google/devtools/build/skyframe:skyframe-objects", "//third_party:guava", @@ -1852,6 +1848,29 @@ ) java_library( + name = "platform_lookup_util", + srcs = ["PlatformLookupUtil.java"], + deps = [ + ":configured_target_key", + ":configured_value_creation_exception", + ":package_value", + ":toolchain_exception", + "//src/main/java/com/google/devtools/build/lib/actions", + "//src/main/java/com/google/devtools/build/lib/analysis:configured_target", + "//src/main/java/com/google/devtools/build/lib/analysis:configured_target_value", + "//src/main/java/com/google/devtools/build/lib/analysis/platform", + "//src/main/java/com/google/devtools/build/lib/analysis/platform:utils", + "//src/main/java/com/google/devtools/build/lib/bugreport", + "//src/main/java/com/google/devtools/build/lib/cmdline", + "//src/main/java/com/google/devtools/build/lib/packages", + "//src/main/java/com/google/devtools/build/skyframe", + "//src/main/protobuf:failure_details_java_proto", + "//third_party:guava", + "//third_party:jsr305", + ], +) + +java_library( name = "precomputed_function", srcs = ["PrecomputedFunction.java"], deps = [
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java index 23a76b7..3a71a96 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
@@ -254,7 +254,7 @@ prereqs.getDepValueMap(), prereqs.getConfigConditions(), toolchainContexts, - prereqs.getExecGroupCollectionsBuilder(), + state.computeDependenciesState.execGroupCollectionBuilder, state.transitivePackages); if (ans != null && configuredTargetProgress != null) { configuredTargetProgress.doneConfigureTarget();
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/NoMatchingPlatformData.java b/src/main/java/com/google/devtools/build/lib/skyframe/NoMatchingPlatformData.java index d99be9c..12859d5 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/NoMatchingPlatformData.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/NoMatchingPlatformData.java
@@ -23,7 +23,7 @@ /** Contains information related to missing execution platform. */ @AutoValue -abstract class NoMatchingPlatformData { +public abstract class NoMatchingPlatformData { abstract ImmutableSet<ToolchainTypeRequirement> toolchainTypes(); abstract ImmutableList<ConfiguredTargetKey> availableExecutionPlatformKeys();
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/PlatformLookupUtil.java b/src/main/java/com/google/devtools/build/lib/skyframe/PlatformLookupUtil.java index e2cc40a..d29e871 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/PlatformLookupUtil.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/PlatformLookupUtil.java
@@ -156,7 +156,7 @@ } } - static boolean hasPlatformInfo(Target target) { + public static boolean hasPlatformInfo(Target target) { Rule rule = target.getAssociatedRule(); // If the rule uses toolchain resolution, it can't be used as a target or exec platform. if (rule == null) { @@ -175,11 +175,11 @@ public static final class InvalidPlatformException extends ToolchainException { private static final String DEFAULT_ERROR = "does not provide PlatformInfo"; - InvalidPlatformException(Label label) { + public InvalidPlatformException(Label label) { super(formatError(label, DEFAULT_ERROR)); } - InvalidPlatformException(Label label, ConfiguredValueCreationException e) { + public InvalidPlatformException(Label label, ConfiguredValueCreationException e) { super(formatError(label, DEFAULT_ERROR), e); }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/PrerequisiteProducer.java b/src/main/java/com/google/devtools/build/lib/skyframe/PrerequisiteProducer.java index b40a494..fa218eb 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/PrerequisiteProducer.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/PrerequisiteProducer.java
@@ -13,9 +13,6 @@ // limitations under the License. package com.google.devtools.build.lib.skyframe; -import static com.google.devtools.build.lib.packages.ExecGroup.DEFAULT_EXEC_GROUP_NAME; - -import com.google.auto.value.AutoValue; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; @@ -50,8 +47,13 @@ import com.google.devtools.build.lib.analysis.config.ToolchainTypeRequirement; import com.google.devtools.build.lib.analysis.config.transitions.PatchTransition; import com.google.devtools.build.lib.analysis.constraints.IncompatibleTargetChecker.IncompatibleTargetException; -import com.google.devtools.build.lib.analysis.constraints.IncompatibleTargetChecker.IncompatibleTargetProducer; import com.google.devtools.build.lib.analysis.platform.PlatformInfo; +import com.google.devtools.build.lib.analysis.producers.DependencyContext; +import com.google.devtools.build.lib.analysis.producers.DependencyContextError; +import com.google.devtools.build.lib.analysis.producers.DependencyContextProducer; +import com.google.devtools.build.lib.analysis.producers.DependencyContextProducerWithCompatibilityCheck; +import com.google.devtools.build.lib.analysis.producers.TransitiveDependencyState; +import com.google.devtools.build.lib.analysis.producers.UnloadedToolchainContextsInputs; import com.google.devtools.build.lib.bugreport.BugReport; import com.google.devtools.build.lib.causes.Cause; import com.google.devtools.build.lib.causes.LoadingFailedCause; @@ -63,12 +65,9 @@ import com.google.devtools.build.lib.events.StoredEventHandler; import com.google.devtools.build.lib.packages.Aspect; import com.google.devtools.build.lib.packages.BuildType; -import com.google.devtools.build.lib.packages.ConfiguredAttributeMapper.ValidationException; -import com.google.devtools.build.lib.packages.ExecGroup; import com.google.devtools.build.lib.packages.NoSuchTargetException; import com.google.devtools.build.lib.packages.NonconfigurableAttributeMapper; import com.google.devtools.build.lib.packages.Package; -import com.google.devtools.build.lib.packages.RawAttributeMapper; import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.packages.RuleClass; import com.google.devtools.build.lib.packages.RuleClassProvider; @@ -87,64 +86,66 @@ import com.google.devtools.build.skyframe.state.Driver; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.Set; import javax.annotation.Nullable; -import net.starlark.java.syntax.Location; /** - * Helper logic for {@link ConfiguredTargetFunction}: performs the analysis phase through - * computation of prerequisites. + * Helper logic for {@link ConfiguredTargetFunction} and {@link AspectFunction}: performs the + * analysis phase through computation of prerequisites. * - * <p>This includes: + * <p>For the {@link ConfiguredTargetFunction} this includes: * * <ul> * <li>getting this target's {@link Target} and {@link BuildConfigurationValue} - * <li>getting this target's {@code select()} keys (config conditions), which are used to evaluate - * all rule attributes with {@code select()} and determine exact dependencies + * <li>getting this target's {@code select()} keys ({@link ConfigConditions}), which are used to + * evaluate all rule attributes with {@code select()} and determine exact dependencies * <li>figuring out which toolchains this target needs * <li>getting the {@link ConfiguredTargetValue}s of this target's prerequisites (through * recursive calls to {@link ConfiguredTargetFunction} * </ul> * + * <p>Figuring out which toolchains are needed and computing the {@link ConfigConditions} is + * performed by the {@link DependencyContextProducerWithCompatibilityCheck}, which additionally + * checks for directly incompatible targets using the {@link + * IncompatibleTargetChecker.IncompatibleTargetProducer}. + * * <p>Cumulatively, this is enough information to run the target's rule logic. * * <p>This class also provides getters for the above data for subsequent analysis logic to use. * * <p>See {@link ConfiguredTargetFunction} for more review on analysis implementation. + * + * <p>{@link AspectFunction} shares the logic computing a target's prerequisites via the {@link + * PrerequisiteProducer#computeDependencies}. */ public final class PrerequisiteProducer { - static class State implements SkyKeyComputeState, IncompatibleTargetProducer.ResultSink { - @Nullable TargetAndConfiguration targetAndConfiguration; + /** + * Memoizies computation steps of {@link #evaluate} so they do not need to be repeated on {@code + * Skyframe} restart. + */ + @VisibleForTesting + public static class State implements SkyKeyComputeState, DependencyContextProducer.ResultSink { + @VisibleForTesting @Nullable public TargetAndConfiguration targetAndConfiguration; + + /** Set once {@link #dependencyContextProducer} starts. */ + @VisibleForTesting public ExecGroupCollection.Builder execGroupCollectionBuilder; /** - * Drives the stateful computation of {@link #incompatibleTarget}. + * Computes the dependency context, comprised of the unloaded toolchain contexts and the config + * conditions. * - * <p>Non-null only while the computation is in-flight. + * <p>One of {@link #dependencyContext} or {@link #dependencyContextError} will be set upon + * completion. */ - @Nullable private Driver incompatibleTargetProducer; + @Nullable // Non-null when in-flight. + Driver dependencyContextProducer; - /** - * If a value is present, it means the target was directly incompatible. - * - * <p>Either this or {@link #validationException} will be non-null after the {@link - * #incompatibleTargetProducer} completes. - */ - @Nullable private Optional<RuleConfiguredTargetValue> incompatibleTarget; - - /** - * If this is set, an exception occurred during validation of the {@code target_compatible_with} - * attribute. - * - * <p>Either this or {@link #incompatibleTarget} will be non-null after the {@link - * #incompatibleTargetProducer} completes. - */ - @Nullable private ValidationException validationException; + @Nullable DependencyContext dependencyContext; + @Nullable DependencyContextError dependencyContextError; /** Null if not yet computed or if {@link #resolveConfigurationsResult} is non-null. */ @Nullable private OrderedSetMultimap<DependencyKind, DependencyKey> dependentNodeMapResult; @@ -182,13 +183,13 @@ @Nullable private StoredEventHandler storedEventHandlerFromResolveConfigurations; @Override - public void accept(Optional<RuleConfiguredTargetValue> incompatibleTarget) { - this.incompatibleTarget = incompatibleTarget; + public void acceptDependencyContext(DependencyContext value) { + this.dependencyContext = value; } @Override - public void acceptValidationException(ValidationException e) { - this.validationException = e; + public void acceptDependencyContextError(DependencyContextError error) { + this.dependencyContextError = error; } } @@ -207,8 +208,7 @@ private OrderedSetMultimap<DependencyKind, ConfiguredTargetAndData> depValueMap = null; private ConfigConditions configConditions = null; private PlatformInfo platformInfo = null; - private ToolchainCollection<UnloadedToolchainContext> unloadedToolchainContexts = null; - private ExecGroupCollection.Builder execGroupCollectionBuilder = null; + @Nullable private ToolchainCollection<UnloadedToolchainContext> unloadedToolchainContexts = null; /** * Return this target's {@link TargetAndConfiguration}. @@ -258,15 +258,6 @@ } /** - * Return this target's {@link ExecGroupCollection}, as far as this phase of analysis computes. - * - * <p>{@link #evaluate} must be called before this info is available. - */ - ExecGroupCollection.Builder getExecGroupCollectionsBuilder() { - return Preconditions.checkNotNull(execGroupCollectionBuilder); - } - - /** * Run's the analysis phase for this target through prerequisite evaluation. * * <p>See {@link PrerequisiteProducer} javadoc for details. @@ -315,72 +306,22 @@ // would exit this SkyFunction and restart it when permits were available. semaphoreLocker.acquireSemaphore(); try { - // Check target compatibility before requesting toolchains - known missing toolchains are a - // valid reason for declaring a target incompatible and should not result in the target - // failing to build rather than being skipped as incompatible. - // Non-rule targets and those that are part of the toolchain resolution system do not support - // target compatibility checking anyway. - if (targetAndConfiguration.getTarget() instanceof Rule - && ((Rule) targetAndConfiguration.getTarget()).useToolchainResolution()) { - platformInfo = loadTargetPlatformInfo(env); - // loadTargetPlatformInfo may return null even when no deps are missing. - if (env.valuesMissing()) { - return false; - } - - configConditions = - computeConfigConditions( - env, - targetAndConfiguration, - transitivePackages, - platformInfo, - transitiveRootCauses); - if (configConditions == null) { - return false; - } - - if (!checkForIncompatibleTarget( - env, - state, - targetAndConfiguration, - configConditions, - platformInfo, - transitivePackages)) { - return false; - } - } - var unloadedToolchainContextsInputs = - getUnloadedToolchainContextsInputs( - targetAndConfiguration, + var dependencyContext = + getDependencyContext( + state, configuredTargetKey.getExecutionPlatformLabel(), + transitiveRootCauses, + transitivePackages, ruleClassProvider, - env.getListener()); - // Determine what toolchains are needed by this target. - Optional<ToolchainCollection<UnloadedToolchainContext>> result = - computeUnloadedToolchainContexts(env, unloadedToolchainContextsInputs); - if (result == null) { + env); + if (dependencyContext == null) { return false; } - this.unloadedToolchainContexts = result.orElse(null); - this.execGroupCollectionBuilder = unloadedToolchainContextsInputs; + this.unloadedToolchainContexts = dependencyContext.unloadedToolchainContexts(); this.platformInfo = unloadedToolchainContexts != null ? unloadedToolchainContexts.getTargetPlatform() : null; + this.configConditions = dependencyContext.configConditions(); - // Get the configuration targets that trigger this rule's configurable attributes. - // Has been computed as part of checking for incompatible targets at the beginning of this - // function unless the current target is not a rule participating in toolchain resolution. - if (configConditions == null) { - configConditions = - computeConfigConditions( - env, - targetAndConfiguration, - transitivePackages, - platformInfo, - transitiveRootCauses); - } - if (configConditions == null) { - return false; - } // TODO(ulfjack): ConfiguredAttributeMapper (indirectly used from computeDependencies) isn't // safe to use if there are missing config conditions, so we stop here, but only if there are // config conditions - though note that we can't check if configConditions is non-empty - it @@ -439,87 +380,81 @@ return true; } - // May return null even when no deps are missing, use env.valuesMissing() to check. - @Nullable - private PlatformInfo loadTargetPlatformInfo(Environment env) - throws InterruptedException, ToolchainException { - PlatformConfiguration platformConfiguration = - targetAndConfiguration.getConfiguration().getFragment(PlatformConfiguration.class); - if (platformConfiguration == null) { - // No restart required in this case. - return null; - } - Label targetPlatformLabel = platformConfiguration.getTargetPlatform(); - ConfiguredTargetKey targetPlatformKey = - ConfiguredTargetKey.builder() - .setLabel(targetPlatformLabel) - .setConfiguration(targetAndConfiguration.getConfiguration()) - .build(); - - Map<ConfiguredTargetKey, PlatformInfo> platformInfoMap = - PlatformLookupUtil.getPlatformInfo(ImmutableList.of(targetPlatformKey), env); - if (platformInfoMap == null) { - return null; - } - - return platformInfoMap.get(targetPlatformKey); - } - - /** - * Checks if a target is incompatible because of its "target_compatible_with" attribute. - * - * @return false if a {@code Skyframe} restart is needed. - */ - private static boolean checkForIncompatibleTarget( - Environment env, + @VisibleForTesting + @Nullable // Null when a Skyframe restart is needed. + public static DependencyContext getDependencyContext( State state, - TargetAndConfiguration targetAndConfiguration, - @Nullable ConfigConditions configConditions, - @Nullable PlatformInfo targetPlatformInfo, - @Nullable NestedSetBuilder<Package> transitivePackages) - throws InterruptedException, IncompatibleTargetException, DependencyEvaluationException { - if (state.incompatibleTarget == null) { - BuildConfigurationValue configuration = targetAndConfiguration.getConfiguration(); - if (state.incompatibleTargetProducer == null) { - state.incompatibleTargetProducer = - new Driver( - new IncompatibleTargetProducer( - targetAndConfiguration.getTarget(), - configuration, - configConditions, - targetPlatformInfo, - transitivePackages, - state)); - } - if (!state.incompatibleTargetProducer.drive(env, env.getListener())) { - return false; - } - state.incompatibleTargetProducer = null; - if (state.validationException != null) { - Label label = targetAndConfiguration.getLabel(); - Location location = targetAndConfiguration.getTarget().getLocation(); - env.getListener() - .post( - new AnalysisRootCauseEvent( - configuration, label, state.validationException.getMessage())); - throw new DependencyEvaluationException( - new ConfiguredValueCreationException( - location, - state.validationException.getMessage(), - label, - configuration.getEventId(), - null, - null), - // These errors occur within DependencyResolver, which is attached to the current - // target. i.e. no dependent ConfiguredTargetFunction call happens to report its own - // error. - /* depReportedOwnError= */ false); - } - if (state.incompatibleTarget.isPresent()) { - throw new IncompatibleTargetException(state.incompatibleTarget.get()); - } + @Nullable Label parentExecutionPlatformLabel, + NestedSetBuilder<Cause> transitiveRootCauses, + @Nullable NestedSetBuilder<Package> transitivePackages, + RuleClassProvider ruleClassProvider, + Environment env) + throws InterruptedException, + ToolchainException, + ConfiguredValueCreationException, + IncompatibleTargetException, + DependencyEvaluationException { + if (state.dependencyContext != null) { + return state.dependencyContext; } - return true; + if (state.dependencyContextProducer == null) { + var targetAndConfiguration = state.targetAndConfiguration; + var unloadedToolchainContextsInputs = + getUnloadedToolchainContextsInputs( + targetAndConfiguration, + parentExecutionPlatformLabel, + ruleClassProvider, + env.getListener()); + state.execGroupCollectionBuilder = unloadedToolchainContextsInputs; + state.dependencyContextProducer = + new Driver( + new DependencyContextProducerWithCompatibilityCheck( + targetAndConfiguration, + unloadedToolchainContextsInputs, + new TransitiveDependencyState( + transitiveRootCauses, transitivePackages, /* prerequisitePackages= */ null), + (DependencyContextProducer.ResultSink) state)); + } + if (state.dependencyContextProducer.drive(env, env.getListener())) { + state.dependencyContextProducer = null; + } + + // During error bubbling, the state machine might not be done, but still emit an error. + var error = state.dependencyContextError; + if (error != null) { + switch (error.kind()) { + case TOOLCHAIN: + throw error.toolchain(); + case CONFIGURED_VALUE_CREATION: + throw error.configuredValueCreation(); + case INCOMPATIBLE_TARGET: + throw error.incompatibleTarget(); + case VALIDATION: + var targetAndConfiguration = state.targetAndConfiguration; + var configuration = targetAndConfiguration.getConfiguration(); + Label label = targetAndConfiguration.getLabel(); + var validationException = error.validation(); + env.getListener() + .post( + new AnalysisRootCauseEvent( + configuration, label, validationException.getMessage())); + throw new DependencyEvaluationException( + new ConfiguredValueCreationException( + targetAndConfiguration.getTarget().getLocation(), + validationException.getMessage(), + label, + configuration.getEventId(), + null, + null), + // These errors occur within DependencyResolver, which is attached to the current + // target. i.e. no dependent ConfiguredTargetFunction call happens to report its own + // error. + /* depReportedOwnError= */ false); + } + throw new IllegalStateException("unreachable"); + } + + return state.dependencyContext; // Null if not yet done. } /** @@ -655,92 +590,6 @@ return state.targetAndConfiguration; } - @AutoValue - abstract static class UnloadedToolchainContextsInputs extends ExecGroupCollection.Builder { - @Nullable // Null if no toolchain resolution is required. - abstract ToolchainContextKey targetToolchainContextKey(); - - static UnloadedToolchainContextsInputs create( - ImmutableMap<String, ExecGroup> processedExecGroups, - @Nullable ToolchainContextKey targetToolchainContextKey) { - return new AutoValue_PrerequisiteProducer_UnloadedToolchainContextsInputs( - processedExecGroups, targetToolchainContextKey); - } - - static UnloadedToolchainContextsInputs empty() { - return new AutoValue_PrerequisiteProducer_UnloadedToolchainContextsInputs( - ImmutableMap.of(), /* targetToolchainContextKey= */ null); - } - } - - /** - * Computes the unloaded toolchain contexts. - * - * @return null if a Skyframe restart is needed, {@link Optional#empty} if there is no toolchain - * resolution and the unloaded toolchain contexts otherwise. - */ - @VisibleForTesting // package private - @Nullable - public static Optional<ToolchainCollection<UnloadedToolchainContext>> - computeUnloadedToolchainContexts(Environment env, UnloadedToolchainContextsInputs inputs) - throws InterruptedException, ToolchainException { - var targetToolchainContextKey = inputs.targetToolchainContextKey(); - // Short circuit and end now if this target doesn't require toolchain resolution. - if (targetToolchainContextKey == null) { - return Optional.empty(); - } - - var toolchainContextKeys = new HashMap<String, ToolchainContextKey>(); - toolchainContextKeys.put(DEFAULT_EXEC_GROUP_NAME, targetToolchainContextKey); - - var keyBuilder = - ToolchainContextKey.key() - .configurationKey(targetToolchainContextKey.configurationKey()) - .debugTarget(targetToolchainContextKey.debugTarget()); - for (Map.Entry<String, ExecGroup> entry : inputs.execGroups().entrySet()) { - var execGroup = entry.getValue(); - toolchainContextKeys.put( - entry.getKey(), - keyBuilder - .toolchainTypes(execGroup.toolchainTypes()) - .execConstraintLabels(execGroup.execCompatibleWith()) - .build()); - } - - SkyframeLookupResult values = env.getValuesAndExceptions(toolchainContextKeys.values()); - boolean valuesMissing = env.valuesMissing(); - - ToolchainCollection.Builder<UnloadedToolchainContext> toolchainContexts = - valuesMissing - ? null - : ToolchainCollection.builderWithExpectedSize(toolchainContextKeys.size()); - for (Map.Entry<String, ToolchainContextKey> unloadedToolchainContextKey : - toolchainContextKeys.entrySet()) { - UnloadedToolchainContext unloadedToolchainContext = - (UnloadedToolchainContext) - values.getOrThrow(unloadedToolchainContextKey.getValue(), ToolchainException.class); - if (valuesMissing != env.valuesMissing()) { - BugReport.logUnexpected( - "Value for: '%s' was missing, this should never happen", - unloadedToolchainContextKey.getValue()); - break; - } - if (unloadedToolchainContext != null && unloadedToolchainContext.errorData() != null) { - throw new NoMatchingPlatformException(unloadedToolchainContext.errorData()); - } - if (!valuesMissing) { - toolchainContexts.addContext( - unloadedToolchainContextKey.getKey(), unloadedToolchainContext); - } - } - - if (valuesMissing) { - return null; - } - - return Optional.of(toolchainContexts.build()); - } - /** * Returns the target-specific execution platform constraints, based on the rule definition and * any constraints added by the target, including those added for the target on the command line. @@ -927,99 +776,6 @@ } } - /** - * Returns the targets that key the configurable attributes used by this rule. - * - * <p>If the configured targets supplying those providers aren't yet resolved by the dependency - * resolver, returns null. - */ - @Nullable - static ConfigConditions computeConfigConditions( - Environment env, - TargetAndConfiguration ctgValue, - @Nullable NestedSetBuilder<Package> transitivePackages, - @Nullable PlatformInfo platformInfo, - NestedSetBuilder<Cause> transitiveRootCauses) - throws ConfiguredValueCreationException, InterruptedException { - Target target = ctgValue.getTarget(); - if (!(target instanceof Rule)) { - return ConfigConditions.EMPTY; - } - RawAttributeMapper attrs = RawAttributeMapper.of(((Rule) target)); - if (!attrs.has(RuleClass.CONFIG_SETTING_DEPS_ATTRIBUTE)) { - return ConfigConditions.EMPTY; - } - - // Collect the labels of the configured targets we need to resolve. - List<Label> configLabels = - attrs.get(RuleClass.CONFIG_SETTING_DEPS_ATTRIBUTE, BuildType.LABEL_LIST); - if (configLabels.isEmpty()) { - return ConfigConditions.EMPTY; - } - - // Collect the actual deps without a configuration transition (since by definition config - // conditions evaluate over the current target's configuration). If the dependency is - // (erroneously) something that needs the null configuration, its analysis will be - // short-circuited. That error will be reported later. - ImmutableList.Builder<Dependency> depsBuilder = ImmutableList.builder(); - for (Label configurabilityLabel : configLabels) { - Dependency configurabilityDependency = - Dependency.builder() - .setLabel(configurabilityLabel) - .setConfiguration(ctgValue.getConfiguration()) - .build(); - depsBuilder.add(configurabilityDependency); - } - - ImmutableList<Dependency> configConditionDeps = depsBuilder.build(); - - Map<SkyKey, ConfiguredTargetAndData> configValues; - try { - configValues = - resolveConfiguredTargetDependencies( - env, ctgValue, configConditionDeps, transitivePackages, transitiveRootCauses); - if (configValues == null) { - return null; - } - } catch (DependencyEvaluationException e) { - // One of the config dependencies doesn't exist, and we need to report that. Unfortunately, - // there's not enough information to know which configurable attribute has the problem. - throw new ConfiguredValueCreationException( - // The precise error is reported by the dependency that failed to load. - // TODO(gregce): beautify this error: https://github.com/bazelbuild/bazel/issues/11984. - ctgValue, "errors encountered resolving select() keys for " + target.getLabel()); - } - - ImmutableMap.Builder<Label, ConfiguredTargetAndData> asConfiguredTargets = - ImmutableMap.builder(); - ImmutableMap.Builder<Label, ConfigMatchingProvider> asConfigConditions = ImmutableMap.builder(); - - // Get the configured targets as ConfigMatchingProvider interfaces. - for (Dependency entry : configConditionDeps) { - SkyKey baseKey = entry.getConfiguredTargetKey(); - // The code above guarantees that selectKeyTarget is non-null here. - ConfiguredTargetAndData selectKeyTarget = configValues.get(baseKey); - asConfiguredTargets.put(entry.getLabel(), selectKeyTarget); - try { - asConfigConditions.put( - entry.getLabel(), ConfigConditions.fromConfiguredTarget(selectKeyTarget, platformInfo)); - } catch (ConfigConditions.InvalidConditionException e) { - String message = - String.format( - "%s is not a valid select() condition for %s.\n", - selectKeyTarget.getTargetLabel(), target.getLabel()) - + String.format( - "To inspect the select(), run: bazel query --output=build %s.\n", - target.getLabel()) - + "For more help, see https://bazel.build/reference/be/functions#select.\n\n"; - throw new ConfiguredValueCreationException(ctgValue, message); - } - } - - return ConfigConditions.create( - asConfiguredTargets.buildOrThrow(), asConfigConditions.buildOrThrow()); - } - static ToolchainContextKey createDefaultToolchainContextKey( BuildConfigurationKey configurationKey, ImmutableSet<Label> defaultExecConstraintLabels,
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ToolchainContextKey.java b/src/main/java/com/google/devtools/build/lib/skyframe/ToolchainContextKey.java index f5d4ad8..c9551c9 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/ToolchainContextKey.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/ToolchainContextKey.java
@@ -49,7 +49,7 @@ return interner; } - abstract BuildConfigurationKey configurationKey(); + public abstract BuildConfigurationKey configurationKey(); abstract ImmutableSet<ToolchainTypeRequirement> toolchainTypes(); @@ -57,7 +57,7 @@ abstract Optional<Label> forceExecutionPlatform(); - abstract boolean debugTarget(); + public abstract boolean debugTarget(); /** Builder for {@link ToolchainContextKey}. */ @AutoValue.Builder
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/BUILD b/src/test/java/com/google/devtools/build/lib/analysis/util/BUILD index 30fe172..2b09e71 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/util/BUILD +++ b/src/test/java/com/google/devtools/build/lib/analysis/util/BUILD
@@ -62,6 +62,7 @@ "//src/main/java/com/google/devtools/build/lib/analysis:config/transitions/patch_transition", "//src/main/java/com/google/devtools/build/lib/analysis:configured_target", "//src/main/java/com/google/devtools/build/lib/analysis:configured_target_value", + "//src/main/java/com/google/devtools/build/lib/analysis:constraints/incompatible_target_checker", "//src/main/java/com/google/devtools/build/lib/analysis:dependency_key", "//src/main/java/com/google/devtools/build/lib/analysis:dependency_kind", "//src/main/java/com/google/devtools/build/lib/analysis:duplicate_exception", @@ -84,6 +85,7 @@ "//src/main/java/com/google/devtools/build/lib/analysis:view_creation_failed_exception", "//src/main/java/com/google/devtools/build/lib/analysis:workspace_status_action", "//src/main/java/com/google/devtools/build/lib/analysis/platform", + "//src/main/java/com/google/devtools/build/lib/analysis/producers", "//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:resolution", "//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:resolution_impl", "//src/main/java/com/google/devtools/build/lib/bazel/repository:repository_options", @@ -110,6 +112,7 @@ "//src/main/java/com/google/devtools/build/lib/skyframe:client_environment_function", "//src/main/java/com/google/devtools/build/lib/skyframe:configured_target_and_data", "//src/main/java/com/google/devtools/build/lib/skyframe:configured_target_key", + "//src/main/java/com/google/devtools/build/lib/skyframe:configured_value_creation_exception", "//src/main/java/com/google/devtools/build/lib/skyframe:diff_awareness", "//src/main/java/com/google/devtools/build/lib/skyframe:package_roots_no_symlink_creation", "//src/main/java/com/google/devtools/build/lib/skyframe:package_value",
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewForTesting.java b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewForTesting.java index 39d184e..8404f8e 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewForTesting.java +++ b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewForTesting.java
@@ -17,6 +17,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.common.collect.Streams.stream; +import static com.google.devtools.build.lib.skyframe.PrerequisiteProducer.getDependencyContext; import com.google.common.base.Functions; import com.google.common.base.Preconditions; @@ -68,13 +69,16 @@ import com.google.devtools.build.lib.analysis.config.ConfigConditions; import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider; import com.google.devtools.build.lib.analysis.config.ConfigurationResolver; +import com.google.devtools.build.lib.analysis.config.DependencyEvaluationException; import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException; import com.google.devtools.build.lib.analysis.config.TransitionResolver; import com.google.devtools.build.lib.analysis.config.transitions.ConfigurationTransition; import com.google.devtools.build.lib.analysis.config.transitions.NoTransition; import com.google.devtools.build.lib.analysis.configuredtargets.MergedConfiguredTarget; +import com.google.devtools.build.lib.analysis.constraints.IncompatibleTargetChecker.IncompatibleTargetException; import com.google.devtools.build.lib.analysis.platform.ConstraintValueInfo; import com.google.devtools.build.lib.analysis.platform.PlatformInfo; +import com.google.devtools.build.lib.analysis.producers.DependencyContext; import com.google.devtools.build.lib.analysis.starlark.StarlarkTransition; import com.google.devtools.build.lib.analysis.test.CoverageReportActionFactory; import com.google.devtools.build.lib.bugreport.BugReporter; @@ -101,6 +105,7 @@ import com.google.devtools.build.lib.skyframe.AspectKeyCreator.AspectKey; import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndData; import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey; +import com.google.devtools.build.lib.skyframe.ConfiguredValueCreationException; import com.google.devtools.build.lib.skyframe.PackageValue; import com.google.devtools.build.lib.skyframe.PrerequisiteProducer; import com.google.devtools.build.lib.skyframe.SkyFunctionEnvironmentForTesting; @@ -123,7 +128,6 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; @@ -636,18 +640,31 @@ SkyFunctionEnvironmentForTesting skyfunctionEnvironment = new SkyFunctionEnvironmentForTesting(eventHandler, skyframeExecutor); + var state = new PrerequisiteProducer.State(); + state.targetAndConfiguration = + new TargetAndConfiguration(target.getAssociatedRule(), targetConfig); + NestedSetBuilder<Cause> transitiveRootCauses = NestedSetBuilder.stableOrder(); - var unloadedToolchainContextsInputs = - PrerequisiteProducer.getUnloadedToolchainContextsInputs( - new TargetAndConfiguration(target.getAssociatedRule(), targetConfig), - /* parentExecutionPlatformLabel= */ null, - ruleClassProvider, - eventHandler); - Optional<ToolchainCollection<UnloadedToolchainContext>> result = - PrerequisiteProducer.computeUnloadedToolchainContexts( - skyfunctionEnvironment, unloadedToolchainContextsInputs); - - ToolchainCollection<UnloadedToolchainContext> unloadedToolchainCollection = result.orElse(null); + DependencyContext result; + try { + result = + getDependencyContext( + state, + /* parentExecutionPlatformLabel= */ null, + transitiveRootCauses, + /* transitivePackages= */ null, + ruleClassProvider, + skyfunctionEnvironment); + } catch (ConfiguredValueCreationException + | IncompatibleTargetException + | DependencyEvaluationException e) { + throw new IllegalStateException(e); + } + if (!transitiveRootCauses.isEmpty()) { + throw new IllegalStateException("expected empty: " + transitiveRootCauses.build().toList()); + } + ToolchainCollection<UnloadedToolchainContext> unloadedToolchainCollection = + result.unloadedToolchainContexts(); OrderedSetMultimap<DependencyKind, ConfiguredTargetAndData> prerequisiteMap = getPrerequisiteMapForTesting( @@ -681,7 +698,7 @@ .setPrerequisites(ConfiguredTargetFactory.transformPrerequisiteMap(prerequisiteMap)) .setConfigConditions(ConfigConditions.EMPTY) .setToolchainContexts(resolvedToolchainContext.build()) - .setExecGroupCollectionBuilder(unloadedToolchainContextsInputs) + .setExecGroupCollectionBuilder(state.execGroupCollectionBuilder) .unsafeBuild(); }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/BUILD b/src/test/java/com/google/devtools/build/lib/skyframe/BUILD index d2905e2..c46ff90 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/BUILD +++ b/src/test/java/com/google/devtools/build/lib/skyframe/BUILD
@@ -137,6 +137,7 @@ "//src/main/java/com/google/devtools/build/lib/analysis:config/transitions/transition_factory", "//src/main/java/com/google/devtools/build/lib/analysis:configured_target", "//src/main/java/com/google/devtools/build/lib/analysis:configured_target_value", + "//src/main/java/com/google/devtools/build/lib/analysis:constraints/incompatible_target_checker", "//src/main/java/com/google/devtools/build/lib/analysis:dependency", "//src/main/java/com/google/devtools/build/lib/analysis:dependency_kind", "//src/main/java/com/google/devtools/build/lib/analysis:file_provider", @@ -151,6 +152,7 @@ "//src/main/java/com/google/devtools/build/lib/analysis:transitive_info_provider", "//src/main/java/com/google/devtools/build/lib/analysis:view_creation_failed_exception", "//src/main/java/com/google/devtools/build/lib/analysis/platform", + "//src/main/java/com/google/devtools/build/lib/analysis/producers", "//src/main/java/com/google/devtools/build/lib/bazel:main", "//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:common", "//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:resolution_impl", @@ -158,6 +160,7 @@ "//src/main/java/com/google/devtools/build/lib/bazel/repository:repository_options", "//src/main/java/com/google/devtools/build/lib/bazel/rules", "//src/main/java/com/google/devtools/build/lib/bugreport", + "//src/main/java/com/google/devtools/build/lib/causes", "//src/main/java/com/google/devtools/build/lib/clock", "//src/main/java/com/google/devtools/build/lib/cmdline", "//src/main/java/com/google/devtools/build/lib/collect", @@ -201,6 +204,7 @@ "//src/main/java/com/google/devtools/build/lib/skyframe:configured_target_and_data", "//src/main/java/com/google/devtools/build/lib/skyframe:configured_target_key", "//src/main/java/com/google/devtools/build/lib/skyframe:configured_target_progress_receiver", + "//src/main/java/com/google/devtools/build/lib/skyframe:configured_value_creation_exception", "//src/main/java/com/google/devtools/build/lib/skyframe:containing_package_lookup_function", "//src/main/java/com/google/devtools/build/lib/skyframe:containing_package_lookup_value", "//src/main/java/com/google/devtools/build/lib/skyframe:default_syscall_cache", @@ -229,6 +233,7 @@ "//src/main/java/com/google/devtools/build/lib/skyframe:package_lookup_value", "//src/main/java/com/google/devtools/build/lib/skyframe:package_progress_receiver", "//src/main/java/com/google/devtools/build/lib/skyframe:package_value", + "//src/main/java/com/google/devtools/build/lib/skyframe:platform_lookup_util", "//src/main/java/com/google/devtools/build/lib/skyframe:precomputed_value", "//src/main/java/com/google/devtools/build/lib/skyframe:prepare_deps_of_pattern_value", "//src/main/java/com/google/devtools/build/lib/skyframe:prepare_deps_of_patterns_value",
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ToolchainsForTargetsTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ToolchainsForTargetsTest.java index ac28444..b06b974 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/ToolchainsForTargetsTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/ToolchainsForTargetsTest.java
@@ -17,6 +17,7 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.devtools.build.lib.analysis.testing.ToolchainCollectionSubject.assertThat; import static com.google.devtools.build.lib.analysis.testing.ToolchainContextSubject.assertThat; +import static com.google.devtools.build.lib.skyframe.PrerequisiteProducer.getDependencyContext; import com.google.auto.value.AutoValue; import com.google.common.collect.ImmutableList; @@ -31,11 +32,17 @@ import com.google.devtools.build.lib.analysis.TargetAndConfiguration; import com.google.devtools.build.lib.analysis.ToolchainCollection; import com.google.devtools.build.lib.analysis.ToolchainContext; +import com.google.devtools.build.lib.analysis.config.DependencyEvaluationException; import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget; +import com.google.devtools.build.lib.analysis.constraints.IncompatibleTargetChecker.IncompatibleTargetException; +import com.google.devtools.build.lib.analysis.producers.DependencyContext; +import com.google.devtools.build.lib.analysis.producers.DependencyContextProducer; import com.google.devtools.build.lib.analysis.test.BaselineCoverageAction; import com.google.devtools.build.lib.analysis.util.AnalysisMock; import com.google.devtools.build.lib.analysis.util.AnalysisTestCase; +import com.google.devtools.build.lib.causes.Cause; import com.google.devtools.build.lib.cmdline.Label; +import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.packages.RuleClassProvider; import com.google.devtools.build.lib.skyframe.util.SkyframeExecutorTestUtils; import com.google.devtools.build.skyframe.EvaluationResult; @@ -45,7 +52,6 @@ import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.SkyValue; import com.google.errorprone.annotations.CanIgnoreReturnValue; -import java.util.Optional; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -54,19 +60,19 @@ /** * Tests {@link ConfiguredTargetFunction}'s logic for determining each target toolchain context. * - * <p>This is essentially an integration test for {@link - * PrerequisiteProducer#computeUnloadedToolchainContexts}. These methods form the core logic that - * figures out what a target's toolchain dependencies are. + * <p>This is essentially an integration test for the toolchain part of {@link + * DependencyContextProducer}. These methods form the core logic that figures out what a target's + * toolchain dependencies are. * * <p>{@link ConfiguredTargetFunction} is a complicated class that does a lot of things. This test * focuses purely on the task of toolchain resolution. So instead of evaluating full {@link * ConfiguredTargetFunction} instances, it evaluates a mock {@link SkyFunction} that just wraps the - * {@link PrerequisiteProducer#computeUnloadedToolchainContexts} part. This keeps focus tight and - * integration dependencies narrow. + * {@link PrerequisiteProducer#getDependencyContext} part. This keeps focus tight and integration + * dependencies narrow. * - * <p>We can't just call {@link PrerequisiteProducer#computeUnloadedToolchainContexts} directly - * because that method needs a {@link SkyFunction.Environment} and Blaze's test infrastructure - * doesn't support direct access to environments. + * <p>We can't just call {@link ToolchainContextProducer} directly because that method needs a + * {@link SkyFunction.Environment} and Blaze's test infrastructure doesn't support direct access to + * environments. */ @RunWith(JUnit4.class) public final class ToolchainsForTargetsTest extends AnalysisTestCase { @@ -91,7 +97,7 @@ /** * Returns a {@link ToolchainCollection<UnloadedToolchainContext>} as the result of {@link - * PrerequisiteProducer#computeUnloadedToolchainContexts}. + * PrerequisiteProducer#getDependencyContext}. */ @AutoValue abstract static class Value implements SkyValue { @@ -103,8 +109,8 @@ } /** - * A mock {@link SkyFunction} that just calls {@link - * PrerequisiteProducer#computeUnloadedToolchainContexts} and returns its results. + * A mock {@link SkyFunction} that just calls {@link PrerequisiteProducer#getDependencyContext} + * and returns its results. */ static class ComputeUnloadedToolchainContextsFunction implements SkyFunction { static final SkyFunctionName SKYFUNCTION_NAME = @@ -120,25 +126,33 @@ @Override public SkyValue compute(SkyKey skyKey, Environment env) throws ComputeUnloadedToolchainContextsException, InterruptedException { + Key key = (Key) skyKey.argument(); + var state = env.getState(PrerequisiteProducer.State::new); + state.targetAndConfiguration = key.targetAndConfiguration(); + NestedSetBuilder<Cause> transitiveRootCauses = NestedSetBuilder.stableOrder(); + DependencyContext result; try { - Key key = (Key) skyKey.argument(); - var unloadedToolchainContextsInputs = - PrerequisiteProducer.getUnloadedToolchainContextsInputs( - key.targetAndConfiguration(), + result = + getDependencyContext( + state, key.configuredTargetKey().getExecutionPlatformLabel(), + transitiveRootCauses, + /* transitivePackages= */ null, stateProvider.lateBoundRuleClassProvider(), - env.getListener()); - Optional<ToolchainCollection<UnloadedToolchainContext>> result = - PrerequisiteProducer.computeUnloadedToolchainContexts( - env, unloadedToolchainContextsInputs); - if (result == null) { - return null; - } - ToolchainCollection<UnloadedToolchainContext> toolchainCollection = result.orElse(null); - return Value.create(toolchainCollection); - } catch (ToolchainException e) { + env); + } catch (ToolchainException + | ConfiguredValueCreationException + | IncompatibleTargetException + | DependencyEvaluationException e) { throw new ComputeUnloadedToolchainContextsException(e); } + if (!transitiveRootCauses.isEmpty()) { + throw new IllegalStateException("expected empty: " + transitiveRootCauses.build().toList()); + } + if (result == null) { + return null; + } + return Value.create(result.unloadedToolchainContexts()); } private static class ComputeUnloadedToolchainContextsException extends SkyFunctionException {