blob: 6e554aca213bf2d6956bcb1084ae47b2929d6aa2 [file] [log] [blame]
// 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.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.devtools.build.lib.buildeventstream.BuildEventIdUtil.configurationId;
import com.google.auto.value.AutoOneOf;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.actions.ActionLookupKey;
import com.google.devtools.build.lib.analysis.ConfiguredTargetValue;
import com.google.devtools.build.lib.analysis.ExecGroupCollection;
import com.google.devtools.build.lib.analysis.InconsistentNullConfigException;
import com.google.devtools.build.lib.analysis.PlatformConfiguration;
import com.google.devtools.build.lib.analysis.PlatformOptions;
import com.google.devtools.build.lib.analysis.TargetAndConfiguration;
import com.google.devtools.build.lib.analysis.TransitiveDependencyState;
import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue;
import com.google.devtools.build.lib.analysis.config.BuildOptions;
import com.google.devtools.build.lib.analysis.config.BuildOptionsView;
import com.google.devtools.build.lib.analysis.config.ConfigConditions;
import com.google.devtools.build.lib.analysis.config.ConfigurationTransitionEvent;
import com.google.devtools.build.lib.analysis.config.CoreOptions;
import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
import com.google.devtools.build.lib.analysis.config.StarlarkTransitionCache;
import com.google.devtools.build.lib.analysis.config.ToolchainTypeRequirement;
import com.google.devtools.build.lib.analysis.config.transitions.ComposingTransition;
import com.google.devtools.build.lib.analysis.config.transitions.ConfigurationTransition;
import com.google.devtools.build.lib.analysis.config.transitions.PatchTransition;
import com.google.devtools.build.lib.analysis.config.transitions.TransitionFactory;
import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
import com.google.devtools.build.lib.analysis.starlark.StarlarkTransition.TransitionException;
import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildEventId.ConfigurationId;
import com.google.devtools.build.lib.causes.AnalysisFailedCause;
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.collect.nestedset.Order;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.ExtendedEventHandler;
import com.google.devtools.build.lib.packages.BuildType;
import com.google.devtools.build.lib.packages.NoSuchPackageException;
import com.google.devtools.build.lib.packages.NoSuchTargetException;
import com.google.devtools.build.lib.packages.NoSuchThingException;
import com.google.devtools.build.lib.packages.NonconfigurableAttributeMapper;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.RuleClass;
import com.google.devtools.build.lib.packages.RuleTransitionData;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.packages.TargetUtils;
import com.google.devtools.build.lib.packages.Type;
import com.google.devtools.build.lib.server.FailureDetails.Analysis;
import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
import com.google.devtools.build.lib.skyframe.BuildConfigurationKey;
import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey;
import com.google.devtools.build.lib.skyframe.ConfiguredValueCreationException;
import com.google.devtools.build.lib.skyframe.toolchains.PlatformLookupUtil.InvalidPlatformException;
import com.google.devtools.build.lib.skyframe.toolchains.ToolchainContextKey;
import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.skyframe.SkyValue;
import com.google.devtools.build.skyframe.state.StateMachine;
import com.google.devtools.common.options.OptionsParsingException;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.function.Consumer;
import javax.annotation.Nullable;
import net.starlark.java.syntax.Location;
/**
* Computes the target and configuration for a configured target key.
*
* <p>If the key has a configuration and the target is configurable, attempts to apply a rule side
* transition. If the configuration changes, delegates to a target with the new configuration. If
* the target is not configurable, directly delegates to the null configuration.
*/
public final class TargetAndConfigurationProducer
implements StateMachine,
StateMachine.ValueOrExceptionSink<InvalidConfigurationException>,
Consumer<SkyValue>,
TargetProducer.ResultSink {
/** Accepts results of this producer. */
public interface ResultSink {
void acceptTargetAndConfiguration(TargetAndConfiguration value, ConfiguredTargetKey fullKey);
void acceptTargetAndConfigurationDelegatedValue(ConfiguredTargetValue value);
void acceptTargetAndConfigurationError(TargetAndConfigurationError error);
}
/** Tagged union of possible errors. */
@AutoOneOf(TargetAndConfigurationError.Kind.class)
public abstract static class TargetAndConfigurationError {
/** Tags the error type. */
public enum Kind {
CONFIGURED_VALUE_CREATION,
NO_SUCH_THING,
INCONSISTENT_NULL_CONFIG
}
public abstract Kind kind();
public abstract ConfiguredValueCreationException configuredValueCreation();
public abstract NoSuchThingException noSuchThing();
public abstract InconsistentNullConfigException inconsistentNullConfig();
private static TargetAndConfigurationError of(ConfiguredValueCreationException e) {
return AutoOneOf_TargetAndConfigurationProducer_TargetAndConfigurationError
.configuredValueCreation(e);
}
private static TargetAndConfigurationError of(NoSuchThingException e) {
return AutoOneOf_TargetAndConfigurationProducer_TargetAndConfigurationError.noSuchThing(e);
}
private static TargetAndConfigurationError of(InconsistentNullConfigException e) {
return AutoOneOf_TargetAndConfigurationProducer_TargetAndConfigurationError
.inconsistentNullConfig(e);
}
}
// -------------------- Input --------------------
private final ConfiguredTargetKey preRuleTransitionKey;
@Nullable private final TransitionFactory<RuleTransitionData> trimmingTransitionFactory;
private final PatchTransition toolchainTaggedTrimmingTransition;
private final StarlarkTransitionCache transitionCache;
private final TransitiveDependencyState transitiveState;
// -------------------- Output --------------------
private final ResultSink sink;
private final ExtendedEventHandler eventHandler;
// -------------------- Internal State --------------------
private Target target;
public TargetAndConfigurationProducer(
ConfiguredTargetKey preRuleTransitionKey,
@Nullable TransitionFactory<RuleTransitionData> trimmingTransitionFactory,
PatchTransition toolchainTaggedTrimmingTransition,
StarlarkTransitionCache transitionCache,
TransitiveDependencyState transitiveState,
ResultSink sink,
ExtendedEventHandler eventHandler) {
this.preRuleTransitionKey = preRuleTransitionKey;
this.trimmingTransitionFactory = trimmingTransitionFactory;
this.toolchainTaggedTrimmingTransition = toolchainTaggedTrimmingTransition;
this.transitionCache = transitionCache;
this.transitiveState = transitiveState;
this.sink = sink;
this.eventHandler = eventHandler;
}
@Override
public StateMachine step(Tasks tasks) {
return new TargetProducer(
preRuleTransitionKey.getLabel(),
transitiveState,
(TargetProducer.ResultSink) this,
/* runAfter= */ this::determineConfiguration);
}
@Override
public void acceptTarget(Target target) {
this.target = target;
}
@Override
public void acceptTargetError(NoSuchPackageException e) {
eventHandler.handle(Event.error(e.getMessage()));
sink.acceptTargetAndConfigurationError(TargetAndConfigurationError.of(e));
}
@Override
public void acceptTargetError(NoSuchTargetException e, Location location) {
eventHandler.handle(Event.error(location, e.getMessage()));
sink.acceptTargetAndConfigurationError(TargetAndConfigurationError.of(e));
}
private StateMachine determineConfiguration(Tasks tasks) {
if (target == null) {
return DONE; // A target could not be determined.
}
BuildConfigurationKey configurationKey = preRuleTransitionKey.getConfigurationKey();
if (configurationKey == null) {
if (target.isConfigurable()) {
// We somehow ended up in a target that requires a non-null configuration but with a key
// that doesn't have a configuration. This is always an error, but we need to bubble this
// up to the parent to provide more context.
sink.acceptTargetAndConfigurationError(
TargetAndConfigurationError.of(new InconsistentNullConfigException()));
return DONE;
}
sink.acceptTargetAndConfiguration(
new TargetAndConfiguration(target, /* configuration= */ null), preRuleTransitionKey);
return DONE;
}
if (!target.isConfigurable()) {
// If target is not configurable, but requested with a configuration. Delegates to a key with
// the null configuration. This is expected to be uncommon. The common case of a
// non-configurable target is an input file, but those are usually package local and requested
// correctly with the null configuration.
delegateTo(
tasks,
ConfiguredTargetKey.builder()
.setLabel(preRuleTransitionKey.getLabel())
.setExecutionPlatformLabel(preRuleTransitionKey.getExecutionPlatformLabel())
.build());
return DONE;
}
if (!preRuleTransitionKey.shouldApplyRuleTransition()) {
lookUpConfigurationValue(tasks);
return DONE;
}
return new RuleTransitionApplier();
}
private void delegateTo(Tasks tasks, ActionLookupKey delegate) {
tasks.lookUp(delegate, (Consumer<SkyValue>) this);
}
@Override
public void accept(SkyValue value) {
sink.acceptTargetAndConfigurationDelegatedValue((ConfiguredTargetValue) value);
}
private void lookUpConfigurationValue(Tasks tasks) {
tasks.lookUp(
preRuleTransitionKey.getConfigurationKey(),
InvalidConfigurationException.class,
(ValueOrExceptionSink<InvalidConfigurationException>) this);
}
@Override
public void acceptValueOrException(
@Nullable SkyValue value, @Nullable InvalidConfigurationException error) {
if (value != null) {
sink.acceptTargetAndConfiguration(
new TargetAndConfiguration(target, (BuildConfigurationValue) value),
preRuleTransitionKey);
return;
}
emitError(
error.getMessage(), TargetUtils.getLocationMaybe(target), error.getDetailedExitCode());
}
/** Applies any requested rule transition before producing the final configuration. */
private class RuleTransitionApplier
implements StateMachine,
TransitionApplier.ResultSink,
ConfigConditionsProducer.ResultSink,
PlatformInfoProducer.ResultSink {
// -------------------- Internal State --------------------
@Nullable private PlatformInfo platformInfo;
private ConfigConditions configConditions;
private ConfigurationTransition ruleTransition;
private BuildConfigurationKey configurationKey;
@Override
public StateMachine step(Tasks tasks) throws InterruptedException {
UnloadedToolchainContextsInputs unloadedToolchainContextsInputs =
getUnloadedToolchainContextsInputs(
target, preRuleTransitionKey.getExecutionPlatformLabel());
if (unloadedToolchainContextsInputs.targetToolchainContextKey() != null) {
PlatformConfiguration platformConfiguration =
new PlatformConfiguration(preRuleTransitionKey.getConfigurationKey().getOptions());
tasks.enqueue(
new PlatformInfoProducer(
ConfiguredTargetKey.builder()
.setLabel(platformConfiguration.getTargetPlatform())
.setConfigurationKey(
unloadedToolchainContextsInputs
.targetToolchainContextKey()
.configurationKey())
.build(),
(PlatformInfoProducer.ResultSink) this,
this::computeConfigConditions));
} else {
this.platformInfo = null;
computeConfigConditions(tasks);
}
return DONE;
}
// TODO: @aranguyen b/297077082
public UnloadedToolchainContextsInputs getUnloadedToolchainContextsInputs(
Target target, @Nullable Label parentExecutionPlatformLabel) throws InterruptedException {
if (!(target instanceof Rule)) {
return UnloadedToolchainContextsInputs.empty();
}
Rule rule = (Rule) target;
if (!preRuleTransitionKey
.getConfigurationKey()
.getOptions()
.contains(PlatformOptions.class)) {
return UnloadedToolchainContextsInputs.empty();
}
PlatformConfiguration platformConfiguration =
new PlatformConfiguration(preRuleTransitionKey.getConfigurationKey().getOptions());
var defaultExecConstraintLabels =
getExecutionPlatformConstraints(rule, platformConfiguration);
var ruleClass = rule.getRuleClassObject();
boolean useAutoExecGroups =
rule.isAttrDefined("$use_auto_exec_groups", Type.BOOLEAN)
? (boolean) rule.getAttr("$use_auto_exec_groups")
: preRuleTransitionKey
.getConfigurationKey()
.getOptions()
.get(CoreOptions.class)
.useAutoExecGroups;
var processedExecGroups =
ExecGroupCollection.process(
ruleClass.getExecGroups(),
defaultExecConstraintLabels,
ruleClass.getToolchainTypes(),
useAutoExecGroups);
if (!rule.useToolchainResolution()) {
return UnloadedToolchainContextsInputs.create(processedExecGroups, null);
}
return UnloadedToolchainContextsInputs.create(
processedExecGroups,
createDefaultToolchainContextKey(
computeToolchainConfigurationKey(
preRuleTransitionKey.getConfigurationKey().getOptions(),
toolchainTaggedTrimmingTransition),
defaultExecConstraintLabels,
/* debugTarget= */ platformConfiguration.debugToolchainResolution(rule.getLabel()),
/* useAutoExecGroups= */ useAutoExecGroups,
ruleClass.getToolchainTypes(),
parentExecutionPlatformLabel));
}
public ToolchainContextKey createDefaultToolchainContextKey(
BuildConfigurationKey configurationKey,
ImmutableSet<Label> defaultExecConstraintLabels,
boolean debugTarget,
boolean useAutoExecGroups,
ImmutableSet<ToolchainTypeRequirement> toolchainTypes,
@Nullable Label parentExecutionPlatformLabel) {
ToolchainContextKey.Builder toolchainContextKeyBuilder =
ToolchainContextKey.key()
.configurationKey(configurationKey)
.execConstraintLabels(defaultExecConstraintLabels)
.debugTarget(debugTarget);
// Add toolchain types only if automatic exec groups are not created for this target.
if (!useAutoExecGroups) {
toolchainContextKeyBuilder.toolchainTypes(toolchainTypes);
}
if (parentExecutionPlatformLabel != null) {
// Find out what execution platform the parent used, and force that.
// This should only be set for direct toolchain dependencies.
toolchainContextKeyBuilder.forceExecutionPlatform(parentExecutionPlatformLabel);
}
return toolchainContextKeyBuilder.build();
}
private BuildConfigurationKey computeToolchainConfigurationKey(
BuildOptions buildOptions, PatchTransition toolchainTaggedTrimmingTransition)
throws InterruptedException {
// The toolchain context's options are the parent rule's options with manual trimming
// auto-applied. This means toolchains don't inherit feature flags. This helps build
// performance: if the toolchain context had the exact same configuration of its parent and
// that
// included feature flags, all the toolchain's dependencies would apply this transition
// individually. That creates a lot more potentially expensive applications of that transition
// (especially since manual trimming applies to every configured target in the build).
//
// In other words: without this modification:
// parent rule -> toolchain context -> toolchain
// -> toolchain dep 1 # applies manual trimming to remove feature flags
// -> toolchain dep 2 # applies manual trimming to remove feature flags
// ...
//
// With this modification:
// parent rule -> toolchain context # applies manual trimming to remove feature flags
// -> toolchain
// -> toolchain dep 1
// -> toolchain dep 2
// ...
//
// None of this has any effect on rules that don't utilize manual trimming.
BuildOptions toolchainOptions =
toolchainTaggedTrimmingTransition.patch(
new BuildOptionsView(
buildOptions, toolchainTaggedTrimmingTransition.requiresOptionFragments()),
eventHandler);
return BuildConfigurationKey.withoutPlatformMapping(toolchainOptions);
}
private ImmutableSet<Label> getExecutionPlatformConstraints(
Rule rule, @Nullable PlatformConfiguration platformConfiguration) {
if (platformConfiguration == null) {
return ImmutableSet.of(); // See NoConfigTransition.
}
NonconfigurableAttributeMapper mapper = NonconfigurableAttributeMapper.of(rule);
ImmutableSet.Builder<Label> execConstraintLabels = new ImmutableSet.Builder<>();
execConstraintLabels.addAll(rule.getRuleClassObject().getExecutionPlatformConstraints());
if (rule.getRuleClassObject()
.hasAttr(RuleClass.EXEC_COMPATIBLE_WITH_ATTR, BuildType.LABEL_LIST)) {
execConstraintLabels.addAll(
mapper.get(RuleClass.EXEC_COMPATIBLE_WITH_ATTR, BuildType.LABEL_LIST));
}
execConstraintLabels.addAll(
platformConfiguration.getAdditionalExecutionConstraintsFor(rule.getLabel()));
return execConstraintLabels.build();
}
@CanIgnoreReturnValue
public StateMachine computeConfigConditions(Tasks tasks) {
// TODO @aranguyen b/297077082
tasks.enqueue(
new ConfigConditionsProducer(
target,
preRuleTransitionKey.getLabel(),
preRuleTransitionKey.getConfigurationKey(),
platformInfo,
transitiveState,
(ConfigConditionsProducer.ResultSink) this,
this::computeTransition));
return DONE;
}
public StateMachine computeTransition(Tasks tasks) {
// logic to compute transition with ConfigConditions
var transitionData =
RuleTransitionData.create(
target.getAssociatedRule(),
configConditions != null ? configConditions.asProviders() : null,
preRuleTransitionKey.getConfigurationKey().getOptionsChecksum());
ConfigurationTransition transition = null;
TransitionFactory<RuleTransitionData> transitionFactory =
target.getAssociatedRule().getRuleClassObject().getTransitionFactory();
if (transitionFactory != null) {
transition = transitionFactory.create(transitionData);
}
boolean isAlias = target.getAssociatedRule().getName().equals("alias");
if (trimmingTransitionFactory != null && !isAlias) {
var trimmingTransition = trimmingTransitionFactory.create(transitionData);
if (transition != null) {
transition = ComposingTransition.of(transition, trimmingTransition);
} else {
transition = trimmingTransition;
}
}
if (transition == null) {
lookUpConfigurationValue(tasks);
return DONE;
}
this.ruleTransition = transition;
return new TransitionApplier(
preRuleTransitionKey.getConfigurationKey(),
ruleTransition,
transitionCache,
(TransitionApplier.ResultSink) this,
eventHandler,
/* runAfter= */ this::processTransitionedKey);
}
@Override
public void acceptTransitionedConfigurations(
ImmutableMap<String, BuildConfigurationKey> transitionResult) {
checkState(transitionResult.size() == 1, "Expected exactly one result: %s", transitionResult);
this.configurationKey =
checkNotNull(
transitionResult.get(ConfigurationTransition.PATCH_TRANSITION_KEY),
"Transition result missing patch transition entry: %s",
transitionResult);
}
@Override
public void acceptTransitionError(TransitionException e) {
emitTransitionErrorMessage(e.getMessage());
}
@Override
public void acceptTransitionError(OptionsParsingException e) {
emitTransitionErrorMessage(e.getMessage());
}
@Override
public void acceptConfigConditionsError(ConfiguredValueCreationException e) {
emitTransitionErrorMessage(e.getMessage());
}
@Override
public void acceptConfigConditions(ConfigConditions configConditions) {
this.configConditions = configConditions;
}
@Override
public void acceptPlatformInfo(PlatformInfo info) {
this.platformInfo = info;
}
@Override
public void acceptPlatformInfoError(InvalidPlatformException error) {
emitTransitionErrorMessage(error.getMessage());
}
private StateMachine processTransitionedKey(Tasks tasks) {
if (configurationKey == null) {
return DONE; // There was an error.
}
BuildConfigurationKey parentConfiguration = preRuleTransitionKey.getConfigurationKey();
if (configurationKey.equals(parentConfiguration)) {
// This key owns the configuration and the computation completes normally.
lookUpConfigurationValue(tasks);
return DONE;
}
eventHandler.post(
ConfigurationTransitionEvent.create(
parentConfiguration.getOptionsChecksum(), configurationKey.getOptionsChecksum()));
return new IdempotencyChecker();
}
/**
* Checks the transition for idempotency before applying delegation.
*
* <p>If the transition is non-idempotent, marks {@link
* ConfiguredTargetKey#shouldApplyRuleTransition} false in the delegate key.
*/
private class IdempotencyChecker implements StateMachine, TransitionApplier.ResultSink {
/* At first glance, it seems like setting `shouldApplyRuleTransition=false` should be benign
* in both cases, but it would be an error in the idempotent case.
*
* Idempotent Case
*
* If we were to mark the idempotent case with `shouldApplyRuleTransition=false`, it would
* lead to action conflicts. Let `//foo[123]` be a key that rule transitions to `//foo[abc]`
* and suppose the outcome is marked `//foo[abc] shouldApplyRuleTransition=false`.
*
* A different parent might directly request `//foo[abc] shouldApplyRuleTransition=true`.
* Since the rule transition is a idempotent, it would result in the same actions as
* `//foo[abc] shouldApplyRuleTransition=false` with a different key, causing action
* conflicts.
*
* Non-idempotent Case
*
* In the example of //foo[abc] shouldApplyRuleTransition=false and //foo[abc]
* shouldApplyRuleTransition=true, there should be no action conflicts because the
* `shouldApplyRuleTransition=false` is the result of a non-idempotent rule transition and
* `shouldApplyRuleTransition=true` will produce a different configuration. */
// -------------------- Internal State --------------------
private BuildConfigurationKey configurationKey2;
@Override
public StateMachine step(Tasks tasks) {
return new TransitionApplier(
configurationKey,
ruleTransition,
transitionCache,
(TransitionApplier.ResultSink) this,
eventHandler,
/* runAfter= */ this::checkIdempotencyAndDelegate);
}
@Override
public void acceptTransitionedConfigurations(
ImmutableMap<String, BuildConfigurationKey> transitionResult) {
checkState(
transitionResult.size() == 1, "Expected exactly one result: %s", transitionResult);
this.configurationKey2 =
checkNotNull(
transitionResult.get(ConfigurationTransition.PATCH_TRANSITION_KEY),
"Transition result missing patch transition entry: %s",
transitionResult);
}
@Override
public void acceptTransitionError(TransitionException e) {
emitTransitionErrorMessage(e.getMessage());
}
@Override
public void acceptTransitionError(OptionsParsingException e) {
emitTransitionErrorMessage(e.getMessage());
}
private StateMachine checkIdempotencyAndDelegate(Tasks tasks) {
if (configurationKey2 == null) {
return DONE; // There was an error.
}
ConfiguredTargetKey.Builder keyBuilder =
ConfiguredTargetKey.builder()
.setLabel(preRuleTransitionKey.getLabel())
.setExecutionPlatformLabel(preRuleTransitionKey.getExecutionPlatformLabel())
.setConfigurationKey(configurationKey);
if (!configurationKey.equals(configurationKey2)) {
// The transition was not idempotent. Explicitly informs the delegate to avoid applying a
// rule transition.
keyBuilder.setShouldApplyRuleTransition(false);
}
delegateTo(tasks, keyBuilder.build());
return DONE;
}
}
private void emitTransitionErrorMessage(String message) {
emitError(message, TargetUtils.getLocationMaybe(target), /* exitCode= */ null);
}
}
private void emitError(
String message, @Nullable Location location, @Nullable DetailedExitCode exitCode) {
Cause cause =
new AnalysisFailedCause(
preRuleTransitionKey.getLabel(),
configurationIdMessage(preRuleTransitionKey.getConfigurationKey().getOptionsChecksum()),
exitCode != null ? exitCode : createDetailedExitCode(message));
sink.acceptTargetAndConfigurationError(
TargetAndConfigurationError.of(
new ConfiguredValueCreationException(
target,
location,
message,
target.getLabel(),
configurationId(preRuleTransitionKey.getConfigurationKey()),
NestedSetBuilder.create(Order.STABLE_ORDER, cause),
exitCode != null ? exitCode : createDetailedExitCode(message))));
}
public static ConfigurationId configurationIdMessage(@Nullable String optionsCheckSum) {
if (optionsCheckSum == null) {
return ConfigurationId.newBuilder().setId("none").build();
}
return ConfigurationId.newBuilder().setId(optionsCheckSum).build();
}
public static DetailedExitCode createDetailedExitCode(String message) {
return DetailedExitCode.of(
FailureDetail.newBuilder()
.setMessage(message)
.setAnalysis(
Analysis.newBuilder().setCode(Analysis.Code.CONFIGURED_VALUE_CREATION_FAILED))
.build());
}
// Public for Cquery.
// TODO: @aranguyen keep cquery in sync with ConfiguredTargetFunction
@Nullable
public static ConfigurationTransition computeTransition(
Rule rule, @Nullable TransitionFactory<RuleTransitionData> trimmingTransitionFactory) {
var transitionData = RuleTransitionData.create(rule, /* configConditions= */ null, "");
ConfigurationTransition transition = null;
TransitionFactory<RuleTransitionData> transitionFactory =
rule.getRuleClassObject().getTransitionFactory();
if (transitionFactory != null) {
transition = transitionFactory.create(transitionData);
}
boolean isAlias = rule.getAssociatedRule().getName().equals("alias");
if (trimmingTransitionFactory != null && !isAlias) {
var trimmingTransition = trimmingTransitionFactory.create(transitionData);
if (transition != null) {
transition = ComposingTransition.of(transition, trimmingTransition);
} else {
transition = trimmingTransition;
}
}
return transition;
}
}