Skip to content

Commit

Permalink
[Xamarin.Android.Build.Tasks] move <GenerateTypeMaps/> into <Generate…
Browse files Browse the repository at this point in the history
…JavaStubs/>

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.
  • Loading branch information
jonathanpeppers authored and grendello committed Feb 6, 2020
1 parent 21337d8 commit dfe29a8
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 196 deletions.
1 change: 0 additions & 1 deletion .external
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
xamarin/monodroid:master@01a4e0a0c436bf1b0b62acf870cc06e67e62034c
mono/mono:2019-12@8b72dbb708681fbde7f0da13fe76d128d62f8686

Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ Copyright (C) 2016 Xamarin. All rights reserved.
AndroidSdkDir="$(_AndroidSdkDirectory)"
PackageName="$(_AndroidPackage)"
OutputDirectory="$(IntermediateOutputPath)android"
TypemapOutputDirectory="$(_NativeAssemblySourceDir)"
GenerateNativeAssembly="false"
MergedAndroidManifestOutput="$(_ManifestOutput)"
UseSharedRuntime="$(AndroidUseSharedRuntime)"
EmbedAssemblies="$(EmbedAssembliesIntoApk)"
Expand Down
105 changes: 74 additions & 31 deletions src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ public class GenerateJavaStubs : AndroidTask
[Required]
public string [] SupportedAbis { get; set; }

[Required]
public string TypemapOutputDirectory { get; set; }

[Required]
public bool GenerateNativeAssembly { get; set; }

public string ManifestTemplate { get; set; }
public string[] MergedManifestDocuments { get; set; }

Expand Down Expand Up @@ -71,6 +77,9 @@ public class GenerateJavaStubs : AndroidTask

public string ApplicationJavaClass { get; set; }

[Output]
public string [] GeneratedBinaryTypeMaps { get; set; }

internal const string AndroidSkipJavaStubGeneration = "AndroidSkipJavaStubGeneration";

public override bool RunTask ()
Expand Down Expand Up @@ -108,71 +117,97 @@ void Run (DirectoryAssemblyResolver res)
}

// Put every assembly we'll need in the resolver
bool hasExportReference = false;
bool haveMonoAndroid = false;
var allTypemapAssemblies = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
var userAssemblies = new Dictionary<string, string> (StringComparer.OrdinalIgnoreCase);
foreach (var assembly in ResolvedAssemblies) {
if (bool.TryParse (assembly.GetMetadata (AndroidSkipJavaStubGeneration), out bool value) && value) {
bool value;
if (bool.TryParse (assembly.GetMetadata (AndroidSkipJavaStubGeneration), out value) && value) {
Log.LogDebugMessage ($"Skipping Java Stub Generation for {assembly.ItemSpec}");
continue;
}

bool addAssembly = false;
string fileName = Path.GetFileName (assembly.ItemSpec);
if (!hasExportReference && String.Compare ("Mono.Android.Export.dll", fileName, StringComparison.OrdinalIgnoreCase) == 0) {
hasExportReference = true;
addAssembly = true;
} else if (!haveMonoAndroid && String.Compare ("Mono.Android.dll", fileName, StringComparison.OrdinalIgnoreCase) == 0) {
haveMonoAndroid = true;
addAssembly = true;
} else if (MonoAndroidHelper.FrameworkAssembliesToTreatAsUserAssemblies.Contains (fileName)) {
if (!bool.TryParse (assembly.GetMetadata (AndroidSkipJavaStubGeneration), out value) || !value) {
string name = Path.GetFileNameWithoutExtension (fileName);
if (!userAssemblies.ContainsKey (name))
userAssemblies.Add (name, assembly.ItemSpec);
addAssembly = true;
}
}

if (addAssembly) {
if (!allTypemapAssemblies.Contains (assembly.ItemSpec))
allTypemapAssemblies.Add (assembly.ItemSpec);
}

res.Load (assembly.ItemSpec);
}

// However we only want to look for JLO types in user code
List<string> assemblies = new List<string> ();
// However we only want to look for JLO types in user code for Java stub code generation
foreach (var asm in ResolvedUserAssemblies) {
if (bool.TryParse (asm.GetMetadata (AndroidSkipJavaStubGeneration), out bool value) && value) {
Log.LogDebugMessage ($"Skipping Java Stub Generation for {asm.ItemSpec}");
continue;
}
if (!assemblies.All (x => Path.GetFileName (x) != Path.GetFileName (asm.ItemSpec)))
continue;
Log.LogDebugMessage ($"Adding {asm.ItemSpec} to assemblies.");
assemblies.Add (asm.ItemSpec);
}
foreach (var asm in MonoAndroidHelper.GetFrameworkAssembliesToTreatAsUserAssemblies (ResolvedAssemblies)) {
if (bool.TryParse (asm.GetMetadata (AndroidSkipJavaStubGeneration), out bool value) && value) {
Log.LogDebugMessage ($"Skipping Java Stub Generation for {asm.ItemSpec}");
continue;
}
if (!assemblies.All (x => Path.GetFileName (x) != Path.GetFileName (asm.ItemSpec)))
continue;
Log.LogDebugMessage ($"Adding {asm.ItemSpec} to assemblies.");
assemblies.Add (asm.ItemSpec);
if (!allTypemapAssemblies.Contains (asm.ItemSpec))
allTypemapAssemblies.Add (asm.ItemSpec);
userAssemblies.Add (Path.GetFileNameWithoutExtension (asm.ItemSpec), asm.ItemSpec);
}

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

var java_types = all_java_types
.Where (t => !JavaTypeScanner.ShouldSkipJavaCallableWrapperGeneration (t))
.ToArray ();
List<TypeDefinition> allJavaTypes = scanner.GetJavaTypes (allTypemapAssemblies, res);

// Step 2 - Generate Java stub code
// Step 2 - Generate type maps
// Type mappings need to use all the assemblies, always.
WriteTypeMappings (res, allJavaTypes);

var javaTypes = new List<TypeDefinition> ();
foreach (TypeDefinition td in allJavaTypes) {
if (!userAssemblies.ContainsKey (td.Module.Assembly.Name.Name) || JavaTypeScanner.ShouldSkipJavaCallableWrapperGeneration (td)) {
continue;
}

javaTypes.Add (td);
}

// Step 3 - Generate Java stub code
var success = Generator.CreateJavaSources (
Log,
java_types,
javaTypes,
Path.Combine (OutputDirectory, "src"),
ApplicationJavaClass,
AndroidSdkPlatform,
UseSharedRuntime,
int.Parse (AndroidSdkPlatform) <= 10,
ResolvedAssemblies.Any (assembly => Path.GetFileName (assembly.ItemSpec) == "Mono.Android.Export.dll"));
hasExportReference);
if (!success)
return;

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

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

