Remove use of run_shell (#4252)

Avoiding the use of shell on Windows
diff --git a/test/root_path/BUILD.bazel b/test/root_path/BUILD.bazel
index 7c59ac7..9cb6a26 100644
--- a/test/root_path/BUILD.bazel
+++ b/test/root_path/BUILD.bazel
@@ -1,10 +1,16 @@
 load("@bazel_skylib//rules:build_test.bzl", "build_test")
 load("//cargo:defs.bzl", "cargo_build_script")
-load("//rust:defs.bzl", "rust_library", "rust_test")
+load("//rust:defs.bzl", "rust_binary", "rust_library", "rust_test")
 load(":defs.bzl", "package_dir_artifact")
 
 package(default_visibility = ["//visibility:private"])
 
+rust_binary(
+    name = "dir_artifact_packager",
+    srcs = ["dir_artifact_packager.rs"],
+    edition = "2021",
+)
+
 package_dir_artifact(
     name = "dir_artifact",
     srcs = [
diff --git a/test/root_path/defs.bzl b/test/root_path/defs.bzl
index e56b98b..a157513 100644
--- a/test/root_path/defs.bzl
+++ b/test/root_path/defs.bzl
@@ -1,18 +1,18 @@
-"""Custom rule to package sources into a directory TreeArtifact for testing root_path."""
+"""package_dir_artifact"""
 
 def _package_dir_artifact_impl(ctx):
     outdir = ctx.actions.declare_directory(ctx.attr.name + ".dir")
 
     args = ctx.actions.args()
-    args.add(outdir.path)
-    for src in ctx.files.srcs:
-        args.add(src.path)
+    args.add_all([outdir], expand_directories = False)
+    args.add_all(ctx.files.srcs)
 
-    ctx.actions.run_shell(
+    ctx.actions.run(
+        executable = ctx.executable._packager,
         outputs = [outdir],
         inputs = ctx.files.srcs,
-        command = 'out="$1"; shift; mkdir -p "$out/src"; cp "$@" "$out/src/"',
         arguments = [args],
+        mnemonic = "PackageDirArtifact",
         progress_message = "Packaging srcs into directory artifact %s" % outdir.short_path,
     )
 
@@ -21,11 +21,17 @@
     ]
 
 package_dir_artifact = rule(
+    doc = "Custom rule to package sources into a directory TreeArtifact for testing root_path",
     implementation = _package_dir_artifact_impl,
     attrs = {
         "srcs": attr.label_list(
             allow_files = True,
             mandatory = True,
         ),
+        "_packager": attr.label(
+            default = Label("//test/root_path:dir_artifact_packager"),
+            executable = True,
+            cfg = "exec",
+        ),
     },
 )
diff --git a/test/root_path/dir_artifact_packager.rs b/test/root_path/dir_artifact_packager.rs
new file mode 100644
index 0000000..6214a42
--- /dev/null
+++ b/test/root_path/dir_artifact_packager.rs
@@ -0,0 +1,20 @@
+//! A tool which copies source files into the `src` subdirectory of a directory artifact.
+
+use std::path::PathBuf;
+
+fn main() {
+    let mut args = std::env::args_os().skip(1).map(PathBuf::from);
+
+    let outdir = args.next().expect("No output directory was provided");
+    let dest = outdir.join("src");
+    std::fs::create_dir_all(&dest)
+        .unwrap_or_else(|e| panic!("Failed to create `{}`\n{:?}", dest.display(), e));
+
+    for src in args {
+        let name = src
+            .file_name()
+            .unwrap_or_else(|| panic!("Source `{}` has no file name", src.display()));
+        std::fs::copy(&src, dest.join(name))
+            .unwrap_or_else(|e| panic!("Failed to copy `{}`\n{:?}", src.display(), e));
+    }
+}