internal static ExportAttribute ToExportAttribute(CustomAttribute attr, IMemberDefinition declaringMember) { var name = attr.ConstructorArguments.Count > 0 ? (string)attr.ConstructorArguments [0].Value : declaringMember.Name; if (attr.Properties.Count == 0) { return(new ExportAttribute(name)); } var typeArgs = (CustomAttributeArgument [])attr.Properties.FirstOrDefault(p => p.Name == "Throws").Argument.Value; var thrown = typeArgs != null && typeArgs.Any() ? (from caa in typeArgs select JavaNativeTypeManager.Parse(GetJniTypeName((TypeReference)caa.Value)).Type).ToArray() : null; var superArgs = (string)attr.Properties.FirstOrDefault(p => p.Name == "SuperArgumentsString").Argument.Value; return(new ExportAttribute(name) { ThrownNames = thrown, SuperArgumentsString = superArgs }); }
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); 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) { } }
JavaCallableWrapperGenerator(TypeDefinition type, string outerType, Action <string, object[]> log) { this.type = type; this.log = log; if (type.IsEnum || type.IsInterface || type.IsValueType) { Diagnostic.Error(4200, LookupSource(type), "Can only generate Java wrappers for 'class' types, not type '{0}'.", type.FullName); } string jniName = JavaNativeTypeManager.ToJniName(type); if (jniName == null) { Diagnostic.Error(4201, LookupSource(type), "Unable to determine Java name for type {0}", type.FullName); } if (!string.IsNullOrEmpty(outerType)) { string p; jniName = jniName.Substring(outerType.Length + 1); ExtractJavaNames(outerType, out p, out outerType); } ExtractJavaNames(jniName, out package, out name); if (string.IsNullOrEmpty(package) && (type.IsSubclassOf("Android.App.Activity") || type.IsSubclassOf("Android.App.Application") || type.IsSubclassOf("Android.App.Service") || type.IsSubclassOf("Android.Content.BroadcastReceiver") || type.IsSubclassOf("Android.Content.ContentProvider"))) { Diagnostic.Error(4203, LookupSource(type), "The Name property must be a fully qualified 'package.TypeName' value, and no package was found for '{0}'.", jniName); } foreach (MethodDefinition minfo in type.Methods.Where(m => !m.IsConstructor)) { var baseMethods = GetBaseMethods(minfo); var baseRegiteredMethod = baseMethods.FirstOrDefault(m => GetRegisterAttributes(m).Any()); if (baseRegiteredMethod != null) { AddMethod(baseRegiteredMethod, minfo); } else if (GetExportFieldAttributes(minfo).Any()) { AddMethod(null, minfo); HasExport = true; } else if (GetExportAttributes(minfo).Any()) { AddMethod(null, minfo); HasExport = true; } } foreach (MethodDefinition imethod in type.Interfaces.Select(ifaceInfo => ifaceInfo.InterfaceType) .Select(r => { var d = r.Resolve(); if (d == null) { Diagnostic.Error(4204, LookupSource(type), "Unable to resolve interface type '{0}'. Are you missing an assembly reference?", r.FullName); } return(d); }) .Where(d => GetRegisterAttributes(d).Any()) .SelectMany(d => d.Methods)) { AddMethod(imethod, imethod); } var ctorTypes = new List <TypeDefinition> () { type, }; foreach (var bt in type.GetBaseTypes()) { ctorTypes.Add(bt); RegisterAttribute rattr = GetRegisterAttributes(bt).FirstOrDefault(); if (rattr != null && rattr.DoNotGenerateAcw) { break; } } ctorTypes.Reverse(); var curCtors = new List <MethodDefinition> (); foreach (MethodDefinition minfo in type.Methods.Where(m => m.IsConstructor)) { if (GetExportAttributes(minfo).Any()) { if (minfo.IsStatic) { // Diagnostic.Warning (log, "ExportAttribute does not work on static constructor"); } else { AddConstructor(minfo, ctorTypes [0], outerType, null, curCtors, false, true); HasExport = true; } } } AddConstructors(ctorTypes [0], outerType, null, curCtors, true); for (int i = 1; i < ctorTypes.Count; ++i) { var baseCtors = curCtors; curCtors = new List <MethodDefinition> (); AddConstructors(ctorTypes [i], outerType, baseCtors, curCtors, false); } }
public override void RegisterNativeMembers(JniType nativeClass, Type type, string methods) { if (FastRegisterNativeMembers(nativeClass, type, methods)) { return; } if (string.IsNullOrEmpty(methods)) { base.RegisterNativeMembers(nativeClass, type, methods); return; } string[] members = methods.Split('\n'); if (members.Length < 2) { base.RegisterNativeMembers(nativeClass, type, methods); return; } JniNativeMethodRegistration[] natives = new JniNativeMethodRegistration [members.Length - 1]; for (int i = 0; i < members.Length; ++i) { string method = members [i]; if (string.IsNullOrEmpty(method)) { continue; } string[] toks = members [i].Split(new[] { ':' }, 4); Delegate callback; if (toks [2] == "__export__") { var mname = toks [0].Substring(2); var minfo = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static).Where(m => m.Name == mname && JavaNativeTypeManager.GetJniSignature(m) == toks [1]).FirstOrDefault(); if (minfo == null) { throw new InvalidOperationException(String.Format("Specified managed method '{0}' was not found. Signature: {1}", mname, toks [1])); } callback = CreateDynamicCallback(minfo); } else { GetCallbackHandler connector = (GetCallbackHandler)Delegate.CreateDelegate(typeof(GetCallbackHandler), toks.Length == 4 ? Type.GetType(toks [3], true) : type, toks [2]); callback = connector(); } natives [i] = new JniNativeMethodRegistration(toks [0], toks [1], callback); } JniEnvironment.Types.RegisterNatives(nativeClass.PeerReference, natives, natives.Length); }
public override void RegisterNativeMembers(JniType nativeClass, Type type, string?methods) { if (FastRegisterNativeMembers(nativeClass, type, methods)) { return; } if (string.IsNullOrEmpty(methods)) { if (jniAddNativeMethodRegistrationAttributePresent) { base.RegisterNativeMembers(nativeClass, type, methods); } return; } string[] members = methods !.Split('\n'); if (members.Length < 2) { if (jniAddNativeMethodRegistrationAttributePresent) { base.RegisterNativeMembers(nativeClass, type, methods); } return; } JniNativeMethodRegistration[] natives = new JniNativeMethodRegistration [members.Length - 1]; for (int i = 0; i < members.Length; ++i) { string method = members [i]; if (string.IsNullOrEmpty(method)) { continue; } string[] toks = members [i].Split(new[] { ':' }, 4); Delegate callback; if (toks [2] == "__export__") { var mname = toks [0].Substring(2); MethodInfo?minfo = null; foreach (var mi in type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)) { if (mi.Name == mname && JavaNativeTypeManager.GetJniSignature(mi) == toks [1]) { minfo = mi; break; } } if (minfo == null) { throw new InvalidOperationException(String.Format("Specified managed method '{0}' was not found. Signature: {1}", mname, toks [1])); } callback = CreateDynamicCallback(minfo); } else { Type callbackDeclaringType = type; if (toks.Length == 4) { callbackDeclaringType = Type.GetType(toks [3], throwOnError: true) !; } while (callbackDeclaringType.ContainsGenericParameters) { callbackDeclaringType = callbackDeclaringType.BaseType !; } GetCallbackHandler connector = (GetCallbackHandler)Delegate.CreateDelegate(typeof(GetCallbackHandler), callbackDeclaringType, toks [2]); callback = connector(); } natives [i] = new JniNativeMethodRegistration(toks [0], toks [1], callback); } JniEnvironment.Types.RegisterNatives(nativeClass.PeerReference, natives, natives.Length); }
static bool CannotRegisterInStaticConstructor(TypeDefinition type) { return(JavaNativeTypeManager.IsApplication(type) || JavaNativeTypeManager.IsInstrumentation(type)); }
public void Lowercase() { JavaNativeTypeManager.PackageNamingPolicy = PackageNamingPolicy.Lowercase; Assert.AreEqual("system", JavaNativeTypeManager.GetPackageName(typeof(string))); }
public static string ToString(TypeDefinition typeDef) { return(JavaNativeTypeManager.ToJniName(typeDef).Replace('/', '.')); }
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); // 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; 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()); success = false; continue; } 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); } // 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); }
public IList <string> Merge(List <TypeDefinition> subclasses, List <string> selectedWhitelistAssemblies, string applicationClass, bool embed, string bundledWearApplicationName, IEnumerable <string> mergedManifestDocuments) { string applicationName = ApplicationName; var manifest = doc.Root; if (manifest == null || manifest.Name != "manifest") { throw new Exception("Root element must be 'manifest'"); } var manifest_package = (string)manifest.Attribute("package"); if (!string.IsNullOrWhiteSpace(manifest_package)) { PackageName = manifest_package; } manifest.SetAttributeValue(XNamespace.Xmlns + "android", "http://schemas.android.com/apk/res/android"); if (manifest.Attribute(androidNs + "versionCode") == null) { manifest.SetAttributeValue(androidNs + "versionCode", "1"); } if (manifest.Attribute(androidNs + "versionName") == null) { manifest.SetAttributeValue(androidNs + "versionName", "1.0"); } app = CreateApplicationElement(manifest, applicationClass, subclasses, selectedWhitelistAssemblies); if (app.Attribute(androidNs + "label") == null && applicationName != null) { app.SetAttributeValue(androidNs + "label", applicationName); } var existingTypes = new HashSet <string> ( app.Descendants().Select(a => (string)a.Attribute(attName)).Where(v => v != null)); if (!string.IsNullOrEmpty(bundledWearApplicationName)) { if (!app.Elements("meta-data").Any(e => e.Attributes(androidNs + "name").Any(a => a.Value == bundledWearApplicationName))) { app.Add(new XElement("meta-data", new XAttribute(androidNs + "name", "com.google.android.wearable.beta.app"), new XAttribute(androidNs + "resource", "@xml/wearable_app_desc"))); } } // If no <uses-sdk> is specified, add it with both minSdkVersion and // targetSdkVersion set to TargetFrameworkVersion if (!manifest.Elements("uses-sdk").Any()) { manifest.AddFirst( new XElement("uses-sdk", new XAttribute(androidNs + "minSdkVersion", SdkVersionName), new XAttribute(androidNs + "targetSdkVersion", SdkVersionName))); } // If no minSdkVersion is specified, set it to TargetFrameworkVersion var uses = manifest.Element("uses-sdk"); if (uses.Attribute(androidNs + "minSdkVersion") == null) { int minSdkVersion; if (!int.TryParse(SdkVersionName, out minSdkVersion)) { minSdkVersion = 11; } minSdkVersion = Math.Min(minSdkVersion, 11); uses.SetAttributeValue(androidNs + "minSdkVersion", minSdkVersion.ToString()); } string targetSdkVersion; var tsv = uses.Attribute(androidNs + "targetSdkVersion"); if (tsv != null) { targetSdkVersion = tsv.Value; } else { targetSdkVersion = SdkVersionName; uses.AddBeforeSelf(new XComment("suppress UsesMinSdkAttributes")); } int?tryTargetSdkVersion = MonoAndroidHelper.SupportedVersions.GetApiLevelFromId(targetSdkVersion); if (!tryTargetSdkVersion.HasValue) { throw new InvalidOperationException(string.Format("The targetSdkVersion ({0}) is not a valid API level", targetSdkVersion)); } int targetSdkVersionValue = tryTargetSdkVersion.Value; foreach (var t in subclasses) { if (t.IsAbstract) { continue; } if (PackageName == null) { PackageName = t.Namespace; } var name = JavaNativeTypeManager.ToJniName(t).Replace('/', '.'); var compatName = JavaNativeTypeManager.ToCompatJniName(t).Replace('/', '.'); if (((string)app.Attribute(attName)) == compatName) { app.SetAttributeValue(attName, name); } Func <TypeDefinition, string, int, XElement> generator = GetGenerator(t); if (generator == null) { continue; } try { // activity not present: create a launcher for it IFF it has attribute if (!existingTypes.Contains(name) && !existingTypes.Contains(compatName)) { XElement fromCode = generator(t, name, targetSdkVersionValue); if (fromCode == null) { continue; } if (!t.Methods.Where(m => m.IsConstructor).Cast <MethodDefinition> ().Any(c => !c.HasParameters && c.IsPublic)) { throw new InvalidOperationException(string.Format("The type '{0}' needs to have a public default constructor.", t.FullName)); } app.Add(fromCode); } foreach (var d in app.Descendants().Where(a => ((string)a.Attribute(attName)) == compatName)) { d.SetAttributeValue(attName, name); } } catch (InvalidActivityNameException ex) { log.LogErrorFromException(ex); } } var icon = app.Attribute(androidNs + "icon"); if (icon == null) { var activity = app.Element("activity"); if (activity != null) { var activityIcon = activity.Attribute(androidNs + "icon"); if (activityIcon != null) { app.Add(new XAttribute(androidNs + "icon", activityIcon.Value)); } } } PackageName = AndroidAppManifest.CanonicalizePackageName(PackageName); if (!PackageName.Contains('.')) { throw new InvalidOperationException("/manifest/@package attribute MUST contain a period ('.')."); } manifest.SetAttributeValue("package", PackageName); var providerNames = AddMonoRuntimeProviders(app); if (Debug) { app.Add(new XComment("suppress ExportedReceiver")); app.Add(new XElement("receiver", new XAttribute(androidNs + "name", "mono.android.Seppuku"), new XElement("intent-filter", new XElement("action", new XAttribute(androidNs + "name", "mono.android.intent.action.SEPPUKU")), new XElement("category", new XAttribute(androidNs + "name", "mono.android.intent.category.SEPPUKU." + PackageName))))); if (app.Attribute(androidNs + "debuggable") == null) { app.Add(new XAttribute(androidNs + "debuggable", "true")); } } if (Debug || NeedsInternet) { AddInternetPermissionForDebugger(); } if (!embed) { AddFastDeployPermissions(); } AddAddOns(app, SdkDir, SdkVersionName, Addons); // If the manifest has android:installLocation, but we are targeting // API 7 or lower, remove it for the user and show a warning if (manifest.Attribute(androidNs + "installLocation") != null) { if (targetSdkVersionValue < 8) { manifest.Attribute(androidNs + "installLocation").Remove(); Console.Error.WriteLine("monodroid: warning 1 : installLocation cannot be specified for Android versions less than 2.2. Attribute installLocation ignored."); } } AddInstrumentations(manifest, subclasses, targetSdkVersionValue); AddPermissions(app, selectedWhitelistAssemblies); AddPermissionGroups(app, selectedWhitelistAssemblies); AddPermissionTrees(app, selectedWhitelistAssemblies); AddUsesPermissions(app, selectedWhitelistAssemblies); AddUsesFeatures(app, selectedWhitelistAssemblies); AddSupportsGLTextures(app, selectedWhitelistAssemblies); ReorderActivityAliases(app); ReorderElements(app); if (mergedManifestDocuments != null) { foreach (var mergedManifest in mergedManifestDocuments) { try { MergeLibraryManifest(mergedManifest); } catch (Exception ex) { log.LogWarningFromException(ex); } } } return(providerNames); }
public IList <string> Merge(List <TypeDefinition> subclasses, string applicationClass, bool embed, string bundledWearApplicationName) { string applicationName = ApplicationName; var manifest = doc.Root; if (manifest == null || manifest.Name != "manifest") { throw new Exception("Root element must be 'manifest'"); } var manifest_package = (string)manifest.Attribute("package"); if (!string.IsNullOrWhiteSpace(manifest_package)) { PackageName = manifest_package; } manifest.SetAttributeValue(XNamespace.Xmlns + "android", "http://schemas.android.com/apk/res/android"); if (manifest.Attribute(androidNs + "versionCode") == null) { manifest.SetAttributeValue(androidNs + "versionCode", "1"); } if (manifest.Attribute(androidNs + "versionName") == null) { manifest.SetAttributeValue(androidNs + "versionName", "1.0"); } app = CreateApplicationElement(manifest, applicationClass, subclasses); if (app.Attribute(androidNs + "label") == null && applicationName != null) { app.SetAttributeValue(androidNs + "label", applicationName); } var existingTypes = new HashSet <string> ( app.Descendants().Select(a => (string)a.Attribute(attName)).Where(v => v != null)); if (!string.IsNullOrEmpty(bundledWearApplicationName)) { if (!app.Elements("meta-data").Any(e => e.Attributes(androidNs + "name").Any(a => a.Value == bundledWearApplicationName))) { app.Add(new XElement("meta-data", new XAttribute(androidNs + "name", "com.google.android.wearable.beta.app"), new XAttribute(androidNs + "resource", "@xml/wearable_app_desc"))); } } // If no <uses-sdk> is specified, add it with both minSdkVersion and // targetSdkVersion set to TargetFrameworkVersion if (!manifest.Elements("uses-sdk").Any()) { manifest.AddFirst( new XElement("uses-sdk", new XAttribute(androidNs + "minSdkVersion", SdkVersionName), new XAttribute(androidNs + "targetSdkVersion", SdkVersionName))); } // If no minSdkVersion is specified, set it to TargetFrameworkVersion var uses = manifest.Element("uses-sdk"); if (uses.Attribute(androidNs + "minSdkVersion") == null) { int minSdkVersion; if (!int.TryParse(SdkVersionName, out minSdkVersion)) { minSdkVersion = XABuildConfig.NDKMinimumApiAvailable; } minSdkVersion = Math.Min(minSdkVersion, XABuildConfig.NDKMinimumApiAvailable); uses.SetAttributeValue(androidNs + "minSdkVersion", minSdkVersion.ToString()); } string targetSdkVersion; var tsv = uses.Attribute(androidNs + "targetSdkVersion"); if (tsv != null) { targetSdkVersion = tsv.Value; } else { targetSdkVersion = SdkVersionName; uses.AddBeforeSelf(new XComment("suppress UsesMinSdkAttributes")); } int?tryTargetSdkVersion = MonoAndroidHelper.SupportedVersions.GetApiLevelFromId(targetSdkVersion); if (!tryTargetSdkVersion.HasValue) { throw new InvalidOperationException(string.Format("The targetSdkVersion ({0}) is not a valid API level", targetSdkVersion)); } int targetSdkVersionValue = tryTargetSdkVersion.Value; foreach (var t in subclasses) { if (t.IsAbstract) { continue; } if (PackageName == null) { PackageName = t.Namespace; } var name = JavaNativeTypeManager.ToJniName(t).Replace('/', '.'); var compatName = JavaNativeTypeManager.ToCompatJniName(t).Replace('/', '.'); if (((string)app.Attribute(attName)) == compatName) { app.SetAttributeValue(attName, name); } Func <TypeDefinition, string, int, XElement> generator = GetGenerator(t); if (generator == null) { continue; } try { // activity not present: create a launcher for it IFF it has attribute if (!existingTypes.Contains(name) && !existingTypes.Contains(compatName)) { XElement fromCode = generator(t, name, targetSdkVersionValue); if (fromCode == null) { continue; } IEnumerable <MethodDefinition> constructors = t.Methods.Where(m => m.IsConstructor).Cast <MethodDefinition> (); if (!constructors.Any(c => !c.HasParameters && c.IsPublic)) { string message = $"The type '{t.FullName}' must provide a public default constructor"; SequencePoint sourceLocation = FindSource(constructors); if (sourceLocation != null && sourceLocation.Document?.Url != null) { log.LogError( subcategory: String.Empty, errorCode: "XA4213", helpKeyword: String.Empty, file: sourceLocation.Document.Url, lineNumber: sourceLocation.StartLine, columnNumber: sourceLocation.StartColumn, endLineNumber: sourceLocation.EndLine, endColumnNumber: sourceLocation.EndColumn, message: message); } else { log.LogCodedError("XA4213", message); } continue; } app.Add(fromCode); } foreach (var d in app.Descendants().Where(a => ((string)a.Attribute(attName)) == compatName)) { d.SetAttributeValue(attName, name); } } catch (InvalidActivityNameException ex) { log.LogErrorFromException(ex); } } var icon = app.Attribute(androidNs + "icon"); if (icon == null) { var activity = app.Element("activity"); if (activity != null) { var activityIcon = activity.Attribute(androidNs + "icon"); if (activityIcon != null) { app.Add(new XAttribute(androidNs + "icon", activityIcon.Value)); } } } PackageName = AndroidAppManifest.CanonicalizePackageName(PackageName); if (!PackageName.Contains('.')) { throw new InvalidOperationException("/manifest/@package attribute MUST contain a period ('.')."); } manifest.SetAttributeValue("package", PackageName); if (MultiDex) { app.Add(CreateMonoRuntimeProvider("mono.android.MultiDexLoader", null, initOrder: --AppInitOrder)); } var providerNames = AddMonoRuntimeProviders(app); if (Debug && !embed && InstantRunEnabled) { if (int.TryParse(SdkVersion, out int apiLevel) && apiLevel >= 19) { app.Add(CreateMonoRuntimeProvider("mono.android.ResourcePatcher", null, initOrder: --AppInitOrder)); } } if (Debug) { app.Add(new XComment("suppress ExportedReceiver")); app.Add(new XElement("receiver", new XAttribute(androidNs + "name", "mono.android.Seppuku"), new XElement("intent-filter", new XElement("action", new XAttribute(androidNs + "name", "mono.android.intent.action.SEPPUKU")), new XElement("category", new XAttribute(androidNs + "name", "mono.android.intent.category.SEPPUKU." + PackageName))))); if (app.Attribute(androidNs + "debuggable") == null) { app.Add(new XAttribute(androidNs + "debuggable", "true")); } } if (Debug || NeedsInternet) { AddInternetPermissionForDebugger(); } if (!embed) { AddFastDeployPermissions(); } // If the manifest has android:installLocation, but we are targeting // API 7 or lower, remove it for the user and show a warning if (manifest.Attribute(androidNs + "installLocation") != null) { if (targetSdkVersionValue < 8) { manifest.Attribute(androidNs + "installLocation").Remove(); Console.Error.WriteLine("monodroid: warning 1 : installLocation cannot be specified for Android versions less than 2.2. Attribute installLocation ignored."); } } AddInstrumentations(manifest, subclasses, targetSdkVersionValue); AddPermissions(app); AddPermissionGroups(app); AddPermissionTrees(app); AddUsesPermissions(app); AddUsesFeatures(app); AddSupportsGLTextures(app); ReorderActivityAliases(app); ReorderElements(app); return(providerNames); SequencePoint FindSource(IEnumerable <MethodDefinition> methods) { if (methods == null) { return(null); } SequencePoint ret = null; foreach (MethodDefinition method in methods.Where(m => m != null && m.HasBody && m.DebugInformation != null)) { foreach (Instruction ins in method.Body.Instructions) { SequencePoint seq = method.DebugInformation.GetSequencePoint(ins); if (seq == null) { continue; } if (ret == null || seq.StartLine < ret.StartLine) { ret = seq; } break; } } return(ret); } }
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 the CacheFile if needed if (!string.IsNullOrEmpty(CacheFile)) { bool extractNativeLibraries = manifest.ExtractNativeLibraries(); if (!extractNativeLibraries) { //We need to write the value to a file, if _GenerateJavaStubs is skipped on incremental builds var document = new XDocument( new XDeclaration("1.0", "UTF-8", null), new XElement("Properties", new XElement(nameof(ReadJavaStubsCache.EmbeddedDSOsEnabled), "True")) ); document.SaveIfChanged(CacheFile); } else { //Delete the file otherwise, since we only need to specify when EmbeddedDSOsEnabled=True File.Delete(CacheFile); } } // 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); }
public static void GenerateTypeRegistrations(CodeGenerationOptions opt, GenerationInfo gen_info) { StreamWriter sw = gen_info.Writer = gen_info.OpenStream(opt.GetFileName("Java.Interop.__TypeRegistrations")); Dictionary <string, List <KeyValuePair <string, string> > > mapping = new Dictionary <string, List <KeyValuePair <string, string> > >(); foreach (KeyValuePair <string, string> reg in gen_info.TypeRegistrations) { int ls = reg.Key.LastIndexOf('/'); string package = ls >= 0 ? reg.Key.Substring(0, ls) : ""; if (JavaNativeTypeManager.ToCliType(reg.Key) == reg.Value) { continue; } List <KeyValuePair <string, string> > v; if (!mapping.TryGetValue(package, out v)) { mapping.Add(package, v = new List <KeyValuePair <string, string> >()); } v.Add(new KeyValuePair <string, string>(reg.Key, reg.Value)); } sw.WriteLine("using System;"); sw.WriteLine("using System.Collections.Generic;"); sw.WriteLine("using Android.Runtime;"); sw.WriteLine(); sw.WriteLine("namespace Java.Interop {"); sw.WriteLine(); sw.WriteLine("\tpartial class __TypeRegistrations {"); sw.WriteLine(); sw.WriteLine("\t\tpublic static void RegisterPackages ()"); sw.WriteLine("\t\t{"); sw.WriteLine("#if MONODROID_TIMING"); sw.WriteLine("\t\t\tvar start = DateTime.Now;"); sw.WriteLine("\t\t\tAndroid.Util.Log.Info (\"MonoDroid-Timing\", \"RegisterPackages start: \" + (start - new DateTime (1970, 1, 1)).TotalMilliseconds);"); sw.WriteLine("#endif // def MONODROID_TIMING"); sw.WriteLine("\t\t\tJava.Interop.TypeManager.RegisterPackages ("); sw.WriteLine("\t\t\t\t\tnew string[]{"); foreach (KeyValuePair <string, List <KeyValuePair <string, string> > > e in mapping) { sw.WriteLine("\t\t\t\t\t\t\"{0}\",", e.Key); } sw.WriteLine("\t\t\t\t\t},"); sw.WriteLine("\t\t\t\t\tnew Converter<string, Type>[]{"); foreach (KeyValuePair <string, List <KeyValuePair <string, string> > > e in mapping) { sw.WriteLine("\t\t\t\t\t\tlookup_{0}_package,", e.Key.Replace('/', '_')); } sw.WriteLine("\t\t\t\t\t});"); sw.WriteLine("#if MONODROID_TIMING"); sw.WriteLine("\t\t\tvar end = DateTime.Now;"); sw.WriteLine("\t\t\tAndroid.Util.Log.Info (\"MonoDroid-Timing\", \"RegisterPackages time: \" + (end - new DateTime (1970, 1, 1)).TotalMilliseconds + \" [elapsed: \" + (end - start).TotalMilliseconds + \" ms]\");"); sw.WriteLine("#endif // def MONODROID_TIMING"); sw.WriteLine("\t\t}"); sw.WriteLine(); sw.WriteLine("\t\tstatic Type Lookup (string[] mappings, string javaType)"); sw.WriteLine("\t\t{"); sw.WriteLine("\t\t\tstring managedType = Java.Interop.TypeManager.LookupTypeMapping (mappings, javaType);"); sw.WriteLine("\t\t\tif (managedType == null)"); sw.WriteLine("\t\t\t\treturn null;"); sw.WriteLine("\t\t\treturn Type.GetType (managedType);"); sw.WriteLine("\t\t}"); foreach (KeyValuePair <string, List <KeyValuePair <string, string> > > map in mapping) { sw.WriteLine(); string package = map.Key.Replace('/', '_'); sw.WriteLine("\t\tstatic string[] package_{0}_mappings;", package); sw.WriteLine("\t\tstatic Type lookup_{0}_package (string klass)", package); sw.WriteLine("\t\t{"); sw.WriteLine("\t\t\tif (package_{0}_mappings == null) {{", package); sw.WriteLine("\t\t\t\tpackage_{0}_mappings = new string[]{{", package); map.Value.Sort((a, b) => a.Key.CompareTo(b.Key)); foreach (KeyValuePair <string, string> t in map.Value) { sw.WriteLine("\t\t\t\t\t\"{0}:{1}\",", t.Key, t.Value); } sw.WriteLine("\t\t\t\t};"); sw.WriteLine("\t\t\t}"); sw.WriteLine(""); sw.WriteLine("\t\t\treturn Lookup (package_{0}_mappings, klass);", package); sw.WriteLine("\t\t}"); } sw.WriteLine("\t}"); sw.WriteLine("}"); sw.Close(); gen_info.Writer = null; }
public void LowercaseWithAssemblyName() { JavaNativeTypeManager.PackageNamingPolicy = PackageNamingPolicy.LowercaseWithAssemblyName; Assert.AreEqual("assembly_mscorlib.system", JavaNativeTypeManager.GetPackageName(typeof(string))); }
static string GetJniTypeName(TypeReference typeRef, ExportParameterKind exportKind) { return(JavaNativeTypeManager.GetJniTypeName(typeRef, exportKind)); }
JavaCallableWrapperGenerator(TypeDefinition type, string outerType, Action <string, object[]> log, IMetadataResolver resolver) { this.type = type; this.log = log; this.cache = resolver ?? new TypeDefinitionCache(); if (type.IsEnum || type.IsInterface || type.IsValueType) { Diagnostic.Error(4200, LookupSource(type), Localization.Resources.JavaCallableWrappers_XA4200, type.FullName); } string jniName = JavaNativeTypeManager.ToJniName(type); if (jniName == null) { Diagnostic.Error(4201, LookupSource(type), Localization.Resources.JavaCallableWrappers_XA4201, type.FullName); } if (!string.IsNullOrEmpty(outerType)) { string p; jniName = jniName.Substring(outerType.Length + 1); ExtractJavaNames(outerType, out p, out outerType); } ExtractJavaNames(jniName, out package, out name); if (string.IsNullOrEmpty(package) && (type.IsSubclassOf("Android.App.Activity", cache) || type.IsSubclassOf("Android.App.Application", cache) || type.IsSubclassOf("Android.App.Service", cache) || type.IsSubclassOf("Android.Content.BroadcastReceiver", cache) || type.IsSubclassOf("Android.Content.ContentProvider", cache))) { Diagnostic.Error(4203, LookupSource(type), Localization.Resources.JavaCallableWrappers_XA4203, jniName); } foreach (MethodDefinition minfo in type.Methods.Where(m => !m.IsConstructor)) { var baseRegisteredMethod = GetBaseRegisteredMethod(minfo); if (baseRegisteredMethod != null) { AddMethod(baseRegisteredMethod, minfo); } else if (GetExportFieldAttributes(minfo).Any()) { AddMethod(null, minfo); HasExport = true; } else if (GetExportAttributes(minfo).Any()) { AddMethod(null, minfo); HasExport = true; } } foreach (MethodDefinition imethod in type.Interfaces.Select(ifaceInfo => ifaceInfo.InterfaceType) .Select(r => { var d = r.Resolve(); if (d == null) { Diagnostic.Error(4204, LookupSource(type), Localization.Resources.JavaCallableWrappers_XA4204, r.FullName); } return(d); }) .Where(d => GetRegisterAttributes(d).Any()) .SelectMany(d => d.Methods) .Where(m => !m.IsStatic)) { AddMethod(imethod, imethod); } var ctorTypes = new List <TypeDefinition> () { type, }; foreach (var bt in type.GetBaseTypes(cache)) { ctorTypes.Add(bt); RegisterAttribute rattr = GetRegisterAttributes(bt).FirstOrDefault(); if (rattr != null && rattr.DoNotGenerateAcw) { break; } } ctorTypes.Reverse(); var curCtors = new List <MethodDefinition> (); foreach (MethodDefinition minfo in type.Methods.Where(m => m.IsConstructor)) { if (GetExportAttributes(minfo).Any()) { if (minfo.IsStatic) { // Diagnostic.Warning (log, "ExportAttribute does not work on static constructor"); } else { AddConstructor(minfo, ctorTypes [0], outerType, null, curCtors, false, true); HasExport = true; } } } AddConstructors(ctorTypes [0], outerType, null, curCtors, true); for (int i = 1; i < ctorTypes.Count; ++i) { var baseCtors = curCtors; curCtors = new List <MethodDefinition> (); AddConstructors(ctorTypes [i], outerType, baseCtors, curCtors, false); } }
static string GetJniSignature(MethodDefinition ctor) { return(JavaNativeTypeManager.GetJniSignature(ctor)); }
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 foreach (var assembly in ResolvedAssemblies) { if (bool.TryParse(assembly.GetMetadata(AndroidSkipJavaStubGeneration), out bool value) && value) { Log.LogDebugMessage($"Skipping Java Stub Generation for {assembly.ItemSpec}"); continue; } res.Load(assembly.ItemSpec); } // However we only want to look for JLO types in user code List <string> assemblies = new List <string> (); 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); } // 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, AndroidSdkPlatform, 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", 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", "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, ApplicationJavaClass, EmbedAssemblies, BundledWearApplicationName); 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(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())); }
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())); }
public void Crc64() { JavaNativeTypeManager.PackageNamingPolicy = PackageNamingPolicy.LowercaseCrc64; Assert.AreEqual("crc64b74743e9328eed0a", JavaNativeTypeManager.GetPackageName(typeof(string))); }