// 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.analysis;
import static com.google.devtools.build.lib.analysis.ExtraActionUtils.createExtraActionProvider;
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.ActionAnalysisMetadata;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.constraints.ConstraintSemantics;
import com.google.devtools.build.lib.analysis.constraints.EnvironmentCollection;
import com.google.devtools.build.lib.analysis.constraints.SupportedEnvironments;
import com.google.devtools.build.lib.analysis.constraints.SupportedEnvironmentsProvider;
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.collect.nestedset.Order;
import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.packages.SkylarkClassObject;
import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.packages.TargetUtils;
import com.google.devtools.build.lib.rules.test.ExecutionInfoProvider;
import com.google.devtools.build.lib.rules.test.InstrumentedFilesProvider;
import com.google.devtools.build.lib.rules.test.TestActionBuilder;
import com.google.devtools.build.lib.rules.test.TestEnvironmentProvider;
import com.google.devtools.build.lib.rules.test.TestProvider;
import com.google.devtools.build.lib.rules.test.TestProvider.TestParams;
import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.util.Preconditions;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
/**
* Builder class for analyzed rule instances.
*
*
This is used to tell Bazel which {@link TransitiveInfoProvider}s are produced by the analysis
* of a configured target. For more information about analysis, see
* {@link com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory}.
*
* @see com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory
*/
public final class RuleConfiguredTargetBuilder {
private final RuleContext ruleContext;
private final TransitiveInfoProviderMap.Builder providersBuilder =
TransitiveInfoProviderMap.builder();
private final ImmutableMap.Builder skylarkProviders = ImmutableMap.builder();
private final ImmutableMap.Builder
skylarkDeclaredProviders = ImmutableMap.builder();
private final Map> outputGroupBuilders = new TreeMap<>();
/** These are supported by all configured targets and need to be specially handled. */
private NestedSet filesToBuild = NestedSetBuilder.emptySet(Order.STABLE_ORDER);
private RunfilesSupport runfilesSupport;
private Artifact executable;
private ImmutableSet actionsWithoutExtraAction = ImmutableSet.of();
public RuleConfiguredTargetBuilder(RuleContext ruleContext) {
this.ruleContext = ruleContext;
add(LicensesProvider.class, LicensesProviderImpl.of(ruleContext));
add(VisibilityProvider.class, new VisibilityProviderImpl(ruleContext.getVisibility()));
}
/**
* Constructs the RuleConfiguredTarget instance based on the values set for this Builder.
*/
public ConfiguredTarget build() {
if (ruleContext.getConfiguration().enforceConstraints()) {
checkConstraints();
}
if (ruleContext.hasErrors()) {
return null;
}
FilesToRunProvider filesToRunProvider = new FilesToRunProvider(
getFilesToRun(runfilesSupport, filesToBuild), runfilesSupport, executable);
addProvider(new FileProvider(filesToBuild));
addProvider(filesToRunProvider);
addSkylarkTransitiveInfo(FilesToRunProvider.SKYLARK_NAME, filesToRunProvider);
if (runfilesSupport != null) {
// If a binary is built, build its runfiles, too
addOutputGroup(
OutputGroupProvider.HIDDEN_TOP_LEVEL, runfilesSupport.getRunfilesMiddleman());
} else if (providersBuilder.contains(RunfilesProvider.class)) {
// If we don't have a RunfilesSupport (probably because this is not a binary rule), we still
// want to build the files this rule contributes to runfiles of dependent rules so that we
// report an error if one of these is broken.
//
// Note that this is a best-effort thing: there is .getDataRunfiles() and all the language-
// specific *RunfilesProvider classes, which we don't add here for reasons that are lost in
// the mists of time.
addOutputGroup(
OutputGroupProvider.HIDDEN_TOP_LEVEL,
providersBuilder
.getProvider(RunfilesProvider.class)
.getDefaultRunfiles()
.getAllArtifacts());
}
// Create test action and artifacts if target was successfully initialized
// and is a test.
if (TargetUtils.isTestRule(ruleContext.getTarget())) {
Preconditions.checkState(runfilesSupport != null);
add(TestProvider.class, initializeTestProvider(filesToRunProvider));
}
ExtraActionArtifactsProvider extraActionsProvider =
createExtraActionProvider(actionsWithoutExtraAction, ruleContext);
add(ExtraActionArtifactsProvider.class, extraActionsProvider);
if (!outputGroupBuilders.isEmpty()) {
ImmutableMap.Builder> outputGroups = ImmutableMap.builder();
for (Map.Entry> entry : outputGroupBuilders.entrySet()) {
outputGroups.put(entry.getKey(), entry.getValue().build());
}
add(OutputGroupProvider.class, new OutputGroupProvider(outputGroups.build()));
}
TransitiveInfoProviderMap providers = providersBuilder.build();
addRegisteredProvidersToSkylarkProviders(providers);
return new RuleConfiguredTarget(
ruleContext,
providers,
new SkylarkProviders(skylarkProviders.build(), skylarkDeclaredProviders.build()));
}
/** Adds skylark providers from a skylark provider registry, and checks for collisions. */
private void addRegisteredProvidersToSkylarkProviders(TransitiveInfoProviderMap providers) {
Map nativeSkylarkProviders = new HashMap<>();
for (Map.Entry, TransitiveInfoProvider> entry :
providers.entrySet()) {
if (ruleContext.getSkylarkProviderRegistry().containsValue(entry.getKey())) {
String skylarkName = ruleContext.getSkylarkProviderRegistry().inverse().get(entry.getKey());
nativeSkylarkProviders.put(skylarkName, entry.getValue());
}
}
try {
skylarkProviders.putAll(nativeSkylarkProviders);
} catch (IllegalArgumentException e) {
ruleContext.ruleError("Collision caused by duplicate skylark providers: " + e.getMessage());
}
}
/**
* Like getFilesToBuild(), except that it also includes the runfiles middleman, if any.
* Middlemen are expanded in the SpawnStrategy or by the Distributor.
*/
private ImmutableList getFilesToRun(
RunfilesSupport runfilesSupport, NestedSet filesToBuild) {
if (runfilesSupport == null) {
return ImmutableList.copyOf(filesToBuild);
} else {
ImmutableList.Builder allFilesToBuild = ImmutableList.builder();
allFilesToBuild.addAll(filesToBuild);
allFilesToBuild.add(runfilesSupport.getRunfilesMiddleman());
return allFilesToBuild.build();
}
}
/**
* Invokes Blaze's constraint enforcement system: checks that this rule's dependencies
* support its environments and reports appropriate errors if violations are found. Also
* publishes this rule's supported environments for the rules that depend on it.
*/
private void checkConstraints() {
if (!ruleContext.getRule().getRuleClassObject().supportsConstraintChecking()) {
return;
}
EnvironmentCollection supportedEnvironments =
ConstraintSemantics.getSupportedEnvironments(ruleContext);
if (supportedEnvironments != null) {
EnvironmentCollection.Builder refinedEnvironments = new EnvironmentCollection.Builder();
Map