| // Copyright 2016 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.rules; | |
| import static com.google.common.base.Preconditions.checkNotNull; | |
| import com.google.common.base.MoreObjects; | |
| import com.google.common.collect.ImmutableClassToInstanceMap; | |
| import com.google.common.collect.ImmutableCollection; | |
| import com.google.common.collect.ImmutableMap; | |
| import com.google.devtools.build.lib.actions.Artifact; | |
| import com.google.devtools.build.lib.analysis.AliasProvider; | |
| import com.google.devtools.build.lib.analysis.ConfiguredTarget; | |
| import com.google.devtools.build.lib.analysis.FileProvider; | |
| import com.google.devtools.build.lib.analysis.RequiredConfigFragmentsProvider; | |
| import com.google.devtools.build.lib.analysis.RuleContext; | |
| import com.google.devtools.build.lib.analysis.TransitiveInfoProvider; | |
| import com.google.devtools.build.lib.analysis.VisibilityProvider; | |
| import com.google.devtools.build.lib.analysis.VisibilityProviderImpl; | |
| import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider; | |
| import com.google.devtools.build.lib.cmdline.Label; | |
| import com.google.devtools.build.lib.collect.nestedset.Depset; | |
| import com.google.devtools.build.lib.collect.nestedset.NestedSet; | |
| import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; | |
| import com.google.devtools.build.lib.packages.Info; | |
| import com.google.devtools.build.lib.packages.PackageSpecification.PackageGroupContents; | |
| import com.google.devtools.build.lib.packages.Provider; | |
| import com.google.devtools.build.lib.skyframe.BuildConfigurationKey; | |
| import javax.annotation.Nullable; | |
| import net.starlark.java.eval.EvalException; | |
| import net.starlark.java.eval.Printer; | |
| import net.starlark.java.eval.StarlarkSemantics; | |
| import net.starlark.java.eval.Structure; | |
| /** | |
| * A {@link ConfiguredTarget} that pretends to be whatever type of target {@link #getActual} is, | |
| * mirroring its label and transitive info providers. | |
| * | |
| * <p>Transitive info providers may also be overridden. At a minimum, {@link #getProvider} provides | |
| * {@link AliasProvider} and an explicit {@link VisibilityProvider} which takes precedent over the | |
| * actual target's visibility. | |
| */ | |
| @Immutable | |
| public final class AliasConfiguredTarget implements ConfiguredTarget, Structure { | |
| public static AliasConfiguredTarget create( | |
| RuleContext ruleContext, | |
| ConfiguredTarget actual, | |
| NestedSet<PackageGroupContents> visibility) { | |
| return createWithOverrides( | |
| ruleContext, actual, visibility, /*overrides=*/ ImmutableClassToInstanceMap.of()); | |
| } | |
| public static AliasConfiguredTarget createWithOverrides( | |
| RuleContext ruleContext, | |
| ConfiguredTarget actual, | |
| NestedSet<PackageGroupContents> visibility, | |
| ImmutableClassToInstanceMap<TransitiveInfoProvider> overrides) { | |
| ImmutableClassToInstanceMap.Builder<TransitiveInfoProvider> allOverrides = | |
| ImmutableClassToInstanceMap.<TransitiveInfoProvider>builder() | |
| .putAll(overrides) | |
| .put(AliasProvider.class, AliasProvider.fromAliasRule(ruleContext.getRule(), actual)) | |
| .put(VisibilityProvider.class, new VisibilityProviderImpl(visibility)); | |
| if (ruleContext.getRequiredConfigFragments() != null) { | |
| // This causes "blaze cquery --show_config_fragments=direct" to only show the | |
| // fragments/options the alias directly uses, not those of its actual target. Since alias | |
| // has a narrow API this practically means whatever a select() in the alias requires. | |
| allOverrides.put( | |
| RequiredConfigFragmentsProvider.class, ruleContext.getRequiredConfigFragments()); | |
| } | |
| return new AliasConfiguredTarget( | |
| ruleContext.getLabel(), | |
| ruleContext.getConfigurationKey(), | |
| actual, | |
| allOverrides.build(), | |
| ruleContext.getConfigConditions()); | |
| } | |
| private final Label label; | |
| private final BuildConfigurationKey configurationKey; | |
| private final ConfiguredTarget actual; | |
| private final ImmutableClassToInstanceMap<TransitiveInfoProvider> overrides; | |
| private final ImmutableMap<Label, ConfigMatchingProvider> configConditions; | |
| private AliasConfiguredTarget( | |
| Label label, | |
| BuildConfigurationKey configurationKey, | |
| ConfiguredTarget actual, | |
| ImmutableClassToInstanceMap<TransitiveInfoProvider> overrides, | |
| ImmutableMap<Label, ConfigMatchingProvider> configConditions) { | |
| this.label = checkNotNull(label); | |
| this.configurationKey = checkNotNull(configurationKey); | |
| this.actual = checkNotNull(actual); | |
| this.overrides = checkNotNull(overrides); | |
| this.configConditions = checkNotNull(configConditions); | |
| } | |
| @Override | |
| public boolean isImmutable() { | |
| return true; // immutable and Starlark-hashable | |
| } | |
| @Override | |
| public ImmutableMap<Label, ConfigMatchingProvider> getConfigConditions() { | |
| return configConditions; | |
| } | |
| @Override | |
| public <P extends TransitiveInfoProvider> P getProvider(Class<P> provider) { | |
| P p = overrides.getInstance(provider); | |
| return p != null ? p : actual.getProvider(provider); | |
| } | |
| @Override | |
| public Label getLabel() { | |
| return actual.getLabel(); | |
| } | |
| @Override | |
| public Object get(String providerKey) { | |
| return actual.get(providerKey); | |
| } | |
| @Nullable | |
| @Override | |
| public Info get(Provider.Key providerKey) { | |
| return actual.get(providerKey); | |
| } | |
| @Override | |
| public Object getIndex(StarlarkSemantics semantics, Object key) throws EvalException { | |
| return actual.getIndex(semantics, key); | |
| } | |
| @Override | |
| public boolean containsKey(StarlarkSemantics semantics, Object key) throws EvalException { | |
| return actual.containsKey(semantics, key); | |
| } | |
| @Override | |
| public BuildConfigurationKey getConfigurationKey() { | |
| // It would be incorrect to return actual.getConfigurationKey() because of two cases: | |
| // 1) actual might be an input file, in which case its configuration key is null, and we don't | |
| // want to have rules with a null configuration key. | |
| // 2) actual has a self transition. Self transitions don't get applied to the alias rule, and so | |
| // the configuration keys actually differ. | |
| return configurationKey; | |
| } | |
| /* Structure methods */ | |
| @Override | |
| public Object getValue(String name) { | |
| if (name.equals(LABEL_FIELD)) { | |
| return getLabel(); | |
| } else if (name.equals(FILES_FIELD)) { | |
| // A shortcut for files to build in Starlark. FileConfiguredTarget and RuleConfiguredTarget | |
| // always has FileProvider and Error- and PackageGroupConfiguredTarget-s shouldn't be | |
| // accessible in Starlark. | |
| return Depset.of(Artifact.TYPE, getProvider(FileProvider.class).getFilesToBuild()); | |
| } | |
| return actual.getValue(name); | |
| } | |
| @Override | |
| public ImmutableCollection<String> getFieldNames() { | |
| return actual.getFieldNames(); | |
| } | |
| @Nullable | |
| @Override | |
| public String getErrorMessageForUnknownField(String name) { | |
| // Use the default error message. | |
| return null; | |
| } | |
| @Override | |
| public ConfiguredTarget getActual() { | |
| // This will either dereference an alias chain, or return the final ConfiguredTarget. | |
| return actual.getActual(); | |
| } | |
| @Override | |
| public Label getOriginalLabel() { | |
| return label; | |
| } | |
| @Override | |
| public void repr(Printer printer) { | |
| printer.append("<alias target " + label + " of " + actual.getLabel() + ">"); | |
| } | |
| @Override | |
| public String toString() { | |
| return MoreObjects.toStringHelper(this) | |
| .add("label", label) | |
| .add("configurationKey", configurationKey) | |
| .add("actual", actual) | |
| .add("overrides", overrides) | |
| .add("configConditions", configConditions) | |
| .toString(); | |
| } | |
| } |