diff --git a/src/analyzer.rs b/src/analyzer.rs index bc6cbd56a6..4c122d67be 100644 --- a/src/analyzer.rs +++ b/src/analyzer.rs @@ -169,6 +169,10 @@ impl<'run, 'src> Analyzer<'run, 'src> { unstable_features.insert(UnstableFeature::ScriptInterpreterSetting); } + if settings.windows_script_interpreter.is_some() { + unstable_features.insert(UnstableFeature::WindowsScriptInterpreterSetting); + } + let source = root.to_owned(); let root = paths.get(root).unwrap(); diff --git a/src/keyword.rs b/src/keyword.rs index 7cae3606fa..fb392e0106 100644 --- a/src/keyword.rs +++ b/src/keyword.rs @@ -31,6 +31,7 @@ pub(crate) enum Keyword { Unexport, Unstable, WindowsPowershell, + WindowsScriptInterpreter, WindowsShell, WorkingDirectory, X, diff --git a/src/node.rs b/src/node.rs index 02aa2d0c7c..97dccf1ebf 100644 --- a/src/node.rs +++ b/src/node.rs @@ -317,6 +317,7 @@ impl<'src> Node<'src> for Set<'src> { } Setting::ScriptInterpreter(Interpreter { command, arguments }) | Setting::Shell(Interpreter { command, arguments }) + | Setting::WindowsScriptInterpreter(Interpreter { command, arguments }) | Setting::WindowsShell(Interpreter { command, arguments }) => { set.push_mut(Tree::string(&command.cooked)); for argument in arguments { diff --git a/src/parser.rs b/src/parser.rs index f00d87f085..a609785d38 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1146,6 +1146,9 @@ impl<'run, 'src> Parser<'run, 'src> { Keyword::ScriptInterpreter => Some(Setting::ScriptInterpreter(self.parse_interpreter()?)), Keyword::Shell => Some(Setting::Shell(self.parse_interpreter()?)), Keyword::Tempdir => Some(Setting::Tempdir(self.parse_string_literal()?)), + Keyword::WindowsScriptInterpreter => { + Some(Setting::WindowsScriptInterpreter(self.parse_interpreter()?)) + } Keyword::WindowsShell => Some(Setting::WindowsShell(self.parse_interpreter()?)), Keyword::WorkingDirectory => Some(Setting::WorkingDirectory(self.parse_string_literal()?)), _ => None, diff --git a/src/recipe.rs b/src/recipe.rs index 11b8f2edac..ba363f651a 100644 --- a/src/recipe.rs +++ b/src/recipe.rs @@ -395,6 +395,11 @@ impl<'src, D> Recipe<'src, D> { Executor::Command( interpreter .as_ref() + .or(if cfg!(windows) { + context.module.settings.windows_script_interpreter.as_ref() + } else { + None + }) .or(context.module.settings.script_interpreter.as_ref()) .unwrap_or_else(|| Interpreter::default_script_interpreter()), ) diff --git a/src/setting.rs b/src/setting.rs index 211ba9d319..8aeaec83ac 100644 --- a/src/setting.rs +++ b/src/setting.rs @@ -20,6 +20,7 @@ pub(crate) enum Setting<'src> { Tempdir(StringLiteral<'src>), Unstable(bool), WindowsPowerShell(bool), + WindowsScriptInterpreter(Interpreter<'src>), WindowsShell(Interpreter<'src>), WorkingDirectory(StringLiteral<'src>), } @@ -40,7 +41,10 @@ impl Display for Setting<'_> { | Self::Quiet(value) | Self::Unstable(value) | Self::WindowsPowerShell(value) => write!(f, "{value}"), - Self::ScriptInterpreter(shell) | Self::Shell(shell) | Self::WindowsShell(shell) => { + Self::ScriptInterpreter(shell) + | Self::Shell(shell) + | Self::WindowsScriptInterpreter(shell) + | Self::WindowsShell(shell) => { write!(f, "[{shell}]") } Self::DotenvFilename(value) diff --git a/src/settings.rs b/src/settings.rs index 1b352d61f6..49d5a8dcea 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -26,6 +26,8 @@ pub(crate) struct Settings<'src> { pub(crate) tempdir: Option, pub(crate) unstable: bool, pub(crate) windows_powershell: bool, + #[serde(skip)] + pub(crate) windows_script_interpreter: Option>, pub(crate) windows_shell: Option>, pub(crate) working_directory: Option, } @@ -87,6 +89,9 @@ impl<'src> Settings<'src> { Setting::WindowsPowerShell(windows_powershell) => { settings.windows_powershell = windows_powershell; } + Setting::WindowsScriptInterpreter(windows_script_interpreter) => { + settings.windows_script_interpreter = Some(windows_script_interpreter); + } Setting::WindowsShell(windows_shell) => { settings.windows_shell = Some(windows_shell); } diff --git a/src/unstable_feature.rs b/src/unstable_feature.rs index daa2270b1e..7bec680c61 100644 --- a/src/unstable_feature.rs +++ b/src/unstable_feature.rs @@ -7,6 +7,7 @@ pub(crate) enum UnstableFeature { ScriptAttribute, ScriptInterpreterSetting, WhichFunction, + WindowsScriptInterpreterSetting, } impl Display for UnstableFeature { @@ -22,6 +23,12 @@ impl Display for UnstableFeature { write!(f, "The `script-interpreter` setting is currently unstable.") } Self::WhichFunction => write!(f, "The `which()` function is currently unstable."), + Self::WindowsScriptInterpreterSetting => { + write!( + f, + "The `windows-script-interpreter` setting is currently unstable." + ) + } } } } diff --git a/tests/lib.rs b/tests/lib.rs index 450ac29525..1245b87844 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -133,6 +133,8 @@ mod which_function; #[cfg(windows)] mod windows; #[cfg(target_family = "windows")] +mod windows_script_interpreter; +#[cfg(target_family = "windows")] mod windows_shell; mod working_directory; diff --git a/tests/windows_script_interpreter.rs b/tests/windows_script_interpreter.rs new file mode 100644 index 0000000000..0c39143884 --- /dev/null +++ b/tests/windows_script_interpreter.rs @@ -0,0 +1,48 @@ +use super::*; + +#[test] +fn windows_script_interpreter_setting() { + Test::new() + .justfile( + r#" + set unstable + set windows-script-interpreter := ["pwsh.exe", "-NoLogo", "-Command"] + set script-interpreter := ["asdfasdfasdfasdf"] + + [script] + foo: + Write-Output bar + "#, + ) + .shell(false) + .stdout("bar\r\n") + .run(); +} + +#[test] +fn script_interpreter_setting_is_unstable() { + Test::new() + .justfile("set windows-script-interpreter := ['sh']") + .status(EXIT_FAILURE) + .stderr_regex(r"error: The `windows-script-interpreter` setting is currently unstable\..*") + .run(); +} + +#[test] +fn overrides_script_interpreter() { + Test::new() + .justfile( + r#" + set unstable + set script-interpreter := ["cmd.exe", "/c"] + set windows-script-interpreter := ["pwsh.exe", "-NoLogo", "-Command"] + + [script] + foo: + Write-Output bar + "#, + ) + .shell(false) + .stdout("bar\r\n") + .run(); +}