Skip to content

Commit 9ed1469

Browse files
authored
[Xamarin.Android.Build.Tasks] Rework DTB to use .aar files directly (#9700)
Context: https://developer.android.com/studio/projects/android-library#aar-contents One of the big issues we have with design time builds is the shear amount of time they take. This is principally because we have to discover and extract *all* the `.aar`/`.zip` files which we need before we can do a build. This used to be because we embedded the data into the assemblies, but we ship these resources as `.aar` file these days. Let's take advantage of that. Rather then extracting to disk and then reading the files from disk, we instead read the `.aar` directly for a design time build. We extract the required data into memory which will allow us to generate the required `R.txt` file. Note that only a small amount of data is needed from the `.aar`, its mostly entry filenames and the contents of any `.xml`/`.axml` files which we find in the `res` folder. This will cut down the amount of data we need. We only use this for design time builds. The typical build is left as-is, and if the on disk cache is available the design time build will use that instead of the `.aar`s. One of the things that was missing from our design time build tests was the `SkipCompilerExecution` MSBuild parameter. This is one of the important parameters which VS uses, as it stops the `<Csc/>` Task from creating an assembly on disk. This was the main speed issue we had in our unit tests, and adding this flag cut a second off of our design time build times. | | Average Time | | ------------- | ------------: | | ef9912a | 2026.2504ms | | This Commit | 592.1775ms |
1 parent 7a772f0 commit 9ed1469

21 files changed

+291
-109
lines changed

build-tools/xaprepare/xaprepare/Steps/Step_CopyExtraResultFilesForCI.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ void CopyExtraBuildFiles (string destinationRoot, Context context)
9090
"*log",
9191
"TestOutput-*.txt",
9292
"Timing_*",
93+
"*.runsettings",
9394
};
9495

9596
void CopyExtraTestFiles (string destinationRoot, Context context)

src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Aapt2.targets

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
2727

2828

2929
<Target Name="_InjectAaptDependencies">
30-
<PropertyGroup>
30+
<PropertyGroup Condition="'$(DesignTimeBuild)' != 'True' Or ('$(DesignTimeBuild)' == 'True' And '$(AndroidUseManagedDesignTimeResourceGenerator)' == 'False') ">
3131
<_SetLatestTargetFrameworkVersionDependsOnTargets>
3232
$(_SetLatestTargetFrameworkVersionDependsOnTargets);
3333
_CreateAapt2VersionCache;

src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Resource.Designer.targets

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ Copyright (C) 2016 Xamarin. All rights reserved.
4444
This will mean only one copy of the assembly will be created. Rather than one
4545
per TargetFramework.
4646
-->
47+
<_DesignerIntermediateOutputPath Condition=" '$(_DesignerIntermediateOutputPath)' == '' And '$(_OuterIntermediateOutputPath)' != '' And '$(AndroidUseDesignerAssembly)' == 'True' And '$(DesignTimeBuild)' == 'true' ">$(_OuterIntermediateOutputPath)designtime\</_DesignerIntermediateOutputPath>
4748
<_DesignerIntermediateOutputPath Condition=" '$(_DesignerIntermediateOutputPath)' == '' And '$(_OuterIntermediateOutputPath)' != '' ">$(_OuterIntermediateOutputPath)</_DesignerIntermediateOutputPath>
49+
<_DesignerIntermediateOutputPath Condition=" '$(_DesignerIntermediateOutputPath)' == '' And '$(AndroidUseDesignerAssembly)' == 'True' And '$(DesignTimeBuild)' == 'true' ">$(IntermediateOutputPath)designtime\</_DesignerIntermediateOutputPath>
4850
<_DesignerIntermediateOutputPath Condition=" '$(_DesignerIntermediateOutputPath)' == '' ">$(IntermediateOutputPath)</_DesignerIntermediateOutputPath>
4951
<_GenerateResourceDesignerAssemblyOutput>$(_DesignerIntermediateOutputPath)$(_DesignerAssemblyName).dll</_GenerateResourceDesignerAssemblyOutput>
5052
<_GenerateResourceDesignerClassFile Condition=" '$(Language)' == 'F#' ">$(_DesignerIntermediateOutputPath)_$(_DesignerAssemblyName).fs</_GenerateResourceDesignerClassFile>
@@ -56,15 +58,45 @@ Copyright (C) 2016 Xamarin. All rights reserved.
5658
<Message Text="IntermediateOutputPath: $(IntermediateOutputPath)" />
5759
</Target>
5860

