// 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.ConfiguredRuleClassProvider; import com.google.devtools.build.lib.analysis.ConfiguredTarget; 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.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.Attribute; 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.util.ArrayList; import java.util.HashMap; import java.util.Map; 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; AspectFunction( BuildViewProvider buildViewProvider, RuleClassProvider ruleClassProvider, @Nullable SkylarkImportLookupFunction skylarkImportLookupFunctionForInlining, boolean storeTransitivePackagesForPackageRootResolution, BuildOptions defaultBuildOptions) { this.buildViewProvider = buildViewProvider; this.ruleClassProvider = ruleClassProvider; this.skylarkImportLookupFunctionForInlining = skylarkImportLookupFunctionForInlining; this.storeTransitivePackagesForPackageRootResolution = storeTransitivePackagesForPackageRootResolution; this.defaultBuildOptions = defaultBuildOptions; } /** * 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 skylark-defined aspect", skylarkValueName, extensionLabel.toString()), 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, false); try { SkylarkImportLookupValue skylarkImportLookupValue; if (skylarkImportLookupFunctionForInlining == null) { // not inlining skylarkImportLookupValue = (SkylarkImportLookupValue) env.getValueOrThrow(importFileKey, SkylarkImportFailedException.class); } else { skylarkImportLookupValue = skylarkImportLookupFunctionForInlining.computeWithInlineCalls(importFileKey, env, 1); } if (skylarkImportLookupValue == null) { Preconditions.checkState( env.valuesMissing(), "no skylark 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 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 keys = aspectHasConfiguration ? ImmutableSet.of(key.getBaseConfiguredTargetKey(), key.getAspectConfigurationKey()) : ImmutableSet.of(key.getBaseConfiguredTargetKey()); Map> 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 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 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 aspectPathSkyKeysBuilder = ImmutableList.builder(); ImmutableMap aspectKeys = getSkyKeysForAspectsAndCollectAspectPath(key.getBaseKeys(), aspectPathSkyKeysBuilder); Map values = env.getValues(aspectKeys.values()); if (env.valuesMissing()) { return null; } ImmutableList 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 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 aspectPath = aspectPathBuilder.build(); try { // Get the configuration targets that trigger this rule's configurable attributes. ImmutableMap configConditions = ConfiguredTargetFunction.getConfigConditions( associatedConfiguredTargetAndData.getTarget(), env, resolver, originalTargetAndAspectConfiguration, transitivePackagesForPackageRootResolution, transitiveRootCauses, ((ConfiguredRuleClassProvider) ruleClassProvider).getTrimmingTransitionFactory()); if (configConditions == null) { // Those targets haven't yet been resolved. return null; } // Determine what toolchains are needed by this target. ToolchainContext toolchainContext; try { ImmutableSet