Skip to content

Commit

Permalink
[Xamarin.Android.Build.Tasks] Run aapt2 compile incrementally.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
dellis1972 committed Mar 20, 2020
1 parent b6eba4e commit 802f0d3
Show file tree
Hide file tree
Showing 27 changed files with 1,238 additions and 303 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
"bin/TestDebug/MSBuildDeviceIntegration/MSBuildDeviceIntegration.dll",
"bin/TestDebug/Xamarin.Android.Build.Tests.dll",
"bin/TestDebug/Xamarin.Android.Build.Tests.Commercial.dll",
]
],
"cmake.configureOnOpen": false
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@ Copyright (C) 2019 Microsoft Corporation. All rights reserved.

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

<Target Name="_InjectAaptDependencies" AfterTargets="_ResolveSdks" Condition=" '$(_AndroidUseAapt2)' != 'True' ">
<PropertyGroup>
<_UpdateAndroidResgenInputs>
$(_UpdateAndroidResgenInputs);
@(_LibraryResourceDirectoryStamps);
</_UpdateAndroidResgenInputs>
<_CreateBaseApkInputs>
$(_CreateBaseApkInputs);
@(_LibraryResourceDirectoryStamps);
</_CreateBaseApkInputs>
</PropertyGroup>
</Target>

<Target Name="_UpdateAndroidResgenAapt"
Condition="'$(_AndroidUseAapt2)' != 'True'">

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,30 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.

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

<PropertyGroup>
<Aapt2DaemonMaxInstanceCount Condition=" '$(Aapt2DaemonMaxInstanceCount)' == '' " >0</Aapt2DaemonMaxInstanceCount>
<_Aapt2DaemonKeepInDomain Condition=" '$(_Aapt2DaemonKeepInDomain)' == '' ">false</_Aapt2DaemonKeepInDomain>
</PropertyGroup>


<Target Name="_InjectAapt2Dependencies" AfterTargets="_ResolveSdks" Condition=" '$(_AndroidUseAapt2)' == 'True' ">
<PropertyGroup>
<_SetLatestTargetFrameworkVersionDependsOnTargets>
$(_SetLatestTargetFrameworkVersionDependsOnTargets);
_CreateAapt2VersionCache;
</_SetLatestTargetFrameworkVersionDependsOnTargets>
<_PrepareUpdateAndroidResgenDependsOnTargets>
_CompileResources;
_Aapt2UpdateAndroidResgenInputs;
$(_PrepareUpdateAndroidResgenDependsOnTargets);
</_PrepareUpdateAndroidResgenDependsOnTargets>
<_AfterConvertCustomView>
$(_AfterConvertCustomView);
_FixupCustomViewsForAapt2;
</_AfterConvertCustomView>
</PropertyGroup>
</Target>

<Target Name="_ReadAapt2VersionCache">
<ReadLinesFromFile File="$(_AndroidAapt2VersionFile)"
Condition="Exists('$(_AndroidAapt2VersionFile)')">
Expand All @@ -37,7 +61,9 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
/>
<ItemGroup Condition="'$(_Aapt2Version)' != '@(_Aapt2VersionCache)'">
<_CompiledFlataArchive Include="$(_AndroidLibrayProjectIntermediatePath)**\*.flata" />
<_CompiledFlataArchive Include="$(IntermediateOutputPath)\*.flata" />
<_CompiledFlataArchive Include="$(_AndroidLibrayProjectIntermediatePath)**\*.flat" />
<_CompiledFlataArchive Include="$(_AndroidLibraryFlatFilesDirectory)*.flat" />
<_CompiledFlataArchive Include="$(_AndroidLibraryFlatArchivesDirectory)\*.flata" />
<_CompiledFlataStamp Include="$(_AndroidLibrayProjectIntermediatePath)**\compiled.stamp" />
</ItemGroup>
<Delete
Expand All @@ -54,68 +80,19 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
LibraryProjectIntermediatePath="$(_AndroidLibrayProjectIntermediatePath)"
StampDirectory="$(_AndroidStampDirectory)">
<Output TaskParameter="Output" ItemName="_LibraryResourceDirectories" />
<Output TaskParameter="LibraryResourceFiles" ItemName="_LibraryResourceFiles" />
<Output TaskParameter="LibraryResourceFiles" ItemName="_CompileResourcesInputs" />
</CollectNonEmptyDirectories>
<ComputeHash Source="@(_LibraryResourceDirectories)" >
<Output TaskParameter="Output" ItemName="_LibraryResourceHashDirectories" />
</ComputeHash>
</Target>

