Skip to content

Commit

Permalink
[Xamarin.Android.Build.Tasks] reduce System.Linq usage in ManifestDoc…
Browse files Browse the repository at this point in the history
…ument (#9281)

Profiling an incremental build of a `dotnet new maui` project with a
XAML change, one thing I saw was time spent in `<GenerateJavaStubs/>`
MSBuild task, and the `ManifestDocument` class:

    67.57ms (1.80%) xamarin.android.build.tasks!Xamarin.Android.Tasks.ManifestDocument.Merge(class Microsoft.Build.Utilities.TaskLoggingHel
    19.99ms (0.53%) xamarin.android.build.tasks!Xamarin.Android.Tasks.ManifestDocument.CreateApplicationElement(class System.Xml.Linq.XEleme...

Reviewing the code, there was a decent amount of LINQ usage that would
create extra allocations for closures, such as:

    var properties =
        Assemblies.SelectMany (path => PropertyAttribute.FromCustomAttributeProvider (Resolver.GetAssembly (path), cache))

I unrolled the System.Linq usage, and just used plain `foreach` loops
instead.

I also found some places that did:

    if (!attrs.Any ())
        yield break;

We could just remove these as the `yield return` wouldn't return if
`attrs` is empty *anyway*.

After removing this code, I see a small improvement:

    62.56ms (1.80%) xamarin.android.build.tasks!Xamarin.Android.Tasks.ManifestDocument.Merge(class Microsoft.Build.Utilities.TaskLoggingHe...
    16.03ms (0.46%) xamarin.android.build.tasks!Xamarin.Android.Tasks.ManifestDocument.CreateApplicationElement(class System.Xml.Linq.XEleme...

This probably saves ~5ms to incremental builds, but seems like a
straightforward change.
  • Loading branch information
jonathanpeppers authored Sep 4, 2024
1 parent e237401 commit aeeb51a
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ partial class GrantUriPermissionAttribute {
public static IEnumerable<GrantUriPermissionAttribute> FromTypeDefinition (TypeDefinition type, TypeDefinitionCache cache)
{
IEnumerable<CustomAttribute> attrs = type.GetCustomAttributes ("Android.Content.GrantUriPermissionAttribute");
if (!attrs.Any ())
yield break;
foreach (CustomAttribute attr in attrs) {
var self = new GrantUriPermissionAttribute ();
self.specified = mapping.Load (self, attr, cache);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ static string[] ToStringArray (object value)
public static IEnumerable<IntentFilterAttribute> FromTypeDefinition (TypeDefinition type, IMetadataResolver cache)
{
IEnumerable<CustomAttribute> attrs = type.GetCustomAttributes ("Android.App.IntentFilterAttribute");
if (!attrs.Any ())
yield break;
foreach (CustomAttribute attr in attrs) {
var self = new IntentFilterAttribute (ToStringArray (attr.ConstructorArguments [0].Value));
foreach (var e in attr.Properties) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ partial class MetaDataAttribute {
public static IEnumerable<MetaDataAttribute> FromCustomAttributeProvider (ICustomAttributeProvider type, TypeDefinitionCache cache)
{
IEnumerable<CustomAttribute> attrs = type.GetCustomAttributes ("Android.App.MetaDataAttribute");
if (!attrs.Any ())
yield break;
foreach (CustomAttribute attr in attrs) {
var self = new MetaDataAttribute ((string) attr.ConstructorArguments [0].Value);
self.specified = mapping.Load (self, attr, cache);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ partial class PropertyAttribute {
public static IEnumerable<PropertyAttribute> FromCustomAttributeProvider (ICustomAttributeProvider type, TypeDefinitionCache cache)
{
IEnumerable<CustomAttribute> attrs = type.GetCustomAttributes ("Android.App.PropertyAttribute");
if (!attrs.Any ())
yield break;
foreach (CustomAttribute attr in attrs) {
var self = new PropertyAttribute ((string) attr.ConstructorArguments [0].Value);
self.specified = mapping.Load (self, attr, cache);
Expand Down
117 changes: 66 additions & 51 deletions src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -572,33 +572,45 @@ XElement CreateApplicationElement (XElement manifest, string applicationClass, L
{
var application = manifest.Descendants ("application").FirstOrDefault ();

List<ApplicationAttribute> assemblyAttr =
Assemblies.Select (path => ApplicationAttribute.FromCustomAttributeProvider (Resolver.GetAssembly (path), cache))
.Where (attr => attr != null)
.ToList ();
List<MetaDataAttribute> metadata =
Assemblies.SelectMany (path => MetaDataAttribute.FromCustomAttributeProvider (Resolver.GetAssembly (path), cache))
.Where (attr => attr != null)
.ToList ();
var properties =
Assemblies.SelectMany (path => PropertyAttribute.FromCustomAttributeProvider (Resolver.GetAssembly (path), cache))
.Where (attr => attr != null)
.ToList ();
var usesLibraryAttr =
Assemblies.SelectMany (path => UsesLibraryAttribute.FromCustomAttributeProvider (Resolver.GetAssembly (path), cache))
.Where (attr => attr != null);
var usesConfigurationAttr =
Assemblies.SelectMany (path => UsesConfigurationAttribute.FromCustomAttributeProvider (Resolver.GetAssembly (path), cache))
.Where (attr => attr != null);
List<ApplicationAttribute> assemblyAttr = [];
List<MetaDataAttribute> metadata = [];
List<PropertyAttribute> properties = [];
List<UsesLibraryAttribute> usesLibraryAttr = [];
List<UsesConfigurationAttribute> usesConfigurationAttr = [];
foreach (var assemblyPath in Assemblies) {
var assembly = Resolver.GetAssembly (assemblyPath);
if (ApplicationAttribute.FromCustomAttributeProvider (assembly, cache) is ApplicationAttribute a) {
assemblyAttr.Add (a);
}
foreach (var m in MetaDataAttribute.FromCustomAttributeProvider (assembly, cache)) {
if (m is null)
continue;
metadata.Add (m);
}
foreach (var p in PropertyAttribute.FromCustomAttributeProvider (assembly, cache)) {
if (p is null)
continue;
properties.Add (p);
}
foreach (var u in UsesLibraryAttribute.FromCustomAttributeProvider (assembly, cache)) {
if (u is null)
continue;
usesLibraryAttr.Add (u);
}
foreach (var uc in UsesConfigurationAttribute.FromCustomAttributeProvider (assembly, cache)) {
if (uc is null)
continue;
usesConfigurationAttr.Add (uc);
}
}

if (assemblyAttr.Count > 1)
throw new InvalidOperationException ("There can be only one [assembly:Application] attribute defined.");

List<ApplicationAttribute> typeAttr = new List<ApplicationAttribute> ();
List<UsesLibraryAttribute> typeUsesLibraryAttr = new List<UsesLibraryAttribute> ();
List<UsesConfigurationAttribute> typeUsesConfigurationAttr = new List<UsesConfigurationAttribute> ();
List<ApplicationAttribute> typeAttr = [];
foreach (TypeDefinition t in subclasses) {
ApplicationAttribute aa = ApplicationAttribute.FromCustomAttributeProvider (t, cache);
if (aa == null)
if (aa is null)
continue;

if (!t.IsSubclassOf ("Android.App.Application", cache))
Expand All @@ -608,7 +620,7 @@ XElement CreateApplicationElement (XElement manifest, string applicationClass, L
metadata.AddRange (MetaDataAttribute.FromCustomAttributeProvider (t, cache));
properties.AddRange (PropertyAttribute.FromCustomAttributeProvider (t, cache));

typeUsesLibraryAttr.AddRange (UsesLibraryAttribute.FromCustomAttributeProvider (t, cache));
usesLibraryAttr.AddRange (UsesLibraryAttribute.FromCustomAttributeProvider (t, cache));
}

if (typeAttr.Count > 1)
Expand All @@ -619,12 +631,6 @@ XElement CreateApplicationElement (XElement manifest, string applicationClass, L
throw new InvalidOperationException ("Application cannot have both a type with an [Application] attribute and an [assembly:Application] attribute.");

ApplicationAttribute appAttr = assemblyAttr.SingleOrDefault () ?? typeAttr.SingleOrDefault ();
var ull1 = usesLibraryAttr ?? Array.Empty<UsesLibraryAttribute> ();
var ull2 = typeUsesLibraryAttr.AsEnumerable () ?? Array.Empty<UsesLibraryAttribute> ();
var usesLibraryAttrs = ull1.Concat (ull2);
var ucl1 = usesConfigurationAttr ?? Array.Empty<UsesConfigurationAttribute>();
var ucl2 = typeUsesConfigurationAttr.AsEnumerable () ?? Array.Empty<UsesConfigurationAttribute> ();
var usesConfigurationattrs = ucl1.Concat (ucl2);
bool needManifestAdd = true;

if (appAttr != null) {
Expand All @@ -643,14 +649,18 @@ XElement CreateApplicationElement (XElement manifest, string applicationClass, L
application = new XElement ("application");
else
needManifestAdd = false;
application.Add (metadata.Select (md => md.ToElement (PackageName, cache)));
application.Add (properties.Select (md => md.ToElement (PackageName, cache)));
foreach (var m in metadata) {
application.Add (m.ToElement (PackageName, cache));
}
foreach (var p in properties) {
application.Add (p.ToElement (PackageName, cache));
}

if (needManifestAdd)
manifest.Add (application);

AddUsesLibraries (application, usesLibraryAttrs, cache);
AddUsesConfigurations (application, usesConfigurationattrs, cache);
AddUsesLibraries (application, usesLibraryAttr, cache);
AddUsesConfigurations (application, usesConfigurationAttr, cache);

if (applicationClass != null && application.Attribute (androidNs + "name") == null)
application.Add (new XAttribute (androidNs + "name", applicationClass));
Expand Down Expand Up @@ -780,16 +790,18 @@ XElement ToElement<TAttribute> (TypeDefinition type, string name, Func<TypeDefin
if (attr == null)
return null;

IEnumerable<MetaDataAttribute> metadata = MetaDataAttribute.FromCustomAttributeProvider (type, cache);
IEnumerable<IntentFilterAttribute> intents = IntentFilterAttribute.FromTypeDefinition (type, cache);
var properties = PropertyAttribute.FromCustomAttributeProvider (type, cache);

XElement element = toElement (attr);
if (element.Attribute (attName) == null)
element.Add (new XAttribute (attName, name));
element.Add (metadata.Select (md => md.ToElement (PackageName, cache)));
element.Add (intents.Select (intent => intent.ToElement (PackageName)));
element.Add (properties.Select (md => md.ToElement (PackageName, cache)));
foreach (var m in MetaDataAttribute.FromCustomAttributeProvider (type, cache)) {
element.Add (m.ToElement (PackageName, cache));
}
foreach (var i in IntentFilterAttribute.FromTypeDefinition (type, cache)) {
element.Add (i.ToElement (PackageName));
}
foreach (var p in PropertyAttribute.FromCustomAttributeProvider (type, cache)) {
element.Add (p.ToElement (PackageName, cache));
}
if (update != null)
update (attr, element);
return element;
Expand All @@ -801,18 +813,21 @@ XElement ToProviderElement (TypeDefinition type, string name, TypeDefinitionCach
if (attr == null)
return null;

IEnumerable<MetaDataAttribute> metadata = MetaDataAttribute.FromCustomAttributeProvider (type, cache);
IEnumerable<GrantUriPermissionAttribute> grants = GrantUriPermissionAttribute.FromTypeDefinition (type, cache);
IEnumerable<IntentFilterAttribute> intents = IntentFilterAttribute.FromTypeDefinition (type, cache);
var properties = PropertyAttribute.FromCustomAttributeProvider (type, cache);

XElement element = attr.ToElement (PackageName, cache);
if (element.Attribute (attName) == null)
element.Add (new XAttribute (attName, name));
element.Add (metadata.Select (md => md.ToElement (PackageName, cache)));
element.Add (grants.Select (intent => intent.ToElement (PackageName, cache)));
element.Add (intents.Select (intent => intent.ToElement (PackageName)));
element.Add (properties.Select (md => md.ToElement (PackageName, cache)));
foreach (var m in MetaDataAttribute.FromCustomAttributeProvider (type, cache)) {
element.Add (m.ToElement (PackageName, cache));
}
foreach (var g in GrantUriPermissionAttribute.FromTypeDefinition (type, cache)) {
element.Add (g.ToElement (PackageName, cache));
}
foreach (var i in IntentFilterAttribute.FromTypeDefinition (type, cache)) {
element.Add (i.ToElement (PackageName));
}
foreach (var p in PropertyAttribute.FromCustomAttributeProvider (type, cache)) {
element.Add (p.ToElement (PackageName, cache));
}

return element;
}
Expand Down Expand Up @@ -886,13 +901,13 @@ void AddUsesPermissions (XElement application, TypeDefinitionCache cache)
if (!application.Parent.Descendants ("uses-permission").Any (x => (string)x.Attribute (attName) == upa.Name))
application.AddBeforeSelf (upa.ToElement (PackageName, cache));
}
void AddUsesConfigurations (XElement application, IEnumerable<UsesConfigurationAttribute> configs, TypeDefinitionCache cache)
void AddUsesConfigurations (XElement application, List<UsesConfigurationAttribute> configs, TypeDefinitionCache cache)
{
foreach (var uca in configs)
application.Add (uca.ToElement (PackageName, cache));
}

void AddUsesLibraries (XElement application, IEnumerable<UsesLibraryAttribute> libraries, TypeDefinitionCache cache)
void AddUsesLibraries (XElement application, List<UsesLibraryAttribute> libraries, TypeDefinitionCache cache)
{
// Add unique libraries to the manifest
foreach (var ula in libraries)
Expand Down

0 comments on commit aeeb51a

Please sign in to comment.