Skip to content

Commit 06b186d

Browse files
committed
[Xamarin.Android.Build.Tasks] Run aapt2 compile incrementally.
The `aapt2 compile` command runs in two modes. The one we currently use is the `archive` mode. We calling `aapt2 compile` with the `--dir` argument you end up generating one `flata` archive for all the files. The side effect of this is that even if you only change one file, it will need to regenerate the entire `flata` archive. But it has a second mode. Rather than using `--dir` you just send in a single file. This then writes a single `.flat` file to the output directory. While this does mean you have to call `aapt2 compile` for EVERY file, it does mean we can leverage MSbuilds support for partial targets. This means MSbuild will detect ONLY the files which changed and allow us to call `aapt2 compile` with just THOSE files. One exception to this new system are references which use the `AndroidSkipResourceProcessing` metadata. In those cases the chance of those libraries being updated on a regular basis is quite low. So in that case using a `flata` archive will be better since the files won't be changing much. While this may impact on initial build times, the goal is to make incremental builds quicker. This is especially true for users to use ALLOT of `AndroidResource` items. A note regarding the `aapt2 daemon` mode. In order to write accented characters we need to set the `StandardInput` encoding to UTF8. This is not possible directly in netstandard 2.0. So we have to use `Console.InputEncoding` instead. Also not that we MUST not include a BOM when writting the commands. This is because `aapt2` will try to parse the BOM as command characters. Also the `aapt2 link` command sometimes reports it is "Done" before it has even written the archive for the file. So we can get into a position where we think we are done but the file is not on disk. So we have had to include a nasty wait which will poll for the existence of the expected output file and only return when it exists. The good news is we know at this point if the command failed or not, so we can bypass the check on failure.
1 parent 5c6c828 commit 06b186d

27 files changed

+1238
-303
lines changed

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
"bin/TestDebug/MSBuildDeviceIntegration/MSBuildDeviceIntegration.dll",
55
"bin/TestDebug/Xamarin.Android.Build.Tests.dll",
66
"bin/TestDebug/Xamarin.Android.Build.Tests.Commercial.dll",
7-
]
7+
],
8+
"cmake.configureOnOpen": false
89
}

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,19 @@ Copyright (C) 2019 Microsoft Corporation. All rights reserved.
1616

1717
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
1818

19+
<Target Name="_InjectAaptDependencies" AfterTargets="_ResolveSdks" Condition=" '$(_AndroidUseAapt2)' != 'True' ">
20+
<PropertyGroup>
21+
<_UpdateAndroidResgenInputs>
22+
$(_UpdateAndroidResgenInputs);
23+
@(_LibraryResourceDirectoryStamps);
24+
</_UpdateAndroidResgenInputs>
25+
<_CreateBaseApkInputs>
26+
$(_CreateBaseApkInputs);
27+
@(_LibraryResourceDirectoryStamps);
28+
</_CreateBaseApkInputs>
29+
</PropertyGroup>
30+
</Target>
31+
1932
<Target Name="_UpdateAndroidResgenAapt"
2033
Condition="'$(_AndroidUseAapt2)' != 'True'">
2134

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

Lines changed: 88 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,30 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
1717

1818
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
1919

