Skip to content

Commit b9e8b2e

Browse files
[Xamarin.Android.Build.Tasks] mechanism to skip ConvertResourcesCases
Currently, we run the `ConvertResourcesCases` for all assemblies (their `ResolveLibraryProjectImports` output). `ConvertResourcesCases` is one of our slower targets. Luckily, there are some "well-known" number of assemblies that we could skip, so it seems beneficial to give library authors a way to do this. So for example, an assembly could contain: [assembly: Android.SkipAndroidResourceProcessing] And this would be an indicator for `ConvertResourcesCases` to just completely skip this assembly. Additionally, we can flat out skip `aar` files in the same manner. To make this work: - Added support to put `ITaskItem` metadata in the cache file produced by `ResolveLibraryProjectImports` - Added item metadata for `SkipAndroidResourceProcessing` and `OriginalFile`. - `ConvertResourcesCases` now skips these directories and logs `OriginalFile`. - `CollectNonEmptyDirectories` needs to preserve item metadata for `$(AndroidUseAapt2)` to take advantage of the functionality. The results appear to be well worth the effort! To test this, I kept most of the changes here, except skipped any assemblies starting with "Xamarin.Android.Support". Results with `$(AndroidUseAapt2)` enabled: Before: 15650 ms ConvertResourcesCases 9 calls After: 112 ms ConvertResourcesCases 9 calls Results with `$(AndroidUseAapt2)` disabled: Before: 2847 ms ConvertResourcesCases 1 calls After: 52 ms ConvertResourcesCases 1 calls This was the Xamarin.Forms-Integration project in this repo, an initial clean build. It is basically a "Hello World" Xamarin.Forms project. This is a proposal, so if someone has a nicer (better!) idea for enabling this functionality, let me know!
1 parent 19e3ce5 commit b9e8b2e

File tree

6 files changed

+92
-19
lines changed

6 files changed

+92
-19
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System;
2+
3+
namespace Android
4+
{
5+
[AttributeUsage (AttributeTargets.Assembly)]
6+
public sealed class SkipAndroidResourceProcessingAttribute : Attribute
7+
{
8+
}
9+
}
10+

src/Mono.Android/Mono.Android.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@
102102
<Compile Include="Android\LinkerSafeAttribute.cs" />
103103
<Compile Include="Android\NativeLibraryReferenceAttribute.cs" />
104104
<Compile Include="Android\ReferenceFilesAttribute.cs" />
105+
<Compile Include="Android\SkipAndroidResourceProcessingAttribute.cs" />
105106
<Compile Include="Android.Accounts\AccountManager.cs" />
106107
<Compile Include="Android.Animation\Animator.cs" />
107108
<Compile Include="Android.Animation\AnimatorSet.cs" />

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ public override bool Execute ()
2121
foreach (var directory in Directories) {
2222
var firstFile = Directory.EnumerateFiles(directory.ItemSpec, "*.*", SearchOption.AllDirectories).FirstOrDefault ();
2323
if (firstFile != null) {
24-
output.Add (new TaskItem (directory.ItemSpec, new Dictionary<string, string> () {
24+
var taskItem = new TaskItem (directory.ItemSpec, new Dictionary<string, string> () {
2525
{"FileFound", firstFile}
26-
}));
26+
});
27+
directory.CopyMetadataTo (taskItem);
28+
output.Add (taskItem);
2729
}
2830
}
2931
return !Log.HasLoggedErrors;

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,18 @@ public override bool Execute ()
4747
return true;
4848
}
4949

50-
5150
void FixupResources (Dictionary<string, string> acwMap)
5251
{
53-
foreach (var dir in ResourceDirectories)
52+
foreach (var dir in ResourceDirectories) {
53+
var skipResourceProcessing = dir.GetMetadata (ResolveLibraryProjectImports.SkipAndroidResourceProcessing);
54+
if (skipResourceProcessing != null && skipResourceProcessing.Equals ("true", StringComparison.OrdinalIgnoreCase)) {
55+
var originalFile = dir.GetMetadata (ResolveLibraryProjectImports.OriginalFile);
56+
Log.LogDebugMessage ($"Skipping `{dir.ItemSpec}` due to `{ResolveLibraryProjectImports.SkipAndroidResourceProcessing}`, original file: `{originalFile}`...");
57+
continue;
58+
}
59+
5460
FixupResources (dir, acwMap);
61+
}
5562
}
5663

