diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_CopyExtraResultFilesForCI.cs b/build-tools/xaprepare/xaprepare/Steps/Step_CopyExtraResultFilesForCI.cs index fd88a337d04..be286992a62 100644 --- a/build-tools/xaprepare/xaprepare/Steps/Step_CopyExtraResultFilesForCI.cs +++ b/build-tools/xaprepare/xaprepare/Steps/Step_CopyExtraResultFilesForCI.cs @@ -90,6 +90,7 @@ void CopyExtraBuildFiles (string destinationRoot, Context context) "*log", "TestOutput-*.txt", "Timing_*", + "*.runsettings", }; void CopyExtraTestFiles (string destinationRoot, Context context) diff --git a/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Aapt2.targets b/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Aapt2.targets index d1bfde205de..be0c6c61539 100644 --- a/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Aapt2.targets +++ b/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Aapt2.targets @@ -27,7 +27,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. - + <_SetLatestTargetFrameworkVersionDependsOnTargets> $(_SetLatestTargetFrameworkVersionDependsOnTargets); _CreateAapt2VersionCache; diff --git a/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Resource.Designer.targets b/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Resource.Designer.targets index 3fa46d4a2b9..fc1dea7a636 100644 --- a/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Resource.Designer.targets +++ b/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Resource.Designer.targets @@ -45,6 +45,8 @@ Copyright (C) 2016 Xamarin. All rights reserved. per TargetFramework. --> <_DesignerIntermediateOutputPath Condition=" '$(_DesignerIntermediateOutputPath)' == '' And '$(_OuterIntermediateOutputPath)' != '' ">$(_OuterIntermediateOutputPath) + <_DesignerIntermediateOutputPath Condition=" '$(_DesignerIntermediateOutputPath)' == '' And '$(_OuterIntermediateOutputPath)' != '' And '$(AndroidUseDesignerAssembly)' == 'True' And '$(DesignTimeBuild)' == 'true' ">$(_OuterIntermediateOutputPath)designtime\ + <_DesignerIntermediateOutputPath Condition=" '$(_DesignerIntermediateOutputPath)' == '' And '$(AndroidUseDesignerAssembly)' == 'True' And '$(DesignTimeBuild)' == 'true' ">$(IntermediateOutputPath)designtime\ <_DesignerIntermediateOutputPath Condition=" '$(_DesignerIntermediateOutputPath)' == '' ">$(IntermediateOutputPath) <_GenerateResourceDesignerAssemblyOutput>$(_DesignerIntermediateOutputPath)$(_DesignerAssemblyName).dll <_GenerateResourceDesignerClassFile Condition=" '$(Language)' == 'F#' ">$(_DesignerIntermediateOutputPath)_$(_DesignerAssemblyName).fs @@ -56,15 +58,45 @@ Copyright (C) 2016 Xamarin. All rights reserved. + + + <_ProjectReferenceResourceDirectory Include="$(MSBuildProjectDirectory)\$(MonoAndroidResourcePrefix)" + StampFile="$(MSBuildProjectFile)" + Condition="!Exists('$(OutputPath)$(TargetName).aar')" + /> + + + + + + + + + + + + + <_DesignTimeAarFiles Include="@(AndroidAarLibrary)" Condition=" '@(LibraryResourceDirectories->Count())' == '0' " /> + <_DesignTimeAarFiles Include="@(LibraryProjectZip)" Condition="'%(LibraryProjectZip.Extension)' == '.aar' and '@(LibraryResourceDirectories->Count())' == '0'" /> + + + <_BuildResourceDesignerDependsOn> _SetupDesignerProperties; + _ResolveAars; + _CalculateDesignTimeAars; _GenerateResourceCaseMap; _GenerateRtxt; _GenerateResourceDesignerIntermediateClass; diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AndroidLibraries.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AndroidLibraries.targets index 14b9e8c2dee..e678933ab8b 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AndroidLibraries.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AndroidLibraries.targets @@ -18,7 +18,7 @@ projects. <_AarOutputPath>$(OutputPath)$(TargetName).aar - + <_AarSearchDirectory Include="@(_ReferencePath->'%(RootDir)%(Directory)')" /> <_AarSearchDirectory Include="@(_ReferenceDependencyPaths->'%(RootDir)%(Directory)')" /> diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets index 5bb9041cb5f..afda07d8c7d 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets @@ -90,6 +90,7 @@ _ResolveAssemblies MSBuild target. <_AdditionalProperties> _ComputeFilesToPublishForRuntimeIdentifiers=true ;SelfContained=true + ;DesignTimeBuild=$(DesignTimeBuild) ;AppendRuntimeIdentifierToOutputPath=true ;ResolveAssemblyReferencesFindRelatedSatellites=false ;SkipCompilerExecution=true @@ -105,6 +106,7 @@ _ResolveAssemblies MSBuild target. <_ProjectToBuild Include="$(MSBuildProjectFile)" AdditionalProperties="RuntimeIdentifier=%(_RIDs.Identity);$(_AdditionalProperties)" /> diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets index ee627e65d03..f54dd4f15d6 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets @@ -112,14 +112,9 @@ properties that determine build ordering. $(CoreResolveReferencesDependsOn); UpdateAndroidResources; - _BuildResourceDesigner; UpdateAndroidInterfaceProxies; _CheckForInvalidDesignerConfig; - - $(DesignTimeResolveAssemblyReferencesDependsOn); - _BuildResourceDesigner; - <_UpdateAndroidResourcesDependsOn> $(CoreResolveReferencesDependsOn); _CreatePropertiesCache; @@ -127,7 +122,6 @@ properties that determine build ordering. _ComputeAndroidResourcePaths; _UpdateAndroidResgen; _CreateAar; - _BuildResourceDesigner; _SetupMSBuildAllProjects; @@ -142,9 +136,9 @@ properties that determine build ordering. _CollectGeneratedManagedBindingFiles; _ClearGeneratedManagedBindings; AddBindingsToCompile; + $(CompileDependsOn); _BuildResourceDesigner; _AddResourceDesignerFiles; - $(CompileDependsOn); _CheckAndroidHttpClientHandlerType; @@ -171,6 +165,7 @@ properties that determine build ordering. _ResolveMonoAndroidSdks; + ResolveAssemblyReferences; _ExtractLibraryProjectImports; _GetLibraryImports; _ExtractAar; diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.targets index 31755d131e8..a48a9b20058 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.targets @@ -26,7 +26,7 @@ - + Condition="Exists('$(MSBuildThisFileDirectory)..\tools\Xamarin.Android.Common.Debugging.props') And '$(DesignTimeBuild)' != 'true'"/> + diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateResourceCaseMap.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateResourceCaseMap.cs index bf6aa0563aa..fe617657044 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateResourceCaseMap.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateResourceCaseMap.cs @@ -3,9 +3,13 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using Microsoft.Android.Build.Tasks; +using Xamarin.Android.Tools; +//using Xamarin.Tools.Zip; +using System.IO.Compression; namespace Xamarin.Android.Tasks { @@ -22,6 +26,8 @@ public class GenerateResourceCaseMap : AndroidTask public ITaskItem[] AdditionalResourceDirectories { get; set; } + public string[] AarLibraries { get; set; } + [Required] public ITaskItem OutputFile { get; set; } @@ -64,6 +70,42 @@ public override bool RunTask () AddRename (tok [1].Replace ('/', Path.DirectorySeparatorChar), tok [0].Replace ('/', Path.DirectorySeparatorChar)); } } + var resmap = ".net/__res_name_case_map.txt"; + foreach (var aar in AarLibraries ?? Array.Empty()) { + Log.LogDebugMessage ($"Processing Aar file {aar}"); + if (!File.Exists (aar)) { + Log.LogDebugMessage ($"Skipping non-existent aar: {aar}"); + continue; + } + using (var file = File.OpenRead (aar)) { + using var zip = new ZipArchive (file); + var entry = zip.GetEntry (resmap); + if (entry is null) { + Log.LogDebugMessage ($"Skipping non-existent file: {resmap}"); + continue; + } + Log.LogDebugMessage ($"Found: {entry.FullName}"); + var ms = MemoryStreamPool.Shared.Rent (); + try { + using (var entryStream = entry.Open ()) { + entryStream.CopyTo (ms); + } + ms.Position = 0; + using (var reader = new StreamReader (ms, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: -1, leaveOpen: true)) { + string line; + // Read each line until the end of the file + while ((line = reader.ReadLine()) != null) { + if (string.IsNullOrEmpty (line)) + continue; + string [] tok = line.Split (';'); + AddRename (tok [1].Replace ('/', Path.DirectorySeparatorChar), tok [0].Replace ('/', Path.DirectorySeparatorChar)); + } + } + } finally { + MemoryStreamPool.Shared.Return (ms); + } + } + } if (MonoAndroidHelper.SaveMapFile (BuildEngine4, Path.GetFullPath (OutputFile.ItemSpec), resource_fixup)) { Log.LogDebugMessage ($"Writing to: {OutputFile.ItemSpec}"); diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateRtxt.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateRtxt.cs index 563318d357c..5de36f9cd7c 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateRtxt.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateRtxt.cs @@ -17,6 +17,8 @@ public class GenerateRtxt : AndroidTask public string ResourceDirectory { get; set; } public string[] AdditionalResourceDirectories { get; set; } + public string[] AarLibraries { get; set; } + public string JavaPlatformJarPath { get; set; } public string ResourceFlagFile { get; set; } @@ -31,7 +33,7 @@ public override bool RunTask () var javaPlatformDirectory = string.IsNullOrEmpty (JavaPlatformJarPath) ? "" : Path.GetDirectoryName (JavaPlatformJarPath); var parser = new FileResourceParser () { Log = Log, JavaPlatformDirectory = javaPlatformDirectory, ResourceFlagFile = ResourceFlagFile}; - var resources = parser.Parse (ResourceDirectory, AdditionalResourceDirectories, resource_fixup); + var resources = parser.Parse (ResourceDirectory, AdditionalResourceDirectories, AarLibraries, resource_fixup); // only update if it changed. writer.Write (RTxtFile, resources); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AndroidUpdateResourcesTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AndroidUpdateResourcesTest.cs index 07e6be2bf72..4b3dfa5594a 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AndroidUpdateResourcesTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AndroidUpdateResourcesTest.cs @@ -1029,7 +1029,7 @@ public void BuildAppWithManagedResourceParserAndLibraries () Assert.LessOrEqual (libBuilder.LastBuildTime.TotalMilliseconds, maxBuildTimeMs, $"DesignTime build should be less than {maxBuildTimeMs} milliseconds."); Assert.IsFalse (libProj.CreateBuildOutput (libBuilder).IsTargetSkipped ("_ManagedUpdateAndroidResgen"), "Target '_ManagedUpdateAndroidResgen' should have run."); - Assert.IsFalse (appBuilder.DesignTimeBuild (appProj), "Application project should have built"); + Assert.IsTrue (appBuilder.DesignTimeBuild (appProj), "Application project should have built"); Assert.LessOrEqual (appBuilder.LastBuildTime.TotalMilliseconds, maxBuildTimeMs, $"DesignTime build should be less than {maxBuildTimeMs} milliseconds."); Assert.IsFalse (appProj.CreateBuildOutput (appBuilder).IsTargetSkipped ("_ManagedUpdateAndroidResgen"), "Target '_ManagedUpdateAndroidResgen' should have run."); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BindingBuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BindingBuildTest.cs index 14e72a467b4..7e7b749df8f 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BindingBuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BindingBuildTest.cs @@ -544,11 +544,8 @@ public void DesignTimeBuild (string classParser) Assert.IsNotNull (element, "api.xml should contain an `api` element!"); Assert.IsTrue (element.HasElements, "api.xml should contain elements!"); - var assemblyFile = Path.Combine (intermediate, proj.ProjectName + ".dll"); - using (var assembly = AssemblyDefinition.ReadAssembly (assemblyFile)) { - var typeName = "Com.Balysv.Material.Drawable.Menu.MaterialMenuView"; - Assert.IsTrue (assembly.MainModule.Types.Any (t => t.FullName == typeName), $"Type `{typeName}` should exist!"); - } + var sourceFile = Path.Combine (intermediate, "generated", "src", "Com.Balysv.Material.Drawable.Menu.MaterialMenuView.cs"); + FileAssert.Exists (sourceFile, $"'{sourceFile}' should have been generated."); } } 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 47f6c3dbff5..48c1ed3685b 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 @@ -752,6 +752,7 @@ public void BuildAfterUpgradingNuget () } [Test] + [Category ("SmokeTests")] public void BuildInDesignTimeMode ([Values(false, true)] bool useManagedParser) { var proj = new XamarinAndroidApplicationProject () { @@ -762,20 +763,20 @@ public void BuildInDesignTimeMode ([Values(false, true)] bool useManagedParser) builder.Target = "UpdateAndroidResources"; builder.Build (proj, parameters: new string[] { "DesignTimeBuild=true" }); Assert.IsFalse (builder.Output.IsTargetSkipped ("_CreatePropertiesCache"), "target \"_CreatePropertiesCache\" should have been run."); - Assert.IsFalse (builder.Output.IsTargetSkipped ("_ResolveLibraryProjectImports"), "target \"_ResolveLibraryProjectImports\' should have been run."); + Assert.IsFalse (builder.Output.IsTargetSkipped ("_GenerateRtxt"), "target \"_GenerateRtxt\' should have been run."); var intermediate = Path.Combine (Root, builder.ProjectDirectory, proj.IntermediateOutputPath); - var librarycache = Path.Combine (intermediate, "designtime", "libraryprojectimports.cache"); - Assert.IsTrue (File.Exists (librarycache), $"'{librarycache}' should exist."); - librarycache = Path.Combine (intermediate, "libraryprojectimports.cache"); - Assert.IsFalse (File.Exists (librarycache), $"'{librarycache}' should not exist."); + var rTxtFile = Path.Combine (intermediate, "designtime", "R.txt"); + Assert.IsTrue (File.Exists (rTxtFile), $"'{rTxtFile}' should exist."); + rTxtFile = Path.Combine (intermediate, "R.txt"); + Assert.IsFalse (File.Exists (rTxtFile), $"'{rTxtFile}' should not exist."); builder.Build (proj, parameters: new string[] { "DesignTimeBuild=true" }); Assert.IsFalse (builder.Output.IsTargetSkipped ("_CreatePropertiesCache"), "target \"_CreatePropertiesCache\" should have been run."); - Assert.IsTrue (builder.Output.IsTargetSkipped ("_ResolveLibraryProjectImports"), "target \"_ResolveLibraryProjectImports\' should have been skipped."); + Assert.IsTrue (builder.Output.IsTargetSkipped ("_GenerateRtxt"), "target \"_GenerateRtxt\' should have been skipped."); Assert.IsTrue (builder.Clean (proj), "Clean Should have succeeded"); builder.Target = "_CleanDesignTimeIntermediateDir"; Assert.IsTrue (builder.Build (proj), "_CleanDesignTimeIntermediateDir should have succeeded"); - librarycache = Path.Combine (intermediate, "designtime", "libraryprojectimports.cache"); - Assert.IsFalse (File.Exists (librarycache), $"'{librarycache}' should not exist."); + rTxtFile = Path.Combine (intermediate, "designtime", "R.txt"); + Assert.IsFalse (File.Exists (rTxtFile), $"'{rTxtFile}' should not exist."); } } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs index 75f45a1d833..32b79d45e83 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs @@ -1199,17 +1199,26 @@ public void DesignTimeBuild () { var proj = new XamarinAndroidApplicationProject (); using (var b = CreateApkBuilder (Path.Combine ("temp", $"{nameof (IncrementalBuildTest)}{TestName}"))) { + b.BuildLogFile = "dtb1.log"; Assert.IsTrue (b.DesignTimeBuild (proj), "first dtb should have succeeded."); - var target = "_ResolveLibraryProjectImports"; + var target = "_GenerateResourceDesignerAssembly"; Assert.IsFalse (b.Output.IsTargetSkipped (target), $"`{target}` should not have been skipped."); + var importsTarget = "_ResolveLibraryProjectImports"; + Assert.IsTrue (b.Output.IsTargetSkipped (importsTarget, defaultIfNotUsed: true), $"`{importsTarget}` should have been skipped."); // DesignTimeBuild=true lowercased var parameters = new [] { "DesignTimeBuild=true" }; + b.BuildLogFile = "compile.log"; Assert.IsTrue (b.RunTarget (proj, "Compile", doNotCleanupOnUpdate: true, parameters: parameters), "second dtb should have succeeded."); - Assert.IsTrue (b.Output.IsTargetSkipped (target), $"`{target}` should have been skipped."); + Assert.IsTrue (b.Output.IsTargetSkipped (target, defaultIfNotUsed: true), $"`{target}` should have been skipped."); + Assert.IsTrue (b.Output.IsTargetSkipped (importsTarget, defaultIfNotUsed: true), $"`{importsTarget}` should have been skipped."); + b.BuildLogFile = "updategeneratedfiles.log"; Assert.IsTrue (b.RunTarget (proj, "UpdateGeneratedFiles", doNotCleanupOnUpdate: true, parameters: parameters), "UpdateGeneratedFiles should have succeeded."); - Assert.IsTrue (b.Output.IsTargetSkipped (target), $"`{target}` should have been skipped."); + Assert.IsTrue (b.Output.IsTargetSkipped (target, defaultIfNotUsed: true), $"`{target}` should have been skipped."); + Assert.IsTrue (b.Output.IsTargetSkipped (importsTarget, defaultIfNotUsed: true), $"`{importsTarget}` should have been skipped."); + b.BuildLogFile = "build.log"; Assert.IsTrue (b.Build (proj, doNotCleanupOnUpdate: true, saveProject: false), "full build should have succeeded."); Assert.IsFalse (b.Output.IsTargetSkipped (target), $"`{target}` should not have been skipped."); + Assert.IsFalse (b.Output.IsTargetSkipped (importsTarget), $"`{importsTarget}` should not have been skipped."); } } @@ -1234,7 +1243,7 @@ public void DesignTimeBuildSignAndroidPackage () Assert.IsTrue (builder.RunTarget (proj, "SignAndroidPackage", parameters: parameters), $"{proj.ProjectName} should succeed"); builder.Output.AssertTargetIsNotSkipped ("_GenerateResourceCaseMap", occurrence: 2); builder.Output.AssertTargetIsSkipped ("_GenerateRtxt", occurrence: 1); - builder.Output.AssertTargetIsSkipped ("_GenerateResourceDesignerIntermediateClass", occurrence: 1); + builder.Output.AssertTargetIsNotSkipped ("_GenerateResourceDesignerIntermediateClass"); builder.Output.AssertTargetIsSkipped ("_GenerateResourceDesignerAssembly", occurrence: 2); builder.BuildLogFile = "build2.log"; Assert.IsTrue (builder.RunTarget (proj, "SignAndroidPackage", parameters: parameters), $"{proj.ProjectName} should succeed 2"); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BaseTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BaseTest.cs index 1ce0280f232..1d3d5dca3fe 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BaseTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BaseTest.cs @@ -35,7 +35,9 @@ public void AssertCommercialBuild (bool fail = false) { if (!TestEnvironment.CommercialBuildAvailable) { var message = $"'{TestName}' requires a commercial build of .NET for Android."; - if (fail) { + var runningOnCI = false; + bool.TryParse (Environment.GetEnvironmentVariable ("RunningOnCI"), out runningOnCI); + if (fail || runningOnCI) { Assert.Fail (message); } else { Assert.Ignore (message); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/ProjectBuilder.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/ProjectBuilder.cs index 514c16f7312..c601a43d897 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/ProjectBuilder.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/ProjectBuilder.cs @@ -101,11 +101,12 @@ public bool UpdateAndroidResources (XamarinProject project, bool doNotCleanupOnU public bool DesignTimeBuild (XamarinProject project, string target = "Compile", bool doNotCleanupOnUpdate = false, string [] parameters = null) { if (parameters == null) { - return RunTarget (project, target, doNotCleanupOnUpdate, parameters: new string [] { "DesignTimeBuild=True" }); + return RunTarget (project, target, doNotCleanupOnUpdate, parameters: new string [] { "DesignTimeBuild=True", "SkipCompilerExecution=true" }); } else { - var designTimeParameters = new string [parameters.Length + 1]; + var designTimeParameters = new string [parameters.Length + 2]; parameters.CopyTo (designTimeParameters, 0); - designTimeParameters [parameters.Length] = "DesignTimeBuild=True"; + designTimeParameters [designTimeParameters.Length - 2] = "DesignTimeBuild=True"; + designTimeParameters [designTimeParameters.Length - 1] = "SkipCompilerExecution=true"; return RunTarget (project, target, doNotCleanupOnUpdate, parameters: designTimeParameters); } } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/TestEnvironment.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/TestEnvironment.cs index 52f4a8ba835..a52ce07494f 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/TestEnvironment.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/TestEnvironment.cs @@ -148,7 +148,17 @@ public static bool UseLocalBuildOutput { } } - public static bool CommercialBuildAvailable => File.Exists (Path.Combine (AndroidMSBuildDirectory, "Xamarin.Android.Common.Debugging.targets")); + public static bool CommercialBuildAvailable { + get { + var result = File.Exists (Path.Combine (AndroidMSBuildDirectory, "Xamarin.Android.Common.Debugging.targets")); + Console.WriteLine ($"DEBUG! checking `{AndroidMSBuildDirectory}` for `Xamarin.Android.Common.Debugging.targets`: {result}."); + if (!result) { + Console.WriteLine ($"DEBUG! checking `{LocalDotNetAndroidSdkDirectory}\tools` for `Xamarin.Android.Common.Debugging.targets`: {result}."); + result = File.Exists (Path.Combine (LocalDotNetAndroidSdkDirectory, "tools", "Xamarin.Android.Common.Debugging.targets")); + } + return result; + } + } public static string OSBinDirectory { get { diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/FileResourceParser.cs b/src/Xamarin.Android.Build.Tasks/Utilities/FileResourceParser.cs index cfac54ddb42..a5e75a0e4be 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/FileResourceParser.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/FileResourceParser.cs @@ -1,5 +1,6 @@ using System; using System.CodeDom; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; @@ -11,6 +12,7 @@ using System.Text.RegularExpressions; using Microsoft.Build.Utilities; using Microsoft.Android.Build.Tasks; +using System.IO.Compression; namespace Xamarin.Android.Tasks { @@ -43,7 +45,7 @@ protected XDocument LoadPublicXml () { return null; } - public IList Parse (string resourceDirectory, IEnumerable additionalResourceDirectories, Dictionary resourceMap) + public IList Parse (string resourceDirectory, IEnumerable additionalResourceDirectories, IEnumerable aarLibraries, Dictionary resourceMap) { Log.LogDebugMessage ($"Parsing Directory {resourceDirectory}"); publicXml = LoadPublicXml (); @@ -71,6 +73,40 @@ public IList Parse (string resourceDirectory, IEnumerable additionalR Log.LogDebugMessage ($"Skipping non-existent directory: {dir}"); } } + foreach (var aar in aarLibraries ?? Array.Empty()) { + Log.LogDebugMessage ($"Processing Aar file {aar}"); + if (!File.Exists (aar)) { + Log.LogDebugMessage ($"Skipping non-existent aar: {aar}"); + continue; + } + using (var file = File.Open (aar, FileMode.Open, FileAccess.ReadWrite, FileShare.Read)) { + using var zip = new ZipArchive (file); + foreach (var entry in zip.Entries) { + if (entry.IsDirectory ()) + continue; + if (!entry.FullName.StartsWith ("res")) + continue; + var ext = Path.GetExtension (entry.FullName); + var path = Directory.GetParent (entry.FullName).Name; + if (ext == ".xml" || ext == ".axml") { + if (string.Compare (path, "raw", StringComparison.OrdinalIgnoreCase) != 0) { + var ms = MemoryStreamPool.Shared.Rent (); + try { + using (var entryStream = entry.Open ()) { + entryStream.CopyTo (ms); + } + ms.Position = 0; + using XmlReader reader = XmlReader.Create (ms); + ProcessXmlFile (reader, resources); + } finally { + MemoryStreamPool.Shared.Return (ms); + } + } + } + ProcessResourceFile (entry.FullName, resources, processXml: false); + } + } + } // now generate the Id's we need in a specific order List declarationIds = new List (); @@ -172,7 +208,7 @@ int GetId (ICollection resources, string identifier) return -1; } - void ProcessResourceFile (string file, Dictionary> resources) + void ProcessResourceFile (string file, Dictionary> resources, bool processXml = true) { Log.LogDebugMessage ($"{nameof(ProcessResourceFile)} {file}"); var fileName = Path.GetFileNameWithoutExtension (file); @@ -181,6 +217,10 @@ void ProcessResourceFile (string file, Dictionary> resour if (fileName.EndsWith (".9", StringComparison.OrdinalIgnoreCase)) fileName = Path.GetFileNameWithoutExtension (fileName); var path = Directory.GetParent (file).Name; + if (!processXml) { + CreateResourceField (path, fileName, resources); + return; + } var ext = Path.GetExtension (file); switch (ext) { case ".xml": @@ -299,8 +339,6 @@ void ProcessStyleable (XmlReader reader, Dictionary> reso fields.Add (r); } } - if (field.Type != RType.Array) - return; arrayMapping.Add (field, fields.ToArray ()); field.Ids = new int [attribs.Count]; @@ -321,72 +359,81 @@ resources [field.ResourceTypeName].Add (new R () { void ProcessXmlFile (string file, Dictionary> resources) { - Log.LogDebugMessage ($"{nameof(ProcessXmlFile)}"); using (var reader = XmlReader.Create (file)) { - while (reader.Read ()) { - if (reader.NodeType == XmlNodeType.Whitespace || reader.NodeType == XmlNodeType.Comment) - continue; - if (reader.IsStartElement ()) { - var elementName = reader.Name; - var elementNS = reader.NamespaceURI; - if (!string.IsNullOrEmpty (elementNS)) { - if (elementNS != "http://schemas.android.com/apk/res/android") - continue; - } - if (elementName == "declare-styleable" || elementName == "configVarying" || elementName == "add-resource") { - ProcessStyleable (reader.ReadSubtree (), resources); + ProcessXmlFile (reader, resources); + } + } + + void ProcessXmlFile (XmlReader reader, Dictionary> resources) + { + Log.LogDebugMessage ($"{nameof(ProcessXmlFile)}"); + while (reader.Read ()) { + if (reader.NodeType == XmlNodeType.Whitespace || reader.NodeType == XmlNodeType.Comment) + continue; + if (reader.IsStartElement ()) { + var elementName = reader.Name; + var elementNS = reader.NamespaceURI; + if (!string.IsNullOrEmpty (elementNS)) { + if (elementNS != "http://schemas.android.com/apk/res/android") continue; + } + if (elementName == "declare-styleable" || elementName == "configVarying" || elementName == "add-resource") { + try { + ProcessStyleable (reader.ReadSubtree (), resources); + } catch (Exception ex) { + Log.LogErrorFromException (ex); } - if (reader.HasAttributes) { - string name = null; - string type = null; - string id = null; - string custom_id = null; - while (reader.MoveToNextAttribute ()) { - if (reader.LocalName == "name") - name = reader.Value; - if (reader.LocalName == "type") - type = reader.Value; - if (reader.LocalName == "id") { - string[] values = reader.Value.Split ('/'); - if (values.Length != 2) { - id = reader.Value.Replace ("@+id/", "").Replace ("@id/", ""); - } else { - if (values [0] != "@+id" && values [0] != "@id" && !values [0].Contains ("android:")) { - custom_id = values [0].Replace ("@", "").Replace ("+", ""); - } - id = values [1]; + continue; + } + if (reader.HasAttributes) { + string name = null; + string type = null; + string id = null; + string custom_id = null; + while (reader.MoveToNextAttribute ()) { + if (reader.LocalName == "name") + name = reader.Value; + if (reader.LocalName == "type") + type = reader.Value; + if (reader.LocalName == "id") { + string[] values = reader.Value.Split ('/'); + if (values.Length != 2) { + id = reader.Value.Replace ("@+id/", "").Replace ("@id/", ""); + } else { + if (values [0] != "@+id" && values [0] != "@id" && !values [0].Contains ("android:")) { + custom_id = values [0].Replace ("@", "").Replace ("+", ""); } - - } - if (reader.LocalName == "inflatedId") { - string inflateId = reader.Value.Replace ("@+id/", "").Replace ("@id/", ""); - var r = new R () { - ResourceTypeName = "id", - Identifier = inflateId, - Id = -1, - }; - Log.LogDebugMessage ($"Adding 1 {r}"); - resources[r.ResourceTypeName].Add (r); + id = values [1]; } + } - if (name?.Contains ("android:") ?? false) - continue; - if (id?.Contains ("android:") ?? false) - continue; - // Move the reader back to the element node. - reader.MoveToElement (); - if (!string.IsNullOrEmpty (name)) { - CreateResourceField (type ?? elementName, name, resources); - } - if (!string.IsNullOrEmpty (custom_id) && !resources.ContainsKey (custom_id)) { - resources.Add (custom_id, new SortedSet (new RComparer ())); - custom_types.Add (custom_id); - } - if (!string.IsNullOrEmpty (id)) { - CreateResourceField (custom_id ?? "id", id.Replace ("-", "_").Replace (".", "_"), resources); + if (reader.LocalName == "inflatedId") { + string inflateId = reader.Value.Replace ("@+id/", "").Replace ("@id/", ""); + var r = new R () { + ResourceTypeName = "id", + Identifier = inflateId, + Id = -1, + }; + Log.LogDebugMessage ($"Adding 1 {r}"); + resources[r.ResourceTypeName].Add (r); } } + if (name?.Contains ("android:") ?? false) + continue; + if (id?.Contains ("android:") ?? false) + continue; + // Move the reader back to the element node. + reader.MoveToElement (); + if (!string.IsNullOrEmpty (name)) { + CreateResourceField (type ?? elementName, name, resources); + } + if (!string.IsNullOrEmpty (custom_id) && !resources.ContainsKey (custom_id)) { + resources.Add (custom_id, new SortedSet (new RComparer ())); + custom_types.Add (custom_id); + } + if (!string.IsNullOrEmpty (id)) { + CreateResourceField (custom_id ?? "id", id.Replace ("-", "_").Replace (".", "_"), resources); + } } } } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ZipArchiveEntryExtensions.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ZipArchiveEntryExtensions.cs new file mode 100644 index 00000000000..5d1743580c3 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ZipArchiveEntryExtensions.cs @@ -0,0 +1,8 @@ +using System; +using System.IO.Compression; +public static class ZipArchiveEntryExtensions { + public static bool IsDirectory (this ZipArchiveEntry entry) + { + return entry.Length == 0 && entry.FullName.EndsWith ("/", StringComparison.Ordinal); + } +} \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index ba56eff5b92..65f767b5325 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -118,7 +118,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. --> + Condition="Exists('$(MSBuildThisFileDirectory)Xamarin.Installer.Common.props') And '$(DesignTimeBuild)' != 'true'"/> @@ -361,7 +361,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. - + @@ -2865,14 +2865,14 @@ because xbuild doesn't support framework reference assemblies. + Condition="Exists('$(MSBuildThisFileDirectory)Xamarin.Android.Common.Debugging.targets') And '$(DesignTimeBuild)' != 'true' "/> + Condition="Exists('$(MSBuildThisFileDirectory)Xamarin.Installer.Common.targets') And '$(DesignTimeBuild)' != 'true' "/>