diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/D8.cs b/src/Xamarin.Android.Build.Tasks/Tasks/D8.cs index 797d63e657c..78717d986d5 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/D8.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/D8.cs @@ -80,7 +80,7 @@ protected virtual CommandLineBuilder GetCommandLineBuilder () // Create response file with all D8/R8 arguments to avoid command line length limits responseFilePath = CreateResponseFile (); - cmd.AppendSwitch ($"@{responseFilePath}"); + cmd.AppendSwitch ($"\"@{responseFilePath}\""); return cmd; } @@ -91,7 +91,7 @@ protected virtual CommandLineBuilder GetCommandLineBuilder () /// protected virtual string CreateResponseFile () { - var responseFile = Path.GetTempFileName (); + var responseFile = CreateResponseFilePath (); Log.LogDebugMessage ($"[{MainClass}] response file: {responseFile}"); using var response = new StreamWriter (responseFile, append: false, encoding: Files.UTF8withoutBOM); @@ -170,6 +170,8 @@ protected virtual string CreateResponseFile () return responseFile; } + protected virtual string CreateResponseFilePath () => Path.GetTempFileName (); + /// /// Writes a single argument to the response file. /// R8/D8 response files treat each line as a complete argument, so no quoting is needed. diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/JavaSourceUtils.cs b/src/Xamarin.Android.Build.Tasks/Tasks/JavaSourceUtils.cs index 4809a4bac60..55be2657710 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/JavaSourceUtils.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/JavaSourceUtils.cs @@ -90,7 +90,7 @@ protected override string GenerateCommandLineCommands () // Arguments sent to java.exe cmd.AppendSwitchIfNotNull ("-jar ", JavaSourceUtilsJar); - cmd.AppendSwitch ($"@{responseFilePath}"); + cmd.AppendSwitch ($"\"@{responseFilePath}\""); return cmd.ToString (); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/D8Tests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/D8Tests.cs index f86c0b4f9d4..7ed141a91f0 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/D8Tests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/D8Tests.cs @@ -6,6 +6,7 @@ using Microsoft.Build.Utilities; using NUnit.Framework; using Xamarin.Android.Tasks; +using Xamarin.Android.Tools; namespace Xamarin.Android.Build.Tests { @@ -73,7 +74,7 @@ public void ResponseFileContainsLibAndInputJars () FileAssert.Exists (responseFilePath, "Response file should exist"); // Verify the response file is referenced in the command line - Assert.IsTrue (commandLine.Contains ($"@{responseFilePath}"), "Command line should reference the response file"); + Assert.IsTrue (commandLine.Contains ($"\"@{responseFilePath}\""), "Command line should quote the response file"); // Read and verify response file content string [] responseFileContent = File.ReadAllLines (responseFilePath); @@ -116,16 +117,19 @@ public void ResponseFileHandlesPathsWithSpaces () JarPath = "d8.jar", JavaPlatformJarPath = platformJar, OutputDirectory = tempDir, + ResponseFileDirectory = pathWithSpaces, JavaLibrariesToEmbed = new ITaskItem [] { new TaskItem (inputJar), }, }; - d8Task.TestGenerateCommandLineCommands (); + string commandLine = d8Task.TestGenerateCommandLineCommands (); string responseFilePath = d8Task.ResponseFilePath; try { + Assert.IsNotNull (responseFilePath, "Response file path should not be null"); FileAssert.Exists (responseFilePath, "Response file should exist"); + Assert.IsTrue (commandLine.Contains ($"\"@{responseFilePath}\""), "Command line should quote a response file path containing spaces"); string responseFileContent = File.ReadAllText (responseFilePath); // Paths with spaces should NOT be quoted (R8/D8 treats each line as a complete argument) @@ -150,30 +154,30 @@ internal class D8TestTask : D8 /// public string ResponseFilePath { get; private set; } + public string ResponseFileDirectory { get; set; } + + protected override string CreateResponseFilePath () + { + if (!ResponseFileDirectory.IsNullOrEmpty ()) { + return Path.Combine (ResponseFileDirectory, Path.GetRandomFileName ()); + } + return base.CreateResponseFilePath (); + } + + protected override string CreateResponseFile () + { + var responseFile = base.CreateResponseFile (); + ResponseFilePath = responseFile; + return responseFile; + } + /// /// Test method that generates command line without actually running the task. /// public string TestGenerateCommandLineCommands () { var cmd = GetCommandLineBuilder (); - // Capture the response file path after command line generation - ResponseFilePath = GetResponseFilePathFromCommandLine (cmd.ToString ()); return cmd.ToString (); } - - private static string GetResponseFilePathFromCommandLine (string commandLine) - { - // Find the @filepath argument - var startIndex = commandLine.IndexOf ("@", StringComparison.Ordinal); - if (startIndex < 0) return null; - - // Check if '@' is the last character - if (startIndex + 1 >= commandLine.Length) return null; - - var endIndex = commandLine.IndexOf (" ", startIndex, StringComparison.Ordinal); - if (endIndex < 0) endIndex = commandLine.Length; - - return commandLine.Substring (startIndex + 1, endIndex - startIndex - 1); - } } }