61+
<Target Name="_GetProjectResourceDirectory" Returns="@(_ProjectReferenceResourceDirectory)">
62+
<ItemGroup>
63+
<_ProjectReferenceResourceDirectory Include="$(MSBuildProjectDirectory)\$(MonoAndroidResourcePrefix)"
64+
Condition=" !Exists('$(OutputPath)$(TargetName).aar') "
65+
StampFile="$(MSBuildProjectFile)"
66+
/>
67+
</ItemGroup>
68+
</Target>
69+
70+
<Target Name="_CollectProjectReferenceResources"
71+
Condition=" '$(AndroidUseDesignerAssembly)' == 'True' And '$(DesignTimeBuild)' == 'True' "
72+
>
73+
<MSBuild
74+
Projects="@(ProjectReference)"
75+
Targets="_GetProjectResourceDirectory"
76+
SkipNonexistentTargets="true"
77+
>
78+
<Output TaskParameter="TargetOutputs" ItemName="LibraryResourceDirectories" />
79+
</MSBuild>
80+
</Target>
81+
82+
<Target Name="_CalculateDesignTimeAars" Condition=" '$(DesignTimeBuild)' == 'True' ">
83+
<ItemGroup>
84+
<!-- Only use the aar files if we have not extracted the data -->
85+
<_DesignTimeAarFiles Include="@(AndroidAarLibrary)" Condition=" '@(LibraryResourceDirectories->Count())' == '0' " />
86+
<_DesignTimeAarFiles Include="@(LibraryProjectZip)" Condition=" '%(LibraryProjectZip.Extension)' == '.aar' and '@(LibraryResourceDirectories->Count())' == '0' " />
87+
</ItemGroup>
88+
</Target>
89+
5990
<Target Name="_GenerateRtxt"
6091
Condition="'$(AndroidUseDesignerAssembly)' == 'True' And '$(DesignTimeBuild)' == 'True' "
61-
DependsOnTargets="_CreatePropertiesCache;_ResolveSdks;_ResolveAndroidTooling;_GetJavaPlatformJar;_GenerateAndroidResourceDir;_SetupDesignerProperties"
92+
DependsOnTargets="_CreatePropertiesCache;_ResolveSdks;_ResolveAndroidTooling;_GetJavaPlatformJar;_GenerateAndroidResourceDir;_SetupDesignerProperties;_CollectProjectReferenceResources;_CalculateDesignTimeAars"
6293
Inputs="$(_AndroidResFlagFile);@(_AndroidResourceDest);@(LibraryResourceDirectories->'%(StampFile)')"
6394
Outputs="$(_DesignerIntermediateOutputPath)R.txt"
6495
>
6596
<!-- Generate an R.txt file using the Managed Parser -->
6697
<GenerateRtxt
6798
AdditionalResourceDirectories="@(LibraryResourceDirectories)"
99+
AarLibraries="@(_DesignTimeAarFiles)"
68100
CaseMapFile="$(_GenerateResourceCaseMapFile)"
69101
JavaPlatformJarPath="$(JavaPlatformJarPath)"
70102
ResourceDirectory="$(MonoAndroidResDirIntermediate)"
@@ -75,13 +107,14 @@ Copyright (C) 2016 Xamarin. All rights reserved.
75107
</Target>
76108

77109
<Target Name="_GenerateResourceCaseMap"
78-
DependsOnTargets="_ComputeAndroidResourcePaths;_SetupDesignerProperties;_GetLibraryImports"
110+
DependsOnTargets="_ComputeAndroidResourcePaths;_SetupDesignerProperties;_GetLibraryImports;_CollectProjectReferenceResources;_CalculateDesignTimeAars"
79111
Inputs="@(_AndroidResourceDest);@(LibraryResourceDirectories->'%(StampFile)')"
80112
Outputs="$(_GenerateResourceCaseMapFile)"
81113
>
82114
<!-- Generate a ResourceMap file for the project and its resources -->
83115
<GenerateResourceCaseMap
84116
AdditionalResourceDirectories="@(LibraryResourceDirectories)"
117+
AarLibraries="@(_DesignTimeAarFiles)"
85118
OutputFile="$(_GenerateResourceCaseMapFile)"
86119
ProjectDir="$(ProjectDir)"
87120
ResourceDirectory="$(MonoAndroidResDirIntermediate)"
@@ -159,6 +192,8 @@ Copyright (C) 2016 Xamarin. All rights reserved.
159192
<PropertyGroup>
160193
<_BuildResourceDesignerDependsOn>
161194
_SetupDesignerProperties;
195+
_ResolveAars;
196+
_CalculateDesignTimeAars;
162197
_GenerateResourceCaseMap;
163198
_GenerateRtxt;
164199
_GenerateResourceDesignerIntermediateClass;

src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AndroidLibraries.targets

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ projects.
1818
<_AarOutputPath>$(OutputPath)$(TargetName).aar</_AarOutputPath>
1919
</PropertyGroup>
2020

