blob: d4f071e10d5953925b6c027bb1c85a75dac66208 [file] [log] [blame]
// 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);
}
}
}