Skip to content

Commit bcab81d

Browse files
committed
[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.
1 parent 9efb5f9 commit bcab81d

File tree

2 files changed

+75
-70
lines changed

2 files changed

+75
-70
lines changed

src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs

Lines changed: 70 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.IO;
66
using System.Linq;
77
using System.Reflection;
8+
using System.Text;
89
using System.Threading;
910
using Microsoft.Build.Framework;
1011
using Microsoft.Build.Utilities;
@@ -173,19 +174,11 @@ static string GetNdkToolchainLibraryDir (string binDir, AndroidTargetArch arch)
173174
return GetNdkToolchainLibraryDir (binDir, NdkUtil.GetArchDirName (arch));
174175
}
175176

176-
static string GetShortPath (string path)
177-
{
178-
if (Environment.OSVersion.Platform != PlatformID.Win32NT)
179-
return QuoteFileName (path);
180-
var shortPath = KernelEx.GetShortPathName (Path.GetDirectoryName (path));
181-
return Path.Combine (shortPath, Path.GetFileName (path));
182-
}
183-
184177
static string QuoteFileName(string fileName)
185178
{
186179
var builder = new CommandLineBuilder();
187180
builder.AppendFileNameIfNotNull(fileName);
188-
return builder.ToString();
181+
return builder.ToString().Replace (@"\", @"\\").Normalize (NormalizationForm.FormC);
189182
}
190183

191184
static bool ValidateAotConfiguration (TaskLoggingHelper log, AndroidTargetArch arch, bool enableLLVM)
@@ -379,7 +372,6 @@ IEnumerable<Config> GetAotConfigs ()
379372
} catch (InvalidOperationException ex) {
380373
Diagnostic.Error (5101, ex.Message);
381374
}
382-
383375
string toolchainLibDir;
384376
if (NdkUtil.UsingClangNDK)
385377
toolchainLibDir = GetNdkToolchainLibraryDir (toolchainPath, arch);
@@ -388,19 +380,19 @@ IEnumerable<Config> GetAotConfigs ()
388380

389381
var libs = new List<string>();
390382
if (NdkUtil.UsingClangNDK) {
391-
libs.Add ($"-L{GetShortPath (toolchainLibDir)}");
392-
libs.Add ($"-L{GetShortPath (androidLibPath)}");
383+
libs.Add ($"-L{QuoteFileName (toolchainLibDir)}");
384+
libs.Add ($"-L{QuoteFileName (androidLibPath)}");
393385

394386
if (arch == AndroidTargetArch.Arm) {
395387
// Needed for -lunwind to work
396388
string compilerLibDir = Path.Combine (toolchainPath, "..", "sysroot", "usr", "lib", NdkUtil.GetArchDirName (arch));
397-
libs.Add ($"-L{GetShortPath (compilerLibDir)}");
389+
libs.Add ($"-L{QuoteFileName (compilerLibDir)}");
398390
}
399391
}
400392

401-
libs.Add (GetShortPath (Path.Combine (toolchainLibDir, "libgcc.a")));
402-
libs.Add (GetShortPath (Path.Combine (androidLibPath, "libc.so")));
403-
libs.Add (GetShortPath (Path.Combine (androidLibPath, "libm.so")));
393+
libs.Add (QuoteFileName (Path.Combine (toolchainLibDir, "libgcc.a")));
394+
libs.Add (QuoteFileName (Path.Combine (androidLibPath, "libc.so")));
395+
libs.Add (QuoteFileName (Path.Combine (androidLibPath, "libm.so")));
404396

405397
ldFlags = string.Join(";", libs);
406398
}
@@ -422,23 +414,23 @@ IEnumerable<Config> GetAotConfigs ()
422414
aotOptions.Add ("profile-only");
423415
foreach (var p in Profiles) {
424416
var fp = Path.GetFullPath (p.ItemSpec);
425-
aotOptions.Add ($"profile={GetShortPath (fp)}");
417+
aotOptions.Add ($"profile={QuoteFileName (fp)}");
426418
}
427419
}
428420
if (!string.IsNullOrEmpty (AotAdditionalArguments))
429421
aotOptions.Add (AotAdditionalArguments);
430422
if (sequencePointsMode == SequencePointsMode.Offline)
431-
aotOptions.Add ("msym-dir=" + GetShortPath (outdir));
423+
aotOptions.Add ("msym-dir=" + QuoteFileName (outdir));
432424
if (AotMode != AotMode.Normal)
433425
aotOptions.Add (AotMode.ToString ().ToLowerInvariant ());
434426

