From 1b19111886c5046e5317fbefc266d39ab1b51f89 Mon Sep 17 00:00:00 2001 From: Atsushi Eno Date: Thu, 29 Mar 2018 17:50:40 +0900 Subject: [PATCH] [msbuild][r8] add r8 support to alter dx, proguard and desugar. See https://github.com/xamarin/xamarin-android/issues/1423 for details. multidex support is not done yet (needs to investigate what's expected there). --- .../Tasks/Proguard.cs | 37 ++-- src/Xamarin.Android.Build.Tasks/Tasks/R8.cs | 167 ++++++++++++++++++ .../Xamarin.Android.Build.Tasks.csproj | 2 + .../Xamarin.Android.Common.targets | 75 +++++++- {build-tools => src}/r8/r8.csproj | 0 {build-tools => src}/r8/r8.props | 0 {build-tools => src}/r8/r8.targets | 5 + 7 files changed, 261 insertions(+), 25 deletions(-) create mode 100644 src/Xamarin.Android.Build.Tasks/Tasks/R8.cs rename {build-tools => src}/r8/r8.csproj (100%) rename {build-tools => src}/r8/r8.props (100%) rename {build-tools => src}/r8/r8.targets (94%) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/Proguard.cs b/src/Xamarin.Android.Build.Tasks/Tasks/Proguard.cs index 58963235d51..20fa256108d 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/Proguard.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/Proguard.cs @@ -41,23 +41,17 @@ public class Proguard : ToolTask [Required] public string ProguardJarOutput { get; set; } - [Required] public string ProguardGeneratedReferenceConfiguration { get; set; } - - [Required] public string ProguardGeneratedApplicationConfiguration { get; set; } - - [Required] public string ProguardCommonXamarinConfiguration { get; set; } + [Required] public string ProguardConfigurationFiles { get; set; } public ITaskItem[] JavaLibrariesToEmbed { get; set; } - public ITaskItem[] ExternalJavaLibraries { get; set; } + public ITaskItem[] JavaLibrariesToReference { get; set; } - public ITaskItem[] DoNotPackageJavaLibraries { get; set; } - public bool UseProguard { get; set; } public string JavaOptions { get; set; } @@ -86,14 +80,13 @@ public override bool Execute () Log.LogDebugMessage (" JavaPlatformJarPath: {0}", JavaPlatformJarPath); Log.LogDebugMessage (" ClassesOutputDirectory: {0}", ClassesOutputDirectory); Log.LogDebugMessage (" AcwMapFile: {0}", AcwMapFile); - Log.LogDebugMessage (" ProguardGeneratedApplicationConfiguration: {0}", ProguardGeneratedApplicationConfiguration); Log.LogDebugMessage (" ProguardJarOutput: {0}", ProguardJarOutput); Log.LogDebugTaskItems (" ProguardGeneratedReferenceConfiguration:", ProguardGeneratedReferenceConfiguration); Log.LogDebugTaskItems (" ProguardGeneratedApplicationConfiguration:", ProguardGeneratedApplicationConfiguration); Log.LogDebugTaskItems (" ProguardCommonXamarinConfiguration:", ProguardCommonXamarinConfiguration); Log.LogDebugTaskItems (" ProguardConfigurationFiles:", ProguardConfigurationFiles); - Log.LogDebugTaskItems (" ExternalJavaLibraries:", ExternalJavaLibraries); - Log.LogDebugTaskItems (" DoNotPackageJavaLibraries:", DoNotPackageJavaLibraries); + Log.LogDebugTaskItems (" JavaLibrariesToEmbed:", JavaLibrariesToEmbed); + Log.LogDebugTaskItems (" JavaLibrariesToReference:", JavaLibrariesToReference); Log.LogDebugMessage (" UseProguard: {0}", UseProguard); Log.LogDebugMessage (" EnableLogging: {0}", EnableLogging); Log.LogDebugMessage (" DumpOutput: {0}", DumpOutput); @@ -139,15 +132,9 @@ protected override string GenerateCommandLineCommands () // skip invalid lines } - var injars = new List (); - var libjars = new List (); - injars.Add (classesZip); - if (JavaLibrariesToEmbed != null) - foreach (var jarfile in JavaLibrariesToEmbed) - injars.Add (jarfile.ItemSpec); - - using (var xamcfg = File.Create (ProguardCommonXamarinConfiguration)) - GetType ().Assembly.GetManifestResourceStream ("proguard_xamarin.cfg").CopyTo (xamcfg); + if (!string.IsNullOrWhiteSpace (ProguardCommonXamarinConfiguration)) + using (var xamcfg = File.Create (ProguardCommonXamarinConfiguration)) + GetType ().Assembly.GetManifestResourceStream ("proguard_xamarin.cfg").CopyTo (xamcfg); var configs = ProguardConfigurationFiles .Replace ("{sdk.dir}", AndroidSdkDirectory + Path.DirectorySeparatorChar) @@ -168,9 +155,15 @@ protected override string GenerateCommandLineCommands () Log.LogWarning ("Proguard configuration file '{0}' was not found.", file); } + var injars = new List (); + var libjars = new List (); + injars.Add (classesZip); + if (JavaLibrariesToEmbed != null) + foreach (var jarfile in JavaLibrariesToEmbed) + injars.Add (jarfile.ItemSpec); libjars.Add (JavaPlatformJarPath); - if (ExternalJavaLibraries != null) - foreach (var jarfile in ExternalJavaLibraries.Select (p => p.ItemSpec)) + if (JavaLibrariesToReference != null) + foreach (var jarfile in JavaLibrariesToReference.Select (p => p.ItemSpec)) libjars.Add (jarfile); cmd.AppendSwitchUnquotedIfNotNull ("-injars ", "\"'" + string.Join ($"'{ProguardInputJarFilter}{Path.PathSeparator}'", injars.Distinct ()) + $"'{ProguardInputJarFilter}\""); diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/R8.cs b/src/Xamarin.Android.Build.Tasks/Tasks/R8.cs new file mode 100644 index 00000000000..90f4f252252 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Tasks/R8.cs @@ -0,0 +1,167 @@ +// Copyright (C) 2018 Xamarin, Inc. All rights reserved. + +using System; +using System.Linq; +using System.IO; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using System.Text; +using System.Collections.Generic; +using Xamarin.Android.Tools; + +namespace Xamarin.Android.Tasks +{ + + public class R8 : JavaToolTask + { + [Required] + public string R8JarPath { get; set; } + + [Required] + public string OutputDirectory { get; set; } + + public string Configuration { get; set; } + + // It is loaded to calculate --min-api, which is used by desugaring part to determine which levels of desugaring it performs. + [Required] + public string AndroidManifestFile { get; set; } + + // general r8 feature options. + public bool EnableDesugar { get; set; } + public bool EnableMinify { get; set; } // The Task has the option, but it is not supported at all. + public bool EnableTreeShaking { get; set; } + + // Java libraries to embed or reference + [Required] + public string ClassesZip { get; set; } + [Required] + public string JavaPlatformJarPath { get; set; } + public ITaskItem [] JavaLibrariesToEmbed { get; set; } + public ITaskItem [] JavaLibrariesToReference { get; set; } + + // used for proguard configuration settings + [Required] + public string AndroidSdkDirectory { get; set; } + [Required] + public string AcwMapFile { get; set; } + public string ProguardGeneratedReferenceConfiguration { get; set; } + public string ProguardGeneratedApplicationConfiguration { get; set; } + public string ProguardCommonXamarinConfiguration { get; set; } + [Required] + public string ProguardConfigurationFiles { get; set; } + public string ProguardMappingOutput { get; set; } + + // multidex + public string MultiDexMainDexListFile { get; set; } + + public string R8ExtraArguments { get; set; } + + public override bool Execute () + { + Log.LogDebugMessage ("R8 Task"); + Log.LogDebugTaskItems (" R8JarPath: ", R8JarPath); + Log.LogDebugTaskItems (" OutputDirectory: ", OutputDirectory); + Log.LogDebugTaskItems (" AndroidManifestFile: ", AndroidManifestFile); + Log.LogDebugMessage (" Configuration: {0}", Configuration); + Log.LogDebugTaskItems (" JavaPlatformJarPath: ", JavaPlatformJarPath); + Log.LogDebugTaskItems (" ClassesZip: ", ClassesZip); + Log.LogDebugTaskItems (" JavaLibrariesToEmbed: ", JavaLibrariesToEmbed); + Log.LogDebugTaskItems (" JavaLibrariesToReference: ", JavaLibrariesToReference); + Log.LogDebugMessage (" EnableDesugar: {0}", EnableDesugar); + Log.LogDebugMessage (" EnableTreeShaking: {0}", EnableTreeShaking); + Log.LogDebugTaskItems (" AndroidSdkDirectory:", AndroidSdkDirectory); + Log.LogDebugTaskItems (" AcwMapFile: ", AcwMapFile); + Log.LogDebugTaskItems (" ProguardGeneratedReferenceConfiguration:", ProguardGeneratedReferenceConfiguration); + Log.LogDebugTaskItems (" ProguardGeneratedApplicationConfiguration:", ProguardGeneratedApplicationConfiguration); + Log.LogDebugTaskItems (" ProguardCommonXamarinConfiguration:", ProguardCommonXamarinConfiguration); + Log.LogDebugTaskItems (" ProguardConfigurationFiles:", ProguardConfigurationFiles); + Log.LogDebugTaskItems (" ProguardMappingOutput:", ProguardMappingOutput); + Log.LogDebugTaskItems (" MultiDexMainDexListFile: ", MultiDexMainDexListFile); + Log.LogDebugTaskItems (" R8ExtraArguments: ", R8ExtraArguments); + + return base.Execute (); + } + + protected override string GenerateCommandLineCommands () + { + var cmd = new CommandLineBuilder (); + + cmd.AppendSwitchIfNotNull ("-jar ", R8JarPath); + + if (!string.IsNullOrEmpty (R8ExtraArguments)) + cmd.AppendSwitch (R8ExtraArguments); // it should contain "--dex". + if (Configuration.Equals ("Debug", StringComparison.OrdinalIgnoreCase)) + cmd.AppendSwitch ("--debug"); + + // generating proguard application configuration + if (EnableTreeShaking) { + var acwLines = File.ReadAllLines (AcwMapFile); + using (var appcfg = File.CreateText (ProguardGeneratedApplicationConfiguration)) + for (int i = 0; i + 2 < acwLines.Length; i += 3) + try { + var line = acwLines [i + 2]; + var java = line.Substring (line.IndexOf (';') + 1); + appcfg.WriteLine ("-keep class " + java + " { *; }"); + } catch { + // skip invalid lines + } + if (!string.IsNullOrWhiteSpace (ProguardCommonXamarinConfiguration)) + using (var xamcfg = File.Create (ProguardCommonXamarinConfiguration)) + GetType ().Assembly.GetManifestResourceStream ("proguard_xamarin.cfg").CopyTo (xamcfg); + var configs = ProguardConfigurationFiles + .Replace ("{sdk.dir}", AndroidSdkDirectory + Path.DirectorySeparatorChar) + .Replace ("{intermediate.common.xamarin}", ProguardCommonXamarinConfiguration) + .Replace ("{intermediate.references}", ProguardGeneratedReferenceConfiguration) + .Replace ("{intermediate.application}", ProguardGeneratedApplicationConfiguration) + .Replace ("{project}", string.Empty) // current directory anyways. + .Split (';') + .Select (s => s.Trim ()) + .Where (s => !string.IsNullOrWhiteSpace (s)); + var enclosingChar = "\""; + foreach (var file in configs) { + if (File.Exists (file)) + cmd.AppendSwitchUnquotedIfNotNull ("--pg-conf ", $"{enclosingChar}{file}{enclosingChar}"); + else + Log.LogWarning ("Proguard configuration file '{0}' was not found.", file); + } + cmd.AppendSwitchIfNotNull ("--pg-map-output ", ProguardMappingOutput); + + // multidexing + if (!string.IsNullOrWhiteSpace (MultiDexMainDexListFile) && File.Exists (MultiDexMainDexListFile)) + cmd.AppendSwitchIfNotNull ("--main-dex-list ", MultiDexMainDexListFile); + } + + // desugaring + var doc = AndroidAppManifest.Load (AndroidManifestFile, MonoAndroidHelper.SupportedVersions); + int minApiVersion = doc.MinSdkVersion == null ? 4 : (int)doc.MinSdkVersion; + cmd.AppendSwitchIfNotNull ("--min-api ", minApiVersion.ToString ()); + + if (!EnableTreeShaking) + cmd.AppendSwitch ("--no-tree-shaking"); + if (!EnableDesugar) + cmd.AppendSwitch ("--no-desugaring"); + if (!EnableMinify) + cmd.AppendSwitch ("--no-minification"); + + var injars = new List (); + var libjars = new List (); + injars.Add (ClassesZip); + if (JavaLibrariesToEmbed != null) + foreach (var jarfile in JavaLibrariesToEmbed) + injars.Add (jarfile.ItemSpec); + libjars.Add (JavaPlatformJarPath); + if (JavaLibrariesToReference != null) + foreach (var jarfile in JavaLibrariesToReference.Select (p => p.ItemSpec)) + libjars.Add (jarfile); + + cmd.AppendSwitchIfNotNull ("--output ", OutputDirectory); + foreach (var jar in libjars) + cmd.AppendSwitchIfNotNull ("--lib ", jar); + foreach (var jar in injars) + cmd.AppendFileNameIfNotNull (jar); + + return cmd.ToString (); + } + } + +} diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj index 9193e809861..5fea7b03e89 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj @@ -554,7 +554,9 @@ + + desugar_deploy.jar PreserveNewest diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index 11d4c94836e..e39f35bf5ea 100755 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -66,6 +66,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. + @@ -264,6 +265,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. $(EnableProguard) False False + False 1G @@ -853,12 +855,36 @@ because xbuild doesn't support framework reference assemblies. /> + + + + + + + + + + + + + + + + @@ -2227,8 +2254,7 @@ because xbuild doesn't support framework reference assemblies. ProguardGeneratedApplicationConfiguration="$(IntermediateOutputPath)proguard\proguard_project_primary.cfg" ProguardConfigurationFiles="$(ProguardConfigFiles)" JavaLibrariesToEmbed="@(_JarsToProguard);@(_InstantRunJavaReference)" - ExternalJavaLibraries="@(AndroidExternalJavaLibrary)" - DoNotPackageJavaLibraries="@(_ResolvedDoNotPackageAttributes)" + JavaLibrariesToReference="@(AndroidExternalJavaLibrary)" ProguardJarOutput="$(IntermediateOutputPath)proguard\__proguard_output__.jar" EnableLogging="$(ProguardEnableLogging)" DumpOutput="$(IntermediateOutputPath)proguard\dump.txt" @@ -2286,8 +2312,51 @@ because xbuild doesn't support framework reference assemblies. + + + + + + + + + + + + + + + + DependsOnTargets="_CompileToDalvikWithDx;_CompileToDalvikWithR8"> <_DexFile Include="$(IntermediateOutputPath)android\bin\dex\*.dex" /> diff --git a/build-tools/r8/r8.csproj b/src/r8/r8.csproj similarity index 100% rename from build-tools/r8/r8.csproj rename to src/r8/r8.csproj diff --git a/build-tools/r8/r8.props b/src/r8/r8.props similarity index 100% rename from build-tools/r8/r8.props rename to src/r8/r8.props diff --git a/build-tools/r8/r8.targets b/src/r8/r8.targets similarity index 94% rename from build-tools/r8/r8.targets rename to src/r8/r8.targets index adfb0c28cd5..f453fe8711b 100644 --- a/build-tools/r8/r8.targets +++ b/src/r8/r8.targets @@ -54,6 +54,11 @@ +