Skip to content

Commit dfe29a8

Browse files
jonathanpeppersgrendello
authored andcommitted
[Xamarin.Android.Build.Tasks] move <GenerateTypeMaps/> into <GenerateJavaStubs/>
95afa4d originally split up `<GenerateJavaStubs/>` and moved the typemap generation to a new target. Unfortunately, this is causing some of the build performance tests to fail: Build_CSharp_Change Exceeded expected time of 4100ms, actual 5460ms This would be equivalent to editing `MainActivity.cs` and doing an incremental build. If we remove the `<GenerateTypeMaps/>` MSBuild task and keep the logic in `<GenerateJavaStubs/>`, then we can reuse the `DirectoryAssemblyResolver` and any `AssemblyDefinition` already loaded in memory. We can tackle splitting up `<GenerateJavaStubs/>` at a later date. I also had to allow `supportedAbis` to be an empty list, as this happens during the designer's MSBuild targets.
1 parent 21337d8 commit dfe29a8

File tree

8 files changed

+111
-196
lines changed

8 files changed

+111
-196
lines changed

.external

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
xamarin/monodroid:master@01a4e0a0c436bf1b0b62acf870cc06e67e62034c
22
mono/mono:2019-12@8b72dbb708681fbde7f0da13fe76d128d62f8686
3-

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ Copyright (C) 2016 Xamarin. All rights reserved.
8686
AndroidSdkDir="$(_AndroidSdkDirectory)"
8787
PackageName="$(_AndroidPackage)"
8888
OutputDirectory="$(IntermediateOutputPath)android"
89+
TypemapOutputDirectory="$(_NativeAssemblySourceDir)"
90+
GenerateNativeAssembly="false"
8991
MergedAndroidManifestOutput="$(_ManifestOutput)"
9092
UseSharedRuntime="$(AndroidUseSharedRuntime)"
9193
EmbedAssemblies="$(EmbedAssembliesIntoApk)"

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

Lines changed: 74 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ public class GenerateJavaStubs : AndroidTask
4242
[Required]
4343
public string [] SupportedAbis { get; set; }
4444

45+
[Required]
46+
public string TypemapOutputDirectory { get; set; }
47+
48+
[Required]
49+
public bool GenerateNativeAssembly { get; set; }
50+
4551
public string ManifestTemplate { get; set; }
4652
public string[] MergedManifestDocuments { get; set; }
4753

@@ -71,6 +77,9 @@ public class GenerateJavaStubs : AndroidTask
7177

7278
public string ApplicationJavaClass { get; set; }
7379

80+
[Output]
81+
public string [] GeneratedBinaryTypeMaps { get; set; }
82+
7483
internal const string AndroidSkipJavaStubGeneration = "AndroidSkipJavaStubGeneration";
7584

7685
public override bool RunTask ()
@@ -108,71 +117,97 @@ void Run (DirectoryAssemblyResolver res)
108117
}
109118