<Target Name="_ConvertLibraryResourcesCases"
Condition=" '$(_AndroidUseAapt2)' == 'True' "
DependsOnTargets="_CollectLibraryResourceDirectories"
Inputs="@(_LibraryResourceHashDirectories->'%(StampFile)')"
Outputs="$(_AndroidStampDirectory)_ConvertLibraryResourcesCases.stamp">
<ConvertResourcesCases
Condition=" '@(_LibraryResourceDirectories)' != '' "
ContinueOnError="$(DesignTimeBuild)"
AcwMapFile="$(_AcwMapFile)"
AndroidConversionFlagFile="$(_AndroidStampDirectory)_ConvertLibraryResourcesCases.stamp"
CustomViewMapFile="$(_CustomViewMapFile)"
ResourceDirectories="@(_LibraryResourceDirectories)"
ResourceNameCaseMap="$(_AndroidResourceNameCaseMap)"
/>
<Touch Files="$(_AndroidStampDirectory)_ConvertLibraryResourcesCases.stamp" AlwaysCreate="True" />
</Target>

<Target Name="_CompileAndroidLibraryResources"
Condition=" '$(_AndroidUseAapt2)' == 'True' "
DependsOnTargets="_ConvertLibraryResourcesCases"
Inputs="@(_LibraryResourceHashDirectories->'%(StampFile)')"
Outputs="@(_LibraryResourceHashDirectories->'$(_AndroidLibraryFlatArchivesDirectory)%(Hash).stamp')"
>
<MakeDir Directories="$(_AndroidLibraryFlatArchivesDirectory)" Condition="!Exists('$(_AndroidLibraryFlatArchivesDirectory)')" />
<Aapt2Compile
Condition=" '@(_LibraryResourceHashDirectories)' != '' "
ContinueOnError="$(DesignTimeBuild)"
ExtraArgs="$(AndroidAapt2CompileExtraArgs)"
FlatArchivesDirectory="$(_AndroidLibraryFlatArchivesDirectory)"
ResourceDirectories="@(_LibraryResourceHashDirectories)"
ToolPath="$(Aapt2ToolPath)"
ToolExe="$(Aapt2ToolExe)"
/>
<ItemGroup>
<_MissingStampFiles Include="@(_LibraryResourceHashDirectories->'%(StampFile)')" Condition="!Exists('%(StampFile)')" />
<_HashStampFiles Include="@(_LibraryResourceHashDirectories->'$(_AndroidLibraryFlatArchivesDirectory)%(Hash).stamp')" />
<_HashFlataFiles Include="@(_LibraryResourceHashDirectories->'$(_AndroidLibraryFlatArchivesDirectory)%(Hash).flata')" />
</ItemGroup>
<Touch
Files="@(_MissingStampFiles);@(_HashStampFiles)"
AlwaysCreate="True"
/>
<ItemGroup>
<FileWrites Include="@(_MissingStampFiles)" />
<FileWrites Include="@(_HashStampFiles)" />
<FileWrites Include="@(_HashFlataFiles)" />
</ItemGroup>
</Target>

<Target Name="_ConvertResourcesCases"
Condition=" '$(_AndroidUseAapt2)' == 'True' "
Inputs="$(MSBuildAllProjects);$(_AndroidBuildPropertiesCache);@(AndroidResource)"
Inputs="$(MSBuildAllProjects);$(_AndroidBuildPropertiesCache);@(AndroidResource);@(_LibraryResourceDirectories->'%(StampFile)')"
Outputs="$(_AndroidStampDirectory)_ConvertResourcesCases.stamp"
DependsOnTargets="$(_BeforeConvertResourcesCases)"
DependsOnTargets="_CollectLibraryResourceDirectories;$(_BeforeConvertResourcesCases)"
>
<MakeDir Directories="$(_AndroidLibraryFlatArchivesDirectory)" Condition="!Exists('$(_AndroidLibraryFlatArchivesDirectory)')" />
<MakeDir Directories="$(_AndroidLibraryFlatFilesDirectory)" Condition="!Exists('$(_AndroidLibraryFlatFilesDirectory)')" />
<!-- Change cases so we support mixed case resource names -->
<ConvertResourcesCases
ContinueOnError="$(DesignTimeBuild)"
Expand All @@ -128,36 +105,64 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
<Touch Files="$(_AndroidStampDirectory)_ConvertResourcesCases.stamp" AlwaysCreate="True" />
</Target>

<Target Name="_CalculateResourceFileName"
Condition=" '$(_AndroidUseAapt2)' == 'True' ">
<ItemGroup>
<_CompileResourcesInputs Include="@(_AndroidResourceDest)">
<StampFile>%(Identity)</StampFile>
</_CompileResourcesInputs>
<_CompiledFlatFiles Include="@(_CompileResourcesInputs->'%(_ArchiveDirectory)%(_FlatFile)')" />
</ItemGroup>
</Target>

