// 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.rules;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.Root;
import com.google.devtools.build.lib.analysis.ActionsProvider;
import com.google.devtools.build.lib.analysis.AnalysisUtils;
import com.google.devtools.build.lib.analysis.AspectDescriptor;
import com.google.devtools.build.lib.analysis.ConfigurationMakeVariableContext;
import com.google.devtools.build.lib.analysis.FilesToRunProvider;
import com.google.devtools.build.lib.analysis.LabelExpander;
import com.google.devtools.build.lib.analysis.LabelExpander.NotUniqueExpansionException;
import com.google.devtools.build.lib.analysis.MakeVariableExpander.ExpansionException;
import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.config.FragmentCollection;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.packages.Attribute;
import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition;
import com.google.devtools.build.lib.packages.BuildType;
import com.google.devtools.build.lib.packages.ImplicitOutputsFunction;
import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SkylarkImplicitOutputsFunction;
import com.google.devtools.build.lib.packages.OutputFile;
import com.google.devtools.build.lib.packages.Package;
import com.google.devtools.build.lib.packages.RawAttributeMapper;
import com.google.devtools.build.lib.packages.SkylarkClassObject;
import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
import com.google.devtools.build.lib.rules.test.InstrumentedFilesCollector;
import com.google.devtools.build.lib.rules.test.InstrumentedFilesProvider;
import com.google.devtools.build.lib.shell.ShellUtils;
import com.google.devtools.build.lib.shell.ShellUtils.TokenizationException;
import com.google.devtools.build.lib.skylarkinterface.Param;
import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.syntax.FuncallExpression.FuncallException;
import com.google.devtools.build.lib.syntax.Runtime;
import com.google.devtools.build.lib.syntax.SkylarkDict;
import com.google.devtools.build.lib.syntax.SkylarkList;
import com.google.devtools.build.lib.syntax.SkylarkType;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
/** A Skylark API for the ruleContext. */
@SkylarkModule(
name = "ctx",
category = SkylarkModuleCategory.BUILTIN,
doc =
"The context of the rule containing helper functions and "
+ "information about attributes, depending targets and outputs. "
+ "You get a ctx object as an argument to the implementation
function when "
+ "you create a rule."
)
public final class SkylarkRuleContext {
private static final String DOC_NEW_FILE_TAIL = "Does not actually create a file on the file "
+ "system, just declares that some action will do so. You must create an action that "
+ "generates the file. If the file should be visible to other rules, declare a rule output "
+ "instead when possible. Doing so enables Blaze to associate a label with the file that "
+ "rules can refer to (allowing finer dependency control) instead of referencing the whole "
+ "rule.";
public static final String EXECUTABLE_DOC =
"A struct
containing executable files defined in label type "
+ "attributes marked as executable=True
. The struct fields correspond "
+ "to the attribute names. Each value in the struct is either a file
or "
+ "None
. If an optional attribute is not specified in the rule "
+ "then the corresponding struct value is None
. If a label type is not "
+ "marked as executable=True
, no corresponding struct field is generated.";
public static final String FILES_DOC =
"A struct
containing files defined in label or label list "
+ "type attributes. The struct fields correspond to the attribute names. The struct "
+ "values are list
of file
s. If an optional attribute is "
+ "not specified in the rule, an empty list is generated. "
+ "It is a shortcut for:"
+ "
[f for t in ctx.attr.<ATTR> for f in t.files]"; public static final String FILE_DOC = "A
struct
containing files defined in label type "
+ "attributes marked as single_file=True
. The struct fields correspond "
+ "to the attribute names. The struct value is always a file
or "
+ "None
. If an optional attribute is not specified in the rule "
+ "then the corresponding struct value is None
. If a label type is not "
+ "marked as single_file=True
, no corresponding struct field is generated. "
+ "It is a shortcut for:"
+ "list(ctx.attr.<ATTR>.files)[0]"; public static final String ATTR_DOC = "A struct to access the values of the attributes. The values are provided by " + "the user (if not, a default value is used)."; public static final String OUTPUTS_DOC = "A
struct
containing all the output files."
+ " The struct is generated the following way:executable=True
the struct has an "
+ "\"executable\" field with the rules default executable file
value."
+ "outputs
dict an attr is generated with "
+ "the same name and the corresponding file
value."
+ "file
value or None
, "
+ "if no value is specified in the rule."
+ "list
of file
s value "
+ "(an empty list if no value is specified in the rule).null
* if it is for a rule.
* @throws InterruptedException
*/
public SkylarkRuleContext(RuleContext ruleContext, @Nullable AspectDescriptor aspectDescriptor)
throws EvalException, InterruptedException {
this.ruleContext = Preconditions.checkNotNull(ruleContext);
this.fragments = new FragmentCollection(ruleContext, ConfigurationTransition.NONE);
this.hostFragments = new FragmentCollection(ruleContext, ConfigurationTransition.HOST);
this.aspectDescriptor = aspectDescriptor;
if (aspectDescriptor == null) {
CollectionTrue
, this returns an "
+ "Actions provider representing all actions "
+ "created so far for the current rule. For all other rules, returns None
. "
+ "Note that the provider is not updated when subsequent actions are created, so you "
+ "will have to call this function again if you wish to inspect them. "
+ "ctx
object and create actions on it.")
public Object createdActions() {
if (ruleContext.getRule().getRuleClassObject().isSkylarkTestable()) {
return ActionsProvider.create(
ruleContext.getAnalysisEnvironment().getRegisteredActions());
} else {
return Runtime.NONE;
}
}
@SkylarkCallable(name = "attr", structField = true, doc = ATTR_DOC)
public SkylarkClassObject getAttr() {
return attributesCollection.getAttr();
}
/**
* See {@link RuleContext#getExecutablePrerequisite(String, Mode)}.
*/
@SkylarkCallable(name = "executable", structField = true, doc = EXECUTABLE_DOC)
public SkylarkClassObject getExecutable() {
return attributesCollection.getExecutable();
}
/**
* See {@link RuleContext#getPrerequisiteArtifact(String, Mode)}.
*/
@SkylarkCallable(name = "file", structField = true, doc = FILE_DOC)
public SkylarkClassObject getFile() {
return attributesCollection.getFile();
}
/**
* See {@link RuleContext#getPrerequisiteArtifacts(String, Mode)}.
*/
@SkylarkCallable(name = "files", structField = true, doc = FILES_DOC)
public SkylarkClassObject getFiles() {
return attributesCollection.getFiles();
}
@SkylarkCallable(name = "workspace_name", structField = true,
doc = "Returns the workspace name as defined in the WORKSPACE file.")
public String getWorkspaceName() {
return ruleContext.getWorkspaceName();
}
@SkylarkCallable(name = "label", structField = true, doc = "The label of this rule.")
public Label getLabel() {
return ruleContext.getLabel();
}
@SkylarkCallable(name = "fragments", structField = true,
doc = "Allows access to configuration fragments in target configuration.")
public FragmentCollection getFragments() {
return fragments;
}
@SkylarkCallable(name = "host_fragments", structField = true,
doc = "Allows access to configuration fragments in host configuration.")
public FragmentCollection getHostFragments() {
return hostFragments;
}
@SkylarkCallable(name = "configuration", structField = true,
doc = "Returns the default configuration. See the "
+ "configuration type for more details.")
public BuildConfiguration getConfiguration() {
return ruleContext.getConfiguration();
}
@SkylarkCallable(name = "host_configuration", structField = true,
doc = "Returns the host configuration. See the "
+ "configuration type for more details.")
public BuildConfiguration getHostConfiguration() {
return ruleContext.getHostConfiguration();
}
@SkylarkCallable(name = "coverage_instrumented",
doc = "Returns whether code coverage instrumentation should be generated when performing "
+ "compilation actions for this rule or, if target
is provided, the rule "
+ "specified by that Target. (If a non-rule Target is provided, this returns False.) This "
+ "differs from coverage_enabled
in the "
+ "configuration, which notes whether coverage data collection is enabled for the "
+ "entire run, but not whether a specific target should be instrumented.",
parameters = {
@Param(
name = "target",
type = TransitiveInfoCollection.class,
defaultValue = "None",
noneable = true,
named = true,
doc = "A Target specifying a rule. If not provided, defaults to the current rule.")
})
public boolean instrumentCoverage(Object targetUnchecked) {
BuildConfiguration config = ruleContext.getConfiguration();
if (!config.isCodeCoverageEnabled()) {
return false;
}
if (targetUnchecked == Runtime.NONE) {
return InstrumentedFilesCollector.shouldIncludeLocalSources(ruleContext);
}
TransitiveInfoCollection target = (TransitiveInfoCollection) targetUnchecked;
return (target.getProvider(InstrumentedFilesProvider.class) != null)
&& InstrumentedFilesCollector.shouldIncludeLocalSources(config, target);
}
@SkylarkCallable(name = "features", structField = true,
doc = "Returns the set of features that are enabled for this rule."
)
public ImmutableList