110119
// Put every assembly we'll need in the resolver
120+
bool hasExportReference = false;
121+
bool haveMonoAndroid = false;
122+
var allTypemapAssemblies = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
123+
var userAssemblies = new Dictionary<string, string> (StringComparer.OrdinalIgnoreCase);
111124
foreach (var assembly in ResolvedAssemblies) {
112-
if (bool.TryParse (assembly.GetMetadata (AndroidSkipJavaStubGeneration), out bool value) && value) {
125+
bool value;
126+
if (bool.TryParse (assembly.GetMetadata (AndroidSkipJavaStubGeneration), out value) && value) {
113127
Log.LogDebugMessage ($"Skipping Java Stub Generation for {assembly.ItemSpec}");
114128
continue;
115129
}
130+
131+
bool addAssembly = false;
132+
string fileName = Path.GetFileName (assembly.ItemSpec);
133+
if (!hasExportReference && String.Compare ("Mono.Android.Export.dll", fileName, StringComparison.OrdinalIgnoreCase) == 0) {
134+
hasExportReference = true;
135+
addAssembly = true;
136+
} else if (!haveMonoAndroid && String.Compare ("Mono.Android.dll", fileName, StringComparison.OrdinalIgnoreCase) == 0) {
137+
haveMonoAndroid = true;
138+
addAssembly = true;
139+
} else if (MonoAndroidHelper.FrameworkAssembliesToTreatAsUserAssemblies.Contains (fileName)) {
140+
if (!bool.TryParse (assembly.GetMetadata (AndroidSkipJavaStubGeneration), out value) || !value) {
141+
string name = Path.GetFileNameWithoutExtension (fileName);
142+
if (!userAssemblies.ContainsKey (name))
143+
userAssemblies.Add (name, assembly.ItemSpec);
144+
addAssembly = true;
145+
}
146+
}
147+
148+
if (addAssembly) {
149+
if (!allTypemapAssemblies.Contains (assembly.ItemSpec))
150+
allTypemapAssemblies.Add (assembly.ItemSpec);
151+
}
152+
116153
res.Load (assembly.ItemSpec);
117154
}
118155

119-
// However we only want to look for JLO types in user code
120-
List<string> assemblies = new List<string> ();
156+
// However we only want to look for JLO types in user code for Java stub code generation
121157
foreach (var asm in ResolvedUserAssemblies) {
122158
if (bool.TryParse (asm.GetMetadata (AndroidSkipJavaStubGeneration), out bool value) && value) {
123159
Log.LogDebugMessage ($"Skipping Java Stub Generation for {asm.ItemSpec}");
124160
continue;
125161
}
126-
if (!assemblies.All (x => Path.GetFileName (x) != Path.GetFileName (asm.ItemSpec)))
127-
continue;
128-
Log.LogDebugMessage ($"Adding {asm.ItemSpec} to assemblies.");
129-
assemblies.Add (asm.ItemSpec);
130-
}
131-
foreach (var asm in MonoAndroidHelper.GetFrameworkAssembliesToTreatAsUserAssemblies (ResolvedAssemblies)) {
132-
if (bool.TryParse (asm.GetMetadata (AndroidSkipJavaStubGeneration), out bool value) && value) {
133-
Log.LogDebugMessage ($"Skipping Java Stub Generation for {asm.ItemSpec}");
134-
continue;
135-
}
136-
if (!assemblies.All (x => Path.GetFileName (x) != Path.GetFileName (asm.ItemSpec)))
137-
continue;
138-
Log.LogDebugMessage ($"Adding {asm.ItemSpec} to assemblies.");
139-
assemblies.Add (asm.ItemSpec);
162+
if (!allTypemapAssemblies.Contains (asm.ItemSpec))
163+
allTypemapAssemblies.Add (asm.ItemSpec);
164+
userAssemblies.Add (Path.GetFileNameWithoutExtension (asm.ItemSpec), asm.ItemSpec);
140165
}
141166

142167
// Step 1 - Find all the JLO types
143168
var scanner = new JavaTypeScanner (this.CreateTaskLogger ()) {
144169
ErrorOnCustomJavaObject = ErrorOnCustomJavaObject,
145170
};
146-
var all_java_types = scanner.GetJavaTypes (assemblies, res);
147171

148-
var java_types = all_java_types
149-
.Where (t => !JavaTypeScanner.ShouldSkipJavaCallableWrapperGeneration (t))
150-
.ToArray ();
172+
List<TypeDefinition> allJavaTypes = scanner.GetJavaTypes (allTypemapAssemblies, res);
151173

152-
// Step 2 - Generate Java stub code
174+
// Step 2 - Generate type maps
175+
// Type mappings need to use all the assemblies, always.
176+
WriteTypeMappings (res, allJavaTypes);
177+
178+
var javaTypes = new List<TypeDefinition> ();
179+
foreach (TypeDefinition td in allJavaTypes) {
180+
if (!userAssemblies.ContainsKey (td.Module.Assembly.Name.Name) || JavaTypeScanner.ShouldSkipJavaCallableWrapperGeneration (td)) {
181+
continue;
182+
}
183+
184+
javaTypes.Add (td);
185+
}
186+
187+
// Step 3 - Generate Java stub code
153188
var success = Generator.CreateJavaSources (
154189
Log,
155-
java_types,
190+
javaTypes,
156191
Path.Combine (OutputDirectory, "src"),
157192
ApplicationJavaClass,
158193
AndroidSdkPlatform,
159194
UseSharedRuntime,
160195
int.Parse (AndroidSdkPlatform) <= 10,
161-
ResolvedAssemblies.Any (assembly => Path.GetFileName (assembly.ItemSpec) == "Mono.Android.Export.dll"));
196+
hasExportReference);
162197
if (!success)
163198
return;
164199

