void Run(DirectoryAssemblyResolver res) { PackageNamingPolicy pnp; JavaNativeTypeManager.PackageNamingPolicy = Enum.TryParse(PackageNamingPolicy, out pnp) ? pnp : PackageNamingPolicyEnum.LowercaseCrc64; foreach (var dir in FrameworkDirectories) { if (Directory.Exists(dir.ItemSpec)) { res.SearchDirectories.Add(dir.ItemSpec); } } // 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) { 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) { allTypemapAssemblies.Add(assembly.ItemSpec); } res.Load(assembly.ItemSpec); } // 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; } allTypemapAssemblies.Add(asm.ItemSpec); userAssemblies.Add(Path.GetFileNameWithoutExtension(asm.ItemSpec), asm.ItemSpec); } // Step 1 - Find all the JLO types var cache = new TypeDefinitionCache(); var scanner = new JavaTypeScanner(this.CreateTaskLogger(), cache) { ErrorOnCustomJavaObject = ErrorOnCustomJavaObject, }; List <TypeDefinition> allJavaTypes = scanner.GetJavaTypes(allTypemapAssemblies, res); // Step 2 - Generate type maps // Type mappings need to use all the assemblies, always. WriteTypeMappings(allJavaTypes); var javaTypes = new List <TypeDefinition> (); foreach (TypeDefinition td in allJavaTypes) { if (!userAssemblies.ContainsKey(td.Module.Assembly.Name.Name) || JavaTypeScanner.ShouldSkipJavaCallableWrapperGeneration(td, cache)) { continue; } javaTypes.Add(td); } // Step 3 - Generate Java stub code var success = CreateJavaSources(javaTypes, cache); if (!success) { return; } // We need to save a map of .NET type -> ACW type for resource file fixups 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); using (var acw_map = MemoryStreamPool.Shared.CreateStreamWriter(Encoding.Default)) { foreach (TypeDefinition type in javaTypes) { string managedKey = type.FullName.Replace('/', '.'); string javaKey = JavaNativeTypeManager.ToJniName(type).Replace('/', '.'); acw_map.Write(type.GetPartialAssemblyQualifiedName(cache)); acw_map.Write(';'); acw_map.Write(javaKey); acw_map.WriteLine(); TypeDefinition conflict; bool hasConflict = false; if (managed.TryGetValue(managedKey, out conflict)) { if (!managedConflicts.TryGetValue(managedKey, out var list)) { managedConflicts.Add(managedKey, list = new List <string> { conflict.GetPartialAssemblyName(cache) }); } list.Add(type.GetPartialAssemblyName(cache)); hasConflict = true; } if (java.TryGetValue(javaKey, out conflict)) { if (!javaConflicts.TryGetValue(javaKey, out var list)) { javaConflicts.Add(javaKey, list = new List <string> { conflict.GetAssemblyQualifiedName(cache) }); } list.Add(type.GetAssemblyQualifiedName(cache)); success = false; hasConflict = true; } if (!hasConflict) { managed.Add(managedKey, type); java.Add(javaKey, type); acw_map.Write(managedKey); acw_map.Write(';'); acw_map.Write(javaKey); acw_map.WriteLine(); acw_map.Write(JavaNativeTypeManager.ToCompatJniName(type, cache).Replace('/', '.')); acw_map.Write(';'); acw_map.Write(javaKey); acw_map.WriteLine(); } } acw_map.Flush(); MonoAndroidHelper.CopyIfStreamChanged(acw_map.BaseStream, AcwMapFile); } foreach (var kvp in managedConflicts) { Log.LogCodedWarning("XA4214", Properties.Resources.XA4214, kvp.Key, string.Join(", ", kvp.Value)); Log.LogCodedWarning("XA4214", Properties.Resources.XA4214_Result, kvp.Key, kvp.Value [0]); } foreach (var kvp in javaConflicts) { Log.LogCodedError("XA4215", Properties.Resources.XA4215, kvp.Key); foreach (var typeName in kvp.Value) { Log.LogCodedError("XA4215", Properties.Resources.XA4215_Details, kvp.Key, typeName); } } // Step 3 - Merge [Activity] and friends into AndroidManifest.xml var manifest = new ManifestDocument(ManifestTemplate); manifest.PackageName = PackageName; manifest.ApplicationName = ApplicationName ?? PackageName; manifest.Placeholders = ManifestPlaceholders; manifest.Assemblies.AddRange(userAssemblies.Values); manifest.Resolver = res; manifest.SdkDir = AndroidSdkDir; manifest.SdkVersion = AndroidSdkPlatform; manifest.Debug = Debug; manifest.MultiDex = MultiDex; manifest.NeedsInternet = NeedsInternet; manifest.InstantRunEnabled = InstantRunEnabled; var additionalProviders = manifest.Merge(Log, cache, allJavaTypes, ApplicationJavaClass, EmbedAssemblies, BundledWearApplicationName, MergedManifestDocuments); // Only write the new manifest if it actually changed if (manifest.SaveIfChanged(Log, MergedAndroidManifestOutput)) { Log.LogDebugMessage($"Saving: {MergedAndroidManifestOutput}"); } // Create additional runtime provider java sources. string providerTemplateFile = UseSharedRuntime ? "MonoRuntimeProvider.Shared.java" : "MonoRuntimeProvider.Bundled.java"; string providerTemplate = GetResource(providerTemplateFile); foreach (var provider in additionalProviders) { var contents = providerTemplate.Replace("MonoRuntimeProvider", provider); var real_provider = Path.Combine(OutputDirectory, "src", "mono", provider + ".java"); MonoAndroidHelper.CopyIfStringChanged(contents, real_provider); } // 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 javaTypes) { if (JavaNativeTypeManager.IsApplication(type, cache) || JavaNativeTypeManager.IsInstrumentation(type, cache)) { string javaKey = JavaNativeTypeManager.ToJniName(type).Replace('/', '.'); regCallsWriter.WriteLine("\t\tmono.android.Runtime.register (\"{0}\", {1}.class, {1}.__md_methods);", type.GetAssemblyQualifiedName(cache), javaKey); } } regCallsWriter.Close(); var real_app_dir = Path.Combine(OutputDirectory, "src", "mono", "android", "app"); string applicationTemplateFile = "ApplicationRegistration.java"; SaveResource(applicationTemplateFile, applicationTemplateFile, real_app_dir, template => template.Replace("// REGISTER_APPLICATION_AND_INSTRUMENTATION_CLASSES_HERE", regCallsWriter.ToString())); }
void Run(DirectoryAssemblyResolver res) { PackageNamingPolicy pnp; JavaNativeTypeManager.PackageNamingPolicy = Enum.TryParse(PackageNamingPolicy, out pnp) ? pnp : PackageNamingPolicyEnum.LowercaseHash; var temp = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); Directory.CreateDirectory(temp); var selectedWhitelistAssemblies = new List <string> (); // Put every assembly we'll need in the resolver foreach (var assembly in ResolvedAssemblies) { res.Load(Path.GetFullPath(assembly.ItemSpec)); if (MonoAndroidHelper.FrameworkAttributeLookupTargets.Any(a => Path.GetFileName(assembly.ItemSpec) == a)) { selectedWhitelistAssemblies.Add(Path.GetFullPath(assembly.ItemSpec)); } } // However we only want to look for JLO types in user code var assemblies = ResolvedUserAssemblies.Select(p => p.ItemSpec).ToList(); var fxAdditions = MonoAndroidHelper.GetFrameworkAssembliesToTreatAsUserAssemblies(ResolvedAssemblies) .Where(a => assemblies.All(x => Path.GetFileName(x) != Path.GetFileName(a))); assemblies = assemblies.Concat(fxAdditions).ToList(); // Step 1 - Find all the JLO types var scanner = new JavaTypeScanner(this.CreateTaskLogger()) { ErrorOnCustomJavaObject = ErrorOnCustomJavaObject, }; var all_java_types = scanner.GetJavaTypes(assemblies, res); WriteTypeMappings(all_java_types); var java_types = all_java_types.Where(t => !JavaTypeScanner.ShouldSkipJavaCallableWrapperGeneration(t)); // Step 2 - Generate Java stub code var keep_going = Generator.CreateJavaSources( Log, java_types, temp, ApplicationJavaClass, UseSharedRuntime, int.Parse(AndroidSdkPlatform) <= 10, ResolvedAssemblies.Any(assembly => Path.GetFileName(assembly.ItemSpec) == "Mono.Android.Export.dll")); var temp_map_file = Path.Combine(temp, "acw-map.temp"); // We need to save a map of .NET type -> ACW type for resource file fixups var managed = new Dictionary <string, TypeDefinition> (); var java = new Dictionary <string, TypeDefinition> (); var acw_map = new StreamWriter(temp_map_file); foreach (var type in java_types) { string managedKey = type.FullName.Replace('/', '.'); string javaKey = JavaNativeTypeManager.ToJniName(type).Replace('/', '.'); acw_map.WriteLine("{0};{1}", type.GetPartialAssemblyQualifiedName(), javaKey); acw_map.WriteLine("{0};{1}", type.GetAssemblyQualifiedName(), javaKey); TypeDefinition conflict; if (managed.TryGetValue(managedKey, out conflict)) { Log.LogWarning( "Duplicate managed type found! Mappings between managed types and Java types must be unique. " + "First Type: '{0}'; Second Type: '{1}'.", conflict.GetAssemblyQualifiedName(), type.GetAssemblyQualifiedName()); Log.LogWarning( "References to the type '{0}' will refer to '{1}'.", managedKey, conflict.GetAssemblyQualifiedName()); continue; } if (java.TryGetValue(javaKey, out conflict)) { Log.LogError( "Duplicate Java type found! Mappings between managed types and Java types must be unique. " + "First Type: '{0}'; Second Type: '{1}'", conflict.GetAssemblyQualifiedName(), type.GetAssemblyQualifiedName()); keep_going = false; continue; } managed.Add(managedKey, type); java.Add(javaKey, type); acw_map.WriteLine("{0};{1}", managedKey, javaKey); acw_map.WriteLine("{0};{1}", JavaNativeTypeManager.ToCompatJniName(type).Replace('/', '.'), javaKey); } acw_map.Close(); //The previous steps found an error, so we must abort and not generate any further output //We must do so subsequent unchanged builds fail too. if (!keep_going) { File.Delete(temp_map_file); return; } MonoAndroidHelper.CopyIfChanged(temp_map_file, AcwMapFile); try { File.Delete(temp_map_file); } catch (Exception) { } // Only overwrite files if the contents actually changed foreach (var file in Directory.GetFiles(temp, "*", SearchOption.AllDirectories)) { var dest = Path.GetFullPath(Path.Combine(OutputDirectory, "src", file.Substring(temp.Length + 1))); MonoAndroidHelper.CopyIfChanged(file, dest); } // Step 3 - Merge [Activity] and friends into AndroidManifest.xml var manifest = new ManifestDocument(ManifestTemplate, this.Log); manifest.PackageName = PackageName; manifest.ApplicationName = ApplicationName ?? PackageName; manifest.Placeholders = ManifestPlaceholders; manifest.Assemblies.AddRange(assemblies); manifest.Resolver = res; manifest.SdkDir = AndroidSdkDir; manifest.SdkVersion = AndroidSdkPlatform; manifest.Debug = Debug; manifest.NeedsInternet = NeedsInternet; var additionalProviders = manifest.Merge(all_java_types, selectedWhitelistAssemblies, ApplicationJavaClass, EmbedAssemblies, BundledWearApplicationName, MergedManifestDocuments); var temp_manifest = Path.Combine(temp, "AndroidManifest.xml"); var real_manifest = Path.GetFullPath(MergedAndroidManifestOutput); manifest.Save(temp_manifest); // Only write the new manifest if it actually changed MonoAndroidHelper.CopyIfChanged(temp_manifest, real_manifest); // Create additional runtime provider java sources. string providerTemplateFile = UseSharedRuntime ? "MonoRuntimeProvider.Shared.java" : "MonoRuntimeProvider.Bundled.java"; string providerTemplate = new StreamReader(typeof(JavaCallableWrapperGenerator).Assembly.GetManifestResourceStream(providerTemplateFile)).ReadToEnd(); foreach (var provider in additionalProviders) { var temp_provider = Path.Combine(temp, provider + ".java"); File.WriteAllText(temp_provider, providerTemplate.Replace("MonoRuntimeProvider", provider)); var real_provider_dir = Path.GetFullPath(Path.Combine(OutputDirectory, "src", "mono")); Directory.CreateDirectory(real_provider_dir); var real_provider = Path.Combine(real_provider_dir, provider + ".java"); MonoAndroidHelper.CopyIfChanged(temp_provider, real_provider); } // Create additional application java sources. Action <string, string, string, Func <string, string> > save = (resource, filename, destDir, applyTemplate) => { string temp_file = Path.Combine(temp, filename); string template = applyTemplate(new StreamReader(typeof(GenerateJavaStubs).Assembly.GetManifestResourceStream(resource)).ReadToEnd()); File.WriteAllText(temp_file, template); Directory.CreateDirectory(destDir); var real_file = Path.Combine(destDir, filename); MonoAndroidHelper.CopyIfChanged(temp_file, real_file); }; StringWriter regCallsWriter = new StringWriter(); regCallsWriter.WriteLine("\t\t// Application and Instrumentation ACWs must be registered first."); foreach (var type in java_types) { 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);", type.GetAssemblyQualifiedName(), javaKey); } } regCallsWriter.Close(); var real_app_dir = Path.GetFullPath(Path.Combine(OutputDirectory, "src", "mono", "android", "app")); string applicationTemplateFile = "ApplicationRegistration.java"; save(applicationTemplateFile, applicationTemplateFile, real_app_dir, template => template.Replace("// REGISTER_APPLICATION_AND_INSTRUMENTATION_CLASSES_HERE", regCallsWriter.ToString())); // Create NotifyTimeZoneChanges java sources. string notifyTimeZoneChangesFile = "NotifyTimeZoneChanges.java"; save(notifyTimeZoneChangesFile, notifyTimeZoneChangesFile, real_app_dir, template => template); // Delete our temp directory try { Directory.Delete(temp, true); } catch (Exception) { } }
void Run(DirectoryAssemblyResolver res) { PackageNamingPolicy pnp; JavaNativeTypeManager.PackageNamingPolicy = Enum.TryParse(PackageNamingPolicy, out pnp) ? pnp : PackageNamingPolicyEnum.LowercaseHash; foreach (var dir in FrameworkDirectories) { if (Directory.Exists(dir.ItemSpec)) { res.SearchDirectories.Add(dir.ItemSpec); } } var selectedWhitelistAssemblies = new List <string> (); // Put every assembly we'll need in the resolver foreach (var assembly in ResolvedAssemblies) { var assemblyFullPath = Path.GetFullPath(assembly.ItemSpec); res.Load(assemblyFullPath); if (MonoAndroidHelper.FrameworkAttributeLookupTargets.Any(a => Path.GetFileName(assembly.ItemSpec) == a)) { selectedWhitelistAssemblies.Add(assemblyFullPath); } } // However we only want to look for JLO types in user code var assemblies = ResolvedUserAssemblies.Select(p => p.ItemSpec).ToList(); var fxAdditions = MonoAndroidHelper.GetFrameworkAssembliesToTreatAsUserAssemblies(ResolvedAssemblies) .Where(a => assemblies.All(x => Path.GetFileName(x) != Path.GetFileName(a))); assemblies = assemblies.Concat(fxAdditions).ToList(); // Step 1 - Find all the JLO types var scanner = new JavaTypeScanner(this.CreateTaskLogger()) { ErrorOnCustomJavaObject = ErrorOnCustomJavaObject, }; var all_java_types = scanner.GetJavaTypes(assemblies, res); WriteTypeMappings(all_java_types); var java_types = all_java_types .Where(t => !JavaTypeScanner.ShouldSkipJavaCallableWrapperGeneration(t)) .ToArray(); // Step 2 - Generate Java stub code var success = Generator.CreateJavaSources( Log, java_types, Path.Combine(OutputDirectory, "src"), ApplicationJavaClass, UseSharedRuntime, int.Parse(AndroidSdkPlatform) <= 10, ResolvedAssemblies.Any(assembly => Path.GetFileName(assembly.ItemSpec) == "Mono.Android.Export.dll")); 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 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 acw_map = new StreamWriter(stream)) { foreach (var type in java_types) { string managedKey = type.FullName.Replace('/', '.'); string javaKey = JavaNativeTypeManager.ToJniName(type).Replace('/', '.'); acw_map.Write(type.GetPartialAssemblyQualifiedName()); acw_map.Write(';'); acw_map.Write(javaKey); acw_map.WriteLine(); TypeDefinition conflict; bool hasConflict = false; if (managed.TryGetValue(managedKey, out conflict)) { if (!managedConflicts.TryGetValue(managedKey, out var list)) { managedConflicts.Add(managedKey, list = new List <string> { conflict.GetPartialAssemblyName() }); } list.Add(type.GetPartialAssemblyName()); hasConflict = true; } if (java.TryGetValue(javaKey, out conflict)) { if (!javaConflicts.TryGetValue(javaKey, out var list)) { javaConflicts.Add(javaKey, list = new List <string> { conflict.GetAssemblyQualifiedName() }); } list.Add(type.GetAssemblyQualifiedName()); success = false; hasConflict = true; } if (!hasConflict) { managed.Add(managedKey, type); java.Add(javaKey, type); acw_map.Write(managedKey); acw_map.Write(';'); acw_map.Write(javaKey); acw_map.WriteLine(); acw_map.Write(JavaNativeTypeManager.ToCompatJniName(type).Replace('/', '.')); acw_map.Write(';'); acw_map.Write(javaKey); acw_map.WriteLine(); } } acw_map.Flush(); MonoAndroidHelper.CopyIfStreamChanged(stream, AcwMapFile); } foreach (var kvp in managedConflicts) { Log.LogCodedWarning( "XA4214", "The managed type `{0}` exists in multiple assemblies: {1}. " + "Please refactor the managed type names in these assemblies so that they are not identical.", kvp.Key, string.Join(", ", kvp.Value)); Log.LogCodedWarning("XA4214", "References to the type `{0}` will refer to `{0}, {1}`.", kvp.Key, kvp.Value [0]); } foreach (var kvp in javaConflicts) { Log.LogCodedError( "XA4215", "The Java type `{0}` is generated by more than one managed type. " + "Please change the [Register] attribute so that the same Java type is not emitted.", kvp.Key); foreach (var typeName in kvp.Value) { Log.LogCodedError("XA4215", " `{0}` generated by: {1}", kvp.Key, typeName); } } // Step 3 - Merge [Activity] and friends into AndroidManifest.xml var manifest = new ManifestDocument(ManifestTemplate, this.Log); manifest.PackageName = PackageName; manifest.ApplicationName = ApplicationName ?? PackageName; manifest.Placeholders = ManifestPlaceholders; manifest.Assemblies.AddRange(assemblies); manifest.Resolver = res; manifest.SdkDir = AndroidSdkDir; manifest.SdkVersion = AndroidSdkPlatform; manifest.Debug = Debug; manifest.MultiDex = MultiDex; manifest.NeedsInternet = NeedsInternet; manifest.InstantRunEnabled = InstantRunEnabled; var additionalProviders = manifest.Merge(all_java_types, selectedWhitelistAssemblies, ApplicationJavaClass, EmbedAssemblies, BundledWearApplicationName, MergedManifestDocuments); using (var stream = new MemoryStream()) { manifest.Save(stream); // Only write the new manifest if it actually changed MonoAndroidHelper.CopyIfStreamChanged(stream, MergedAndroidManifestOutput); } // Create additional runtime provider java sources. string providerTemplateFile = UseSharedRuntime ? "MonoRuntimeProvider.Shared.java" : "MonoRuntimeProvider.Bundled.java"; string providerTemplate = GetResource <JavaCallableWrapperGenerator> (providerTemplateFile); foreach (var provider in additionalProviders) { var contents = providerTemplate.Replace("MonoRuntimeProvider", provider); var real_provider = Path.Combine(OutputDirectory, "src", "mono", provider + ".java"); MonoAndroidHelper.CopyIfStringChanged(contents, real_provider); } // 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) { 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);", type.GetAssemblyQualifiedName(), javaKey); } } regCallsWriter.Close(); var real_app_dir = Path.Combine(OutputDirectory, "src", "mono", "android", "app"); string applicationTemplateFile = "ApplicationRegistration.java"; SaveResource(applicationTemplateFile, applicationTemplateFile, real_app_dir, template => template.Replace("// REGISTER_APPLICATION_AND_INSTRUMENTATION_CLASSES_HERE", regCallsWriter.ToString())); // Create NotifyTimeZoneChanges java sources. string notifyTimeZoneChangesFile = "NotifyTimeZoneChanges.java"; SaveResource(notifyTimeZoneChangesFile, notifyTimeZoneChangesFile, real_app_dir, template => template); }