From 85c6ce4576a3b9bc3b2317354c7d16ab44e3f7ad Mon Sep 17 00:00:00 2001 From: Dean Ellis Date: Tue, 14 Aug 2018 11:47:54 +0100 Subject: [PATCH] [Xamarin.Android.Build.Tasks] Use a Response file for AOT Fixes https://devdiv.visualstudio.com/DevDiv/_workitems/edit/609244 Windows has a limit on the length of command line arguments. As a result AOT can fail on windows if the project paths are too long. mono 2018-06 introduced a `--response=FILE` which allows us to provide all the options in a file rather than on the command line directly. --- src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs | 126 +++++++++--------- .../Xamarin.Android.Build.Tests/BuildTest.cs | 2 +- 2 files changed, 64 insertions(+), 64 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs b/src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs index c4486913c45..8bf5550f8ef 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs @@ -166,14 +166,6 @@ static string GetNdkToolchainLibraryDir (string binDir, AndroidTargetArch arch) return GetNdkToolchainLibraryDir (binDir, NdkUtil.GetArchDirName (arch)); } - static string GetShortPath (string path) - { - if (Environment.OSVersion.Platform != PlatformID.Win32NT) - return QuoteFileName (path); - var shortPath = KernelEx.GetShortPathName (Path.GetDirectoryName (path)); - return Path.Combine (shortPath, Path.GetFileName (path)); - } - static string QuoteFileName(string fileName) { var builder = new CommandLineBuilder(); @@ -385,7 +377,6 @@ IEnumerable GetAotConfigs () } catch (InvalidOperationException ex) { Diagnostic.Error (5101, ex.Message); } - string toolchainLibDir; if (NdkUtil.UsingClangNDK) toolchainLibDir = GetNdkToolchainLibraryDir (toolchainPath, arch); @@ -394,19 +385,19 @@ IEnumerable GetAotConfigs () var libs = new List(); if (NdkUtil.UsingClangNDK) { - libs.Add ($"-L{GetShortPath (toolchainLibDir)}"); - libs.Add ($"-L{GetShortPath (androidLibPath)}"); + libs.Add ($"-L{toolchainLibDir}"); + libs.Add ($"-L{androidLibPath}"); if (arch == AndroidTargetArch.Arm) { // Needed for -lunwind to work string compilerLibDir = Path.Combine (toolchainPath, "..", "sysroot", "usr", "lib", NdkUtil.GetArchDirName (arch)); - libs.Add ($"-L{GetShortPath (compilerLibDir)}"); + libs.Add ($"-L{compilerLibDir}"); } } - libs.Add (GetShortPath (Path.Combine (toolchainLibDir, "libgcc.a"))); - libs.Add (GetShortPath (Path.Combine (androidLibPath, "libc.so"))); - libs.Add (GetShortPath (Path.Combine (androidLibPath, "libm.so"))); + libs.Add (Path.Combine (toolchainLibDir, "libgcc.a")); + libs.Add (Path.Combine (androidLibPath, "libc.so")); + libs.Add (Path.Combine (androidLibPath, "libm.so")); ldFlags = string.Join(";", libs); } @@ -427,17 +418,17 @@ IEnumerable GetAotConfigs () if (!string.IsNullOrEmpty (AotAdditionalArguments)) aotOptions.Add (AotAdditionalArguments); if (sequencePointsMode == SequencePointsMode.Offline) - aotOptions.Add ("msym-dir=" + GetShortPath (outdir)); + aotOptions.Add ("msym-dir=" + QuoteFileName (outdir)); if (AotMode != AotMode.Normal) aotOptions.Add (AotMode.ToString ().ToLowerInvariant ()); - aotOptions.Add ("outfile=" + GetShortPath (outputFile)); + aotOptions.Add ("outfile=" + QuoteFileName (outputFile)); aotOptions.Add ("asmwriter"); aotOptions.Add ("mtriple=" + mtriple); - aotOptions.Add ("tool-prefix=" + GetShortPath (toolPrefix)); + aotOptions.Add ("tool-prefix=" + QuoteFileName (toolPrefix)); aotOptions.Add ("ld-flags=" + ldFlags); - aotOptions.Add ("llvm-path=" + GetShortPath (sdkBinDirectory)); - aotOptions.Add ("temp-path=" + GetShortPath (tempDir)); + aotOptions.Add ("llvm-path=" + QuoteFileName (sdkBinDirectory)); + aotOptions.Add ("temp-path=" + QuoteFileName (tempDir)); string aotOptionsStr = (EnableLLVM ? "--llvm " : "") + "--aot=" + string.Join (",", aotOptions); @@ -470,50 +461,59 @@ bool RunAotCompiler (string assembliesPath, string aotCompiler, string aotOption { var stdout_completed = new ManualResetEvent (false); var stderr_completed = new ManualResetEvent (false); - var psi = new ProcessStartInfo () { - FileName = aotCompiler, - Arguments = aotOptions + " " + assembly, - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow=true, - WindowStyle=ProcessWindowStyle.Hidden, - WorkingDirectory = WorkingDirectory, - }; - - // we do not want options to be provided out of band to the cross compilers - psi.EnvironmentVariables ["MONO_ENV_OPTIONS"] = String.Empty; - // the C code cannot parse all the license details, including the activation code that tell us which license level is allowed - // so we provide this out-of-band to the cross-compilers - this can be extended to communicate a few others bits as well - psi.EnvironmentVariables ["MONO_PATH"] = assembliesPath; - - LogDebugMessage ("[AOT] MONO_PATH=\"{0}\" MONO_ENV_OPTIONS=\"{1}\" {2} {3}", - psi.EnvironmentVariables ["MONO_PATH"], psi.EnvironmentVariables ["MONO_ENV_OPTIONS"], psi.FileName, psi.Arguments); - - using (var proc = new Process ()) { - proc.OutputDataReceived += (s, e) => { - if (e.Data != null) - OnAotOutputData (s, e); - else - stdout_completed.Set (); - }; - proc.ErrorDataReceived += (s, e) => { - if (e.Data != null) - OnAotErrorData (s, e); - else - stderr_completed.Set (); + var responseFile = Path.GetTempFileName (); + try { + File.WriteAllText (responseFile, aotOptions + " " + assembly); + + var psi = new ProcessStartInfo () { + FileName = aotCompiler, + Arguments = $"--response=\"{responseFile}\"", + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + WorkingDirectory = WorkingDirectory, }; - proc.StartInfo = psi; - proc.Start (); - proc.BeginOutputReadLine (); - proc.BeginErrorReadLine (); - Token.Register (() => { try { proc.Kill (); } catch (Exception) { } }); - proc.WaitForExit (); - if (psi.RedirectStandardError) - stderr_completed.WaitOne (TimeSpan.FromSeconds (30)); - if (psi.RedirectStandardOutput) - stdout_completed.WaitOne (TimeSpan.FromSeconds (30)); - return proc.ExitCode == 0; + + // we do not want options to be provided out of band to the cross compilers + psi.EnvironmentVariables ["MONO_ENV_OPTIONS"] = String.Empty; + // the C code cannot parse all the license details, including the activation code that tell us which license level is allowed + // so we provide this out-of-band to the cross-compilers - this can be extended to communicate a few others bits as well + psi.EnvironmentVariables ["MONO_PATH"] = assembliesPath; + + LogDebugMessage ($"[AOT] {aotOptions} {assembly}"); + LogDebugMessage ("[AOT] MONO_PATH=\"{0}\" MONO_ENV_OPTIONS=\"{1}\" {2} {3}", + psi.EnvironmentVariables ["MONO_PATH"], psi.EnvironmentVariables ["MONO_ENV_OPTIONS"], psi.FileName, psi.Arguments); + + using (var proc = new Process ()) { + proc.OutputDataReceived += (s, e) => { + if (e.Data != null) + OnAotOutputData (s, e); + else + stdout_completed.Set (); + }; + proc.ErrorDataReceived += (s, e) => { + if (e.Data != null) + OnAotErrorData (s, e); + else + stderr_completed.Set (); + }; + proc.StartInfo = psi; + proc.Start (); + proc.BeginOutputReadLine (); + proc.BeginErrorReadLine (); + Token.Register (() => { try { proc.Kill (); } catch (Exception) { } }); + proc.WaitForExit (); + if (psi.RedirectStandardError) + stderr_completed.WaitOne (TimeSpan.FromSeconds (30)); + if (psi.RedirectStandardOutput) + stdout_completed.WaitOne (TimeSpan.FromSeconds (30)); + return proc.ExitCode == 0; + } + } finally { + if (File.Exists (responseFile)) + File.Delete (responseFile); } } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs index 5c6377e5097..443e1474c71 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs @@ -673,7 +673,7 @@ public void BuildAotApplication (string supportedAbis, bool enableLLVM, bool exp // LLVM passes a direct path to libc.so, and we need to use the libc.so // which corresponds to the *minimum* SDK version specified in AndroidManifest.xml // Since we overrode minSdkVersion=16, that means we should use libc.so from android-16. - var rightLibc = new Regex (@"^\s*\[AOT\].*cross-.*--llvm.*,ld-flags=.*android-16.arch-.*.usr.lib.libc\.so", RegexOptions.Multiline); + var rightLibc = new Regex (@"\s*\[aot-compiler stdout].*android-16.arch-.*.usr.lib.libc\.so", RegexOptions.Multiline); var m = rightLibc.Match (string.Join ("\n",b.LastBuildOutput)); Assert.IsTrue (m.Success, "AOT+LLVM should use libc.so from minSdkVersion!"); }