Add instrumented file provider support to Skylark rules.

RELNOTES: Add instrumented file provider support to Skylark rules.

--
MOS_MIGRATED_REVID=114255963
diff --git a/site/docs/skylark/rules.md b/site/docs/skylark/rules.md
index 12d6f96..f248a5e 100644
--- a/site/docs/skylark/rules.md
+++ b/site/docs/skylark/rules.md
@@ -357,6 +357,29 @@
 Also note that if an action uses an executable, the executable's runfiles can
 be used when the action executes.
 
+Instrumented files
+------------------
+
+Instrumented files are a set of files used by the coverage command. A rule can
+use the `instrumented_files` provider to provide information about which files
+should be used for measuring coverage.
+
+```skylark
+def rule_implementation(ctx):
+  ...
+  return struct(instrumented_files=struct(
+      # Optional: File extensions used to filter files from source_attributes.
+      # If not provided, then all files from source_attributes will be
+      # added to instrumented files, if an empty list is provided, then
+      # no files from source attributes will be added.
+      extensions=["ext1", "ext2"],
+      # Optional: Attributes that contain source files for this rule.
+      source_attributes=["srcs"],
+      # Optional: Attributes for dependencies that could include instrumented
+      # files.
+      dependency_attributes=["data", "deps"]))
+```
+
 Executable rules
 ----------------
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java
index 5fb0bc5..3c38f3f 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java
@@ -52,6 +52,7 @@
 import com.google.devtools.build.lib.packages.Attribute;
 import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition;
 import com.google.devtools.build.lib.packages.Attribute.LateBoundLabel;
+import com.google.devtools.build.lib.packages.Attribute.LateBoundLabelList;
 import com.google.devtools.build.lib.packages.AttributeMap;
 import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SkylarkImplicitOutputsFunctionWithCallback;
 import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SkylarkImplicitOutputsFunctionWithMap;
@@ -93,6 +94,7 @@
 import com.google.devtools.build.lib.util.Pair;
 import com.google.devtools.build.lib.util.Preconditions;
 
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
@@ -120,6 +122,39 @@
         }
       };
 
+  private static final Label COVERAGE_SUPPORT_LABEL =
+      Label.parseAbsoluteUnchecked("//tools/defaults:coverage");
+
+  private static final LateBoundLabelList<BuildConfiguration> GCOV =
+      new LateBoundLabelList<BuildConfiguration>(ImmutableList.of(COVERAGE_SUPPORT_LABEL)) {
+        @Override
+        public List<Label> getDefault(Rule rule, BuildConfiguration configuration) {
+          return configuration.isCodeCoverageEnabled()
+              ? ImmutableList.copyOf(configuration.getGcovLabels())
+              : ImmutableList.<Label>of();
+        }
+      };
+
+  private static final LateBoundLabelList<BuildConfiguration> COVERAGE_REPORT_GENERATOR =
+      new LateBoundLabelList<BuildConfiguration>(ImmutableList.of(COVERAGE_SUPPORT_LABEL)) {
+        @Override
+        public List<Label> getDefault(Rule rule, BuildConfiguration configuration) {
+          return configuration.isCodeCoverageEnabled()
+              ? ImmutableList.copyOf(configuration.getCoverageReportGeneratorLabels())
+              : ImmutableList.<Label>of();
+        }
+      };
+
+  private static final LateBoundLabelList<BuildConfiguration> COVERAGE_SUPPORT =
+      new LateBoundLabelList<BuildConfiguration>(ImmutableList.of(COVERAGE_SUPPORT_LABEL)) {
+        @Override
+        public List<Label> getDefault(Rule rule, BuildConfiguration configuration) {
+          return configuration.isCodeCoverageEnabled()
+              ? ImmutableList.copyOf(configuration.getCoverageLabels())
+              : ImmutableList.<Label>of();
+        }
+      };
+
   // TODO(bazel-team): Copied from ConfiguredRuleClassProvider for the transition from built-in
   // rules to skylark extensions. Using the same instance would require a large refactoring.
   // If we don't want to support old built-in rules and Skylark simultaneously
