Skip to content

Commit a963c30

Browse files
[Xamarin.Android.Build.Tasks] <FilterAssemblies/> in-memory cache (#4975)
In a customer's build log, a large solution build with no changes had: 1956 ms FilterAssemblies 18 calls The `<FilterAssemblies/>` MSBuild task runs on every build for class libraries and application projects. It looks at assemblies to classify them as a "MonoAndroid" assembly. Since we are already using System.Reflection.Metadata to open each assembly, we can use each assembly's MVID and `RegisterTaskObject` to skip future calls to the same assembly. This will be helpful in incremental builds in the IDE, as well as, large solutions that encounter the same assemblies throughout a build. Using the in-memory cache, we don't need to check any file timestamps. Unique MVIDs will yield the same result for the `<FilterAssemblies/>` task -- regardless if the assembly was built with `$(Deterministic)` or not. ~~ Results ~~ I did not have access to the original customer project, so I tested this change using xamarin/Xamarin.Forms@d9926450 instead. There are 8 Xamarin.Android projects during the build. Before: 165 ms FilterAssemblies 16 calls After: 156 ms FilterAssemblies 16 calls After (with MSBuild node running): 99 ms FilterAssemblies 16 calls Even on the first build, many assemblies are pulled from the cache. It printed ~326 instances of this log message: Task "FilterAssemblies" ... Cached: C:\src\Xamarin.Forms\Xamarin.Forms.Platform.Android.FormsViewGroup\bin\Release\FormsViewGroup.dll This saves ~10ms from an initial build of Xamarin.Forms' source and ~66ms from incremental builds. This will have a bigger impact on larger solutions. ~~ Other changes ~~ I removed an unused `DesignTimeBuild` property from `<FilterAssemblies/>`. I removed the log message: // In the rare case, [assembly: TargetFramework("MonoAndroid,Version=v9.0")] may not match Log.LogDebugMessage ($"{nameof (TargetFrameworkIdentifier)} did not match: {assemblyItem.ItemSpec}"); It is actually not a "rare" case, it was printed 330 times from the customer's log. This log message didn't add much value, so I removed it to reduce string allocations.
1 parent 396aca1 commit a963c30

File tree

3 files changed

+27
-12
lines changed

3 files changed

+27
-12
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
### Build and deployment performance
2+
3+
The `<FilterAssemblies/>` MSBuild task caches information in-memory to
4+
improve build times. Incremental builds of Xamarin.Forms' source code
5+
were improved from 165ms to 99ms on a test system. This will have a
6+
bigger impact on large solutions.

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

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ public class FilterAssemblies : AndroidTask
2020
public override string TaskPrefix => "FLT";
2121

2222
const string TargetFrameworkIdentifier = "MonoAndroid";
23-
24-
public bool DesignTimeBuild { get; set; }
23+
const RegisteredTaskObjectLifetime Lifetime = RegisteredTaskObjectLifetime.AppDomain;
24+
const bool AllowEarlyCollection = false;
2525

2626
public ITaskItem [] InputAssemblies { get; set; }
2727

@@ -41,28 +41,41 @@ public override bool RunTask ()
4141
}
4242
using (var pe = new PEReader (File.OpenRead (assemblyItem.ItemSpec))) {
4343
var reader = pe.GetMetadataReader ();
44+
// Check in-memory cache
45+
var module = reader.GetModuleDefinition ();
46+
var key = (nameof (FilterAssemblies), reader.GetGuid (module.Mvid));
47+
var value = BuildEngine4.GetRegisteredTaskObject (key, Lifetime);
48+
if (value is bool isMonoAndroidAssembly) {
49+
if (isMonoAndroidAssembly) {
50+
Log.LogDebugMessage ($"Cached: {assemblyItem.ItemSpec}");
51+
output.Add (assemblyItem);
52+
}
53+
continue;
54+
}
55+
// Check assembly definition
4456
var assemblyDefinition = reader.GetAssemblyDefinition ();
4557
var targetFrameworkIdentifier = GetTargetFrameworkIdentifier (assemblyDefinition, reader);
4658
if (string.Compare (targetFrameworkIdentifier, TargetFrameworkIdentifier, StringComparison.OrdinalIgnoreCase) == 0) {
4759
output.Add (assemblyItem);
60+
BuildEngine4.RegisterTaskObject (key, true, Lifetime, AllowEarlyCollection);
4861
continue;
4962
}
50-
51-
// In the rare case, [assembly: TargetFramework("MonoAndroid,Version=v9.0")] may not match
52-
Log.LogDebugMessage ($"{nameof (TargetFrameworkIdentifier)} did not match: {assemblyItem.ItemSpec}");
53-
5463
// Fallback to looking for a Mono.Android reference
5564
if (MonoAndroidHelper.HasMonoAndroidReference (reader)) {
5665
Log.LogDebugMessage ($"Mono.Android reference found: {assemblyItem.ItemSpec}");
5766
output.Add (assemblyItem);
67+
BuildEngine4.RegisterTaskObject (key, true, Lifetime, AllowEarlyCollection);
5868
continue;
5969
}
6070
// Fallback to looking for *.jar or __Android EmbeddedResource files
6171
if (HasEmbeddedResource (reader)) {
6272
Log.LogDebugMessage ($"EmbeddedResource found: {assemblyItem.ItemSpec}");
6373
output.Add (assemblyItem);
74+
BuildEngine4.RegisterTaskObject (key, true, Lifetime, AllowEarlyCollection);
6475
continue;
6576
}
77+
// Not a MonoAndroid assembly, store false
78+
BuildEngine4.RegisterTaskObject (key, false, Lifetime, AllowEarlyCollection);
6679
}
6780
}
6881
OutputAssemblies = output.ToArray ();

src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -430,14 +430,10 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
430430
<_ReferenceDependencyPaths Include="@(ReferenceDependencyPaths)"
431431
Condition="'$(AndroidApplication)' == '' Or !$(AndroidApplication)"/>
432432
</ItemGroup>
433-
<FilterAssemblies
434-
DesignTimeBuild="$(DesignTimeBuild)"
435-
InputAssemblies="@(_ReferencePath)">
433+
<FilterAssemblies InputAssemblies="@(_ReferencePath)">
436434
<Output TaskParameter="OutputAssemblies" ItemName="_MonoAndroidReferencePath" />
437435
</FilterAssemblies>
438-
<FilterAssemblies
439-
DesignTimeBuild="$(DesignTimeBuild)"
440-
InputAssemblies="@(_ReferenceDependencyPaths)">
436+
<FilterAssemblies InputAssemblies="@(_ReferenceDependencyPaths)">
441437
<Output TaskParameter="OutputAssemblies" ItemName="_MonoAndroidReferenceDependencyPaths" />
442438
</FilterAssemblies>
443439
</Target>

0 commit comments

Comments
 (0)