5764
void FixupResources (ITaskItem item, Dictionary<string, string> acwMap)

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

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ public class ResolveLibraryProjectImports : Task
6060
[Output]
6161
public ITaskItem [] ResolvedResourceDirectoryStamps { get; set; }
6262

63+
internal const string OriginalFile = "OriginalFile";
64+
internal const string SkipAndroidResourceProcessing = "SkipAndroidResourceProcessing";
65+
static readonly string [] knownMetadata = new [] {
66+
"OriginalFile",
67+
"SkipAndroidResourceProcessing"
68+
};
69+
6370
AssemblyIdentityMap assemblyMap = new AssemblyIdentityMap();
6471

6572
public ResolveLibraryProjectImports ()
@@ -79,7 +86,7 @@ public override bool Execute ()
7986
Log.LogDebugTaskItems (" AarLibraries: ", AarLibraries);
8087

8188
var jars = new List<string> ();
82-
var resolvedResourceDirectories = new List<string> ();
89+
var resolvedResourceDirectories = new List<ITaskItem> ();
8390
var resolvedAssetDirectories = new List<string> ();
8491
var resolvedEnvironmentFiles = new List<string> ();
8592

@@ -95,9 +102,7 @@ public override bool Execute ()
95102
}
96103

97104
Jars = jars.ToArray ();
98-
ResolvedResourceDirectories = resolvedResourceDirectories
99-
.Select (s => new TaskItem (Path.GetFullPath (s)))
100-
.ToArray ();
105+
ResolvedResourceDirectories = resolvedResourceDirectories.ToArray ();
101106
ResolvedAssetDirectories = resolvedAssetDirectories.ToArray ();
102107
ResolvedEnvironmentFiles = resolvedEnvironmentFiles.ToArray ();
103108

