Add tool executables (from FilesToRunProvider) to action inputs.

Fixes https://github.com/bazelbuild/bazel/issues/7390

Summary: rules are allowed to set `DefaultInfo(files = depset(), executable = ...)`. If the resulting targets are used as tool inputs to another target, the executable will not be added to the downstream targets' action inputs. This manifests as either an `execvp()` error (from `ctx.actions.run()`) or a Bash error in genrules.

This PR fixes that so the use case of non-default executables works as expected. This is useful for building scripts with complex runfiles, where `bazel run //:something` and `bazel build //:something && bazel-bin/something` cannot be supported by the same output file.

cc @ccate @jmmv

Closes #10110.

PiperOrigin-RevId: 280170524
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java
index 50b2476..84cdd2e 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java
@@ -362,6 +362,9 @@
       NestedSet<Artifact> runfilesMiddlemen, NestedSet<Artifact> filesToBuild) {
     filesToRunBuilder.addTransitive(filesToBuild);
     filesToRunBuilder.addTransitive(runfilesMiddlemen);
+    if (executable != null && ruleContext.getRule().getRuleClassObject().isSkylark()) {
+      filesToRunBuilder.add(executable);
+    }
     return filesToRunBuilder.build();
   }
 
diff --git a/src/test/shell/bazel/bazel_rules_test.sh b/src/test/shell/bazel/bazel_rules_test.sh
index ca94833..ef70918 100755
--- a/src/test/shell/bazel/bazel_rules_test.sh
+++ b/src/test/shell/bazel/bazel_rules_test.sh
@@ -524,4 +524,73 @@
   expect_log "Public or private visibility labels (e.g. //visibility:public or //visibility:private) cannot be used in combination with other labels"
 }
 
+function test_executable_without_default_files() {
+  mkdir pkg
+  cat >pkg/BUILD <<'EOF'
+load(":rules.bzl", "bin_rule", "out_rule")
+bin_rule(name = "hello_bin")
+out_rule(name = "hello_out")
+
+genrule(
+    name = "hello_gen",
+    tools = [":hello_bin"],
+    outs = ["hello_gen.txt"],
+    cmd = "$(location :hello_bin) $@",
+)
+EOF
+
+  # On Windows this file needs to be acceptable by CreateProcessW(), rather
+  # than a Bourne script.
+  if "$is_windows"; then
+    cat >pkg/rules.bzl <<'EOF'
+_SCRIPT_EXT = ".bat"
+_SCRIPT_CONTENT = "@ECHO OFF\necho hello world > %1"
+EOF
+  else
+    cat >pkg/rules.bzl <<'EOF'
+_SCRIPT_EXT = ".sh"
+_SCRIPT_CONTENT = "#!/bin/sh\necho 'hello world' > $@"
+EOF
+  fi
+
+  cat >>pkg/rules.bzl <<'EOF'
+def _bin_rule(ctx):
+    out_sh = ctx.actions.declare_file(ctx.attr.name + _SCRIPT_EXT)
+    ctx.actions.write(
+        output = out_sh,
+        content = _SCRIPT_CONTENT,
+        is_executable = True,
+    )
+    return DefaultInfo(
+        files = depset(direct = []),
+        executable = out_sh,
+    )
+
+def _out_rule(ctx):
+    out = ctx.actions.declare_file(ctx.attr.name + ".txt")
+    ctx.actions.run(
+        executable = ctx.executable._hello_bin,
+        outputs = [out],
+        arguments = [out.path],
+        mnemonic = "HelloOut",
+    )
+    return DefaultInfo(
+        files = depset(direct = [out]),
+    )
+
+bin_rule = rule(_bin_rule, executable = True)
+out_rule = rule(_out_rule, attrs = {
+    "_hello_bin": attr.label(
+        default = ":hello_bin",
+        executable = True,
+        cfg = "host",
+    ),
+})
+EOF
+
+  bazel build //pkg:hello_out //pkg:hello_gen >$TEST_log 2>&1 || fail "Should build"
+  assert_contains "hello world" bazel-bin/pkg/hello_out.txt
+  assert_contains "hello world" bazel-bin/pkg/hello_gen.txt
+}
+
 run_suite "rules test"