Configure coverage and runfiles for sh_library

InstrumentedFilesProvider is already configured for sh_binary and sh_test, it's confusing for this to not be configured the same way. This means that you might get coverage for binaries depended on via a data dependency, even if that dependency is indirect (e.g. an sh_library rule whose sources run the binary provides the data dependency).

sh_library should also gather runfiles from transitive dependencies in the same way as sh_test and sh_binary.

RELNOTES: Configure coverage and runfiles for sh_library.
PiperOrigin-RevId: 319092861
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/ShBinary.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/ShBinary.java
index e8d85d1..d29ddee 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/ShBinary.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/ShBinary.java
@@ -29,11 +29,9 @@
 import com.google.devtools.build.lib.analysis.actions.LauncherFileWriteAction.LaunchInfo;
 import com.google.devtools.build.lib.analysis.actions.SymlinkAction;
 import com.google.devtools.build.lib.analysis.test.InstrumentedFilesCollector;
-import com.google.devtools.build.lib.analysis.test.InstrumentedFilesCollector.InstrumentationSpec;
 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.util.FileTypeSet;
 import com.google.devtools.build.lib.util.OS;
 import com.google.devtools.build.lib.vfs.PathFragment;
 
@@ -97,7 +95,7 @@
         .addNativeDeclaredProvider(
             InstrumentedFilesCollector.collectTransitive(
                 ruleContext,
-                new InstrumentationSpec(FileTypeSet.ANY_FILE, "srcs", "deps", "data"),
+                ShCoverage.INSTRUMENTATION_SPEC,
                 /* reportedToActualSources= */ NestedSetBuilder.emptySet(Order.STABLE_ORDER)))
         .build();
   }
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/ShCoverage.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/ShCoverage.java
new file mode 100644
index 0000000..0d2958d
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/ShCoverage.java
@@ -0,0 +1,26 @@
+// Copyright 2020 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.bazel.rules.sh;
+
+import com.google.devtools.build.lib.analysis.test.InstrumentedFilesCollector.InstrumentationSpec;
+import com.google.devtools.build.lib.util.FileTypeSet;
+
+/** Common logic for coverage for sh_* rules. */
+final class ShCoverage {
+
+  private ShCoverage() {}
+
+  public static final InstrumentationSpec INSTRUMENTATION_SPEC =
+      new InstrumentationSpec(FileTypeSet.ANY_FILE, "srcs", "deps", "data");
+}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/ShLibrary.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/ShLibrary.java
index b36002a..a37d3ac 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/ShLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/ShLibrary.java
@@ -22,8 +22,10 @@
 import com.google.devtools.build.lib.analysis.Runfiles;
 import com.google.devtools.build.lib.analysis.RunfilesProvider;
 import com.google.devtools.build.lib.analysis.TransitionMode;
+import com.google.devtools.build.lib.analysis.test.InstrumentedFilesCollector;
 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;
 
 /**
  * Implementation for the sh_library rule.
@@ -39,13 +41,21 @@
             .addAll(ruleContext.getPrerequisiteArtifacts("deps", TransitionMode.TARGET).list())
             .addAll(ruleContext.getPrerequisiteArtifacts("data", TransitionMode.DONT_CHECK).list())
             .build();
-    Runfiles runfiles = new Runfiles.Builder(
-        ruleContext.getWorkspaceName(), ruleContext.getConfiguration().legacyExternalRunfiles())
-        .addTransitiveArtifacts(filesToBuild)
-        .build();
+    Runfiles runfiles =
+        new Runfiles.Builder(
+                ruleContext.getWorkspaceName(),
+                ruleContext.getConfiguration().legacyExternalRunfiles())
+            .addTransitiveArtifacts(filesToBuild)
+            .addRunfiles(ruleContext, RunfilesProvider.DEFAULT_RUNFILES)
+            .build();
     return new RuleConfiguredTargetBuilder(ruleContext)
         .setFilesToBuild(filesToBuild)
         .addProvider(RunfilesProvider.class, RunfilesProvider.simple(runfiles))
+        .addNativeDeclaredProvider(
+            InstrumentedFilesCollector.collectTransitive(
+                ruleContext,
+                ShCoverage.INSTRUMENTATION_SPEC,
+                /* reportedToActualSources= */ NestedSetBuilder.emptySet(Order.STABLE_ORDER)))
         .build();
   }
 }
diff --git a/src/test/shell/bazel/bazel_coverage_sh_test.sh b/src/test/shell/bazel/bazel_coverage_sh_test.sh
index bd7b67a..517f0b6 100755
--- a/src/test/shell/bazel/bazel_coverage_sh_test.sh
+++ b/src/test/shell/bazel/bazel_coverage_sh_test.sh
@@ -72,12 +72,26 @@
   echo "$coverage_file_path"
 }
 
-function test_sh_test_coverage() {
+function set_up_sh_test_coverage() {
   cat <<EOF > BUILD
 sh_test(
     name = "orange-sh",
     srcs = ["orange-test.sh"],
-    data = ["//java/com/google/orange:orange-bin"]
+    data = ["//java/com/google/orange:orange-bin"],
+)
+
+sh_test(
+    name = "orange-sh-indirect",
+    srcs = ["orange-test.sh"],
+    deps = [":orange-sh-lib"],
+)
+
+# This doesn't test all the combinations, it only exercises
+# a deps dependency from sh_test and a data dependency from
+# sh_library, but they use the same InstrumentedFilesSpec.
+sh_library(
+    name = "orange-sh-lib",
+    data = ["//java/com/google/orange:orange-bin"],
 )
 EOF
   cat <<EOF > orange-test.sh
@@ -127,11 +141,7 @@
 }
 EOF
 
-  bazel coverage --test_output=all //:orange-sh &>$TEST_log || fail "Coverage for //:orange-sh failed"
-
-  local coverage_file_path="$( get_coverage_file_path_from_test_log )"
-
-  cat <<EOF > result.dat
+  cat <<EOF > expected.dat
 SF:java/com/google/orange/orangeBin.java
 FN:3,com/google/orange/orangeBin::<init> ()V
 FN:5,com/google/orange/orangeBin::main ([Ljava/lang/String;)V
@@ -160,8 +170,22 @@
 LF:3
 end_of_record
 EOF
-  diff result.dat "$coverage_file_path" >> $TEST_log
-  cmp result.dat "$coverage_file_path" || fail "Coverage output file is different than the expected file"
+}
+
+function test_sh_test_coverage() {
+  set_up_sh_test_coverage
+  bazel coverage --test_output=all //:orange-sh &>$TEST_log || fail "Coverage for //:orange-sh failed"
+  local coverage_file_path="$( get_coverage_file_path_from_test_log )"
+  diff expected.dat "$coverage_file_path" >> $TEST_log
+  cmp expected.dat "$coverage_file_path" || fail "Coverage output file is different than the expected file for data dep of sh_binary"
+}
+
+function test_sh_test_coverage_indirect() {
+  set_up_sh_test_coverage
+  bazel coverage --test_output=all //:orange-sh-indirect &>$TEST_log || fail "Coverage for //:orange-sh-indirect failed"
+  coverage_file_path="$( get_coverage_file_path_from_test_log )"
+  diff expected.dat "$coverage_file_path" >> $TEST_log
+  cmp expected.dat "$coverage_file_path" || fail "Coverage output file is different than the expected file for data dep of sh_library"
 }
 
 function test_sh_test_coverage_cc_binary() {