@@ -120,7 +125,15 @@ public override bool Execute ()
120125
new XElement ("Jars",
121126
Jars.Select(e => new XElement ("Jar", e))),
122127
new XElement ("ResolvedResourceDirectories",
123-
ResolvedResourceDirectories.Select(e => new XElement ("ResolvedResourceDirectory", e))),
128+
ResolvedResourceDirectories.Select(dir => {
129+
var e = new XElement ("ResolvedResourceDirectory", dir.ItemSpec);
130+
foreach (var name in knownMetadata) {
131+
var value = dir.GetMetadata (name);
132+
if (!string.IsNullOrEmpty (value))
133+
e.SetAttributeValue (name, value);
134+
}
135+
return e;
136+
})),
124137
new XElement ("ResolvedAssetDirectories",
125138
ResolvedAssetDirectories.Select(e => new XElement ("ResolvedAssetDirectory", e))),
126139
new XElement ("ResolvedEnvironmentFiles",
@@ -160,7 +173,7 @@ static string GetTargetAssembly (ITaskItem assemblyName)
160173
void Extract (
161174
DirectoryAssemblyResolver res,
162175
ICollection<string> jars,
163-
ICollection<string> resolvedResourceDirectories,
176+
ICollection<ITaskItem> resolvedResourceDirectories,
164177
ICollection<string> resolvedAssetDirectories,
165178
ICollection<string> resolvedEnvironments)
166179
{
@@ -210,8 +223,16 @@ void Extract (
210223
if (Directory.Exists (binAssemblyDir))
211224
resolvedAssetDirectories.Add (binAssemblyDir);
212225
#endif
213-
if (Directory.Exists (resDir))
214-
resolvedResourceDirectories.Add (resDir);
226+
if (Directory.Exists (resDir)) {
227+
var taskItem = new TaskItem (resDir, new Dictionary<string, string> {
228+
{ OriginalFile, assemblyPath },
229+
});
230+
//TODO: perhaps a better way to store this value here when stamp exists?
231+
var a = res.GetAssembly (assemblyPath);
232+
if (SkipResourceProcessing (a))
233+
taskItem.SetMetadata (SkipAndroidResourceProcessing, "True");
234+
resolvedResourceDirectories.Add (taskItem);
235+
}
215236
if (Directory.Exists (assemblyDir))
216237
resolvedAssetDirectories.Add (assemblyDir);
217238
foreach (var env in Directory.EnumerateFiles (outDirForDll, "__AndroidEnvironment__*", SearchOption.TopDirectoryOnly)) {
@@ -308,8 +329,14 @@ void Extract (
308329
if (Directory.Exists (binAssemblyDir))
309330
resolvedAssetDirectories.Add (binAssemblyDir);
310331
#endif
311-
if (Directory.Exists (resDir))
312-
resolvedResourceDirectories.Add (resDir);
332+
if (Directory.Exists (resDir)) {
333+
var taskItem = new TaskItem (resDir, new Dictionary<string, string> {
334+
{ OriginalFile, assemblyPath }
335+
});
336+
if (SkipResourceProcessing (assembly))
337+
taskItem.SetMetadata (SkipAndroidResourceProcessing, "True");
338+
resolvedResourceDirectories.Add (taskItem);
339+
}
313340
if (Directory.Exists (assemblyDir))
314341
resolvedAssetDirectories.Add (assemblyDir);
315342

@@ -362,7 +389,10 @@ void Extract (
362389
}
363390
}
364391
if (Directory.Exists (resDir))
365-
resolvedResourceDirectories.Add (resDir);
392+
resolvedResourceDirectories.Add (new TaskItem (resDir, new Dictionary<string, string> {
393+
{ OriginalFile, aarFile.ItemSpec },
394+
{ SkipAndroidResourceProcessing, "True" },
395+
}));
366396
if (Directory.Exists (assetsDir))
367397
resolvedAssetDirectories.Add (assetsDir);
368398
}
@@ -373,5 +403,16 @@ void Extract (
373403
jars.Add (f);
374404
}
375405
}
406+
407+
bool SkipResourceProcessing (AssemblyDefinition assembly)
408+
{
409+
if (assembly.HasCustomAttributes) {
410+
foreach (var att in assembly.CustomAttributes) {
411+
if (att.AttributeType.FullName == "Android.SkipAndroidResourceProcessingAttribute")
412+
return true;
413+
}
414+
}
415+
return false;
416+
}
376417
}
377418
}

src/Xamarin.Android.Build.Tasks/Utilities/XDocumentExtensions.cs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,28 @@ namespace Xamarin.Android.Tasks
1010
{
1111
public static class XDocumentExtensions
1212
{
13+
const string PathsElementName = "Paths";
14+
1315
public static ITaskItem[] GetPathsAsTaskItems (this XDocument doc, params string[] paths)
1416
{
15-
return doc.GetPaths (paths)
16-
.Select(x => new TaskItem(x))
17-
.ToArray ();
17+
var e = doc.Elements (PathsElementName);
18+
foreach (var p in paths)
19+
e = e.Elements (p);
20+
return e.Select (ToTaskItem).ToArray ();
21+
}
22+
23+
static ITaskItem ToTaskItem (XElement element)
24+
{
25+
var taskItem = new TaskItem (element.Value);
26+
foreach (var attribute in element.Attributes ()) {
27+
taskItem.SetMetadata (attribute.Name.LocalName, attribute.Value);
28+
}
29+
return taskItem;
1830
}
1931

2032
public static string[] GetPaths (this XDocument doc, params string[] paths)
2133
{
22-
var e = doc.Elements ("Paths");
34+
var e = doc.Elements (PathsElementName);
2335
foreach (var p in paths)
2436
e = e.Elements (p);
2537
return e.Select (p => p.Value).ToArray ();

0 commit comments

Comments
 (0)