<Target Name="_CompileResources"
Condition=" '$(_AndroidUseAapt2)' == 'True' And '@(AndroidResource)' != '' "
Inputs="$(MSBuildAllProjects);$(_AndroidBuildPropertiesCache);@(AndroidResource)"
Outputs="$(_AndroidLibraryFlatArchivesDirectory)\_CompileResources.stamp"
DependsOnTargets="$(_BeforeCompileResources);_ConvertResourcesCases"
Condition=" '$(_AndroidUseAapt2)' == 'True' "
Inputs="$(MSBuildAllProjects);$(_AndroidBuildPropertiesCache);@(_CompileResourcesInputs)"
Outputs="@(_CompileResourcesInputs->'%(_ArchiveDirectory)%(_FlatFile)')"
DependsOnTargets="$(_BeforeCompileResources);_ConvertResourcesCases;_CalculateResourceFileName"
>
<Aapt2Compile
ContinueOnError="$(DesignTimeBuild)"
DaemonMaxInstanceCount="$(Aapt2DaemonMaxInstanceCount)"
DaemonKeepInDomain="$(_Aapt2DaemonKeepInDomain)"
ExtraArgs="$(AndroidAapt2CompileExtraArgs)"
FlatFilesDirectory="$(_AndroidLibraryFlatFilesDirectory)"
FlatArchivesDirectory="$(_AndroidLibraryFlatArchivesDirectory)"
ResourcesToCompile="@(_CompileResourcesInputs)"
ResourceDirectories="$(MonoAndroidResDirIntermediate)"
ResourceNameCaseMap="$(_AndroidResourceNameCaseMap)"
ToolPath="$(Aapt2ToolPath)"
ToolExe="$(Aapt2ToolExe)"
/>
<Touch Files="$(_AndroidLibraryFlatArchivesDirectory)\_CompileResources.stamp" AlwaysCreate="True" />
<ItemGroup>
<FileWrites Include="$(_AndroidLibraryFlatArchivesDirectory)\compiled.flata" />
</ItemGroup>
</Target>

<Target Name="_Aapt2UpdateAndroidResgenInputs">
<PropertyGroup>
<_UpdateAndroidResgenInputs>
$(_UpdateAndroidResgenInputs);
@(_CompiledFlatFiles);
@(_LibraryResourceDirectoryStamps);
</_UpdateAndroidResgenInputs>
<_CreateBaseApkInputs>
$(_CreateBaseApkInputs);
@(_CompiledFlatFiles);
@(_LibraryResourceDirectoryStamps);
</_CreateBaseApkInputs>
</PropertyGroup>
</Target>

<Target Name="_UpdateAndroidResgenAapt2"
Condition="'$(_AndroidUseAapt2)' == 'True'">
Condition=" '$(_AndroidUseAapt2)' == 'True' "
>
<PropertyGroup>
<AndroidAapt2LinkExtraArgs Condition=" '$(_AndroidUseAapt2)' == 'True' And $(AndroidResgenExtraArgs.Contains('--no-version-vectors')) And !($(AndroidAapt2LinkExtraArgs.Contains('--no-version-vectors'))) ">--no-version-vectors $(AndroidAapt2LinkExtraArgs) </AndroidAapt2LinkExtraArgs>
<_Aapt2ProguardRules Condition=" '$(AndroidLinkTool)' != '' ">$(IntermediateOutputPath)aapt_rules.txt</_Aapt2ProguardRules>
</PropertyGroup>
<Aapt2Link
Condition=" '$(_AndroidResourceDesignerFile)' != '' And '$(_AndroidUseAapt2)' == 'True' "
ContinueOnError="$(DesignTimeBuild)"
DaemonMaxInstanceCount="$(Aapt2DaemonMaxInstanceCount)"
DaemonKeepInDomain="$(_Aapt2DaemonKeepInDomain)"
ResourceNameCaseMap="$(_AndroidResourceNameCaseMap)"
AssemblyIdentityMapFile="$(_AndroidLibrayProjectAssemblyMapFile)"
ImportsDirectory="$(_LibraryProjectImportsDirectoryName)"
Expand All @@ -168,10 +173,9 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
ApplicationName="$(_AndroidPackage)"
JavaPlatformJarPath="$(JavaPlatformJarPath)"
JavaDesignerOutputDirectory="$(ResgenTemporaryDirectory)"
CompiledResourceFlatArchive="$(_AndroidLibraryFlatArchivesDirectory)\compiled.flata"
CompiledResourceFlatFiles="@(_CompiledFlatFiles)"
ManifestFiles="$(ResgenTemporaryDirectory)\AndroidManifest.xml"
AdditionalResourceArchives="@(_LibraryResourceHashDirectories->'$(_AndroidLibraryFlatArchivesDirectory)%(Hash).flata')"
AdditionalAndroidResourcePaths="@(_LibraryResourceHashDirectories)"
AdditionalAndroidResourcePaths="@(_LibraryResourceDirectories)"
YieldDuringToolExecution="$(YieldDuringToolExecution)"
ResourceSymbolsTextFile="$(IntermediateOutputPath)R.txt"
ResourceDirectories="$(MonoAndroidResDirIntermediate)"
Expand All @@ -192,45 +196,46 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.

