blob: bac52e52c1ccdfc0b7962b94a25c021b30ae99ad [file] [log] [blame]
// 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 static com.google.devtools.build.lib.packages.RuleClass.Builder.STARLARK_BUILD_SETTING_DEFAULT_ATTR_NAME;
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.ActionAnalysisMetadata;
import com.google.devtools.build.lib.actions.Actions;
import com.google.devtools.build.lib.actions.Actions.GeneratingActions;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
import com.google.devtools.build.lib.analysis.config.CoreOptions;
import com.google.devtools.build.lib.analysis.config.CoreOptions.IncludeConfigFragmentsEnum;
import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget;
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.analysis.constraints.SupportedEnvironmentsProvider.RemovedEnvironmentCulprit;
import com.google.devtools.build.lib.analysis.test.AnalysisTestActionBuilder;
import com.google.devtools.build.lib.analysis.test.AnalysisTestResultInfo;
import com.google.devtools.build.lib.analysis.test.ExecutionInfo;
import com.google.devtools.build.lib.analysis.test.InstrumentedFilesCollector;
import com.google.devtools.build.lib.analysis.test.InstrumentedFilesInfo;
import com.google.devtools.build.lib.analysis.test.TestActionBuilder;
import com.google.devtools.build.lib.analysis.test.TestEnvironmentInfo;
import com.google.devtools.build.lib.analysis.test.TestProvider;
import com.google.devtools.build.lib.analysis.test.TestProvider.TestParams;
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.packages.Attribute;
import com.google.devtools.build.lib.packages.BuildSetting;
import com.google.devtools.build.lib.packages.Info;
import com.google.devtools.build.lib.packages.Provider;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.TargetUtils;
import com.google.devtools.build.lib.packages.Type;
import com.google.devtools.build.lib.packages.Type.LabelClass;
import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.syntax.Location;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.annotation.Nullable;
/**
* Builder class for analyzed rule instances.
*
* <p>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.analysis.RuleConfiguredTargetFactory}.
*
* @see com.google.devtools.build.lib.analysis.RuleConfiguredTargetFactory
*/
public final class RuleConfiguredTargetBuilder {
private final RuleContext ruleContext;
private final TransitiveInfoProviderMapBuilder providersBuilder =
new TransitiveInfoProviderMapBuilder();
private final Map<String, NestedSetBuilder<Artifact>> outputGroupBuilders = new TreeMap<>();
private final ImmutableList.Builder<Artifact> additionalTestActionTools =
new ImmutableList.Builder<>();
/** These are supported by all configured targets and need to be specially handled. */
private NestedSet<Artifact> filesToBuild = NestedSetBuilder.emptySet(Order.STABLE_ORDER);
private NestedSetBuilder<Artifact> filesToRunBuilder = NestedSetBuilder.stableOrder();
private RunfilesSupport runfilesSupport;
private Runfiles persistentTestRunnerRunfiles;
private Artifact executable;
private ImmutableSet<ActionAnalysisMetadata> actionsWithoutExtraAction = ImmutableSet.of();
private final LinkedHashSet<String> ruleImplSpecificRequiredConfigFragments =
new LinkedHashSet<>();
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.
* Returns null if there were rule errors reported.
*/
@Nullable
public ConfiguredTarget build() throws ActionConflictException {
// If allowing analysis failures, the current target may not propagate all of the
// expected providers; be lenient on such cases (for example, avoid precondition checks).
boolean allowAnalysisFailures = ruleContext.getConfiguration().allowAnalysisFailures();
if (ruleContext.getConfiguration().enforceConstraints()) {
checkConstraints();
}
if (ruleContext.hasErrors() && !allowAnalysisFailures) {
return null;
}
maybeAddRequiredConfigFragmentsProvider();
NestedSetBuilder<Artifact> runfilesMiddlemenBuilder = NestedSetBuilder.stableOrder();
if (runfilesSupport != null) {
runfilesMiddlemenBuilder.add(runfilesSupport.getRunfilesMiddleman());
runfilesMiddlemenBuilder.addTransitive(runfilesSupport.getRunfiles().getExtraMiddlemen());
}
NestedSet<Artifact> runfilesMiddlemen = runfilesMiddlemenBuilder.build();
FilesToRunProvider filesToRunProvider =
new FilesToRunProvider(
buildFilesToRun(runfilesMiddlemen, filesToBuild), runfilesSupport, executable);
addProvider(new FileProvider(filesToBuild));
addProvider(filesToRunProvider);
if (runfilesSupport != null) {
// If a binary is built, build its runfiles, too
addOutputGroup(OutputGroupInfo.HIDDEN_TOP_LEVEL, runfilesMiddlemen);
} 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(
OutputGroupInfo.HIDDEN_TOP_LEVEL,
providersBuilder
.getProvider(RunfilesProvider.class)
.getDefaultRunfiles()
.getAllArtifacts());
}
collectTransitiveValidationOutputGroups();
// Add a default provider that forwards InstrumentedFilesInfo from dependencies, even if this
// rule doesn't configure InstrumentedFilesInfo. This needs to be done for non-test rules
// as well, but should be done before initializeTestProvider, which uses that.
if (ruleContext.getConfiguration().isCodeCoverageEnabled()
&& ruleContext.getConfiguration().experimentalForwardInstrumentedFilesInfoByDefault()
&& !providersBuilder.contains(InstrumentedFilesInfo.STARLARK_CONSTRUCTOR.getKey())) {
addNativeDeclaredProvider(InstrumentedFilesCollector.forwardAll(ruleContext));
}
// Create test action and artifacts if target was successfully initialized
// and is a test.
if (TargetUtils.isTestRule(ruleContext.getTarget())) {
if (runfilesSupport != null) {
add(TestProvider.class, initializeTestProvider(filesToRunProvider));
} else {
if (!allowAnalysisFailures) {
throw new IllegalStateException("Test rules must have runfiles");
}
}
}
ExtraActionArtifactsProvider extraActionsProvider =
createExtraActionProvider(actionsWithoutExtraAction, ruleContext);
add(ExtraActionArtifactsProvider.class, extraActionsProvider);
if (!outputGroupBuilders.isEmpty()) {
ImmutableMap.Builder<String, NestedSet<Artifact>> outputGroups = ImmutableMap.builder();
for (Map.Entry<String, NestedSetBuilder<Artifact>> entry : outputGroupBuilders.entrySet()) {
outputGroups.put(entry.getKey(), entry.getValue().build());
}
OutputGroupInfo outputGroupInfo = new OutputGroupInfo(outputGroups.build());
addNativeDeclaredProvider(outputGroupInfo);
}
if (ruleContext.getConfiguration().evaluatingForAnalysisTest()) {
if (ruleContext.getRule().isAnalysisTest()) {
ruleContext.ruleError(
String.format(
"analysis_test rule '%s' cannot be transitively "
+ "depended on by another analysis test rule",
ruleContext.getLabel()));
return null;
}
addProvider(new TransitiveLabelsInfo(transitiveLabels()));
}
if (ruleContext.getRule().hasAnalysisTestTransition()) {
NestedSet<Label> labels = transitiveLabels();
int depCount = labels.toList().size();
if (depCount > ruleContext.getConfiguration().analysisTestingDepsLimit()) {
ruleContext.ruleError(
String.format(
"analysis test rule excedeed maximum dependency edge count. "
+ "Count: %s. Limit is %s. This limit is imposed on analysis test rules which "
+ "use analysis_test_transition attribute transitions. Exceeding this limit "
+ "indicates either the analysis_test has too many dependencies, or the "
+ "underlying toolchains may be large. Try decreasing the number of test "
+ "dependencies, (Analysis tests should not be very large!) or, if possible, "
+ "try not using configuration transitions. If underlying toolchain size is "
+ "to blame, it might be worth considering increasing "
+ "--analysis_testing_deps_limit. (Beware, however, that large values of "
+ "this flag can lead to no safeguards against giant "
+ "test suites that can lead to Out Of Memory exceptions in the build server.)",
depCount, ruleContext.getConfiguration().analysisTestingDepsLimit()));
return null;
}
}
if (ruleContext.getRule().isBuildSetting()) {
BuildSetting buildSetting = ruleContext.getRule().getRuleClassObject().getBuildSetting();
Object defaultValue =
ruleContext
.attributes()
.get(STARLARK_BUILD_SETTING_DEFAULT_ATTR_NAME, buildSetting.getType());
addProvider(
BuildSettingProvider.class,
new BuildSettingProvider(buildSetting, defaultValue, ruleContext.getLabel()));
}
TransitiveInfoProviderMap providers = providersBuilder.build();
if (ruleContext.getRule().isAnalysisTest()) {
// If the target is an analysis test that returned AnalysisTestResultInfo, register a
// test pass/fail action on behalf of the target.
AnalysisTestResultInfo testResultInfo =
providers.get(AnalysisTestResultInfo.STARLARK_CONSTRUCTOR);
if (testResultInfo == null) {
ruleContext.ruleError(
"rules with analysis_test=true must return an instance of AnalysisTestResultInfo");
return null;
}
AnalysisTestActionBuilder.writeAnalysisTestAction(ruleContext, testResultInfo);
}
AnalysisEnvironment analysisEnvironment = ruleContext.getAnalysisEnvironment();
GeneratingActions generatingActions =
Actions.assignOwnersAndFilterSharedActionsAndThrowActionConflict(
analysisEnvironment.getActionKeyContext(),
analysisEnvironment.getRegisteredActions(),
ruleContext.getOwner(),
((Rule) ruleContext.getTarget()).getOutputFiles());
return new RuleConfiguredTarget(
ruleContext,
providers,
generatingActions.getActions(),
generatingActions.getArtifactsByOutputLabel());
}
/**
* Adds {@link RequiredConfigFragmentsProvider} if {@link
* CoreOptions#includeRequiredConfigFragmentsProvider} isn't {@link
* CoreOptions.IncludeConfigFragmentsEnum#OFF}.
*
* <p>See {@link ConfiguredTargetFactory#getRequiredConfigFragments} for a description of the
* meaning of this provider's content. That method populates the results of {@link
* RuleContext#getRequiredConfigFragments} and {@link #ruleImplSpecificRequiredConfigFragments}.
*/
private void maybeAddRequiredConfigFragmentsProvider() {
if (ruleContext
.getConfiguration()
.getOptions()
.get(CoreOptions.class)
.includeRequiredConfigFragmentsProvider
!= IncludeConfigFragmentsEnum.OFF) {
addProvider(
new RequiredConfigFragmentsProvider(
ImmutableSet.<String>builder()
.addAll(ruleContext.getRequiredConfigFragments())
.addAll(ruleImplSpecificRequiredConfigFragments)
.build()));
}
}
private NestedSet<Label> transitiveLabels() {
NestedSetBuilder<Label> nestedSetBuilder = NestedSetBuilder.stableOrder();
for (String attributeName : ruleContext.attributes().getAttributeNames()) {
Type<?> attributeType =
ruleContext.attributes().getAttributeDefinition(attributeName).getType();
if (attributeType.getLabelClass() == LabelClass.DEPENDENCY) {
for (TransitiveLabelsInfo labelsInfo :
ruleContext.getPrerequisites(
attributeName, TransitionMode.DONT_CHECK, TransitiveLabelsInfo.class)) {
nestedSetBuilder.addTransitive(labelsInfo.getLabels());
}
}
}
nestedSetBuilder.add(ruleContext.getLabel());
return nestedSetBuilder.build();
}
/**
* Collects the validation action output groups from every dependency-type attribute on this rule.
* This is done within {@link RuleConfiguredTargetBuilder} so that every rule always and
* automatically propagates the validation action output group.
*
* <p>Note that in addition to {@link LabelClass.DEPENDENCY}, there is also {@link
* LabelClass.FILESET_ENTRY}, however the fileset implementation takes care of propagating the
* validation action output group itself.
*/
private void collectTransitiveValidationOutputGroups() {
for (String attributeName : ruleContext.attributes().getAttributeNames()) {
Attribute attribute = ruleContext.attributes().getAttributeDefinition(attributeName);
// Validation actions for tools, or from implicit deps should
// not fail the overall build, since those dependencies should have their own builds
// and tests that should surface any failing validations.
if (!attribute.getTransitionFactory().isTool()
&& !attribute.isImplicit()
&& attribute.getType().getLabelClass() == LabelClass.DEPENDENCY) {
for (OutputGroupInfo outputGroup :
ruleContext.getPrerequisites(
attributeName, TransitionMode.DONT_CHECK, OutputGroupInfo.STARLARK_CONSTRUCTOR)) {
NestedSet<Artifact> validationArtifacts =
outputGroup.getOutputGroup(OutputGroupInfo.VALIDATION);
if (!validationArtifacts.isEmpty()) {
addOutputGroup(OutputGroupInfo.VALIDATION, validationArtifacts);
}
}
}
}
}
/**
* Compute the artifacts to put into the {@link FilesToRunProvider} for this target. These are the
* filesToBuild, any artifacts added by the rule with {@link #addFilesToRun}, and the runfiles'
* middlemen if they exists.
*/
private NestedSet<Artifact> buildFilesToRun(
NestedSet<Artifact> runfilesMiddlemen, NestedSet<Artifact> filesToBuild) {
filesToRunBuilder.addTransitive(filesToBuild);
filesToRunBuilder.addTransitive(runfilesMiddlemen);
if (executable != null && ruleContext.getRule().getRuleClassObject().isStarlark()) {
filesToRunBuilder.add(executable);
}
return filesToRunBuilder.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;
}
ConstraintSemantics<RuleContext> constraintSemantics = ruleContext.getConstraintSemantics();
EnvironmentCollection supportedEnvironments =
constraintSemantics.getSupportedEnvironments(ruleContext);
if (supportedEnvironments != null) {
EnvironmentCollection.Builder refinedEnvironments = new EnvironmentCollection.Builder();
Map<Label, RemovedEnvironmentCulprit> removedEnvironmentCulprits = new LinkedHashMap<>();
constraintSemantics.checkConstraints(ruleContext, supportedEnvironments, refinedEnvironments,
removedEnvironmentCulprits);
add(SupportedEnvironmentsProvider.class,
new SupportedEnvironments(supportedEnvironments, refinedEnvironments.build(),
removedEnvironmentCulprits));
}
}
private TestProvider initializeTestProvider(FilesToRunProvider filesToRunProvider) {
int explicitShardCount = ruleContext.attributes().get("shard_count", Type.INTEGER);
if (explicitShardCount < 0
&& ruleContext.getRule().isAttributeValueExplicitlySpecified("shard_count")) {
ruleContext.attributeError("shard_count", "Must not be negative.");
}
if (explicitShardCount > 50) {
ruleContext.attributeError("shard_count",
"Having more than 50 shards is indicative of poor test organization. "
+ "Please reduce the number of shards.");
}
TestActionBuilder testActionBuilder =
new TestActionBuilder(ruleContext)
.setInstrumentedFiles(
(InstrumentedFilesInfo)
providersBuilder.getProvider(
InstrumentedFilesInfo.STARLARK_CONSTRUCTOR.getKey()));
TestEnvironmentInfo environmentProvider =
(TestEnvironmentInfo)
providersBuilder.getProvider(TestEnvironmentInfo.PROVIDER.getKey());
if (environmentProvider != null) {
testActionBuilder.addExtraEnv(environmentProvider.getEnvironment());
}
TestParams testParams =
testActionBuilder
.setFilesToRunProvider(filesToRunProvider)
.setPersistentTestRunnerRunfiles(persistentTestRunnerRunfiles)
.addTools(additionalTestActionTools.build())
.setExecutionRequirements(
(ExecutionInfo) providersBuilder.getProvider(ExecutionInfo.PROVIDER.getKey()))
.setShardCount(explicitShardCount)
.build();
ImmutableList<String> testTags = ImmutableList.copyOf(ruleContext.getRule().getRuleTags());
return new TestProvider(testParams, testTags);
}
/**
* Add files required to run the target. Artifacts from {@link #setFilesToBuild} and the runfiles
* middleman, if any, are added automatically.
*/
public RuleConfiguredTargetBuilder addFilesToRun(NestedSet<Artifact> files) {
filesToRunBuilder.addTransitive(files);
return this;
}
/** Add a specific provider. */
public <T extends TransitiveInfoProvider> RuleConfiguredTargetBuilder addProvider(
TransitiveInfoProvider provider) {
providersBuilder.add(provider);
return this;
}
/** Add a collection of specific providers. */
public <T extends TransitiveInfoProvider> RuleConfiguredTargetBuilder addProviders(
Iterable<TransitiveInfoProvider> providers) {
providersBuilder.addAll(providers);
return this;
}
/** Add a collection of specific providers. */
public <T extends TransitiveInfoProvider> RuleConfiguredTargetBuilder addProviders(
TransitiveInfoProviderMap providers) {
providersBuilder.addAll(providers);
return this;
}
/**
* Add a specific provider with a given value.
*
* @deprecated use {@link #addProvider}
*/
@Deprecated
public <T extends TransitiveInfoProvider> RuleConfiguredTargetBuilder add(Class<T> key, T value) {
return addProvider(key, value);
}
/** Add a specific provider with a given value. */
public <T extends TransitiveInfoProvider> RuleConfiguredTargetBuilder addProvider(
Class<? extends T> key, T value) {
Preconditions.checkNotNull(key);
Preconditions.checkNotNull(value);
providersBuilder.put(key, value);
return this;
}
/**
* Add a Starlark transitive info. The provider value must be safe (i.e. a String, a Boolean, an
* Integer, an Artifact, a Label, None, a Java TransitiveInfoProvider or something composed from
* these in Starlark using lists, sets, structs or dicts). Otherwise an EvalException is thrown.
*/
public RuleConfiguredTargetBuilder addStarlarkTransitiveInfo(
String name, Object value, Location loc) throws EvalException {
providersBuilder.put(name, value);
return this;
}
/**
* Adds a "declared provider" defined in Starlark to the rule. Use this method for declared
* providers defined in Skyark.
*
* <p>Has special handling for {@link OutputGroupInfo}: that provider is not added from Starlark
* directly, instead its output groups are added.
*
* <p>Use {@link #addNativeDeclaredProvider(Info)} in definitions of native rules.
*/
public RuleConfiguredTargetBuilder addStarlarkDeclaredProvider(Info provider)
throws EvalException {
Provider constructor = provider.getProvider();
if (!constructor.isExported()) {
throw new EvalException(constructor.getLocation(),
"All providers must be top level values");
}
if (OutputGroupInfo.STARLARK_CONSTRUCTOR.getKey().equals(constructor.getKey())) {
OutputGroupInfo outputGroupInfo = (OutputGroupInfo) provider;
for (String outputGroup : outputGroupInfo) {
addOutputGroup(outputGroup, outputGroupInfo.getOutputGroup(outputGroup));
}
} else {
providersBuilder.put(provider);
}
return this;
}
/**
* Adds "declared providers" defined in native code to the rule. Use this method for declared
* providers in definitions of native rules.
*
* <p>Use {@link #addStarlarkDeclaredProvider(Info)} for Starlark rule implementations.
*/
public RuleConfiguredTargetBuilder addNativeDeclaredProviders(Iterable<Info> providers) {
for (Info provider : providers) {
addNativeDeclaredProvider(provider);
}
return this;
}
/**
* Adds a "declared provider" defined in native code to the rule. Use this method for declared
* providers in definitions of native rules.
*
* <p>Use {@link #addStarlarkDeclaredProvider(Info)} for Starlark rule implementations.
*/
public RuleConfiguredTargetBuilder addNativeDeclaredProvider(Info provider) {
Provider constructor = provider.getProvider();
Preconditions.checkState(constructor.isExported());
providersBuilder.put(provider);
return this;
}
/**
* Returns true if a provider matching the given provider key has already been added to the
* configured target builder.
*/
public boolean containsProviderKey(Provider.Key providerKey) {
return providersBuilder.contains(providerKey);
}
/**
* Returns true if a provider matching the given legacy key has already been added to the
* configured target builder.
*/
public boolean containsLegacyKey(String legacyId) {
return providersBuilder.contains(legacyId);
}
/** Add a Starlark transitive info. The provider value must be safe. */
public RuleConfiguredTargetBuilder addStarlarkTransitiveInfo(String name, Object value) {
providersBuilder.put(name, value);
return this;
}
/**
* Set the runfiles support for executable targets.
*/
public RuleConfiguredTargetBuilder setRunfilesSupport(
RunfilesSupport runfilesSupport, Artifact executable) {
this.runfilesSupport = runfilesSupport;
this.executable = executable;
return this;
}
public RuleConfiguredTargetBuilder setPersistentTestRunnerRunfiles(Runfiles testSupportRunfiles) {
this.persistentTestRunnerRunfiles = testSupportRunfiles;
return this;
}
public RuleConfiguredTargetBuilder addTestActionTools(List<Artifact> tools) {
this.additionalTestActionTools.addAll(tools);
return this;
}
/**
* Set the files to build.
*/
public RuleConfiguredTargetBuilder setFilesToBuild(NestedSet<Artifact> filesToBuild) {
this.filesToBuild = filesToBuild;
return this;
}
private NestedSetBuilder<Artifact> getOutputGroupBuilder(String name) {
NestedSetBuilder<Artifact> result = outputGroupBuilders.get(name);
if (result != null) {
return result;
}
result = NestedSetBuilder.stableOrder();
outputGroupBuilders.put(name, result);
return result;
}
/**
* Adds a set of files to an output group.
*/
public RuleConfiguredTargetBuilder addOutputGroup(String name, NestedSet<Artifact> artifacts) {
getOutputGroupBuilder(name).addTransitive(artifacts);
return this;
}
/**
* Adds a file to an output group.
*/
public RuleConfiguredTargetBuilder addOutputGroup(String name, Artifact artifact) {
getOutputGroupBuilder(name).add(artifact);
return this;
}
/**
* Adds multiple output groups.
*/
public RuleConfiguredTargetBuilder addOutputGroups(Map<String, NestedSet<Artifact>> groups) {
for (Map.Entry<String, NestedSet<Artifact>> group : groups.entrySet()) {
getOutputGroupBuilder(group.getKey()).addTransitive(group.getValue());
}
return this;
}
/**
* Supplements {@link #maybeAddRequiredConfigFragmentsProvider} with rule implementation-specific
* requirements.
*/
public RuleConfiguredTargetBuilder addRequiredConfigFragments(Collection<String> fragments) {
ruleImplSpecificRequiredConfigFragments.addAll(fragments);
return this;
}
/**
* Contains a nested set of transitive dependencies of the target which propagated this object.
*
* <p>This is automatically provided by all targets which are being evaluated in analysis testing.
*
* <p>For large builds, this object will become <i>very large</i>, but analysis tests are required
* to be very small. The small-size of analysis tests are enforced by evaluating the size of this
* object.
*/
private static class TransitiveLabelsInfo implements TransitiveInfoProvider {
private final NestedSet<Label> labels;
public TransitiveLabelsInfo(NestedSet<Label> labels) {
this.labels = labels;
}
public NestedSet<Label> getLabels() {
return labels;
}
}
}