// 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.ListMultimap; import com.google.devtools.build.lib.analysis.Aspect; import com.google.devtools.build.lib.analysis.CachingAnalysisEnvironment; import com.google.devtools.build.lib.analysis.ConfiguredAspectFactory; import com.google.devtools.build.lib.analysis.ConfiguredTarget; import com.google.devtools.build.lib.analysis.RuleConfiguredTarget; import com.google.devtools.build.lib.analysis.TargetAndConfiguration; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.events.StoredEventHandler; import com.google.devtools.build.lib.packages.AspectFactory; import com.google.devtools.build.lib.packages.Attribute; import com.google.devtools.build.lib.packages.BuildFileContainsErrorsException; import com.google.devtools.build.lib.packages.NoSuchTargetException; import com.google.devtools.build.lib.packages.Package; import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.packages.RuleClassProvider; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.rules.SkylarkRuleClassFunctions.SkylarkAspect; import com.google.devtools.build.lib.skyframe.ASTFileLookupValue.ASTLookupInputException; import com.google.devtools.build.lib.skyframe.AspectValue.AspectKey; import com.google.devtools.build.lib.skyframe.AspectValue.NativeAspectKey; import com.google.devtools.build.lib.skyframe.AspectValue.SkylarkAspectKey; import com.google.devtools.build.lib.skyframe.ConfiguredTargetFunction.DependencyEvaluationException; import com.google.devtools.build.lib.skyframe.SkyframeExecutor.BuildViewProvider; import com.google.devtools.build.lib.syntax.Type.ConversionException; 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 java.util.Set; import javax.annotation.Nullable; /** * The Skyframe function that generates aspects. */ public final class AspectFunction implements SkyFunction { private final BuildViewProvider buildViewProvider; private final RuleClassProvider ruleClassProvider; private final AspectFactoryCreator aspectFactoryCreator; public static AspectFunction createNativeAspectFunction( BuildViewProvider buildViewProvider, RuleClassProvider ruleClassProvider) { return new AspectFunction<>( buildViewProvider, ruleClassProvider, new NativeAspectFactoryCreator()); } public static AspectFunction createSkylarkAspectFunction( BuildViewProvider buildViewProvider, RuleClassProvider ruleClassProvider) { return new AspectFunction<>( buildViewProvider, ruleClassProvider, new SkylarkAspectFactoryCreator()); } private AspectFunction( BuildViewProvider buildViewProvider, RuleClassProvider ruleClassProvider, AspectFactoryCreator aspectFactoryCreator) { this.buildViewProvider = buildViewProvider; this.ruleClassProvider = ruleClassProvider; this.aspectFactoryCreator = aspectFactoryCreator; } @Nullable @Override public SkyValue compute(SkyKey skyKey, Environment env) throws AspectFunctionException, InterruptedException { SkyframeBuildView view = buildViewProvider.getSkyframeBuildView(); NestedSetBuilder transitivePackages = NestedSetBuilder.stableOrder(); AspectKey key = (AspectKey) skyKey.argument(); ConfiguredAspectFactory aspectFactory = aspectFactoryCreator.createAspectFactory(skyKey, env); 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( skyKey, new BuildFileContainsErrorsException(key.getLabel().getPackageIdentifier())); } Target target; try { target = pkg.getTarget(key.getLabel().getName()); } catch (NoSuchTargetException e) { throw new AspectFunctionException(skyKey, e); } if (!(target instanceof Rule)) { throw new AspectFunctionException(new AspectCreationException( "aspects must be attached to rules")); } final ConfiguredTargetValue configuredTargetValue = (ConfiguredTargetValue) env.getValue(ConfiguredTargetValue.key(key.getLabel(), key.getConfiguration())); if (configuredTargetValue == null) { // TODO(bazel-team): remove this check when top-level targets also use dynamic configurations. // Right now the key configuration may be dynamic while the original target's configuration // is static, resulting in a Skyframe cache miss even though the original target is, in fact, // precomputed. return null; } RuleConfiguredTarget associatedTarget = (RuleConfiguredTarget) configuredTargetValue.getConfiguredTarget(); if (associatedTarget == null) { return null; } SkyframeDependencyResolver resolver = view.createDependencyResolver(env); if (resolver == null) { return null; } TargetAndConfiguration ctgValue = new TargetAndConfiguration(target, key.getConfiguration()); try { // Get the configuration targets that trigger this rule's configurable attributes. Set configConditions = ConfiguredTargetFunction.getConfigConditions( target, env, resolver, ctgValue, transitivePackages); if (configConditions == null) { // Those targets haven't yet been resolved. return null; } ListMultimap depValueMap = ConfiguredTargetFunction.computeDependencies(env, resolver, ctgValue, aspectFactory.getDefinition(), key.getParameters(), configConditions, ruleClassProvider, view.getHostConfiguration(ctgValue.getConfiguration()), transitivePackages); return createAspect( env, key, aspectFactory, associatedTarget, configConditions, depValueMap, transitivePackages); } catch (DependencyEvaluationException e) { throw new AspectFunctionException(e.getRootCauseSkyKey(), e.getCause()); } catch (AspectCreationException e) { throw new AspectFunctionException(e); } } @Nullable private AspectValue createAspect( Environment env, AspectKey key, ConfiguredAspectFactory aspectFactory, RuleConfiguredTarget associatedTarget, Set configConditions, ListMultimap directDeps, NestedSetBuilder transitivePackages) throws AspectFunctionException, InterruptedException { SkyframeBuildView view = buildViewProvider.getSkyframeBuildView(); BuildConfiguration configuration = associatedTarget.getConfiguration(); StoredEventHandler events = new StoredEventHandler(); CachingAnalysisEnvironment analysisEnvironment = view.createAnalysisEnvironment( key, false, events, env, configuration); if (env.valuesMissing()) { return null; } Aspect aspect = view.createAspect( analysisEnvironment, associatedTarget, aspectFactory, directDeps, configConditions, key.getParameters()); events.replayOn(env.getListener()); if (events.hasErrors()) { analysisEnvironment.disable(associatedTarget.getTarget()); throw new AspectFunctionException(new AspectCreationException( "Analysis of target '" + associatedTarget.getLabel() + "' failed; build aborted")); } Preconditions.checkState(!analysisEnvironment.hasErrors(), "Analysis environment hasError() but no errors reported"); if (env.valuesMissing()) { return null; } analysisEnvironment.disable(associatedTarget.getTarget()); Preconditions.checkNotNull(aspect); return new AspectValue( key, associatedTarget.getLabel(), associatedTarget.getTarget().getLocation(), aspect, ImmutableList.copyOf(analysisEnvironment.getRegisteredActions()), transitivePackages.build()); } @Nullable @Override public String extractTag(SkyKey skyKey) { return null; } /** * An exception indicating that there was a problem creating an aspect. */ public static final class AspectCreationException extends Exception { public AspectCreationException(String message) { super(message); } } /** * Used to indicate errors during the computation of an {@link AspectValue}. */ private static final class AspectFunctionException extends SkyFunctionException { public AspectFunctionException(Exception e) { super(e, Transience.PERSISTENT); } /** Used to rethrow a child error that we cannot handle. */ public AspectFunctionException(SkyKey childKey, Exception transitiveError) { super(transitiveError, childKey); } } /** * Factory for {@link ConfiguredAspectFactory} given a particular kind of {@link AspectKey}. */ private interface AspectFactoryCreator { ConfiguredAspectFactory createAspectFactory(SkyKey skyKey, Environment env) throws AspectFunctionException; } /** * Factory for native aspects. */ private static class NativeAspectFactoryCreator implements AspectFactoryCreator { @Override public ConfiguredAspectFactory createAspectFactory(SkyKey skyKey, Environment env) { NativeAspectKey key = (NativeAspectKey) skyKey.argument(); return (ConfiguredAspectFactory) AspectFactory.Util.create(key.getAspect()); } } /** * Factory for Skylark aspects. */ private static class SkylarkAspectFactoryCreator implements AspectFactoryCreator { @Override public ConfiguredAspectFactory createAspectFactory(SkyKey skyKey, Environment env) throws AspectFunctionException { SkylarkAspectKey skylarkAspectKey = (SkylarkAspectKey) skyKey.argument(); SkyKey importFileKey; try { importFileKey = SkylarkImportLookupValue.key(skylarkAspectKey.getExtensionFile()); } catch (ASTLookupInputException e) { throw new AspectFunctionException(skyKey, e); } SkylarkImportLookupValue skylarkImportLookupValue = (SkylarkImportLookupValue) env.getValue(importFileKey); if (skylarkImportLookupValue == null) { return null; } Object skylarkValue = skylarkImportLookupValue .getEnvironmentExtension() .get(skylarkAspectKey.getSkylarkValueName()); if (!(skylarkValue instanceof SkylarkAspect)) { throw new AspectFunctionException( new ConversionException(skylarkAspectKey.getSkylarkValueName() + " from " + skylarkAspectKey.getExtensionFile().toString() + " is not an aspect")); } SkylarkAspect skylarkAspect = (SkylarkAspect) skylarkValue; return new SkylarkAspectFactory(skylarkAspectKey.getSkylarkValueName(), skylarkAspect); } } }