20+
<PropertyGroup>
21+
<Aapt2DaemonMaxInstanceCount Condition=" '$(Aapt2DaemonMaxInstanceCount)' == '' " >0</Aapt2DaemonMaxInstanceCount>
22+
<_Aapt2DaemonKeepInDomain Condition=" '$(_Aapt2DaemonKeepInDomain)' == '' ">false</_Aapt2DaemonKeepInDomain>
23+
</PropertyGroup>
24+
25+
26+
<Target Name="_InjectAapt2Dependencies" AfterTargets="_ResolveSdks" Condition=" '$(_AndroidUseAapt2)' == 'True' ">
27+
<PropertyGroup>
28+
<_SetLatestTargetFrameworkVersionDependsOnTargets>
29+
$(_SetLatestTargetFrameworkVersionDependsOnTargets);
30+
_CreateAapt2VersionCache;
31+
</_SetLatestTargetFrameworkVersionDependsOnTargets>
32+
<_PrepareUpdateAndroidResgenDependsOnTargets>
33+
_CompileResources;
34+
_Aapt2UpdateAndroidResgenInputs;
35+
$(_PrepareUpdateAndroidResgenDependsOnTargets);
36+
</_PrepareUpdateAndroidResgenDependsOnTargets>
37+
<_AfterConvertCustomView>
38+
$(_AfterConvertCustomView);
39+
_FixupCustomViewsForAapt2;
40+
</_AfterConvertCustomView>
41+
</PropertyGroup>
42+
</Target>
43+
2044
<Target Name="_ReadAapt2VersionCache">
2145
<ReadLinesFromFile File="$(_AndroidAapt2VersionFile)"
2246
Condition="Exists('$(_AndroidAapt2VersionFile)')">
@@ -37,7 +61,9 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
3761
/>
3862
<ItemGroup Condition="'$(_Aapt2Version)' != '@(_Aapt2VersionCache)'">
3963
<_CompiledFlataArchive Include="$(_AndroidLibrayProjectIntermediatePath)**\*.flata" />
40-
<_CompiledFlataArchive Include="$(IntermediateOutputPath)\*.flata" />
64+
<_CompiledFlataArchive Include="$(_AndroidLibrayProjectIntermediatePath)**\*.flat" />
65+
<_CompiledFlataArchive Include="$(_AndroidLibraryFlatFilesDirectory)*.flat" />
66+
<_CompiledFlataArchive Include="$(_AndroidLibraryFlatArchivesDirectory)\*.flata" />
4167
<_CompiledFlataStamp Include="$(_AndroidLibrayProjectIntermediatePath)**\compiled.stamp" />
4268
</ItemGroup>
4369
<Delete
@@ -54,68 +80,19 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
5480
LibraryProjectIntermediatePath="$(_AndroidLibrayProjectIntermediatePath)"
5581
StampDirectory="$(_AndroidStampDirectory)">
5682
<Output TaskParameter="Output" ItemName="_LibraryResourceDirectories" />
83+
<Output TaskParameter="LibraryResourceFiles" ItemName="_LibraryResourceFiles" />
84+
<Output TaskParameter="LibraryResourceFiles" ItemName="_CompileResourcesInputs" />
5785
</CollectNonEmptyDirectories>
58-
<ComputeHash Source="@(_LibraryResourceDirectories)" >
59-
<Output TaskParameter="Output" ItemName="_LibraryResourceHashDirectories" />
60-
</ComputeHash>
61-
</Target>
62-
63-
<Target Name="_ConvertLibraryResourcesCases"
64-
Condition=" '$(_AndroidUseAapt2)' == 'True' "
65-
DependsOnTargets="_CollectLibraryResourceDirectories"
66-
Inputs="@(_LibraryResourceHashDirectories->'%(StampFile)')"
67-
Outputs="$(_AndroidStampDirectory)_ConvertLibraryResourcesCases.stamp">
68-
<ConvertResourcesCases
69-
Condition=" '@(_LibraryResourceDirectories)' != '' "
70-
ContinueOnError="$(DesignTimeBuild)"
71-
AcwMapFile="$(_AcwMapFile)"
72-
AndroidConversionFlagFile="$(_AndroidStampDirectory)_ConvertLibraryResourcesCases.stamp"
73-
CustomViewMapFile="$(_CustomViewMapFile)"
74-
ResourceDirectories="@(_LibraryResourceDirectories)"
75-
ResourceNameCaseMap="$(_AndroidResourceNameCaseMap)"
76-
/>
77-
<Touch Files="$(_AndroidStampDirectory)_ConvertLibraryResourcesCases.stamp" AlwaysCreate="True" />
78-
</Target>
79-
80-
<Target Name="_CompileAndroidLibraryResources"
81-
Condition=" '$(_AndroidUseAapt2)' == 'True' "
82-
DependsOnTargets="_ConvertLibraryResourcesCases"
83-
Inputs="@(_LibraryResourceHashDirectories->'%(StampFile)')"
84-
Outputs="@(_LibraryResourceHashDirectories->'$(_AndroidLibraryFlatArchivesDirectory)%(Hash).stamp')"
85-
>
86-
<MakeDir Directories="$(_AndroidLibraryFlatArchivesDirectory)" Condition="!Exists('$(_AndroidLibraryFlatArchivesDirectory)')" />
87-
<Aapt2Compile
88-
Condition=" '@(_LibraryResourceHashDirectories)' != '' "
89-
ContinueOnError="$(DesignTimeBuild)"
90-
ExtraArgs="$(AndroidAapt2CompileExtraArgs)"
91-
FlatArchivesDirectory="$(_AndroidLibraryFlatArchivesDirectory)"
92-
ResourceDirectories="@(_LibraryResourceHashDirectories)"
93-
ToolPath="$(Aapt2ToolPath)"
94-
ToolExe="$(Aapt2ToolExe)"
95-
/>
96-
<ItemGroup>
97-
<_MissingStampFiles Include="@(_LibraryResourceHashDirectories->'%(StampFile)')" Condition="!Exists('%(StampFile)')" />
98-
<_HashStampFiles Include="@(_LibraryResourceHashDirectories->'$(_AndroidLibraryFlatArchivesDirectory)%(Hash).stamp')" />
99-
<_HashFlataFiles Include="@(_LibraryResourceHashDirectories->'$(_AndroidLibraryFlatArchivesDirectory)%(Hash).flata')" />
100-
</ItemGroup>
101-
<Touch
102-
Files="@(_MissingStampFiles);@(_HashStampFiles)"
103-
AlwaysCreate="True"
104-
/>
105-
<ItemGroup>
106-
<FileWrites Include="@(_MissingStampFiles)" />
107-
<FileWrites Include="@(_HashStampFiles)" />
108-
<FileWrites Include="@(_HashFlataFiles)" />
109-
</ItemGroup>
11086
</Target>
11187