435-
aotOptions.Add ("outfile=" + GetShortPath (outputFile));
427+
aotOptions.Add ("outfile=" + QuoteFileName (outputFile));
436428
aotOptions.Add ("asmwriter");
437429
aotOptions.Add ("mtriple=" + mtriple);
438-
aotOptions.Add ("tool-prefix=" + GetShortPath (toolPrefix));
430+
aotOptions.Add ("tool-prefix=" + QuoteFileName (toolPrefix));
439431
aotOptions.Add ("ld-flags=" + ldFlags);
440-
aotOptions.Add ("llvm-path=" + GetShortPath (sdkBinDirectory));
441-
aotOptions.Add ("temp-path=" + GetShortPath (tempDir));
432+
aotOptions.Add ("llvm-path=" + QuoteFileName (sdkBinDirectory));
433+
aotOptions.Add ("temp-path=" + QuoteFileName (tempDir));
442434

443435
string aotOptionsStr = (EnableLLVM ? "--llvm " : "") + "--aot=" + string.Join (",", aotOptions);
444436

@@ -471,50 +463,63 @@ bool RunAotCompiler (string assembliesPath, string aotCompiler, string aotOption
471463
{
472464
var stdout_completed = new ManualResetEvent (false);
473465
var stderr_completed = new ManualResetEvent (false);
474-
var psi = new ProcessStartInfo () {
475-
FileName = aotCompiler,
476-
Arguments = aotOptions + " " + assembly,
477-
UseShellExecute = false,
478-
RedirectStandardOutput = true,
479-
RedirectStandardError = true,
480-
CreateNoWindow=true,
481-
WindowStyle=ProcessWindowStyle.Hidden,
482-
WorkingDirectory = WorkingDirectory,
483-
};
484-
485-
// we do not want options to be provided out of band to the cross compilers
486-
psi.EnvironmentVariables ["MONO_ENV_OPTIONS"] = String.Empty;
487-
// the C code cannot parse all the license details, including the activation code that tell us which license level is allowed
488-
// so we provide this out-of-band to the cross-compilers - this can be extended to communicate a few others bits as well
489-
psi.EnvironmentVariables ["MONO_PATH"] = assembliesPath;
490-
491-
LogDebugMessage ("[AOT] MONO_PATH=\"{0}\" MONO_ENV_OPTIONS=\"{1}\" {2} {3}",
492-
psi.EnvironmentVariables ["MONO_PATH"], psi.EnvironmentVariables ["MONO_ENV_OPTIONS"], psi.FileName, psi.Arguments);
493-
494-
using (var proc = new Process ()) {
495-
proc.OutputDataReceived += (s, e) => {
496-
if (e.Data != null)
497-
OnAotOutputData (s, e);
498-
else
499-
stdout_completed.Set ();
500-
};
501-
proc.ErrorDataReceived += (s, e) => {
502-
if (e.Data != null)
503-
OnAotErrorData (s, e);
504-
else
505-
stderr_completed.Set ();
466+
var responseFile = Path.GetTempFileName ();
467+
try {
468+
using (var sw = new StreamWriter (path: responseFile, append: false,
469+
encoding: new UTF8Encoding (encoderShouldEmitUTF8Identifier: false))) {
470+
sw.WriteLine (aotOptions + " " + assembly);
471+
}
472+
473+
var psi = new ProcessStartInfo () {
474+
FileName = aotCompiler,
475+
Arguments = $"--response=\"{responseFile}\"",
476+
UseShellExecute = false,
477+
RedirectStandardOutput = true,
478+
RedirectStandardError = true,
479+
CreateNoWindow = true,
480+
WindowStyle = ProcessWindowStyle.Hidden,
481+
WorkingDirectory = WorkingDirectory,
506482
};
507-
proc.StartInfo = psi;
508-
proc.Start ();
509-
proc.BeginOutputReadLine ();
510-
proc.BeginErrorReadLine ();
511-
CancellationToken.Register (() => { try { proc.Kill (); } catch (Exception) { } });
512-
proc.WaitForExit ();
513-
if (psi.RedirectStandardError)
514-
stderr_completed.WaitOne (TimeSpan.FromSeconds (30));
515-
if (psi.RedirectStandardOutput)
516-
stdout_completed.WaitOne (TimeSpan.FromSeconds (30));
517-
return proc.ExitCode == 0;
483+
484+
psi.EnvironmentVariables ["MONO_LOG_LEVEL"] = "debug";
485+
// we do not want options to be provided out of band to the cross compilers
486+
psi.EnvironmentVariables ["MONO_ENV_OPTIONS"] = String.Empty;
487+
// the C code cannot parse all the license details, including the activation code that tell us which license level is allowed
488+
// so we provide this out-of-band to the cross-compilers - this can be extended to communicate a few others bits as well
489+
psi.EnvironmentVariables ["MONO_PATH"] = assembliesPath.Normalize (NormalizationForm.FormC);
490+
491+
LogDebugMessage ($"[AOT] {aotOptions} {assembly}");
492+
LogDebugMessage ("[AOT] MONO_PATH=\"{0}\" MONO_ENV_OPTIONS=\"{1}\" {2} {3}",
493+
psi.EnvironmentVariables ["MONO_PATH"], psi.EnvironmentVariables ["MONO_ENV_OPTIONS"], psi.FileName, psi.Arguments);
494+
495+
using (var proc = new Process ()) {
496+
proc.OutputDataReceived += (s, e) => {
497+
if (e.Data != null)
498+
OnAotOutputData (s, e);
499+
else
500+
stdout_completed.Set ();
501+
};
502+
proc.ErrorDataReceived += (s, e) => {
503+
if (e.Data != null)
504+
OnAotErrorData (s, e);
505+
else
506+
stderr_completed.Set ();
507+
};
508+
proc.StartInfo = psi;
509+
proc.Start ();
510+
proc.BeginOutputReadLine ();
511+
proc.BeginErrorReadLine ();
512+
CancellationToken.Register (() => { try { proc.Kill (); } catch (Exception) { } });
513+
proc.WaitForExit ();
514+
if (psi.RedirectStandardError)
515+
stderr_completed.WaitOne (TimeSpan.FromSeconds (30));
516+
if (psi.RedirectStandardOutput)
517+
stdout_completed.WaitOne (TimeSpan.FromSeconds (30));
518+
return proc.ExitCode == 0;
519+
}
520+
} finally {
521+
if (File.Exists (responseFile))
522+
File.Delete (responseFile);
518523
}
519524
}
520525

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -732,9 +732,9 @@ public void BuildMkBundleApplicationReleaseAllAbi ()
732732
[Test]
733733
[TestCaseSource (nameof (AotChecks))]
734734
[Category ("Minor")]
735-
public void BuildAotApplication (string supportedAbis, bool enableLLVM, bool expectedResult)
735+
public void BuildAotApplicationAndÜmläüts (string supportedAbis, bool enableLLVM, bool expectedResult)
736736
{
737-
var path = Path.Combine ("temp", string.Format ("BuildAotApplication_{0}_{1}_{2}", supportedAbis, enableLLVM, expectedResult));
737+
var path = Path.Combine ("temp", string.Format ("BuildAotApplication AndÜmläüts_{0}_{1}_{2}", supportedAbis, enableLLVM, expectedResult));
738738
var proj = new XamarinAndroidApplicationProject () {
739739
IsRelease = true,
740740
BundleAssemblies = false,
@@ -768,7 +768,7 @@ public void BuildAotApplication (string supportedAbis, bool enableLLVM, bool exp
768768
// LLVM passes a direct path to libc.so, and we need to use the libc.so
769769
// which corresponds to the *minimum* SDK version specified in AndroidManifest.xml
770770
// Since we overrode minSdkVersion=16, that means we should use libc.so from android-16.
771-
var rightLibc = new Regex (@"^\s*\[AOT\].*cross-.*--llvm.*,ld-flags=.*android-16.arch-.*.usr.lib.libc\.so", RegexOptions.Multiline);
771+
var rightLibc = new Regex (@"\s*\[aot-compiler stdout].*android-16.arch-.*.usr.lib.libc\.so", RegexOptions.Multiline);
772772
var m = rightLibc.Match (string.Join ("\n",b.LastBuildOutput));
773773
Assert.IsTrue (m.Success, "AOT+LLVM should use libc.so from minSdkVersion!");
774774
}
@@ -803,9 +803,9 @@ public void BuildAotApplication (string supportedAbis, bool enableLLVM, bool exp
803803
[Test]
804804
[TestCaseSource (nameof (AotChecks))]
805805
[Category ("Minor")]
806-
public void BuildAotApplicationAndBundle (string supportedAbis, bool enableLLVM, bool expectedResult)
806+
public void BuildAotApplicationAndBundleAndÜmläüts (string supportedAbis, bool enableLLVM, bool expectedResult)
807807
{
808-
var path = Path.Combine ("temp", string.Format ("BuildAotApplicationAndBundle_{0}_{1}_{2}", supportedAbis, enableLLVM, expectedResult));
808+
var path = Path.Combine ("temp", string.Format ("BuildAotApplicationAndBundle AndÜmläüts_{0}_{1}_{2}", supportedAbis, enableLLVM, expectedResult));
809809
var proj = new XamarinAndroidApplicationProject () {
810810
IsRelease = true,
811811
BundleAssemblies = true,

0 commit comments

Comments
 (0)