21-
<Target Name="_ResolveAars">
21+
<Target Name="_ResolveAars" AfterTargets="ResolveReferences">
2222
<ItemGroup>
2323
<_AarSearchDirectory Include="@(_ReferencePath->'%(RootDir)%(Directory)')" />
2424
<_AarSearchDirectory Include="@(_ReferenceDependencyPaths->'%(RootDir)%(Directory)')" />

src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ _ResolveAssemblies MSBuild target.
9090
<_AdditionalProperties>
9191
_ComputeFilesToPublishForRuntimeIdentifiers=true
9292
;SelfContained=true
93+
;DesignTimeBuild=$(DesignTimeBuild)
9394
;AppendRuntimeIdentifierToOutputPath=true
9495
;ResolveAssemblyReferencesFindRelatedSatellites=false
9596
;SkipCompilerExecution=true
@@ -105,6 +106,7 @@ _ResolveAssemblies MSBuild target.
105106
<_ProjectToBuild Include="$(MSBuildProjectFile)" AdditionalProperties="RuntimeIdentifier=%(_RIDs.Identity);$(_AdditionalProperties)" />
106107
</ItemGroup>
107108
<MSBuild
109+
Condition=" '$(DesignTimeBuild)' != 'true' "
108110
Projects="@(_ProjectToBuild)"
109111
BuildInParallel="$(_AndroidBuildRuntimeIdentifiersInParallel)"
110112
Targets="_ComputeFilesToPublishForRuntimeIdentifiers">

src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,22 +112,16 @@ properties that determine build ordering.
112112
<ResolveReferencesDependsOn>
113113
$(CoreResolveReferencesDependsOn);
114114
UpdateAndroidResources;
115-
_BuildResourceDesigner;
116115
UpdateAndroidInterfaceProxies;
117116
_CheckForInvalidDesignerConfig;
118117
</ResolveReferencesDependsOn>
119-
<DesignTimeResolveAssemblyReferencesDependsOn>
120-
$(DesignTimeResolveAssemblyReferencesDependsOn);
121-
_BuildResourceDesigner;
122-
</DesignTimeResolveAssemblyReferencesDependsOn>
123118
<_UpdateAndroidResourcesDependsOn>
124119
$(CoreResolveReferencesDependsOn);
125120
_CreatePropertiesCache;
126121
_CheckForDeletedResourceFile;
127122
_ComputeAndroidResourcePaths;
128123
_UpdateAndroidResgen;
129124
_CreateAar;
130-
_BuildResourceDesigner;
131125
</_UpdateAndroidResourcesDependsOn>
132126
<CompileDependsOn>
133127
_SetupMSBuildAllProjects;
@@ -142,9 +136,9 @@ properties that determine build ordering.
142136
_CollectGeneratedManagedBindingFiles;
143137
_ClearGeneratedManagedBindings;
144138
AddBindingsToCompile;
139+
$(CompileDependsOn);
145140
_BuildResourceDesigner;
146141
_AddResourceDesignerFiles;
147-
$(CompileDependsOn);
148142
_CheckAndroidHttpClientHandlerType;
149143
</CompileDependsOn>
150144
<CoreCompileDependsOn>
@@ -171,6 +165,7 @@ properties that determine build ordering.
171165
</CleanDependsOn>
172166
<ExportJarToXmlDependsOnTargets>
173167
_ResolveMonoAndroidSdks;
168+
ResolveAssemblyReferences;
174169
_ExtractLibraryProjectImports;
175170
_GetLibraryImports;
176171
_ExtractAar;

src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.targets

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
<Import Project="Microsoft.Android.Sdk.BundledVersions.targets" />
2727
<Import Project="Microsoft.Android.Sdk.DefaultProperties.targets" />
2828
<Import Project="$(MSBuildThisFileDirectory)..\tools\Xamarin.Android.Common.Debugging.props"
29-
Condition="Exists('$(MSBuildThisFileDirectory)..\tools\Xamarin.Android.Common.Debugging.props')"/>
30-
<Import Project="Microsoft.Android.Sdk.NativeAOT.targets" Condition=" '$(_AndroidRuntime)' == 'NativeAOT' " />
29+
Condition="Exists('$(MSBuildThisFileDirectory)..\tools\Xamarin.Android.Common.Debugging.props') And '$(DesignTimeBuild)' != 'true' "/>
30+
<Import Project="Microsoft.Android.Sdk.NativeAOT.targets" Condition=" '$(_AndroidRuntime)' == 'NativeAOT' And '$(DesignTimeBuild)' != 'true' " />
3131

3232
</Project>

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@
33
using System.Collections.Generic;
44
using System.IO;
55
using System.Linq;
6+
using System.Text;
67
using Microsoft.Build.Framework;
78
using Microsoft.Build.Utilities;
89
using Microsoft.Android.Build.Tasks;
10+
using Xamarin.Android.Tools;
11+
//using Xamarin.Tools.Zip;
12+
using System.IO.Compression;
913