11288
<Target Name="_ConvertResourcesCases"
11389
Condition=" '$(_AndroidUseAapt2)' == 'True' "
114-
Inputs="$(MSBuildAllProjects);$(_AndroidBuildPropertiesCache);@(AndroidResource)"
90+
Inputs="$(MSBuildAllProjects);$(_AndroidBuildPropertiesCache);@(AndroidResource);@(_LibraryResourceDirectories->'%(StampFile)')"
11591
Outputs="$(_AndroidStampDirectory)_ConvertResourcesCases.stamp"
116-
DependsOnTargets="$(_BeforeConvertResourcesCases)"
92+
DependsOnTargets="_CollectLibraryResourceDirectories;$(_BeforeConvertResourcesCases)"
11793
>
11894
<MakeDir Directories="$(_AndroidLibraryFlatArchivesDirectory)" Condition="!Exists('$(_AndroidLibraryFlatArchivesDirectory)')" />
95+
<MakeDir Directories="$(_AndroidLibraryFlatFilesDirectory)" Condition="!Exists('$(_AndroidLibraryFlatFilesDirectory)')" />
11996
<!-- Change cases so we support mixed case resource names -->
12097
<ConvertResourcesCases
12198
ContinueOnError="$(DesignTimeBuild)"
@@ -128,36 +105,64 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
128105
<Touch Files="$(_AndroidStampDirectory)_ConvertResourcesCases.stamp" AlwaysCreate="True" />
129106
</Target>
130107

108+
<Target Name="_CalculateResourceFileName"
109+
Condition=" '$(_AndroidUseAapt2)' == 'True' ">
110+
<ItemGroup>
111+
<_CompileResourcesInputs Include="@(_AndroidResourceDest)">
112+
<StampFile>%(Identity)</StampFile>
113+
</_CompileResourcesInputs>
114+
<_CompiledFlatFiles Include="@(_CompileResourcesInputs->'%(_ArchiveDirectory)%(_FlatFile)')" />
115+
</ItemGroup>
116+
</Target>
117+
131118
<Target Name="_CompileResources"
132-
Condition=" '$(_AndroidUseAapt2)' == 'True' And '@(AndroidResource)' != '' "
133-
Inputs="$(MSBuildAllProjects);$(_AndroidBuildPropertiesCache);@(AndroidResource)"
134-
Outputs="$(_AndroidLibraryFlatArchivesDirectory)\_CompileResources.stamp"
135-
DependsOnTargets="$(_BeforeCompileResources);_ConvertResourcesCases"
119+
Condition=" '$(_AndroidUseAapt2)' == 'True' "
120+
Inputs="$(MSBuildAllProjects);$(_AndroidBuildPropertiesCache);@(_CompileResourcesInputs)"
121+
Outputs="@(_CompileResourcesInputs->'%(_ArchiveDirectory)%(_FlatFile)')"
122+
DependsOnTargets="$(_BeforeCompileResources);_ConvertResourcesCases;_CalculateResourceFileName"
136123
>
137124
<Aapt2Compile
138125
ContinueOnError="$(DesignTimeBuild)"
126+
DaemonMaxInstanceCount="$(Aapt2DaemonMaxInstanceCount)"
127+
DaemonKeepInDomain="$(_Aapt2DaemonKeepInDomain)"
139128
ExtraArgs="$(AndroidAapt2CompileExtraArgs)"
129+
FlatFilesDirectory="$(_AndroidLibraryFlatFilesDirectory)"
140130
FlatArchivesDirectory="$(_AndroidLibraryFlatArchivesDirectory)"
131+
ResourcesToCompile="@(_CompileResourcesInputs)"
141132
ResourceDirectories="$(MonoAndroidResDirIntermediate)"
142133
ResourceNameCaseMap="$(_AndroidResourceNameCaseMap)"
143134
ToolPath="$(Aapt2ToolPath)"
144135
ToolExe="$(Aapt2ToolExe)"
145136
/>
146-
<Touch Files="$(_AndroidLibraryFlatArchivesDirectory)\_CompileResources.stamp" AlwaysCreate="True" />
147-
<ItemGroup>
148-
<FileWrites Include="$(_AndroidLibraryFlatArchivesDirectory)\compiled.flata" />
149-
</ItemGroup>
137+
</Target>
138+
139+
<Target Name="_Aapt2UpdateAndroidResgenInputs">
140+
<PropertyGroup>
141+
<_UpdateAndroidResgenInputs>
142+
$(_UpdateAndroidResgenInputs);
143+
@(_CompiledFlatFiles);
144+
@(_LibraryResourceDirectoryStamps);
145+
</_UpdateAndroidResgenInputs>
146+
<_CreateBaseApkInputs>
147+
$(_CreateBaseApkInputs);
148+
@(_CompiledFlatFiles);
149+
@(_LibraryResourceDirectoryStamps);
150+
</_CreateBaseApkInputs>
151+
</PropertyGroup>
150152
</Target>
151153

