Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/mono/wasm/Wasm.Build.Tests/NativeBuildTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,42 @@ public async Task SimpleNativeBuild(Configuration config, bool aot)
await RunForPublishWithWebServer(new BrowserRunOptions(config, ExpectedExitCode: 42));
}

[Theory]
[BuildAndRun(aot: false)]
[TestCategory("native-mono")]
[SkipOnPlatform(TestPlatforms.AnyUnix, "The cmd.exe quoting behavior this covers is Windows-specific.")]
public async Task NativeBuildWithSpecialCharsInTempPath(Configuration config, bool aot)
{
// Regression test for https://github.com/dotnet/runtime/issues/120327.
// Native compilation runs the compiler through a temporary batch file created under the
// temp directory. Windows user profile names can contain parentheses (e.g. "John(US)"),
// which puts parentheses in %TEMP%. `cmd /c "<path>"` then stripped the quotes around
// that path and mis-parsed it at the first '(', failing the native build with
// "'C:\Users\John' is not recognized as an internal or external command".
// The unicode chars additionally cover the UTF-8 (chcp 65001) handling in the same
// RunShellCommand path, which exists so non-ASCII (e.g. GB18030) temp/user paths work.
ProjectInfo info = CreateWasmTemplateProject(
Template.WasmBrowser,
config,
aot,
"parens_temp",
extraProperties: "<WasmBuildNative>true</WasmBuildNative>");

UpdateBrowserProgramFile();
ReplaceMainJsWithMinimalRunMain();

string tempWithParens = Path.Combine(BuildEnvironment.TmpPath, $"tmp ({GetRandomId()}) {s_unicodeChars}");
Directory.CreateDirectory(tempWithParens);
var envVars = new Dictionary<string, string>
{
["TMP"] = tempWithParens,
["TEMP"] = tempWithParens,
};

PublishProject(info, config, new PublishOptions(ExtraBuildEnvironmentVariables: envVars), isNativeBuild: true);
await RunForPublishWithWebServer(new BrowserRunOptions(config, ExpectedExitCode: 42));
}

[Theory]
[BuildAndRun(aot: true)]
[TestCategory("native-mono")]
Expand Down
8 changes: 7 additions & 1 deletion src/tasks/Common/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,14 @@ public static (int exitCode, string output) RunShellCommand(
string? label=null)
{
string scriptFileName = CreateTemporaryBatchFile(command);
// The script path lives under the temp directory, which is typically inside the user
// profile (e.g. C:\Users\John(US)\AppData\Local\Temp\...). If that path contains cmd
// special characters such as '(' or ')', `cmd /c "<path>"` strips the surrounding quotes
// (because it sees special chars between the two quotes) and then mis-parses the now
// unquoted path at the first parenthesis. Use `/S` together with an extra pair of quotes
// so cmd strips only the outermost quotes and runs the still-quoted path verbatim.
(string shell, string args) = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? ("cmd", $"/c \"{scriptFileName}\"")
? ("cmd", $"/S /c \"\"{scriptFileName}\"\"")
: ("/bin/sh", $"\"{scriptFileName}\"");

string msgPrefix = label == null ? string.Empty : $"[{label}] ";
Expand Down
Loading