Add `ShInfo` and `ShBinaryInfo` providers (#47)

closes https://github.com/bazelbuild/rules_shell/issues/16

---------

Co-authored-by: Fabian Meumertzheim <fabian@meumertzhe.im>
diff --git a/shell/private/providers.bzl b/shell/private/providers.bzl
new file mode 100644
index 0000000..556c39a
--- /dev/null
+++ b/shell/private/providers.bzl
@@ -0,0 +1,27 @@
+# Copyright 2026 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.
+
+"""rules_shell provider definitions."""
+
+visibility("public")
+
+ShInfo = provider(
+    doc = "A provider for shell library rules.",
+    fields = {},
+)
+
+ShBinaryInfo = provider(
+    doc = "A provider for shell binary rules.",
+    fields = {},
+)
diff --git a/shell/private/sh_executable.bzl b/shell/private/sh_executable.bzl
index 359667b..9561f3d 100644
--- a/shell/private/sh_executable.bzl
+++ b/shell/private/sh_executable.bzl
@@ -14,6 +14,8 @@
 
 """Common code for sh_binary and sh_test rules."""
 
+load(":providers.bzl", "ShBinaryInfo", "ShInfo")
+
 visibility(["//shell"])
 
 _SH_TOOLCHAIN_TYPE = Label("//shell:toolchain_type")
@@ -24,6 +26,9 @@
     else:
         return ctx.workspace_name + "/" + file.short_path
 
+# A memory optimization for an empty provider
+_SHARED_PROVIDER = ShBinaryInfo()
+
 def _sh_executable_impl(ctx):
     if len(ctx.files.srcs) != 1:
         fail("you must specify exactly one file in 'srcs'", attr = "srcs")
@@ -117,6 +122,7 @@
         default_info,
         instrumented_files_info,
         run_environment_info,
+        _SHARED_PROVIDER,
     ]
 
 _WINDOWS_EXECUTABLE_EXTENSIONS = [
@@ -200,7 +206,7 @@
                 flags = ["SKIP_CONSTRAINTS_OVERRIDE"],
             ),
             "deps": attr.label_list(
-                allow_rules = ["sh_library"],
+                providers = [ShInfo],
                 doc = """
 The list of "library" targets to be aggregated into this target.
 See general comments about <code>deps</code>
@@ -228,5 +234,6 @@
         toolchains = [
             config_common.toolchain_type(_SH_TOOLCHAIN_TYPE, mandatory = False),
         ],
+        provides = [ShBinaryInfo],
         **kwargs
     )
diff --git a/shell/private/sh_library.bzl b/shell/private/sh_library.bzl
index 2f3613c..8bf463d 100644
--- a/shell/private/sh_library.bzl
+++ b/shell/private/sh_library.bzl
@@ -14,9 +14,14 @@
 
 """sh_library rule definition."""
 
+load(":providers.bzl", "ShInfo")
+
 # For doc generation only.
 visibility("public")
 
+# A memory optimization for an empty provider
+_SHARED_PROVIDER = ShInfo()
+
 def _sh_library_impl(ctx):
     transitive_files = []
     for target in ctx.attr.srcs:
@@ -41,6 +46,7 @@
             runfiles = runfiles,
         ),
         instrumented_files_info,
+        _SHARED_PROVIDER,
     ]
 
 sh_library = rule(
@@ -101,7 +107,7 @@
             flags = ["SKIP_CONSTRAINTS_OVERRIDE"],
         ),
         "deps": attr.label_list(
-            allow_rules = ["sh_library"],
+            providers = [ShInfo],
             doc = """
 The list of "library" targets to be aggregated into this target.
 See general comments about <code>deps</code>
@@ -115,4 +121,5 @@
 """,
         ),
     },
+    provides = [ShInfo],
 )
diff --git a/shell/sh_binary_info.bzl b/shell/sh_binary_info.bzl
new file mode 100644
index 0000000..51d7e19
--- /dev/null
+++ b/shell/sh_binary_info.bzl
@@ -0,0 +1,21 @@
+# Copyright 2026 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.
+
+"""ShInfo provider definition."""
+
+load("//shell/private:providers.bzl", _ShBinaryInfo = "ShBinaryInfo")
+
+visibility("public")
+
+ShBinaryInfo = _ShBinaryInfo
diff --git a/shell/sh_info.bzl b/shell/sh_info.bzl
new file mode 100644
index 0000000..87faf54
--- /dev/null
+++ b/shell/sh_info.bzl
@@ -0,0 +1,21 @@
+# Copyright 2026 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.
+
+"""ShInfo provider definition."""
+
+load("//shell/private:providers.bzl", _ShInfo = "ShInfo")
+
+visibility("public")
+
+ShInfo = _ShInfo