152154
<Target Name="_UpdateAndroidResgenAapt2"
153-
Condition="'$(_AndroidUseAapt2)' == 'True'">
155+
Condition=" '$(_AndroidUseAapt2)' == 'True' "
156+
>
154157
<PropertyGroup>
155158
<AndroidAapt2LinkExtraArgs Condition=" '$(_AndroidUseAapt2)' == 'True' And $(AndroidResgenExtraArgs.Contains('--no-version-vectors')) And !($(AndroidAapt2LinkExtraArgs.Contains('--no-version-vectors'))) ">--no-version-vectors $(AndroidAapt2LinkExtraArgs) </AndroidAapt2LinkExtraArgs>
156159
<_Aapt2ProguardRules Condition=" '$(AndroidLinkTool)' != '' ">$(IntermediateOutputPath)aapt_rules.txt</_Aapt2ProguardRules>
157160
</PropertyGroup>
158161
<Aapt2Link
159162
Condition=" '$(_AndroidResourceDesignerFile)' != '' And '$(_AndroidUseAapt2)' == 'True' "
160163
ContinueOnError="$(DesignTimeBuild)"
164+
DaemonMaxInstanceCount="$(Aapt2DaemonMaxInstanceCount)"
165+
DaemonKeepInDomain="$(_Aapt2DaemonKeepInDomain)"
161166
ResourceNameCaseMap="$(_AndroidResourceNameCaseMap)"
162167
AssemblyIdentityMapFile="$(_AndroidLibrayProjectAssemblyMapFile)"
163168
ImportsDirectory="$(_LibraryProjectImportsDirectoryName)"
@@ -168,10 +173,9 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
168173
ApplicationName="$(_AndroidPackage)"
169174
JavaPlatformJarPath="$(JavaPlatformJarPath)"
170175
JavaDesignerOutputDirectory="$(ResgenTemporaryDirectory)"
171-
CompiledResourceFlatArchive="$(_AndroidLibraryFlatArchivesDirectory)\compiled.flata"
176+
CompiledResourceFlatFiles="@(_CompiledFlatFiles)"
172177
ManifestFiles="$(ResgenTemporaryDirectory)\AndroidManifest.xml"
173-
AdditionalResourceArchives="@(_LibraryResourceHashDirectories->'$(_AndroidLibraryFlatArchivesDirectory)%(Hash).flata')"
174-
AdditionalAndroidResourcePaths="@(_LibraryResourceHashDirectories)"
178+
AdditionalAndroidResourcePaths="@(_LibraryResourceDirectories)"
175179
YieldDuringToolExecution="$(YieldDuringToolExecution)"
176180
ResourceSymbolsTextFile="$(IntermediateOutputPath)R.txt"
177181
ResourceDirectories="$(MonoAndroidResDirIntermediate)"
@@ -192,45 +196,46 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
192196

