| // Copyright 2014 Google Inc. 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.skyframe.AspectValue.AspectKey; |
| import com.google.devtools.build.lib.skyframe.ConfiguredTargetFunction.DependencyEvaluationException; |
| import com.google.devtools.build.lib.skyframe.SkyframeExecutor.BuildViewProvider; |
| 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; |
| |
| public AspectFunction(BuildViewProvider buildViewProvider, RuleClassProvider ruleClassProvider) { |
| this.buildViewProvider = buildViewProvider; |
| this.ruleClassProvider = ruleClassProvider; |
| } |
| |
| @Nullable |
| @Override |
| public SkyValue compute(SkyKey skyKey, Environment env) |
| throws AspectFunctionException, InterruptedException { |
| SkyframeBuildView view = buildViewProvider.getSkyframeBuildView(); |
| NestedSetBuilder<Package> transitivePackages = NestedSetBuilder.stableOrder(); |
| AspectKey key = (AspectKey) skyKey.argument(); |
| ConfiguredAspectFactory aspectFactory = |
| (ConfiguredAspectFactory) AspectFactory.Util.create(key.getAspect()); |
| |
| 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<ConfigMatchingProvider> configConditions = ConfiguredTargetFunction.getConfigConditions( |
| target, env, resolver, ctgValue, transitivePackages); |
| if (configConditions == null) { |
| // Those targets haven't yet been resolved. |
| return null; |
| } |
| |
| ListMultimap<Attribute, ConfiguredTarget> depValueMap = |
| ConfiguredTargetFunction.computeDependencies(env, resolver, ctgValue, |
| aspectFactory.getDefinition(), key.getParameters(), configConditions, |
| ruleClassProvider, view.getHostConfiguration(ctgValue.getConfiguration()), |
| transitivePackages); |
| |
| return createAspect(env, key, 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, |
| RuleConfiguredTarget associatedTarget, Set<ConfigMatchingProvider> configConditions, |
| ListMultimap<Attribute, ConfiguredTarget> directDeps, |
| NestedSetBuilder<Package> transitivePackages) |
| throws AspectFunctionException { |
| |
| 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; |
| } |
| |
| ConfiguredAspectFactory aspectFactory = |
| (ConfiguredAspectFactory) AspectFactory.Util.create(key.getAspect()); |
| 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); |
| } |
| } |
| } |