1014
namespace Xamarin.Android.Tasks
1115
{
@@ -22,6 +26,8 @@ public class GenerateResourceCaseMap : AndroidTask
2226

2327
public ITaskItem[] AdditionalResourceDirectories { get; set; }
2428

29+
public string[] AarLibraries { get; set; }
30+
2531
[Required]
2632
public ITaskItem OutputFile { get; set; }
2733

@@ -64,6 +70,42 @@ public override bool RunTask ()
6470
AddRename (tok [1].Replace ('/', Path.DirectorySeparatorChar), tok [0].Replace ('/', Path.DirectorySeparatorChar));
6571
}
6672
}
73+
var resmap = ".net/__res_name_case_map.txt";
74+
foreach (var aar in AarLibraries ?? Array.Empty<string>()) {
75+
Log.LogDebugMessage ($"Processing Aar file {aar}");
76+
if (!File.Exists (aar)) {
77+
Log.LogDebugMessage ($"Skipping non-existent aar: {aar}");
78+
continue;
79+
}
80+
using (var file = File.OpenRead (aar)) {
81+
using var zip = new ZipArchive (file);
82+
var entry = zip.GetEntry (resmap);
83+
if (entry is null) {
84+
Log.LogDebugMessage ($"Skipping non-existent file: {resmap}");
85+
continue;
86+
}
87+
Log.LogDebugMessage ($"Found: {entry.FullName}");
88+
var ms = MemoryStreamPool.Shared.Rent ();
89+
try {
90+
using (var entryStream = entry.Open ()) {
91+
entryStream.CopyTo (ms);
92+
}
93+
ms.Position = 0;
94+
using (var reader = new StreamReader (ms, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: -1, leaveOpen: true)) {
95+
string line;
96+
// Read each line until the end of the file
97+
while ((line = reader.ReadLine()) != null) {
98+
if (string.IsNullOrEmpty (line))
99+
continue;
100+
string [] tok = line.Split (';');
101+
AddRename (tok [1].Replace ('/', Path.DirectorySeparatorChar), tok [0].Replace ('/', Path.DirectorySeparatorChar));
102+
}
103+
}
104+
} finally {
105+
MemoryStreamPool.Shared.Return (ms);
106+
}
107+
}
108+
}
67109

68110
if (MonoAndroidHelper.SaveMapFile (BuildEngine4, Path.GetFullPath (OutputFile.ItemSpec), resource_fixup)) {
69111
Log.LogDebugMessage ($"Writing to: {OutputFile.ItemSpec}");

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ public class GenerateRtxt : AndroidTask
1717
public string ResourceDirectory { get; set; }
1818
public string[] AdditionalResourceDirectories { get; set; }
1919

20+
public string[] AarLibraries { get; set; }
21+
2022
public string JavaPlatformJarPath { get; set; }
2123

2224
public string ResourceFlagFile { get; set; }
@@ -31,7 +33,7 @@ public override bool RunTask ()
3133

3234
var javaPlatformDirectory = string.IsNullOrEmpty (JavaPlatformJarPath) ? "" : Path.GetDirectoryName (JavaPlatformJarPath);
3335
var parser = new FileResourceParser () { Log = Log, JavaPlatformDirectory = javaPlatformDirectory, ResourceFlagFile = ResourceFlagFile};
34-
var resources = parser.Parse (ResourceDirectory, AdditionalResourceDirectories, resource_fixup);
36+
var resources = parser.Parse (ResourceDirectory, AdditionalResourceDirectories, AarLibraries, resource_fixup);
3537

3638
// only update if it changed.
3739
writer.Write (RTxtFile, resources);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1029,7 +1029,7 @@ public void BuildAppWithManagedResourceParserAndLibraries ()
10291029
Assert.LessOrEqual (libBuilder.LastBuildTime.TotalMilliseconds, maxBuildTimeMs, $"DesignTime build should be less than {maxBuildTimeMs} milliseconds.");
10301030
Assert.IsFalse (libProj.CreateBuildOutput (libBuilder).IsTargetSkipped ("_ManagedUpdateAndroidResgen"),
10311031
"Target '_ManagedUpdateAndroidResgen' should have run.");
1032-
Assert.IsFalse (appBuilder.DesignTimeBuild (appProj), "Application project should have built");
1032+
Assert.IsTrue (appBuilder.DesignTimeBuild (appProj), "Application project should have built");
10331033
Assert.LessOrEqual (appBuilder.LastBuildTime.TotalMilliseconds, maxBuildTimeMs, $"DesignTime build should be less than {maxBuildTimeMs} milliseconds.");
10341034
Assert.IsFalse (appProj.CreateBuildOutput (appBuilder).IsTargetSkipped ("_ManagedUpdateAndroidResgen"),
10351035
"Target '_ManagedUpdateAndroidResgen' should have run.");

0 commit comments

Comments
 (0)