From abe7a325bb7510568e4360f1d5739cde26d2f619 Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Thu, 26 Mar 2026 13:52:13 +0100 Subject: [PATCH 1/8] EmccCompile: prefer TryRunProcess over RunShellCommand. A shell is not needed to run emcc. --- src/tasks/WasmAppBuilder/EmccCompile.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/tasks/WasmAppBuilder/EmccCompile.cs b/src/tasks/WasmAppBuilder/EmccCompile.cs index e93e77203ae30b..d8f70ff6722513 100644 --- a/src/tasks/WasmAppBuilder/EmccCompile.cs +++ b/src/tasks/WasmAppBuilder/EmccCompile.cs @@ -209,19 +209,21 @@ bool ProcessSourceFile(string srcFile, string objFile) string tmpObjFile = Path.GetTempFileName(); try { - string command = $"\"{CompilerBinaryPath}\" {Arguments} -c -o \"{tmpObjFile}\" \"{srcFile}\""; + string args = $"{Arguments} -c -o \"{tmpObjFile}\" \"{srcFile}\""; var startTime = DateTime.Now; // Log the command in a compact format which can be copy pasted StringBuilder envStr = new StringBuilder(string.Empty); foreach (var key in envVarsDict.Keys) envStr.Append($"{key}={envVarsDict[key]} "); - Log.LogMessage(MessageImportance.Low, $"Exec: {envStr}{command}"); - (int exitCode, string output) = Utils.RunShellCommand( + Log.LogMessage(MessageImportance.Low, $"Exec: {envStr}\"{CompilerBinaryPath}\" {args}"); + (int exitCode, string output) = Utils.TryRunProcess( Log, - command, + CompilerBinaryPath, + args, envVarsDict, workingDir: Environment.CurrentDirectory, + silent: false, logStdErrAsMessage: true, debugMessageImportance: messageImportance, label: Path.GetFileName(srcFile)); From b9863bf8d8177377c8ffb4873197dc3bd88efb93 Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Thu, 26 Mar 2026 16:47:00 +0100 Subject: [PATCH 2/8] Remove RunShellCommand. --- src/tasks/Common/Utils.cs | 55 --------------------------------------- 1 file changed, 55 deletions(-) diff --git a/src/tasks/Common/Utils.cs b/src/tasks/Common/Utils.cs index 97be25c8559d38..20c7c98d26c6a3 100644 --- a/src/tasks/Common/Utils.cs +++ b/src/tasks/Common/Utils.cs @@ -48,61 +48,6 @@ public static bool IsNewerThan(string inFile, string outFile) => !File.Exists(inFile) || !File.Exists(outFile) || (File.GetLastWriteTimeUtc(inFile) > File.GetLastWriteTimeUtc(outFile)); - public static (int exitCode, string output) RunShellCommand( - TaskLoggingHelper logger, - string command, - IDictionary envVars, - string workingDir, - bool silent=false, - bool logStdErrAsMessage=false, - MessageImportance debugMessageImportance=MessageImportance.Low, - string? label=null) - { - string scriptFileName = CreateTemporaryBatchFile(command); - (string shell, string args) = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) - ? ("cmd", $"/c \"{scriptFileName}\"") - : ("/bin/sh", $"\"{scriptFileName}\""); - - string msgPrefix = label == null ? string.Empty : $"[{label}] "; - logger.LogMessage(debugMessageImportance, $"{msgPrefix}Running {command} via script {scriptFileName}:", msgPrefix); - logger.LogMessage(debugMessageImportance, File.ReadAllText(scriptFileName), msgPrefix); - - return TryRunProcess(logger, - shell, - args, - envVars, - workingDir, - silent: silent, - logStdErrAsMessage: logStdErrAsMessage, - label: label, - debugMessageImportance: debugMessageImportance); - - static string CreateTemporaryBatchFile(string command) - { - string extn = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".cmd" : ".sh"; - string file = Path.Combine(Path.GetTempPath(), $"tmp{Guid.NewGuid():N}{extn}"); - - using StreamWriter sw = new(file); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - // set encoding to UTF-8 -> full Unicode support is needed for usernames - - // `command` contains tmp dir path with the username - sw.WriteLine(@"%SystemRoot%\System32\chcp.com 65001>nul"); - sw.WriteLine("setlocal"); - sw.WriteLine("set errorlevel=dummy"); - sw.WriteLine("set errorlevel="); - } - else - { - // Use sh rather than bash, as not all 'nix systems necessarily have Bash installed - sw.WriteLine("#!/bin/sh"); - } - - sw.WriteLine(command); - return file; - } - } - public static string RunProcess( TaskLoggingHelper logger, string path, From be862f8518431363c9f682aeefacbaf0000f5aed Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Fri, 27 Mar 2026 14:16:17 +0100 Subject: [PATCH 3/8] Fix Windows invocation. --- src/tasks/WasmAppBuilder/EmccCompile.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/tasks/WasmAppBuilder/EmccCompile.cs b/src/tasks/WasmAppBuilder/EmccCompile.cs index d8f70ff6722513..3209eb4956663b 100644 --- a/src/tasks/WasmAppBuilder/EmccCompile.cs +++ b/src/tasks/WasmAppBuilder/EmccCompile.cs @@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -217,9 +218,18 @@ bool ProcessSourceFile(string srcFile, string objFile) foreach (var key in envVarsDict.Keys) envStr.Append($"{key}={envVarsDict[key]} "); Log.LogMessage(MessageImportance.Low, $"Exec: {envStr}\"{CompilerBinaryPath}\" {args}"); + + // On Windows, the emsdk ships emcc.bat to invoke emcc. Use 'cmd' to execute the batch file. + string processPath = CompilerBinaryPath; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + args = $"/c \"{CompilerBinaryPath}\" {args}"; + processPath = "cmd"; + } + (int exitCode, string output) = Utils.TryRunProcess( Log, - CompilerBinaryPath, + processPath, args, envVarsDict, workingDir: Environment.CurrentDirectory, From a86a5b04c9e826e007bbdd7a2a8e041616f70222 Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Fri, 27 Mar 2026 15:45:03 +0100 Subject: [PATCH 4/8] Try fix cmd for Windows. --- src/tasks/WasmAppBuilder/EmccCompile.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/WasmAppBuilder/EmccCompile.cs b/src/tasks/WasmAppBuilder/EmccCompile.cs index 3209eb4956663b..a310a7eca6837c 100644 --- a/src/tasks/WasmAppBuilder/EmccCompile.cs +++ b/src/tasks/WasmAppBuilder/EmccCompile.cs @@ -223,7 +223,7 @@ bool ProcessSourceFile(string srcFile, string objFile) string processPath = CompilerBinaryPath; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - args = $"/c \"{CompilerBinaryPath}\" {args}"; + args = $"/c \"\"{CompilerBinaryPath}\" {args}\""; processPath = "cmd"; } From 20b75be83edf4c388b7947042018d5d1ff0d9be3 Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Mon, 30 Mar 2026 11:26:42 +0200 Subject: [PATCH 5/8] Support UTF-8 paths. --- src/tasks/Common/Utils.cs | 2 ++ src/tasks/WasmAppBuilder/EmccCompile.cs | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tasks/Common/Utils.cs b/src/tasks/Common/Utils.cs index 20c7c98d26c6a3..077b9c1bee83fc 100644 --- a/src/tasks/Common/Utils.cs +++ b/src/tasks/Common/Utils.cs @@ -95,6 +95,8 @@ public static (int, string) TryRunProcess( CreateNoWindow = true, RedirectStandardError = true, RedirectStandardOutput = true, + StandardOutputEncoding = Encoding.UTF8, + StandardErrorEncoding = Encoding.UTF8, RedirectStandardInput = inputProvider != null, Arguments = args, }; diff --git a/src/tasks/WasmAppBuilder/EmccCompile.cs b/src/tasks/WasmAppBuilder/EmccCompile.cs index a310a7eca6837c..e2c45c8eabba3b 100644 --- a/src/tasks/WasmAppBuilder/EmccCompile.cs +++ b/src/tasks/WasmAppBuilder/EmccCompile.cs @@ -220,10 +220,11 @@ bool ProcessSourceFile(string srcFile, string objFile) Log.LogMessage(MessageImportance.Low, $"Exec: {envStr}\"{CompilerBinaryPath}\" {args}"); // On Windows, the emsdk ships emcc.bat to invoke emcc. Use 'cmd' to execute the batch file. + // Switch to UTF-8 code page to enable cmd to handle non-ASCII paths. string processPath = CompilerBinaryPath; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - args = $"/c \"\"{CompilerBinaryPath}\" {args}\""; + args = $"/c \"chcp 65001 > nul && \"{CompilerBinaryPath}\" {args}\""; processPath = "cmd"; } From 49e8efb0604f6fdf7ca0a044f487dc216b485426 Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Mon, 30 Mar 2026 11:35:41 +0200 Subject: [PATCH 6/8] Ensure process stdin is not used, and ensure child doesn't wait for input by sending EOF. --- src/tasks/Common/Utils.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tasks/Common/Utils.cs b/src/tasks/Common/Utils.cs index 077b9c1bee83fc..03cfb931a7671d 100644 --- a/src/tasks/Common/Utils.cs +++ b/src/tasks/Common/Utils.cs @@ -95,9 +95,10 @@ public static (int, string) TryRunProcess( CreateNoWindow = true, RedirectStandardError = true, RedirectStandardOutput = true, + RedirectStandardInput = true, StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8, - RedirectStandardInput = inputProvider != null, + StandardInputEncoding = Encoding.UTF8, Arguments = args, }; @@ -155,6 +156,7 @@ public static (int, string) TryRunProcess( process.BeginOutputReadLine(); process.BeginErrorReadLine(); inputProvider?.Invoke(process.StandardInput.BaseStream); + process.StandardInput.Close(); process.WaitForExit(); logger.LogMessage(debugMessageImportance, $"{msgPrefix}Exit code: {process.ExitCode}"); From e82f913ccff973e73e94b9c833037497adf46185 Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Mon, 30 Mar 2026 11:55:58 +0200 Subject: [PATCH 7/8] Remove StandardInputEncoding which is not available on .NET Framework. --- src/tasks/Common/Utils.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tasks/Common/Utils.cs b/src/tasks/Common/Utils.cs index 03cfb931a7671d..d22984321a09a9 100644 --- a/src/tasks/Common/Utils.cs +++ b/src/tasks/Common/Utils.cs @@ -98,7 +98,6 @@ public static (int, string) TryRunProcess( RedirectStandardInput = true, StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8, - StandardInputEncoding = Encoding.UTF8, Arguments = args, }; From 11b51bdb51ad156c6c875b624ced3342cbae3b72 Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Wed, 22 Apr 2026 10:56:41 +0200 Subject: [PATCH 8/8] Use cmd /S flag for single pair quote stripping. --- src/tasks/WasmAppBuilder/EmccCompile.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/WasmAppBuilder/EmccCompile.cs b/src/tasks/WasmAppBuilder/EmccCompile.cs index e2c45c8eabba3b..6d06624425f9d5 100644 --- a/src/tasks/WasmAppBuilder/EmccCompile.cs +++ b/src/tasks/WasmAppBuilder/EmccCompile.cs @@ -224,7 +224,7 @@ bool ProcessSourceFile(string srcFile, string objFile) string processPath = CompilerBinaryPath; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - args = $"/c \"chcp 65001 > nul && \"{CompilerBinaryPath}\" {args}\""; + args = $"/S /c \"chcp 65001 > nul && \"{CompilerBinaryPath}\" {args}\""; processPath = "cmd"; }