| // Copyright 2014 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.skyframe; |
| |
| import com.google.common.base.Preconditions; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.devtools.build.lib.actions.InconsistentFilesystemException; |
| import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException; |
| import com.google.devtools.build.lib.analysis.AliasProvider; |
| import com.google.devtools.build.lib.analysis.AspectResolver; |
| import com.google.devtools.build.lib.analysis.CachingAnalysisEnvironment; |
| import com.google.devtools.build.lib.analysis.ConfiguredAspect; |
| import com.google.devtools.build.lib.analysis.ConfiguredAspectFactory; |
| import com.google.devtools.build.lib.analysis.ConfiguredTarget; |
| import com.google.devtools.build.lib.analysis.DependencyResolver; |
| import com.google.devtools.build.lib.analysis.DependencyResolver.DependencyKind; |
| import com.google.devtools.build.lib.analysis.DependencyResolver.InconsistentAspectOrderException; |
| import com.google.devtools.build.lib.analysis.TargetAndConfiguration; |
| import com.google.devtools.build.lib.analysis.ToolchainContext; |
| import com.google.devtools.build.lib.analysis.ToolchainResolver; |
| import com.google.devtools.build.lib.analysis.ToolchainResolver.UnloadedToolchainContext; |
| import com.google.devtools.build.lib.analysis.config.BuildConfiguration; |
| import com.google.devtools.build.lib.analysis.config.BuildOptions; |
| import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider; |
| import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException; |
| import com.google.devtools.build.lib.analysis.configuredtargets.MergedConfiguredTarget; |
| import com.google.devtools.build.lib.analysis.configuredtargets.MergedConfiguredTarget.DuplicateException; |
| 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.causes.LabelCause; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.collect.nestedset.NestedSet; |
| import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; |
| import com.google.devtools.build.lib.events.Event; |
| import com.google.devtools.build.lib.events.StoredEventHandler; |
| import com.google.devtools.build.lib.packages.Aspect; |
| import com.google.devtools.build.lib.packages.AspectDescriptor; |
| import com.google.devtools.build.lib.packages.BuildFileContainsErrorsException; |
| import com.google.devtools.build.lib.packages.NativeAspectClass; |
| import com.google.devtools.build.lib.packages.NoSuchTargetException; |
| import com.google.devtools.build.lib.packages.NoSuchThingException; |
| import com.google.devtools.build.lib.packages.Package; |
| import com.google.devtools.build.lib.packages.RuleClassProvider; |
| import com.google.devtools.build.lib.packages.SkylarkAspect; |
| import com.google.devtools.build.lib.packages.SkylarkAspectClass; |
| import com.google.devtools.build.lib.packages.SkylarkDefinedAspect; |
| import com.google.devtools.build.lib.packages.Target; |
| import com.google.devtools.build.lib.profiler.memory.CurrentRuleTracker; |
| import com.google.devtools.build.lib.skyframe.AspectValue.AspectKey; |
| import com.google.devtools.build.lib.skyframe.ConfiguredTargetFunction.ConfiguredTargetFunctionException; |
| import com.google.devtools.build.lib.skyframe.ConfiguredTargetFunction.ConfiguredValueCreationException; |
| import com.google.devtools.build.lib.skyframe.ConfiguredTargetFunction.DependencyEvaluationException; |
| import com.google.devtools.build.lib.skyframe.SkyframeExecutor.BuildViewProvider; |
| import com.google.devtools.build.lib.skyframe.SkylarkImportLookupFunction.SkylarkImportFailedException; |
| import com.google.devtools.build.lib.syntax.Type.ConversionException; |
| import com.google.devtools.build.lib.util.OrderedSetMultimap; |
| import com.google.devtools.build.skyframe.SkyFunction; |
| import com.google.devtools.build.skyframe.SkyFunctionException; |
| import com.google.devtools.build.skyframe.SkyKey; |
| import com.google.devtools.build.skyframe.SkyValue; |
| import com.google.devtools.build.skyframe.ValueOrException; |
| import java.math.BigInteger; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.function.Supplier; |
| import javax.annotation.Nullable; |
| |
| /** |
| * The Skyframe function that generates aspects. |
| * |
| * This class, together with {@link ConfiguredTargetFunction} drives the analysis phase. For more |
| * information, see {@link com.google.devtools.build.lib.analysis.RuleConfiguredTargetFactory}. |
| * |
| * {@link AspectFunction} takes a SkyKey containing an {@link AspectKey} [a tuple of |
| * (target label, configurations, aspect class and aspect parameters)], |
| * loads an {@link Aspect} from aspect class and aspect parameters, |
| * gets a {@link ConfiguredTarget} for label and configurations, and then creates |
| * a {@link ConfiguredAspect} for a given {@link AspectKey}. |
| * |
| * See {@link com.google.devtools.build.lib.packages.AspectClass} documentation |
| * for an overview of aspect-related classes |
| * |
| * @see com.google.devtools.build.lib.analysis.RuleConfiguredTargetFactory |
| * @see com.google.devtools.build.lib.packages.AspectClass |
| */ |
| public final class AspectFunction implements SkyFunction { |
| private final BuildViewProvider buildViewProvider; |
| private final RuleClassProvider ruleClassProvider; |
| private final BuildOptions defaultBuildOptions; |
| @Nullable SkylarkImportLookupFunction skylarkImportLookupFunctionForInlining; |
| /** |
| * Indicates whether the set of packages transitively loaded for a given {@link AspectValue} will |
| * be needed for package root resolution later in the build. If not, they are not collected and |
| * stored. |
| */ |
| private final boolean storeTransitivePackagesForPackageRootResolution; |
| |
| private final Supplier<BigInteger> nonceVersion; |
| |
| AspectFunction( |
| BuildViewProvider buildViewProvider, |
| RuleClassProvider ruleClassProvider, |
| @Nullable SkylarkImportLookupFunction skylarkImportLookupFunctionForInlining, |
| boolean storeTransitivePackagesForPackageRootResolution, |
| BuildOptions defaultBuildOptions, |
| Supplier<BigInteger> nonceVersion) { |
| this.buildViewProvider = buildViewProvider; |
| this.ruleClassProvider = ruleClassProvider; |
| this.skylarkImportLookupFunctionForInlining = skylarkImportLookupFunctionForInlining; |
| this.storeTransitivePackagesForPackageRootResolution = |
| storeTransitivePackagesForPackageRootResolution; |
| this.defaultBuildOptions = defaultBuildOptions; |
| this.nonceVersion = nonceVersion; |
| } |
| |
| /** |
| * Load Skylark-defined aspect from an extension file. Is to be called from a SkyFunction. |
| * |
| * @return {@code null} if dependencies cannot be satisfied. |
| * @throws AspectCreationException if the value loaded is not a {@link SkylarkDefinedAspect}. |
| */ |
| @Nullable |
| static SkylarkDefinedAspect loadSkylarkDefinedAspect( |
| Environment env, |
| SkylarkAspectClass skylarkAspectClass, |
| @Nullable SkylarkImportLookupFunction skylarkImportLookupFunctionForInlining) |
| throws AspectCreationException, InterruptedException { |
| Label extensionLabel = skylarkAspectClass.getExtensionLabel(); |
| String skylarkValueName = skylarkAspectClass.getExportedName(); |
| |
| SkylarkAspect skylarkAspect = |
| loadSkylarkAspect( |
| env, extensionLabel, skylarkValueName, skylarkImportLookupFunctionForInlining); |
| if (skylarkAspect == null) { |
| return null; |
| } |
| if (!(skylarkAspect instanceof SkylarkDefinedAspect)) { |
| throw new AspectCreationException( |
| String.format( |
| "%s from %s is not a Starlark-defined aspect", skylarkValueName, extensionLabel), |
| extensionLabel); |
| } else { |
| return (SkylarkDefinedAspect) skylarkAspect; |
| } |
| } |
| |
| /** |
| * Load Skylark aspect from an extension file. Is to be called from a SkyFunction. |
| * |
| * @return {@code null} if dependencies cannot be satisfied. |
| */ |
| @Nullable |
| static SkylarkAspect loadSkylarkAspect( |
| Environment env, |
| Label extensionLabel, |
| String skylarkValueName, |
| @Nullable SkylarkImportLookupFunction skylarkImportLookupFunctionForInlining) |
| throws AspectCreationException, InterruptedException { |
| SkyKey importFileKey = SkylarkImportLookupValue.key(extensionLabel); |
| try { |
| SkylarkImportLookupValue skylarkImportLookupValue; |
| if (skylarkImportLookupFunctionForInlining == null) { |
| // not inlining |
| skylarkImportLookupValue = |
| (SkylarkImportLookupValue) |
| env.getValueOrThrow(importFileKey, SkylarkImportFailedException.class); |
| } else { |
| skylarkImportLookupValue = |
| skylarkImportLookupFunctionForInlining.computeWithInlineCalls(importFileKey, env); |
| } |
| if (skylarkImportLookupValue == null) { |
| Preconditions.checkState( |
| env.valuesMissing(), "no Starlark import value for %s", importFileKey); |
| return null; |
| } |
| |
| Object skylarkValue = skylarkImportLookupValue.getEnvironmentExtension().getBindings() |
| .get(skylarkValueName); |
| if (skylarkValue == null) { |
| throw new ConversionException( |
| String.format( |
| "%s is not exported from %s", skylarkValueName, extensionLabel.toString())); |
| } |
| if (!(skylarkValue instanceof SkylarkAspect)) { |
| throw new ConversionException( |
| String.format( |
| "%s from %s is not an aspect", skylarkValueName, extensionLabel.toString())); |
| } |
| return (SkylarkAspect) skylarkValue; |
| } catch (SkylarkImportFailedException |
| | ConversionException |
| | InconsistentFilesystemException e) { |
| env.getListener().handle(Event.error(e.getMessage())); |
| throw new AspectCreationException(e.getMessage(), extensionLabel); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public SkyValue compute(SkyKey skyKey, Environment env) |
| throws AspectFunctionException, InterruptedException { |
| SkyframeBuildView view = buildViewProvider.getSkyframeBuildView(); |
| NestedSetBuilder<Cause> transitiveRootCauses = NestedSetBuilder.stableOrder(); |
| AspectKey key = (AspectKey) skyKey.argument(); |
| ConfiguredAspectFactory aspectFactory; |
| Aspect aspect; |
| if (key.getAspectClass() instanceof NativeAspectClass) { |
| NativeAspectClass nativeAspectClass = (NativeAspectClass) key.getAspectClass(); |
| aspectFactory = (ConfiguredAspectFactory) nativeAspectClass; |
| aspect = Aspect.forNative(nativeAspectClass, key.getParameters()); |
| } else if (key.getAspectClass() instanceof SkylarkAspectClass) { |
| SkylarkAspectClass skylarkAspectClass = (SkylarkAspectClass) key.getAspectClass(); |
| SkylarkDefinedAspect skylarkAspect; |
| try { |
| skylarkAspect = |
| loadSkylarkDefinedAspect( |
| env, skylarkAspectClass, skylarkImportLookupFunctionForInlining); |
| } catch (AspectCreationException e) { |
| throw new AspectFunctionException(e); |
| } |
| if (skylarkAspect == null) { |
| return null; |
| } |
| |
| aspectFactory = new SkylarkAspectFactory(skylarkAspect); |
| aspect = Aspect.forSkylark( |
| skylarkAspect.getAspectClass(), |
| skylarkAspect.getDefinition(key.getParameters()), |
| key.getParameters()); |
| } else { |
| throw new IllegalStateException(); |
| } |
| |
| // Keep this in sync with the same code in ConfiguredTargetFunction. |
| PackageValue packageValue = |
| (PackageValue) env.getValue(PackageValue.key(key.getLabel().getPackageIdentifier())); |
| if (packageValue == null) { |
| return null; |
| } |
| Package pkg = packageValue.getPackage(); |
| if (pkg.containsErrors()) { |
| throw new AspectFunctionException( |
| new BuildFileContainsErrorsException(key.getLabel().getPackageIdentifier())); |
| } |
| |
| boolean aspectHasConfiguration = key.getAspectConfigurationKey() != null; |
| |
| ImmutableSet<SkyKey> keys = |
| aspectHasConfiguration |
| ? ImmutableSet.of(key.getBaseConfiguredTargetKey(), key.getAspectConfigurationKey()) |
| : ImmutableSet.of(key.getBaseConfiguredTargetKey()); |
| |
| Map<SkyKey, ValueOrException<ConfiguredValueCreationException>> baseAndAspectValues = |
| env.getValuesOrThrow(keys, ConfiguredValueCreationException.class); |
| if (env.valuesMissing()) { |
| return null; |
| } |
| |
| ConfiguredTargetValue baseConfiguredTargetValue; |
| BuildConfiguration aspectConfiguration = null; |
| |
| try { |
| baseConfiguredTargetValue = |
| (ConfiguredTargetValue) baseAndAspectValues.get(key.getBaseConfiguredTargetKey()).get(); |
| } catch (ConfiguredValueCreationException e) { |
| throw new AspectFunctionException( |
| new AspectCreationException(e.getMessage(), e.getRootCauses())); |
| } |
| |
| if (aspectHasConfiguration) { |
| try { |
| aspectConfiguration = |
| ((BuildConfigurationValue) |
| baseAndAspectValues.get(key.getAspectConfigurationKey()).get()) |
| .getConfiguration(); |
| } catch (ConfiguredValueCreationException e) { |
| throw new IllegalStateException("Unexpected exception from BuildConfigurationFunction when " |
| + "computing " + key.getAspectConfigurationKey(), e); |
| } |
| } |
| |
| ConfiguredTarget associatedTarget = baseConfiguredTargetValue.getConfiguredTarget(); |
| |
| ConfiguredTargetAndData associatedConfiguredTargetAndData; |
| Package targetPkg; |
| BuildConfiguration configuration = null; |
| PackageValue.Key packageKey = |
| PackageValue.key(associatedTarget.getLabel().getPackageIdentifier()); |
| if (associatedTarget.getConfigurationKey() == null) { |
| PackageValue val = ((PackageValue) env.getValue(packageKey)); |
| if (val == null) { |
| // Unexpected in Bazel logic, but Skyframe makes no guarantees that this package is |
| // actually present. |
| return null; |
| } |
| targetPkg = val.getPackage(); |
| } else { |
| Map<SkyKey, SkyValue> result = |
| env.getValues(ImmutableSet.of(packageKey, associatedTarget.getConfigurationKey())); |
| if (env.valuesMissing()) { |
| // Unexpected in Bazel logic, but Skyframe makes no guarantees that this package and |
| // configuration are actually present. |
| return null; |
| } |
| targetPkg = ((PackageValue) result.get(packageKey)).getPackage(); |
| configuration = |
| ((BuildConfigurationValue) result.get(associatedTarget.getConfigurationKey())) |
| .getConfiguration(); |
| } |
| try { |
| associatedConfiguredTargetAndData = |
| new ConfiguredTargetAndData( |
| associatedTarget, |
| targetPkg.getTarget(associatedTarget.getLabel().getName()), |
| configuration); |
| } catch (NoSuchTargetException e) { |
| throw new IllegalStateException("Name already verified", e); |
| } |
| |
| if (baseConfiguredTargetValue.getConfiguredTarget().getProvider(AliasProvider.class) != null) { |
| return createAliasAspect( |
| env, |
| associatedConfiguredTargetAndData.getTarget(), |
| aspect, |
| key, |
| baseConfiguredTargetValue.getConfiguredTarget()); |
| } |
| |
| |
| ImmutableList.Builder<Aspect> aspectPathBuilder = ImmutableList.builder(); |
| |
| if (!key.getBaseKeys().isEmpty()) { |
| // We transitively collect all required aspects to reduce the number of restarts. |
| // Semantically it is enough to just request key.getBaseKeys(). |
| ImmutableList.Builder<SkyKey> aspectPathSkyKeysBuilder = ImmutableList.builder(); |
| ImmutableMap<AspectDescriptor, SkyKey> aspectKeys = |
| getSkyKeysForAspectsAndCollectAspectPath(key.getBaseKeys(), aspectPathSkyKeysBuilder); |
| |
| Map<SkyKey, SkyValue> values = env.getValues(aspectKeys.values()); |
| if (env.valuesMissing()) { |
| return null; |
| } |
| ImmutableList<SkyKey> aspectPathSkyKeys = aspectPathSkyKeysBuilder.build(); |
| for (SkyKey aspectPathSkyKey : aspectPathSkyKeys) { |
| aspectPathBuilder.add(((AspectValue) values.get(aspectPathSkyKey)).getAspect()); |
| } |
| try { |
| associatedTarget = getBaseTarget( |
| associatedTarget, key.getBaseKeys(), values); |
| } catch (DuplicateException e) { |
| env.getListener() |
| .handle( |
| Event.error( |
| associatedConfiguredTargetAndData.getTarget().getLocation(), e.getMessage())); |
| |
| throw new AspectFunctionException( |
| new AspectCreationException( |
| e.getMessage(), associatedTarget.getLabel(), aspectConfiguration)); |
| } |
| } |
| associatedConfiguredTargetAndData = |
| associatedConfiguredTargetAndData.fromConfiguredTarget(associatedTarget); |
| aspectPathBuilder.add(aspect); |
| |
| SkyframeDependencyResolver resolver = view.createDependencyResolver(env); |
| NestedSetBuilder<Package> transitivePackagesForPackageRootResolution = |
| storeTransitivePackagesForPackageRootResolution ? NestedSetBuilder.stableOrder() : null; |
| |
| // When getting the dependencies of this hybrid aspect+base target, use the aspect's |
| // configuration. The configuration of the aspect will always be a superset of the target's |
| // (trimmed configuration mode: target is part of the aspect's config fragment requirements; |
| // untrimmed mode: target is the same configuration as the aspect), so the fragments |
| // required by all dependencies (both those of the aspect and those of the base target) |
| // will be present this way. |
| TargetAndConfiguration originalTargetAndAspectConfiguration = |
| new TargetAndConfiguration( |
| associatedConfiguredTargetAndData.getTarget(), aspectConfiguration); |
| ImmutableList<Aspect> aspectPath = aspectPathBuilder.build(); |
| try { |
| // Get the configuration targets that trigger this rule's configurable attributes. |
| ImmutableMap<Label, ConfigMatchingProvider> configConditions = |
| ConfiguredTargetFunction.getConfigConditions( |
| associatedConfiguredTargetAndData.getTarget(), |
| env, |
| resolver, |
| originalTargetAndAspectConfiguration, |
| transitivePackagesForPackageRootResolution, |
| transitiveRootCauses); |
| if (configConditions == null) { |
| // Those targets haven't yet been resolved. |
| return null; |
| } |
| |
| // Determine what toolchains are needed by this target. |
| UnloadedToolchainContext unloadedToolchainContext = null; |
| if (configuration != null) { |
| // Configuration can be null in the case of aspects applied to input files. In this case, |
| // there are no chances of toolchains being used, so skip it. |
| try { |
| ImmutableSet<Label> requiredToolchains = aspect.getDefinition().getRequiredToolchains(); |
| unloadedToolchainContext = |
| new ToolchainResolver(env, BuildConfigurationValue.key(configuration)) |
| .setTargetDescription( |
| String.format( |
| "aspect %s applied to %s", |
| aspect.getDescriptor().getDescription(), |
| associatedConfiguredTargetAndData.getTarget())) |
| .setRequiredToolchainTypes(requiredToolchains) |
| .resolve(); |
| } catch (ToolchainException e) { |
| // TODO(katre): better error handling |
| throw new AspectCreationException( |
| e.getMessage(), new LabelCause(key.getLabel(), e.getMessage())); |
| } |
| if (env.valuesMissing()) { |
| return null; |
| } |
| } |
| |
| OrderedSetMultimap<DependencyKind, ConfiguredTargetAndData> depValueMap; |
| try { |
| depValueMap = |
| ConfiguredTargetFunction.computeDependencies( |
| env, |
| resolver, |
| originalTargetAndAspectConfiguration, |
| aspectPath, |
| configConditions, |
| unloadedToolchainContext == null |
| ? ImmutableSet.of() |
| : unloadedToolchainContext.resolvedToolchainLabels(), |
| ruleClassProvider, |
| view.getHostConfiguration(originalTargetAndAspectConfiguration.getConfiguration()), |
| transitivePackagesForPackageRootResolution, |
| transitiveRootCauses, |
| defaultBuildOptions); |
| } catch (ConfiguredTargetFunctionException e) { |
| throw new AspectCreationException(e.getMessage(), key.getLabel(), aspectConfiguration); |
| } |
| if (depValueMap == null) { |
| return null; |
| } |
| if (!transitiveRootCauses.isEmpty()) { |
| throw new AspectFunctionException( |
| new AspectCreationException("Loading failed", transitiveRootCauses.build())); |
| } |
| |
| // Load the requested toolchains into the ToolchainContext, now that we have dependencies. |
| ToolchainContext toolchainContext = null; |
| if (unloadedToolchainContext != null) { |
| toolchainContext = |
| unloadedToolchainContext.load(depValueMap.get(DependencyResolver.TOOLCHAIN_DEPENDENCY)); |
| } |
| |
| return createAspect( |
| env, |
| key, |
| aspectPath, |
| aspect, |
| aspectFactory, |
| associatedConfiguredTargetAndData, |
| aspectConfiguration, |
| configConditions, |
| toolchainContext, |
| depValueMap, |
| transitivePackagesForPackageRootResolution); |
| } catch (DependencyEvaluationException e) { |
| if (e.getCause() instanceof ConfiguredValueCreationException) { |
| ConfiguredValueCreationException cause = (ConfiguredValueCreationException) e.getCause(); |
| throw new AspectFunctionException( |
| new AspectCreationException(cause.getMessage(), cause.getRootCauses())); |
| } else if (e.getCause() instanceof InconsistentAspectOrderException) { |
| InconsistentAspectOrderException cause = (InconsistentAspectOrderException) e.getCause(); |
| throw new AspectFunctionException( |
| new AspectCreationException(cause.getMessage(), key.getLabel(), aspectConfiguration)); |
| } else { |
| // Cast to InvalidConfigurationException as a consistency check. If you add any |
| // DependencyEvaluationException constructors, you may need to change this code, too. |
| InvalidConfigurationException cause = (InvalidConfigurationException) e.getCause(); |
| throw new AspectFunctionException( |
| new AspectCreationException(cause.getMessage(), key.getLabel(), aspectConfiguration)); |
| } |
| } catch (AspectCreationException e) { |
| throw new AspectFunctionException(e); |
| } |
| } |
| |
| /** |
| * Merges aspects defined by {@code aspectKeys} into the {@code target} using |
| * previously computed {@code values}. |
| * |
| * @return A {@link ConfiguredTarget} that is a result of a merge. |
| * @throws DuplicateException if there is a duplicate provider provided by aspects. |
| */ |
| private ConfiguredTarget getBaseTarget(ConfiguredTarget target, |
| ImmutableList<AspectKey> aspectKeys, |
| Map<SkyKey, SkyValue> values) |
| throws DuplicateException { |
| ArrayList<ConfiguredAspect> aspectValues = new ArrayList<>(); |
| for (AspectKey aspectKey : aspectKeys) { |
| AspectValue aspectValue = (AspectValue) values.get(aspectKey); |
| ConfiguredAspect configuredAspect = aspectValue.getConfiguredAspect(); |
| aspectValues.add(configuredAspect); |
| } |
| return MergedConfiguredTarget.of(target, aspectValues); |
| } |
| |
| /** |
| * Collect all SkyKeys that are needed for a given list of AspectKeys, |
| * including transitive dependencies. |
| * |
| * Also collects all propagating aspects in correct order. |
| */ |
| private ImmutableMap<AspectDescriptor, SkyKey> getSkyKeysForAspectsAndCollectAspectPath( |
| ImmutableList<AspectKey> keys, |
| ImmutableList.Builder<SkyKey> aspectPathBuilder) { |
| HashMap<AspectDescriptor, SkyKey> result = new HashMap<>(); |
| for (AspectKey key : keys) { |
| buildSkyKeys(key, result, aspectPathBuilder); |
| } |
| return ImmutableMap.copyOf(result); |
| } |
| |
| private void buildSkyKeys(AspectKey key, HashMap<AspectDescriptor, SkyKey> result, |
| ImmutableList.Builder<SkyKey> aspectPathBuilder) { |
| if (result.containsKey(key.getAspectDescriptor())) { |
| return; |
| } |
| ImmutableList<AspectKey> baseKeys = key.getBaseKeys(); |
| result.put(key.getAspectDescriptor(), key); |
| for (AspectKey baseKey : baseKeys) { |
| buildSkyKeys(baseKey, result, aspectPathBuilder); |
| } |
| // Post-order list of aspect SkyKeys gives the order of propagating aspects: |
| // the aspect comes after all aspects it transitively sees. |
| aspectPathBuilder.add(key); |
| } |
| |
| private SkyValue createAliasAspect( |
| Environment env, |
| Target originalTarget, |
| Aspect aspect, |
| AspectKey originalKey, |
| ConfiguredTarget configuredTarget) |
| throws InterruptedException { |
| ImmutableList<Label> aliasChain = configuredTarget.getProvider(AliasProvider.class) |
| .getAliasChain(); |
| // Find the next alias in the chain: either the next alias (if there are two) or the name of |
| // the real configured target. |
| Label aliasLabel = aliasChain.size() > 1 ? aliasChain.get(1) : configuredTarget.getLabel(); |
| |
| SkyKey depKey = originalKey.withLabel(aliasLabel); |
| |
| // Compute the AspectValue of the target the alias refers to (which can itself be either an |
| // alias or a real target) |
| AspectValue real = (AspectValue) env.getValue(depKey); |
| if (env.valuesMissing()) { |
| return null; |
| } |
| |
| NestedSet<Package> transitivePackagesForPackageRootResolution = |
| storeTransitivePackagesForPackageRootResolution |
| ? NestedSetBuilder.<Package>stableOrder() |
| .addTransitive(real.getTransitivePackagesForPackageRootResolution()) |
| .add(originalTarget.getPackage()) |
| .build() |
| : null; |
| |
| return new AspectValue( |
| originalKey, |
| aspect, |
| originalTarget.getLabel(), |
| originalTarget.getLocation(), |
| ConfiguredAspect.forAlias(real.getConfiguredAspect()), |
| transitivePackagesForPackageRootResolution, |
| nonceVersion.get()); |
| } |
| |
| @Nullable |
| private AspectValue createAspect( |
| Environment env, |
| AspectKey key, |
| ImmutableList<Aspect> aspectPath, |
| Aspect aspect, |
| ConfiguredAspectFactory aspectFactory, |
| ConfiguredTargetAndData associatedTarget, |
| BuildConfiguration aspectConfiguration, |
| ImmutableMap<Label, ConfigMatchingProvider> configConditions, |
| ToolchainContext toolchainContext, |
| OrderedSetMultimap<DependencyKind, ConfiguredTargetAndData> directDeps, |
| @Nullable NestedSetBuilder<Package> transitivePackagesForPackageRootResolution) |
| throws AspectFunctionException, InterruptedException { |
| |
| SkyframeBuildView view = buildViewProvider.getSkyframeBuildView(); |
| |
| StoredEventHandler events = new StoredEventHandler(); |
| CachingAnalysisEnvironment analysisEnvironment = view.createAnalysisEnvironment( |
| key, false, events, env, aspectConfiguration); |
| if (env.valuesMissing()) { |
| return null; |
| } |
| |
| ConfiguredAspect configuredAspect; |
| if (AspectResolver.aspectMatchesConfiguredTarget(associatedTarget, aspect)) { |
| try { |
| CurrentRuleTracker.beginConfiguredAspect(aspect.getAspectClass()); |
| configuredAspect = |
| view.getConfiguredTargetFactory() |
| .createAspect( |
| analysisEnvironment, |
| associatedTarget, |
| aspectPath, |
| aspectFactory, |
| aspect, |
| directDeps, |
| configConditions, |
| toolchainContext, |
| aspectConfiguration, |
| view.getHostConfiguration(aspectConfiguration), |
| key); |
| } finally { |
| CurrentRuleTracker.endConfiguredAspect(); |
| } |
| } else { |
| configuredAspect = ConfiguredAspect.forNonapplicableTarget(aspect.getDescriptor()); |
| } |
| |
| events.replayOn(env.getListener()); |
| if (events.hasErrors()) { |
| analysisEnvironment.disable(associatedTarget.getTarget()); |
| String msg = "Analysis of target '" |
| + associatedTarget.getTarget().getLabel() |
| + "' failed; build aborted"; |
| throw new AspectFunctionException( |
| new AspectCreationException(msg, key.getLabel(), aspectConfiguration)); |
| } |
| Preconditions.checkState(!analysisEnvironment.hasErrors(), |
| "Analysis environment hasError() but no errors reported"); |
| |
| if (env.valuesMissing()) { |
| return null; |
| } |
| |
| analysisEnvironment.disable(associatedTarget.getTarget()); |
| Preconditions.checkNotNull(configuredAspect); |
| |
| return new AspectValue( |
| key, |
| aspect, |
| associatedTarget.getTarget().getLabel(), |
| associatedTarget.getTarget().getLocation(), |
| configuredAspect, |
| transitivePackagesForPackageRootResolution == null |
| ? null |
| : transitivePackagesForPackageRootResolution.build(), |
| nonceVersion.get()); |
| } |
| |
| @Override |
| public String extractTag(SkyKey skyKey) { |
| AspectKey aspectKey = (AspectKey) skyKey.argument(); |
| return Label.print(aspectKey.getLabel()); |
| } |
| |
| /** |
| * An exception indicating that there was a problem creating an aspect. |
| */ |
| public static final class AspectCreationException extends Exception { |
| private static ConfigurationId toId(BuildConfiguration config) { |
| return config == null ? null : config.getEventId().asStreamProto().getConfiguration(); |
| } |
| |
| private final NestedSet<Cause> causes; |
| |
| public AspectCreationException(String message, NestedSet<Cause> causes) { |
| super(message); |
| this.causes = causes; |
| } |
| |
| public AspectCreationException( |
| String message, Label currentTarget, @Nullable BuildConfiguration configuration) { |
| this( |
| message, |
| NestedSetBuilder.<Cause>stableOrder() |
| .add(new AnalysisFailedCause(currentTarget, toId(configuration), message)) |
| .build()); |
| } |
| |
| public AspectCreationException(String message, Label currentTarget) { |
| this(message, currentTarget, null); |
| } |
| |
| public AspectCreationException(String message, Cause cause) { |
| this(message, NestedSetBuilder.<Cause>stableOrder().add(cause).build()); |
| } |
| |
| public NestedSet<Cause> getCauses() { |
| return causes; |
| } |
| } |
| |
| /** Used to indicate errors during the computation of an {@link AspectValue}. */ |
| public static final class AspectFunctionException extends SkyFunctionException { |
| public AspectFunctionException(NoSuchThingException e) { |
| super(e, Transience.PERSISTENT); |
| } |
| |
| public AspectFunctionException(AspectCreationException e) { |
| super(e, Transience.PERSISTENT); |
| } |
| |
| public AspectFunctionException(ActionConflictException cause) { |
| super(cause, Transience.PERSISTENT); |
| } |
| } |
| } |