Exemple #1
0
        void GenerateBody(TextWriter sw)
        {
            foreach (Signature ctor in ctors)
            {
                if (string.IsNullOrEmpty(ctor.Params) && JavaNativeTypeManager.IsApplication(type))
                {
                    continue;
                }
                GenerateConstructor(ctor, sw);
            }

            GenerateApplicationConstructor(sw);

            foreach (JavaFieldInfo field in exported_fields)
            {
                GenerateExportedField(field, sw);
            }

            foreach (Signature method in methods)
            {
                GenerateMethod(method, sw);
            }

            if (GenerateOnCreateOverrides && JavaNativeTypeManager.IsApplication(type) && !methods.Any(m => m.Name == "onCreate"))
            {
                WriteApplicationOnCreate(sw, w => {
                    w.WriteLine("\t\tmono.android.Runtime.register (\"{0}\", {1}.class, __md_methods);", type.GetAssemblyQualifiedName(), name);
                    w.WriteLine("\t\tsuper.onCreate ();");
                });
            }
            if (GenerateOnCreateOverrides && JavaNativeTypeManager.IsInstrumentation(type) && !methods.Any(m => m.Name == "onCreate"))
            {
                WriteInstrumentationOnCreate(sw, w => {
                    w.WriteLine("\t\tmono.android.Runtime.register (\"{0}\", {1}.class, __md_methods);", type.GetAssemblyQualifiedName(), name);
                    w.WriteLine("\t\tsuper.onCreate (arguments);");
                });
            }

            sw.WriteLine();
            sw.WriteLine("\tprivate java.util.ArrayList refList;");
            sw.WriteLine("\tpublic void monodroidAddReference (java.lang.Object obj)");
            sw.WriteLine("\t{");
            sw.WriteLine("\t\tif (refList == null)");
            sw.WriteLine("\t\t\trefList = new java.util.ArrayList ();");
            sw.WriteLine("\t\trefList.add (obj);");
            sw.WriteLine("\t}");
            sw.WriteLine();
            sw.WriteLine("\tpublic void monodroidClearReferences ()");
            sw.WriteLine("\t{");
            sw.WriteLine("\t\tif (refList != null)");
            sw.WriteLine("\t\t\trefList.clear ();");
            sw.WriteLine("\t}");
        }
Exemple #2
0
        void GenerateApplicationConstructor(TextWriter sw)
        {
            if (!JavaNativeTypeManager.IsApplication(type))
            {
                return;
            }

            sw.WriteLine();
            sw.WriteLine("\tpublic {0} ()", name);
            sw.WriteLine("\t{");
            sw.WriteLine("\t\tmono.MonoPackageManager.setContext (this);");
            sw.WriteLine("\t}");
        }
Exemple #3
0
        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 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);
        }
        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);

            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)
            {
                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) { }
        }
        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);
                }
            }

            // 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",
                    "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, 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(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()));
        }
Exemple #6
0
        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, cache);

            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()) {
                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;

            if (!String.IsNullOrWhiteSpace(CheckedBuild))
            {
                // We don't validate CheckedBuild value here, this will be done in BuildApk. We just know that if it's
                // on then we need android:debuggable=true and android:extractNativeLibs=true
                manifest.ForceDebuggable        = true;
                manifest.ForceExtractNativeLibs = true;
            }

            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 = "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()));
        }
Exemple #7
0
 static bool CannotRegisterInStaticConstructor(TypeDefinition type)
 {
     return(JavaNativeTypeManager.IsApplication(type) || JavaNativeTypeManager.IsInstrumentation(type));
 }