Avoid uses of shell to improve Windows tests (#2912)

diff --git a/test/dep_env/BUILD.bazel b/test/dep_env/BUILD.bazel
index 82acfbb..994731e 100644
--- a/test/dep_env/BUILD.bazel
+++ b/test/dep_env/BUILD.bazel
@@ -113,4 +113,5 @@
     srcs = ["dep_dir"],
     outs = ["set_dep_dir.env"],
     cmd = "echo DEP_DIR=\\$${pwd}/$(execpath dep_dir) > $@",
+    cmd_bat = "echo DEP_DIR=$${pwd}/$(execpath dep_dir) > $@",
 )
diff --git a/test/dep_env/dep_env.bzl b/test/dep_env/dep_env.bzl
index f571fa6..84f96a3 100644
--- a/test/dep_env/dep_env.bzl
+++ b/test/dep_env/dep_env.bzl
@@ -1,14 +1,59 @@
-"""Tests for passing configuration to cargo_build_script rules"""
+"""Tests for passing configuration to cargo_dep_env rules"""
 
-def _create_dep_dir(ctx):
-    out = ctx.actions.declare_directory("dep_dir")
-    ctx.actions.run_shell(
-        outputs = [out],
-        arguments = [out.path],
-        command = 'echo contents > "$@/a_file"',
+_SH_WRITER = """\
+#!/usr/bin/env bash
+set -euo pipefail
+echo content > $@
+"""
+
+_BAT_WRITER = """\
+@ECHO OFF
+echo content > %*
+"""
+
+def _create_dep_dir_impl(ctx):
+    is_windows = ctx.attr.platform.values()[0] == "windows"
+    writer = ctx.actions.declare_file("{}.writer.{}".format(
+        ctx.label.name,
+        ".bat" if is_windows else ".sh",
+    ))
+    ctx.actions.write(
+        output = writer,
+        content = _BAT_WRITER if is_windows else _SH_WRITER,
+        is_executable = True,
     )
+
+    out = ctx.actions.declare_directory("{}.dep_dir".format(ctx.label.name))
+    ctx.actions.run(
+        executable = writer,
+        outputs = [out],
+        arguments = [
+            "{}\\a_file".format(out.path) if is_windows else "{}/a_file".format(out.path),
+        ],
+    )
+
     return [DefaultInfo(files = depset(direct = [out]))]
 
-create_dep_dir = rule(
-    implementation = _create_dep_dir,
+_create_dep_dir = rule(
+    implementation = _create_dep_dir_impl,
+    attrs = {
+        "platform": attr.label_keyed_string_dict(
+            doc = "The name of the exec platform",
+            cfg = "exec",
+            mandatory = True,
+        ),
+    },
 )
+
+def create_dep_dir(name, **kwargs):
+    native.filegroup(
+        name = "{}_".format(name),
+    )
+    _create_dep_dir(
+        name = name,
+        platform = select({
+            "@platforms//os:windows": {":{}_".format(name): "windows"},
+            "//conditions:default": {":{}_".format(name): "unix"},
+        }),
+        **kwargs
+    )
diff --git a/test/dep_env/read_b.rs b/test/dep_env/read_b.rs
index b63db42..ad89fdc 100644
--- a/test/dep_env/read_b.rs
+++ b/test/dep_env/read_b.rs
@@ -1,5 +1,5 @@
 use std::env::var;
 
 fn main() {
-    assert_eq!(var("DEP_Y_B").unwrap(), "b_value");
+    assert_eq!(var("DEP_Y_B").unwrap().trim(), "b_value");
 }
diff --git a/test/dep_env/read_dep_dir.rs b/test/dep_env/read_dep_dir.rs
index c9a82ed..a9484ba 100644
--- a/test/dep_env/read_dep_dir.rs
+++ b/test/dep_env/read_dep_dir.rs
@@ -1,9 +1,18 @@
-use std::{env::var, fs, io::Result};
+use std::env::var;
+use std::fs;
+use std::io::Result;
+use std::path::PathBuf;
 
 fn main() {
-    let dep_dir = &var("DEP_DIR").expect("DEP_DIR should be set");
-    let entries = fs::read_dir(dep_dir)
-        .expect("Failed to open DEP_DIR directory")
+    let dep_dir = PathBuf::from(var("DEP_DIR").expect("DEP_DIR should be set").trim());
+    let entries = fs::read_dir(&dep_dir)
+        .unwrap_or_else(|e| {
+            panic!(
+                "Failed to open DEP_DIR directory: {}\n{:?}",
+                dep_dir.display(),
+                e
+            )
+        })
         .collect::<Result<Vec<_>>>()
         .expect("Failed to read DEP_DIR directory entries");
     let entries = entries
diff --git a/test/toolchain/BUILD.bazel b/test/toolchain/BUILD.bazel
index 6400af9..bc8e011 100644
--- a/test/toolchain/BUILD.bazel
+++ b/test/toolchain/BUILD.bazel
@@ -9,6 +9,7 @@
     # rustc assumes it can read things like librustc_driver in order to run this command.
     # This is a test to ensure we supply all of the files rustc needs as part of the current_rust_toolchain.
     cmd = "$(RUSTC) --print=cfg > $@",
+    cmd_bat = "$(RUSTC) --print=cfg > $@",
     toolchains = ["@rules_rust//rust/toolchain:current_rust_toolchain"],
     tools = ["@rules_rust//rust/toolchain:current_rust_toolchain"],
 )
diff --git a/test/unit/location_expansion/location_expansion_test.bzl b/test/unit/location_expansion/location_expansion_test.bzl
index a58d974..96b6bf8 100644
--- a/test/unit/location_expansion/location_expansion_test.bzl
+++ b/test/unit/location_expansion/location_expansion_test.bzl
@@ -1,6 +1,7 @@
 """Unittest to verify location expansion in rustc flags"""
 
 load("@bazel_skylib//lib:unittest.bzl", "analysistest")
+load("@bazel_skylib//rules:write_file.bzl", "write_file")
 load("//rust:defs.bzl", "rust_library")
 load("//test/unit:common.bzl", "assert_action_mnemonic", "assert_argv_contains")
 
@@ -17,10 +18,14 @@
 location_expansion_rustc_flags_test = analysistest.make(_location_expansion_rustc_flags_test)
 
 def _location_expansion_test():
-    native.genrule(
+    write_file(
         name = "flag_generator",
-        outs = ["generated_flag.data"],
-        cmd = "echo --cfg=test_flag > $@",
+        out = "generated_flag.data",
+        content = [
+            "--cfg=test_flag",
+            "",
+        ],
+        newline = "unix",
     )
 
     rust_library(
diff --git a/test/unit/pipelined_compilation/wrap.bzl b/test/unit/pipelined_compilation/wrap.bzl
index 09c2a6c..fb4c6db 100644
--- a/test/unit/pipelined_compilation/wrap.bzl
+++ b/test/unit/pipelined_compilation/wrap.bzl
@@ -6,21 +6,21 @@
 # buildifier: disable=bzl-visibility
 load("//rust/private:rustc.bzl", "rustc_compile_action")
 
-def _wrap_impl(ctx):
-    rs_file = ctx.actions.declare_file(ctx.label.name + "_wrapped.rs")
-    crate_name = ctx.attr.crate_name if ctx.attr.crate_name else ctx.label.name
-    ctx.actions.run_shell(
-        outputs = [rs_file],
-        command = """cat <<EOF > {}
+_CONTENT = """\
 // crate_name: {}
 use to_wrap::to_wrap;
 
 pub fn wrap() {{
     to_wrap();
 }}
-EOF
-""".format(rs_file.path, crate_name),
-        mnemonic = "WriteWrapperRsFile",
+"""
+
+def _wrap_impl(ctx):
+    rs_file = ctx.actions.declare_file(ctx.label.name + "_wrapped.rs")
+    crate_name = ctx.attr.crate_name if ctx.attr.crate_name else ctx.label.name
+    ctx.actions.write(
+        output = rs_file,
+        content = _CONTENT.format(crate_name),
     )
 
     toolchain = ctx.toolchains[Label("//rust:toolchain")]
diff --git a/test/unit/toolchain/subpackage/BUILD.bazel b/test/unit/toolchain/subpackage/BUILD.bazel
index 0c8ace8..e8f3d67 100644
--- a/test/unit/toolchain/subpackage/BUILD.bazel
+++ b/test/unit/toolchain/subpackage/BUILD.bazel
@@ -1,7 +1,10 @@
-genrule(
+load("@bazel_skylib//rules:write_file.bzl", "write_file")
+
+write_file(
     name = "dummy_rlib",
-    outs = ["core.rlib"],
-    cmd = "touch $@",
+    out = "core.rlib",
+    content = [],
+    newline = "unix",
 )
 
 filegroup(