fix: make sphinxdocs support directory inputs (#2375)

The logic to relocate files assumed that all the inputs were plain file
artifacts. When
a directory artifact was used, then `ctx.actions.symlink()` would fail
because it requires
the output artifact and input target artifact to be the same type of
file (plain file or
directory).

To fix, use `File.is_directory` to detect if the input is a directory or
file, then call
`declare_file()` or `declare_directory()` as appropriate. The later
`symlink()` call is
then happy the two args match.

Fixes https://github.com/bazelbuild/rules_python/issues/2374
diff --git a/sphinxdocs/private/sphinx.bzl b/sphinxdocs/private/sphinx.bzl
index 2ee6cfc..678d01b 100644
--- a/sphinxdocs/private/sphinx.bzl
+++ b/sphinxdocs/private/sphinx.bzl
@@ -325,7 +325,12 @@
     def _relocate(source_file, dest_path = None):
         if not dest_path:
             dest_path = source_file.short_path.removeprefix(ctx.attr.strip_prefix)
-        dest_file = ctx.actions.declare_file(paths.join(source_prefix, dest_path))
+
+        dest_path = paths.join(source_prefix, dest_path)
+        if source_file.is_directory:
+            dest_file = ctx.actions.declare_directory(dest_path)
+        else:
+            dest_file = ctx.actions.declare_file(dest_path)
         ctx.actions.symlink(
             output = dest_file,
             target_file = source_file,
diff --git a/sphinxdocs/tests/sphinx_docs/BUILD.bazel b/sphinxdocs/tests/sphinx_docs/BUILD.bazel
new file mode 100644
index 0000000..1a05db0
--- /dev/null
+++ b/sphinxdocs/tests/sphinx_docs/BUILD.bazel
@@ -0,0 +1,45 @@
+load("@bazel_skylib//rules:build_test.bzl", "build_test")
+load("//python/private:util.bzl", "IS_BAZEL_7_OR_HIGHER")  # buildifier: disable=bzl-visibility
+load("//sphinxdocs:sphinx.bzl", "sphinx_build_binary", "sphinx_docs")
+load(":defs.bzl", "gen_directory")
+
+# We only build for Linux and Mac because:
+# 1. The actual doc process only runs on Linux
+# 2. Mac is a common development platform, and is close enough to Linux
+#    it's feasible to make work.
+# Making CI happy under Windows is too much of a headache, though, so we don't
+# bother with that.
+_TARGET_COMPATIBLE_WITH = select({
+    "@platforms//os:linux": [],
+    "@platforms//os:macos": [],
+    "//conditions:default": ["@platforms//:incompatible"],
+}) if IS_BAZEL_7_OR_HIGHER else ["@platforms//:incompatible"]
+
+sphinx_docs(
+    name = "docs",
+    srcs = glob(["*.md"]) + [
+        ":generated_directory",
+    ],
+    config = "conf.py",
+    formats = ["html"],
+    sphinx = ":sphinx-build",
+    target_compatible_with = _TARGET_COMPATIBLE_WITH,
+)
+
+gen_directory(
+    name = "generated_directory",
+)
+
+sphinx_build_binary(
+    name = "sphinx-build",
+    tags = ["manual"],  # Only needed as part of sphinx doc building
+    deps = [
+        "@dev_pip//myst_parser",
+        "@dev_pip//sphinx",
+    ],
+)
+
+build_test(
+    name = "build_tests",
+    targets = [":docs"],
+)
diff --git a/sphinxdocs/tests/sphinx_docs/conf.py b/sphinxdocs/tests/sphinx_docs/conf.py
new file mode 100644
index 0000000..d96fa36
--- /dev/null
+++ b/sphinxdocs/tests/sphinx_docs/conf.py
@@ -0,0 +1,15 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# For the full list of built-in configuration values, see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Project info
+
+project = "Sphinx Docs Test"
+
+extensions = [
+    "myst_parser",
+]
+myst_enable_extensions = [
+    "colon_fence",
+]
diff --git a/sphinxdocs/tests/sphinx_docs/defs.bzl b/sphinxdocs/tests/sphinx_docs/defs.bzl
new file mode 100644
index 0000000..2e47ecc
--- /dev/null
+++ b/sphinxdocs/tests/sphinx_docs/defs.bzl
@@ -0,0 +1,19 @@
+"""Supporting code for tests."""
+
+def _gen_directory_impl(ctx):
+    out = ctx.actions.declare_directory(ctx.label.name)
+
+    ctx.actions.run_shell(
+        outputs = [out],
+        command = """
+echo "# Hello" > {outdir}/index.md
+""".format(
+            outdir = out.path,
+        ),
+    )
+
+    return [DefaultInfo(files = depset([out]))]
+
+gen_directory = rule(
+    implementation = _gen_directory_impl,
+)
diff --git a/sphinxdocs/tests/sphinx_docs/index.md b/sphinxdocs/tests/sphinx_docs/index.md
new file mode 100644
index 0000000..cdce641
--- /dev/null
+++ b/sphinxdocs/tests/sphinx_docs/index.md
@@ -0,0 +1,8 @@
+# Sphinx docs test
+
+:::{toctree}
+:glob:
+
+**
+genindex
+:::