<Target Name="_FixupCustomViewsForAapt2"
Condition=" '$(_AndroidUseAapt2)' == 'True' And '@(_ProcessedCustomViews)' != '' ">
<Delete
Files="@(_ProcessedCustomViews->'$(_AndroidLibraryFlatArchivesDirectory)%(Hash).stamp')"
Condition=" '$(AndroidUseAapt2)' == 'True' And '@(_ProcessedCustomViews)' != '' "
/>
<ItemGroup>
<_ItemsToFixup Include="@(_CompileResourcesInputs)" Condition=" '@(_ProcessedCustomViews->'%(Identity)')' == '%(Identity)' "/>
</ItemGroup>
<Aapt2Compile
Condition=" '$(AndroidUseAapt2)' == 'True' And '@(_ProcessedCustomViews)' != '' "
Condition=" '$(AndroidUseAapt2)' == 'True' And '@(_ItemsToFixup)' != '' "
ContinueOnError="$(DesignTimeBuild)"
ResourceDirectories="@(_ProcessedCustomViews->'%(ResourceDirectory)'->Distinct())"
DaemonMaxInstanceCount="$(Aapt2DaemonMaxInstanceCount)"
DaemonKeepInDomain="$(_Aapt2DaemonKeepInDomain)"
ResourcesToCompile="@(_ItemsToFixup)"
ResourceDirectories="$(MonoAndroidResDirIntermediate);@(_LibraryResourceDirectories)"
ExtraArgs="$(AndroidAapt2CompileExtraArgs)"
FlatFilesDirectory="$(_AndroidLibraryFlatFilesDirectory)"
FlatArchivesDirectory="$(_AndroidLibraryFlatArchivesDirectory)"
ToolPath="$(Aapt2ToolPath)"
ToolExe="$(Aapt2ToolExe)">
<Output TaskParameter="CompiledResourceFlatArchives" ItemName="_UpdatedFlatArchives" />
<Output TaskParameter="CompiledResourceFlatFiles" ItemName="_UpdatedFlatFiles" />
</Aapt2Compile>
<Touch
Files="@(_UpdatedFlatArchives->'$(_AndroidLibraryFlatArchivesDirectory)\%(Filename).stamp')"
Condition=" '$(AndroidUseAapt2)' == 'True' And '@(_UpdatedFlatArchives)' != '' "
AlwaysCreate="True"
/>
<Touch Files="$(_AndroidResgenFlagFile)" AlwaysCreate="True" Condition=" '@(_UpdatedFlatFiles)' != '' " />
</Target>

<Target Name="_CreateBaseApkWithAapt2"
Condition="'$(_AndroidUseAapt2)' == 'True'">
Condition=" '$(_AndroidUseAapt2)' == 'True' "
>
<PropertyGroup>
<_ProtobufFormat Condition=" '$(AndroidPackageFormat)' == 'aab' ">True</_ProtobufFormat>
<_ProtobufFormat Condition=" '$(_ProtobufFormat)' == '' ">False</_ProtobufFormat>
</PropertyGroup>
<Aapt2Link
Condition="'$(_AndroidUseAapt2)' == 'True'"
CompiledResourceFlatArchive="$(_AndroidLibraryFlatArchivesDirectory)\compiled.flata"
CompiledResourceFlatFiles="@(_CompiledFlatFiles)"
DaemonMaxInstanceCount="$(Aapt2DaemonMaxInstanceCount)"
DaemonKeepInDomain="$(_Aapt2DaemonKeepInDomain)"
ResourceNameCaseMap="$(_AndroidResourceNameCaseMap)"
ResourceDirectories="$(MonoAndroidResDirIntermediate)"
AssemblyIdentityMapFile="$(_AndroidLibrayProjectAssemblyMapFile)"
UseShortFileNames="$(UseShortFileNames)"
ImportsDirectory="$(_LibraryProjectImportsDirectoryName)"
OutputImportDirectory="$(_AndroidLibrayProjectIntermediatePath)"
OutputFile="$(_PackagedResources)"
AdditionalResourceArchives="@(_LibraryResourceHashDirectories->'$(_AndroidLibraryFlatArchivesDirectory)%(Hash).flata')"
AdditionalAndroidResourcePaths="@(_LibraryResourceHashDirectories)"
AdditionalAndroidResourcePaths="@(_LibraryResourceDirectories)"
YieldDuringToolExecution="$(YieldDuringToolExecution)"
PackageName="$(_AndroidPackage)"
ApplicationName="$(_AndroidPackage)"
Expand Down
Loading

0 comments on commit 802f0d3

Please sign in to comment.