@@ -183,6 +218,12 @@
           .add(attr("$test_runtime", LABEL_LIST).cfg(HOST).value(ImmutableList.of(
               labelCache.getUnchecked(Constants.TOOLS_REPOSITORY + "//tools/test:runtime"))))
           .add(attr(":run_under", LABEL).cfg(DATA).value(RUN_UNDER))
+          .add(attr(":gcov", LABEL_LIST).cfg(HOST).value(GCOV))
+          .add(attr(":coverage_support", LABEL_LIST).cfg(HOST).value(COVERAGE_SUPPORT))
+          .add(
+              attr(":coverage_report_generator", LABEL_LIST)
+                  .cfg(HOST)
+                  .value(COVERAGE_REPORT_GENERATOR))
           .build();
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleConfiguredTargetBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleConfiguredTargetBuilder.java
index a07fb06..309e68a 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleConfiguredTargetBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleConfiguredTargetBuilder.java
@@ -29,6 +29,9 @@
 import com.google.devtools.build.lib.packages.Rule;
 import com.google.devtools.build.lib.packages.TargetUtils;
 import com.google.devtools.build.lib.rules.SkylarkRuleContext.Kind;
+import com.google.devtools.build.lib.rules.test.InstrumentedFilesCollector;
+import com.google.devtools.build.lib.rules.test.InstrumentedFilesCollector.InstrumentationSpec;
+import com.google.devtools.build.lib.rules.test.InstrumentedFilesProvider;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
 import com.google.devtools.build.lib.syntax.BaseFunction;
 import com.google.devtools.build.lib.syntax.ClassObject;
@@ -43,7 +46,11 @@
 import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
 import com.google.devtools.build.lib.syntax.SkylarkType;
 import com.google.devtools.build.lib.syntax.Type;
+import com.google.devtools.build.lib.util.FileType;
+import com.google.devtools.build.lib.util.FileTypeSet;
 
+import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -221,6 +228,44 @@
           defaultRunfiles = cast("default_runfiles", struct, Runfiles.class, loc);
         } else if (key.equals("output_groups")) {
           addOutputGroups(struct.getValue(key), loc, builder);
+        } else if (key.equals("instrumented_files")) {
+          SkylarkClassObject insStruct =
+              cast("instrumented_files", struct, SkylarkClassObject.class, loc);
+          Location insLoc = insStruct.getCreationLoc();
+          FileTypeSet fileTypeSet = FileTypeSet.ANY_FILE;
+          if (insStruct.getKeys().contains("extensions")) {
+            List<String> exts = cast("extensions", insStruct, List.class, String.class, insLoc);
+            if (exts.isEmpty()) {
+              fileTypeSet = FileTypeSet.NO_FILE;
+            } else {
+              FileType[] fileTypes = new FileType[exts.size()];
+              for (int i = 0; i < fileTypes.length; i++) {
+                fileTypes[i] = FileType.of(exts.get(i));
+              }
+              fileTypeSet = FileTypeSet.of(fileTypes);
+            }
+          }
+          List<String> dependencyAttributes = Collections.emptyList();
+          if (insStruct.getKeys().contains("dependency_attributes")) {
+            dependencyAttributes =
+                cast("dependency_attributes", insStruct, List.class, String.class, insLoc);
+          }
+          List<String> sourceAttributes = Collections.emptyList();
+          if (insStruct.getKeys().contains("source_attributes")) {
+            sourceAttributes =
+                cast("source_attributes", insStruct, List.class, String.class, insLoc);
+          }
+          InstrumentationSpec instrumentationSpec =
+              new InstrumentationSpec(fileTypeSet)
+                  .withSourceAttributes(sourceAttributes.toArray(new String[0]))
+                  .withDependencyAttributes(dependencyAttributes.toArray(new String[0]));
+          InstrumentedFilesProvider instrumentedFilesProvider =
+              InstrumentedFilesCollector.collect(
+                  ruleContext,
+                  instrumentationSpec,
+                  InstrumentedFilesCollector.NO_METADATA_COLLECTOR,
+                  Collections.<Artifact>emptySet());
+          builder.addProvider(InstrumentedFilesProvider.class, instrumentedFilesProvider);
         } else if (!key.equals("executable")) {
           // We handled executable already.
           builder.addSkylarkTransitiveInfo(key, struct.getValue(key), loc);