165200
// We need to save a map of .NET type -> ACW type for resource file fixups
166-
var managed = new Dictionary<string, TypeDefinition> (java_types.Length, StringComparer.Ordinal);
167-
var java = new Dictionary<string, TypeDefinition> (java_types.Length, StringComparer.Ordinal);
201+
var managed = new Dictionary<string, TypeDefinition> (javaTypes.Count, StringComparer.Ordinal);
202+
var java = new Dictionary<string, TypeDefinition> (javaTypes.Count, StringComparer.Ordinal);
168203

169204
var managedConflicts = new Dictionary<string, List<string>> (0, StringComparer.Ordinal);
170205
var javaConflicts = new Dictionary<string, List<string>> (0, StringComparer.Ordinal);
171206

172207
// Allocate a MemoryStream with a reasonable guess at its capacity
173-
using (var stream = new MemoryStream (java_types.Length * 32))
208+
using (var stream = new MemoryStream (javaTypes.Count * 32))
174209
using (var acw_map = new StreamWriter (stream)) {
175-
foreach (var type in java_types) {
210+
foreach (TypeDefinition type in javaTypes) {
176211
string managedKey = type.FullName.Replace ('/', '.');
177212
string javaKey = JavaNativeTypeManager.ToJniName (type).Replace ('/', '.');
178213

@@ -237,7 +272,7 @@ void Run (DirectoryAssemblyResolver res)
237272
manifest.PackageName = PackageName;
238273
manifest.ApplicationName = ApplicationName ?? PackageName;
239274
manifest.Placeholders = ManifestPlaceholders;
240-
manifest.Assemblies.AddRange (assemblies);
275+
manifest.Assemblies.AddRange (userAssemblies.Values);
241276
manifest.Resolver = res;
242277
manifest.SdkDir = AndroidSdkDir;
243278
manifest.SdkVersion = AndroidSdkPlatform;
@@ -246,7 +281,7 @@ void Run (DirectoryAssemblyResolver res)
246281
manifest.NeedsInternet = NeedsInternet;
247282
manifest.InstantRunEnabled = InstantRunEnabled;
248283

249-
var additionalProviders = manifest.Merge (Log, all_java_types, ApplicationJavaClass, EmbedAssemblies, BundledWearApplicationName, MergedManifestDocuments);
284+
var additionalProviders = manifest.Merge (Log, allJavaTypes, ApplicationJavaClass, EmbedAssemblies, BundledWearApplicationName, MergedManifestDocuments);
250285

251286
using (var stream = new MemoryStream ()) {
252287
manifest.Save (Log, stream);
@@ -268,7 +303,7 @@ void Run (DirectoryAssemblyResolver res)
268303
// Create additional application java sources.
269304
StringWriter regCallsWriter = new StringWriter ();
270305
regCallsWriter.WriteLine ("\t\t// Application and Instrumentation ACWs must be registered first.");
271-
foreach (var type in java_types) {
306+
foreach (var type in javaTypes) {
272307
if (JavaNativeTypeManager.IsApplication (type) || JavaNativeTypeManager.IsInstrumentation (type)) {
273308
string javaKey = JavaNativeTypeManager.ToJniName (type).Replace ('/', '.');
274309
regCallsWriter.WriteLine ("\t\tmono.android.Runtime.register (\"{0}\", {1}.class, {1}.__md_methods);",
@@ -296,5 +331,13 @@ void SaveResource (string resource, string filename, string destDir, Func<string
296331
template = applyTemplate (template);
297332
MonoAndroidHelper.CopyIfStringChanged (template, Path.Combine (destDir, filename));
298333
}
334+
335+
void WriteTypeMappings (DirectoryAssemblyResolver resolver, List<TypeDefinition> types)
336+
{
337+
var tmg = new TypeMapGenerator ((string message) => Log.LogDebugMessage (message), SupportedAbis);
338+
if (!tmg.Generate (types, TypemapOutputDirectory, GenerateNativeAssembly))
339+
throw new XamarinAndroidException (99999, "Failed to generate type maps");
340+
GeneratedBinaryTypeMaps = tmg.GeneratedBinaryTypeMaps.ToArray ();
341+
}
299342
}
300343
}

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

Lines changed: 0 additions & 103 deletions
This file was deleted.

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ public static bool IsFrameworkAssembly (string assembly, bool checkSdkPath)
307307
{
308308
if (IsSharedRuntimeAssembly (assembly)) {
309309
#if MSBUILD
310-
bool treatAsUser = Array.BinarySearch (FrameworkAssembliesToTreatAsUserAssemblies, Path.GetFileName (assembly), StringComparer.OrdinalIgnoreCase) >= 0;
310+
bool treatAsUser = FrameworkAssembliesToTreatAsUserAssemblies.Contains (Path.GetFileName (assembly));
311311
// Framework assemblies don't come from outside the SDK Path;
312312
// user assemblies do
313313
if (checkSdkPath && treatAsUser && TargetFrameworkDirectories != null) {
@@ -527,10 +527,14 @@ public static bool IsRawResourcePath (string projectPath)
527527

528528
#if MSBUILD
529529
internal static IEnumerable<ITaskItem> GetFrameworkAssembliesToTreatAsUserAssemblies (ITaskItem[] resolvedAssemblies)
530-
{
531-
return resolvedAssemblies
532-
.Where (f => Array.BinarySearch (FrameworkAssembliesToTreatAsUserAssemblies, Path.GetFileName (f.ItemSpec), StringComparer.OrdinalIgnoreCase) >= 0)
533-
.Select(p => p);
530+
{
531+
var ret = new List<ITaskItem> ();
532+
foreach (ITaskItem item in resolvedAssemblies) {
533+
if (FrameworkAssembliesToTreatAsUserAssemblies.Contains (Path.GetFileName (item.ItemSpec)))
534+
ret.Add (item);
535+
}
536+
537+
return ret;
534538
}
535539
#endif
536540

@@ -544,7 +548,7 @@ internal static IEnumerable<ITaskItem> GetFrameworkAssembliesToTreatAsUserAssemb
544548
"Mono.Posix.dll",
545549
};
546550
// MUST BE SORTED CASE-INSENSITIVE
547-
internal static readonly string[] FrameworkAssembliesToTreatAsUserAssemblies = {
551+
internal static readonly HashSet<string> FrameworkAssembliesToTreatAsUserAssemblies = new HashSet<string> (StringComparer.OrdinalIgnoreCase) {
548552
"Mono.Android.Support.v13.dll",
549553
"Mono.Android.Support.v4.dll",
550554
"Xamarin.Android.NUnitLite.dll",

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

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -80,36 +80,21 @@ public TypeMapGenerator (Action<string> logger, string[] supportedAbis)
8080
this.logger = logger ?? throw new ArgumentNullException (nameof (logger));
8181
if (supportedAbis == null)
8282
throw new ArgumentNullException (nameof (supportedAbis));
83-
if (supportedAbis.Length == 0)
84-
throw new ArgumentException ("must not be empty", nameof (supportedAbis));
8583
this.supportedAbis = supportedAbis;
8684

8785
outputEncoding = new UTF8Encoding (false);
8886
moduleMagicString = outputEncoding.GetBytes (TypeMapMagicString);
8987
typemapIndexMagicString = outputEncoding.GetBytes (TypeMapIndexMagicString);
9088
}
9189

92-
void LoggerShim (TraceLevel level, string message)
90+
public bool Generate (List<TypeDefinition> javaTypes, string outputDirectory, bool generateNativeAssembly)
9391
{
94-
logger (message);
95-
}
96-
97-
public bool Generate (DirectoryAssemblyResolver resolver, IEnumerable<string> assemblies, string outputDirectory, bool generateNativeAssembly)
98-
{
99-
if (assemblies == null)
100-
throw new ArgumentNullException (nameof (assemblies));
10192
if (String.IsNullOrEmpty (outputDirectory))
10293
throw new ArgumentException ("must not be null or empty", nameof (outputDirectory));
10394

10495
if (!Directory.Exists (outputDirectory))
10596
Directory.CreateDirectory (outputDirectory);
10697

107-
var scanner = new JavaTypeScanner (LoggerShim) {
108-
ErrorOnCustomJavaObject = false
109-
};
110-
111-
List<TypeDefinition> javaTypes = scanner.GetJavaTypes (assemblies, resolver);
112-
11398
int assemblyId = 0;
11499
int maxJavaNameLength = 0;
115100
int maxModuleFileNameLength = 0;

0 commit comments

Comments
 (0)