Ejemplo n.º 1
0
        void AddInstrumentations(XElement manifest, IList <TypeDefinition> subclasses, int targetSdkVersion)
        {
            var assemblyAttrs =
                Assemblies.SelectMany(path => InstrumentationAttribute.FromCustomAttributeProvider(Resolver.GetAssembly(path)));

            // Add instrumentation to the manifest
            foreach (var ia in assemblyAttrs)
            {
                if (ia.TargetPackage == null)
                {
                    ia.SetTargetPackage(PackageName);
                }
                if (!manifest.Descendants("instrumentation").Any(x => (string)x.Attribute(attName) == ia.Name))
                {
                    manifest.Add(ia.ToElement(PackageName));
                }
            }

            foreach (var type in subclasses)
            {
                if (type.IsSubclassOf("Android.App.Instrumentation"))
                {
                    var xe = InstrumentationFromTypeDefinition(type, JniType.ToJniName(type).Replace('/', '.'), targetSdkVersion);
                    if (xe != null)
                    {
                        manifest.Add(xe);
                    }
                }
            }
        }
Ejemplo n.º 2
0
        static string GetJavaTypeName(TypeReference r)
        {
            TypeDefinition d       = r.Resolve();
            string         jniName = JniType.ToJniName(d);

            if (jniName == null)
            {
                Diagnostic.Error(4201, "Unable to determine JNI name for type {0}.", r.FullName);
            }
            return(jniName.Replace('/', '.').Replace('$', '.'));
        }
Ejemplo n.º 3
0
        static string ToNameAttribute(ApplicationAttribute self)
        {
            var type = self.provider as TypeDefinition;

            if (string.IsNullOrEmpty(self.Name) && type != null)
            {
                return(JniType.ToJniName(type).Replace('/', '.'));
            }

            return(self.Name);
        }
Ejemplo n.º 4
0
 void AddNestedTypes(TypeDefinition type)
 {
     foreach (TypeDefinition nt in type.NestedTypes)
     {
         if (!nt.IsSubclassOf("Java.Lang.Object"))
         {
             continue;
         }
         if (!JniType.IsNonStaticInnerClass(nt))
         {
             continue;
         }
         children.Add(new JavaCallableWrapperGenerator(nt, JniType.ToJniName(type), log));
         if (nt.HasNestedTypes)
         {
             AddNestedTypes(nt);
         }
     }
     HasExport |= children.Any(t => t.HasExport);
 }
Ejemplo n.º 5
0
        void Run()
        {
            PackageNamingPolicy pnp;

            JniType.PackageNamingPolicy = Enum.TryParse(PackageNamingPolicy, out pnp) ? pnp : PackageNamingPolicyEnum.LowercaseHash;
            var temp = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());

            Directory.CreateDirectory(temp);

            // We're going to do 3 steps here instead of separate tasks so
            // we can share the list of JLO TypeDefinitions between them
            var res = new DirectoryAssemblyResolver(Log.LogWarning, loadDebugSymbols: true);

            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 all_java_types = JavaTypeScanner.GetJavaTypes(assemblies, res, Log.LogWarning);

            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,
                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    = JniType.ToJniName(type).Replace('/', '.');

                acw_map.WriteLine("{0};{1}", type.GetPartialAssemblyQualifiedName(), javaKey);
                acw_map.WriteLine("{0};{1}", type.GetAssemblyQualifiedName(), javaKey);

                TypeDefinition conflict;
                if (managed.TryGetValue(managedKey, out conflict))
                {
                    Log.LogWarning(
                        "Duplicate managed type found! Mappings between managed types and Java types must be unique. " +
                        "First Type: '{0}'; Second Type: '{1}'.",
                        conflict.GetAssemblyQualifiedName(),
                        type.GetAssemblyQualifiedName());
                    Log.LogWarning(
                        "References to the type '{0}' will refer to '{1}'.",
                        managedKey, conflict.GetAssemblyQualifiedName());
                    continue;
                }
                if (java.TryGetValue(javaKey, out conflict))
                {
                    Log.LogError(
                        "Duplicate Java type found! Mappings between managed types and Java types must be unique. " +
                        "First Type: '{0}'; Second Type: '{1}'",
                        conflict.GetAssemblyQualifiedName(),
                        type.GetAssemblyQualifiedName());
                    keep_going = false;
                    continue;
                }
                managed.Add(managedKey, type);
                java.Add(javaKey, type);
                acw_map.WriteLine("{0};{1}", managedKey, javaKey);
                acw_map.WriteLine("{0};{1}", JniType.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.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 (JniType.IsApplication(type) || JniType.IsInstrumentation(type))
                {
                    string javaKey = JniType.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) { }
        }
Ejemplo n.º 6
0
        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 = JniType.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.Cast <TypeReference> ()
                     .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.Cast <MethodDefinition>()))
            {
                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);
            }
        }
Ejemplo n.º 7
0
        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 targetSdkVersionValue;

            if (!int.TryParse(MonoAndroidHelper.GetPlatformApiLevel(targetSdkVersion), out targetSdkVersionValue))
            {
                throw new InvalidOperationException(string.Format("The targetSdkVersion ({0}) is not a valid API level", targetSdkVersion));
            }

            foreach (var t in subclasses)
            {
                if (t.IsAbstract)
                {
                    continue;
                }

                if (PackageName == null)
                {
                    PackageName = t.Namespace;
                }

                var name       = JniType.ToJniName(t).Replace('/', '.');
                var compatName = JniType.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);
        }
Ejemplo n.º 8
0
 public static string ToString(TypeDefinition typeDef)
 {
     return(JniType.ToJniName(typeDef).Replace('/', '.'));
 }