193197
<Target Name="_FixupCustomViewsForAapt2"
194198
Condition=" '$(_AndroidUseAapt2)' == 'True' And '@(_ProcessedCustomViews)' != '' ">
195-
<Delete
196-
Files="@(_ProcessedCustomViews->'$(_AndroidLibraryFlatArchivesDirectory)%(Hash).stamp')"
197-
Condition=" '$(AndroidUseAapt2)' == 'True' And '@(_ProcessedCustomViews)' != '' "
198-
/>
199+
<ItemGroup>
200+
<_ItemsToFixup Include="@(_CompileResourcesInputs)" Condition=" '@(_ProcessedCustomViews->'%(Identity)')' == '%(Identity)' "/>
201+
</ItemGroup>
199202
<Aapt2Compile
200-
Condition=" '$(AndroidUseAapt2)' == 'True' And '@(_ProcessedCustomViews)' != '' "
203+
Condition=" '$(AndroidUseAapt2)' == 'True' And '@(_ItemsToFixup)' != '' "
201204
ContinueOnError="$(DesignTimeBuild)"
202-
ResourceDirectories="@(_ProcessedCustomViews->'%(ResourceDirectory)'->Distinct())"
205+
DaemonMaxInstanceCount="$(Aapt2DaemonMaxInstanceCount)"
206+
DaemonKeepInDomain="$(_Aapt2DaemonKeepInDomain)"
207+
ResourcesToCompile="@(_ItemsToFixup)"
208+
ResourceDirectories="$(MonoAndroidResDirIntermediate);@(_LibraryResourceDirectories)"
203209
ExtraArgs="$(AndroidAapt2CompileExtraArgs)"
210+
FlatFilesDirectory="$(_AndroidLibraryFlatFilesDirectory)"
204211
FlatArchivesDirectory="$(_AndroidLibraryFlatArchivesDirectory)"
205212
ToolPath="$(Aapt2ToolPath)"
206213
ToolExe="$(Aapt2ToolExe)">
207-
<Output TaskParameter="CompiledResourceFlatArchives" ItemName="_UpdatedFlatArchives" />
214+
<Output TaskParameter="CompiledResourceFlatFiles" ItemName="_UpdatedFlatFiles" />
208215
</Aapt2Compile>
209-
<Touch
210-
Files="@(_UpdatedFlatArchives->'$(_AndroidLibraryFlatArchivesDirectory)\%(Filename).stamp')"
211-
Condition=" '$(AndroidUseAapt2)' == 'True' And '@(_UpdatedFlatArchives)' != '' "
212-
AlwaysCreate="True"
213-
/>
216+
<Touch Files="$(_AndroidResgenFlagFile)" AlwaysCreate="True" Condition=" '@(_UpdatedFlatFiles)' != '' " />
214217
</Target>
215218

216219
<Target Name="_CreateBaseApkWithAapt2"
217-
Condition="'$(_AndroidUseAapt2)' == 'True'">
220+
Condition=" '$(_AndroidUseAapt2)' == 'True' "
221+
>
218222
<PropertyGroup>
219223
<_ProtobufFormat Condition=" '$(AndroidPackageFormat)' == 'aab' ">True</_ProtobufFormat>
220224
<_ProtobufFormat Condition=" '$(_ProtobufFormat)' == '' ">False</_ProtobufFormat>
221225
</PropertyGroup>
222226
<Aapt2Link
223227
Condition="'$(_AndroidUseAapt2)' == 'True'"
224-
CompiledResourceFlatArchive="$(_AndroidLibraryFlatArchivesDirectory)\compiled.flata"
228+
CompiledResourceFlatFiles="@(_CompiledFlatFiles)"
229+
DaemonMaxInstanceCount="$(Aapt2DaemonMaxInstanceCount)"
230+
DaemonKeepInDomain="$(_Aapt2DaemonKeepInDomain)"
225231
ResourceNameCaseMap="$(_AndroidResourceNameCaseMap)"
226232
ResourceDirectories="$(MonoAndroidResDirIntermediate)"
227233
AssemblyIdentityMapFile="$(_AndroidLibrayProjectAssemblyMapFile)"
228234
UseShortFileNames="$(UseShortFileNames)"
229235
ImportsDirectory="$(_LibraryProjectImportsDirectoryName)"
230236
OutputImportDirectory="$(_AndroidLibrayProjectIntermediatePath)"
231237
OutputFile="$(_PackagedResources)"
232-
AdditionalResourceArchives="@(_LibraryResourceHashDirectories->'$(_AndroidLibraryFlatArchivesDirectory)%(Hash).flata')"
233-
AdditionalAndroidResourcePaths="@(_LibraryResourceHashDirectories)"
238+
AdditionalAndroidResourcePaths="@(_LibraryResourceDirectories)"
234239
YieldDuringToolExecution="$(YieldDuringToolExecution)"
235240
PackageName="$(_AndroidPackage)"
236241
ApplicationName="$(_AndroidPackage)"

0 commit comments

Comments
 (0)