From 7d0dbb3fe29c27156e362b6065ee826eb393ae1d Mon Sep 17 00:00:00 2001 From: Dean Ellis Date: Wed, 26 Apr 2017 13:51:04 +0100 Subject: [PATCH] [Xamarin.Android.Build.Tasks] The option for multi-dex fails when the path to the Android SDK contains a space Fixes https://bugzilla.xamarin.com/show_bug.cgi?id=33052 Fixes https://bugzilla.xamarin.com/show_bug.cgi?id=38693 The google tooling around multidex is pretty broken on windows. It does not handle paths with spaces and has over quoting path issues. This is one reason why we are shipping our own Proguard. This commit completely removes the user of mainDexClasses.bat and introduces code which will create the ` multidex.keep` by making the required calls manually. --- .../Tasks/CreateMultiDexMainDexClassList.cs | 75 +++++++++++++------ .../Tasks/JavaToolTask.cs | 12 +-- .../Xamarin.Android.Common.targets | 7 +- 3 files changed, 63 insertions(+), 31 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CreateMultiDexMainDexClassList.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CreateMultiDexMainDexClassList.cs index 6afdaa44d16..36faf3a82b7 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/CreateMultiDexMainDexClassList.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/CreateMultiDexMainDexClassList.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2011 Xamarin, Inc. All rights reserved. +// Copyright (C) 2011 Xamarin, Inc. All rights reserved. using System; using System.Collections.Generic; @@ -12,13 +12,16 @@ namespace Xamarin.Android.Tasks { - public class CreateMultiDexMainDexClassList : ToolTask + public class CreateMultiDexMainDexClassList : JavaToolTask { [Required] public string ClassesOutputDirectory { get; set; } [Required] - public string ProguardHome { get; set; } + public string ProguardJarPath { get; set; } + + [Required] + public string AndroidSdkBuildToolsPath { get; set; } [Required] public ITaskItem[] JavaLibraries { get; set; } @@ -26,6 +29,10 @@ public class CreateMultiDexMainDexClassList : ToolTask public string MultiDexMainDexListFile { get; set; } public ITaskItem[] CustomMainDexListFiles { get; set; } + Action commandlineAction; + string tempJar; + bool writeOutputToKeepFile = false; + public override bool Execute () { Log.LogDebugMessage ("CreateMultiDexMainDexClassList"); @@ -35,7 +42,7 @@ public override bool Execute () Log.LogDebugTaskItems (" CustomMainDexListFiles:", CustomMainDexListFiles); Log.LogDebugMessage (" ToolExe: {0}", ToolExe); Log.LogDebugMessage (" ToolPath: {0}", ToolPath); - Log.LogDebugMessage (" ProguardHome: {0}", ProguardHome); + Log.LogDebugMessage (" ProguardJarPath: {0}", ProguardJarPath); if (CustomMainDexListFiles != null && CustomMainDexListFiles.Any ()) { var content = string.Concat (CustomMainDexListFiles.Select (i => File.ReadAllText (i.ItemSpec))); @@ -43,37 +50,61 @@ public override bool Execute () return true; } - EnvironmentVariables = MonoAndroidHelper.GetProguardEnvironmentVaribles (ProguardHome); + tempJar = Path.Combine (Path.GetTempPath (), Path.GetRandomFileName () + ".jar"); + commandlineAction = GenerateProguardCommands; + // run proguard first + var retval = base.Execute (); + if (!retval || Log.HasLoggedErrors) + return false; + + commandlineAction = GenerateMainDexListBuilderCommands; + // run java second + + return base.Execute () && !Log.HasLoggedErrors; - return base.Execute (); } protected override string GenerateCommandLineCommands () { var cmd = new CommandLineBuilder (); + commandlineAction (cmd); + return cmd.ToString (); + } - cmd.AppendSwitch ("--output"); - cmd.AppendFileNameIfNotNull (MultiDexMainDexListFile); - + void GenerateProguardCommands (CommandLineBuilder cmd) + { + var enclosingChar = OS.IsWindows ? "\"" : string.Empty; var jars = JavaLibraries.Select (i => i.ItemSpec).Concat (new string [] { ClassesOutputDirectory }); - string files = string.Join (Path.PathSeparator.ToString (), jars.Select (s => '\'' + s + '\'')); - if (OS.IsWindows) - cmd.AppendSwitch ('"' + files + '"'); - else - cmd.AppendSwitch (files); - - return cmd.ToString (); + cmd.AppendSwitchIfNotNull ("-jar ", ProguardJarPath); + cmd.AppendSwitchUnquotedIfNotNull ("-injars ", $"{enclosingChar}'" + string.Join ($"'{Path.PathSeparator}'", jars) + $"'{enclosingChar}"); + cmd.AppendSwitch ("-dontwarn"); + cmd.AppendSwitch ("-forceprocessing"); + cmd.AppendSwitchIfNotNull ("-outjars ", tempJar); + cmd.AppendSwitchIfNotNull ("-libraryjars ", $"'{Path.Combine (AndroidSdkBuildToolsPath, "lib", "shrinkedAndroid.jar")}'"); + cmd.AppendSwitch ("-dontoptimize"); + cmd.AppendSwitch ("-dontobfuscate"); + cmd.AppendSwitch ("-dontpreverify"); + cmd.AppendSwitchIfNotNull ("-include ", $"'{Path.Combine (AndroidSdkBuildToolsPath, "mainDexClasses.rules")}'"); } - protected override string ToolName { - get { - return OS.IsWindows ? "mainDexClasses.bat" : "mainDexClasses"; - } + void GenerateMainDexListBuilderCommands(CommandLineBuilder cmd) + { + var jars = JavaLibraries.Select (i => i.ItemSpec).Concat (new string [] { ClassesOutputDirectory }); + cmd.AppendSwitchIfNotNull ("-Djava.ext.dirs=", Path.Combine (AndroidSdkBuildToolsPath, "lib")); + cmd.AppendSwitch ("com.android.multidex.MainDexListBuilder"); + cmd.AppendSwitch (tempJar); + cmd.AppendSwitchUnquotedIfNotNull ("", "\"" + string.Join ($"{Path.PathSeparator}", jars) + "\""); + writeOutputToKeepFile = true; } - protected override string GenerateFullPathToTool () + protected override void LogEventsFromTextOutput (string singleLine, MessageImportance messageImportance) { - return Path.Combine (ToolPath, ToolExe); + var match = CodeErrorRegEx.Match (singleLine); + var exceptionMatch = ExceptionRegEx.Match (singleLine); + + if (writeOutputToKeepFile && !match.Success && !exceptionMatch.Success) + File.AppendAllText (MultiDexMainDexListFile, singleLine); + base.LogEventsFromTextOutput (singleLine, messageImportance); } } } diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/JavaToolTask.cs b/src/Xamarin.Android.Build.Tasks/Tasks/JavaToolTask.cs index c646005cc23..d6c34399623 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/JavaToolTask.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/JavaToolTask.cs @@ -66,8 +66,8 @@ at com.android.dx.command.dexer.Main.main(Main.java:215) at com.android.dx.command.Main.main(Main.java:106) */ const string ExceptionRegExString = @"(?java.lang.+):(?.+)"; - Regex codeErrorRegEx = new Regex (CodeErrorRegExString, RegexOptions.Compiled); - Regex exceptionRegEx = new Regex (ExceptionRegExString, RegexOptions.Compiled); + protected static readonly Regex CodeErrorRegEx = new Regex (CodeErrorRegExString, RegexOptions.Compiled); + protected static readonly Regex ExceptionRegEx = new Regex (ExceptionRegExString, RegexOptions.Compiled); bool foundError = false; List errorLines = new List (); StringBuilder errorText = new StringBuilder (); @@ -114,8 +114,8 @@ void LogFromException (string exception, string error) { bool ProcessOutput (string singleLine) { - var match = codeErrorRegEx.Match (singleLine); - var exceptionMatch = exceptionRegEx.Match (singleLine); + var match = CodeErrorRegEx.Match (singleLine); + var exceptionMatch = ExceptionRegEx.Match (singleLine); if (match.Success) { if (!string.IsNullOrEmpty (file)) { @@ -157,8 +157,8 @@ bool ProcessOutput (string singleLine) protected override void LogEventsFromTextOutput (string singleLine, MessageImportance messageImportance) { - var match = codeErrorRegEx.Match (singleLine); - var exceptionMatch = exceptionRegEx.Match (singleLine); + var match = CodeErrorRegEx.Match (singleLine); + var exceptionMatch = ExceptionRegEx.Match (singleLine); if (match.Success || exceptionMatch.Success) { Log.LogMessage (MessageImportance.High, singleLine); diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index ad5fdad002d..bf1d1f2eefb 100755 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -1950,9 +1950,10 @@ because xbuild doesn't support framework reference assemblies.