// Allocate a MemoryStream with a reasonable guess at its capacity
using (var stream = new MemoryStream (java_types.Length * 32))
using (var stream = new MemoryStream (javaTypes.Count * 32))
using (var acw_map = new StreamWriter (stream)) {
foreach (var type in java_types) {
foreach (TypeDefinition type in javaTypes) {
string managedKey = type.FullName.Replace ('/', '.');
string javaKey = JavaNativeTypeManager.ToJniName (type).Replace ('/', '.');

Expand Down Expand Up @@ -237,7 +272,7 @@ void Run (DirectoryAssemblyResolver res)
manifest.PackageName = PackageName;
manifest.ApplicationName = ApplicationName ?? PackageName;
manifest.Placeholders = ManifestPlaceholders;
manifest.Assemblies.AddRange (assemblies);
manifest.Assemblies.AddRange (userAssemblies.Values);
manifest.Resolver = res;
manifest.SdkDir = AndroidSdkDir;
manifest.SdkVersion = AndroidSdkPlatform;
Expand All @@ -246,7 +281,7 @@ void Run (DirectoryAssemblyResolver res)
manifest.NeedsInternet = NeedsInternet;
manifest.InstantRunEnabled = InstantRunEnabled;

var additionalProviders = manifest.Merge (Log, all_java_types, ApplicationJavaClass, EmbedAssemblies, BundledWearApplicationName, MergedManifestDocuments);
var additionalProviders = manifest.Merge (Log, allJavaTypes, ApplicationJavaClass, EmbedAssemblies, BundledWearApplicationName, MergedManifestDocuments);

using (var stream = new MemoryStream ()) {
manifest.Save (Log, stream);
Expand All @@ -268,7 +303,7 @@ void Run (DirectoryAssemblyResolver res)
// Create additional application java sources.
StringWriter regCallsWriter = new StringWriter ();
regCallsWriter.WriteLine ("\t\t// Application and Instrumentation ACWs must be registered first.");
foreach (var type in java_types) {
foreach (var type in javaTypes) {
if (JavaNativeTypeManager.IsApplication (type) || JavaNativeTypeManager.IsInstrumentation (type)) {
string javaKey = JavaNativeTypeManager.ToJniName (type).Replace ('/', '.');
regCallsWriter.WriteLine ("\t\tmono.android.Runtime.register (\"{0}\", {1}.class, {1}.__md_methods);",
Expand Down Expand Up @@ -296,5 +331,13 @@ void SaveResource (string resource, string filename, string destDir, Func<string
template = applyTemplate (template);
MonoAndroidHelper.CopyIfStringChanged (template, Path.Combine (destDir, filename));
}

void WriteTypeMappings (DirectoryAssemblyResolver resolver, List<TypeDefinition> types)
{
var tmg = new TypeMapGenerator ((string message) => Log.LogDebugMessage (message), SupportedAbis);
if (!tmg.Generate (types, TypemapOutputDirectory, GenerateNativeAssembly))
throw new XamarinAndroidException (99999, "Failed to generate type maps");
GeneratedBinaryTypeMaps = tmg.GeneratedBinaryTypeMaps.ToArray ();
}
}
}
103 changes: 0 additions & 103 deletions src/Xamarin.Android.Build.Tasks/Tasks/GenerateTypeMaps.cs

This file was deleted.

16 changes: 10 additions & 6 deletions src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ public static bool IsFrameworkAssembly (string assembly, bool checkSdkPath)
{
if (IsSharedRuntimeAssembly (assembly)) {
#if MSBUILD
bool treatAsUser = Array.BinarySearch (FrameworkAssembliesToTreatAsUserAssemblies, Path.GetFileName (assembly), StringComparer.OrdinalIgnoreCase) >= 0;
bool treatAsUser = FrameworkAssembliesToTreatAsUserAssemblies.Contains (Path.GetFileName (assembly));
// Framework assemblies don't come from outside the SDK Path;
// user assemblies do
if (checkSdkPath && treatAsUser && TargetFrameworkDirectories != null) {
Expand Down Expand Up @@ -527,10 +527,14 @@ public static bool IsRawResourcePath (string projectPath)

#if MSBUILD
internal static IEnumerable<ITaskItem> GetFrameworkAssembliesToTreatAsUserAssemblies (ITaskItem[] resolvedAssemblies)
{
return resolvedAssemblies
.Where (f => Array.BinarySearch (FrameworkAssembliesToTreatAsUserAssemblies, Path.GetFileName (f.ItemSpec), StringComparer.OrdinalIgnoreCase) >= 0)
.Select(p => p);
{
var ret = new List<ITaskItem> ();
foreach (ITaskItem item in resolvedAssemblies) {
if (FrameworkAssembliesToTreatAsUserAssemblies.Contains (Path.GetFileName (item.ItemSpec)))
ret.Add (item);
}

return ret;
}
#endif

Expand All @@ -544,7 +548,7 @@ internal static IEnumerable<ITaskItem> GetFrameworkAssembliesToTreatAsUserAssemb
"Mono.Posix.dll",
};
// MUST BE SORTED CASE-INSENSITIVE
internal static readonly string[] FrameworkAssembliesToTreatAsUserAssemblies = {
internal static readonly HashSet<string> FrameworkAssembliesToTreatAsUserAssemblies = new HashSet<string> (StringComparer.OrdinalIgnoreCase) {
"Mono.Android.Support.v13.dll",
"Mono.Android.Support.v4.dll",
"Xamarin.Android.NUnitLite.dll",
Expand Down
17 changes: 1 addition & 16 deletions src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,36 +80,21 @@ public TypeMapGenerator (Action<string> logger, string[] supportedAbis)
this.logger = logger ?? throw new ArgumentNullException (nameof (logger));
if (supportedAbis == null)
throw new ArgumentNullException (nameof (supportedAbis));
if (supportedAbis.Length == 0)
throw new ArgumentException ("must not be empty", nameof (supportedAbis));
this.supportedAbis = supportedAbis;

outputEncoding = new UTF8Encoding (false);
moduleMagicString = outputEncoding.GetBytes (TypeMapMagicString);
typemapIndexMagicString = outputEncoding.GetBytes (TypeMapIndexMagicString);
}

void LoggerShim (TraceLevel level, string message)
public bool Generate (List<TypeDefinition> javaTypes, string outputDirectory, bool generateNativeAssembly)
{
logger (message);
}

public bool Generate (DirectoryAssemblyResolver resolver, IEnumerable<string> assemblies, string outputDirectory, bool generateNativeAssembly)
{
if (assemblies == null)
throw new ArgumentNullException (nameof (assemblies));
if (String.IsNullOrEmpty (outputDirectory))
throw new ArgumentException ("must not be null or empty", nameof (outputDirectory));

if (!Directory.Exists (outputDirectory))
Directory.CreateDirectory (outputDirectory);

var scanner = new JavaTypeScanner (LoggerShim) {
ErrorOnCustomJavaObject = false
};

List<TypeDefinition> javaTypes = scanner.GetJavaTypes (assemblies, resolver);

int assemblyId = 0;
int maxJavaNameLength = 0;
int maxModuleFileNameLength = 0;
Expand Down
Loading

0 comments on commit dfe29a8

Please sign in to comment.