bool CreateJavaSources(IEnumerable <TypeDefinition> javaTypes, TypeDefinitionCache cache)
        {
            string outputPath                = Path.Combine(OutputDirectory, "src");
            string monoInit                  = GetMonoInitSource(AndroidSdkPlatform, UseSharedRuntime);
            bool   hasExportReference        = ResolvedAssemblies.Any(assembly => Path.GetFileName(assembly.ItemSpec) == "Mono.Android.Export.dll");
            bool   generateOnCreateOverrides = int.Parse(AndroidSdkPlatform) <= 10;

            bool ok = true;

            foreach (var t in javaTypes)
            {
                using (var writer = MemoryStreamPool.Shared.CreateStreamWriter()) {
                    try {
                        var jti = new JavaCallableWrapperGenerator(t, Log.LogWarning, cache)
                        {
                            GenerateOnCreateOverrides = generateOnCreateOverrides,
                            ApplicationJavaClass      = ApplicationJavaClass,
                            MonoRuntimeInitialization = monoInit,
                        };

                        jti.Generate(writer);
                        writer.Flush();

                        var path = jti.GetDestinationPath(outputPath);
                        MonoAndroidHelper.CopyIfStreamChanged(writer.BaseStream, path);
                        if (jti.HasExport && !hasExportReference)
                        {
                            Diagnostic.Error(4210, Properties.Resources.XA4210);
                        }
                    } catch (XamarinAndroidException xae) {
                        ok = false;
                        Log.LogError(
                            subcategory: "",
                            errorCode: "XA" + xae.Code,
                            helpKeyword: string.Empty,
                            file: xae.SourceFile,
                            lineNumber: xae.SourceLine,
                            columnNumber: 0,
                            endLineNumber: 0,
                            endColumnNumber: 0,
                            message: xae.MessageWithoutCode,
                            messageArgs: new object [0]
                            );
                    } catch (DirectoryNotFoundException ex) {
                        ok = false;
                        if (OS.IsWindows)
                        {
                            Diagnostic.Error(5301, Properties.Resources.XA5301, t.FullName, ex);
                        }
                        else
                        {
                            Diagnostic.Error(4209, Properties.Resources.XA4209, t.FullName, ex);
                        }
                    } catch (Exception ex) {
                        ok = false;
                        Diagnostic.Error(4209, Properties.Resources.XA4209, t.FullName, ex);
                    }
                }
            }
            return(ok);
        }
Пример #2
0
        void AddEnvironment()
        {
            var environment = new StringWriter()
            {
                NewLine = "\n",
            };

            if (EnableLLVM)
            {
                WriteEnvironment("mono.llvm", "true");
            }

            AotMode aotMode;

            if (AndroidAotMode != null && Aot.GetAndroidAotMode(AndroidAotMode, out aotMode))
            {
                WriteEnvironment("mono.aot", aotMode.ToString().ToLowerInvariant());
            }

            bool haveLogLevel           = false;
            bool haveMonoDebug          = false;
            bool havebuildId            = false;
            bool haveHttpMessageHandler = false;
            bool haveTlsProvider        = false;
            bool haveMonoGCParams       = false;

            SequencePointsMode sequencePointsMode;

            if (!Aot.TryGetSequencePointsMode(AndroidSequencePointsMode, out sequencePointsMode))
            {
                sequencePointsMode = SequencePointsMode.None;
            }

            foreach (ITaskItem env in Environments ?? new TaskItem[0])
            {
                environment.WriteLine("\t\t// Source File: {0}", env.ItemSpec);
                foreach (string line in File.ReadLines(env.ItemSpec))
                {
                    var lineToWrite = line;
                    if (lineToWrite.StartsWith("MONO_LOG_LEVEL=", StringComparison.Ordinal))
                    {
                        haveLogLevel = true;
                    }
                    if (lineToWrite.StartsWith("MONO_GC_PARAMS=", StringComparison.Ordinal))
                    {
                        haveMonoGCParams = true;
                    }
                    if (lineToWrite.StartsWith("XAMARIN_BUILD_ID=", StringComparison.Ordinal))
                    {
                        havebuildId = true;
                    }
                    if (lineToWrite.StartsWith("MONO_DEBUG=", StringComparison.Ordinal))
                    {
                        haveMonoDebug = true;
                        if (sequencePointsMode != SequencePointsMode.None && !lineToWrite.Contains("gen-compact-seq-points"))
                        {
                            lineToWrite = line + ",gen-compact-seq-points";
                        }
                    }
                    if (lineToWrite.StartsWith("XA_HTTP_CLIENT_HANDLER_TYPE=", StringComparison.Ordinal))
                    {
                        haveHttpMessageHandler = true;
                    }
                    if (lineToWrite.StartsWith("XA_TLS_PROVIDER=", StringComparison.Ordinal))
                    {
                        haveTlsProvider = true;
                    }
                    WriteEnvironmentLine(lineToWrite);
                }
            }

            if (_Debug && !haveLogLevel)
            {
                WriteEnvironment(defaultLogLevel[0], defaultLogLevel[1]);
            }

            if (sequencePointsMode != SequencePointsMode.None && !haveMonoDebug)
            {
                WriteEnvironment(defaultMonoDebug[0], defaultMonoDebug[1]);
            }

            if (!havebuildId)
            {
                WriteEnvironment("XAMARIN_BUILD_ID", buildId.ToString());
            }

            if (!haveHttpMessageHandler)
            {
                if (HttpClientHandlerType == null)
                {
                    WriteEnvironment(defaultHttpMessageHandler[0], defaultHttpMessageHandler[1]);
                }
                else
                {
                    WriteEnvironment("XA_HTTP_CLIENT_HANDLER_TYPE", HttpClientHandlerType.Trim());
                }
            }

            if (!haveTlsProvider)
            {
                if (TlsProvider == null)
                {
                    WriteEnvironment(defaultTlsProvider[0], defaultTlsProvider[1]);
                }
                else
                {
                    WriteEnvironment("XA_TLS_PROVIDER", TlsProvider.Trim());
                }
            }

            if (!haveMonoGCParams)
            {
                if (EnableSGenConcurrent)
                {
                    WriteEnvironment("MONO_GC_PARAMS", "major=marksweep-conc");
                }
                else
                {
                    WriteEnvironment("MONO_GC_PARAMS", "major=marksweep");
                }
            }

            string environmentTemplate;

            using (var sr = new StreamReader(typeof(BuildApk).Assembly.GetManifestResourceStream(EnvironmentFileName))) {
                environmentTemplate = sr.ReadToEnd();
            }

            using (var ms = new MemoryStream()) {
                using (var sw = new StreamWriter(ms)) {
                    sw.Write(environmentTemplate.Replace("//@ENVVARS@", environment.ToString()));
                    sw.Flush();

                    string dest = Path.GetFullPath(Path.Combine(EnvironmentOutputDirectory, EnvironmentFileName));
                    MonoAndroidHelper.CopyIfStreamChanged(ms, dest);
                }
            }

            void WriteEnvironment(string name, string value)
            {
                environment.WriteLine($"\t\t\"{ValidJavaString (name)}\", \"{ValidJavaString (value)}\",");
            }

            void WriteEnvironmentLine(string line)
            {
                if (String.IsNullOrEmpty(line))
                {
                    return;
                }

                string[] nv = line.Split(new char[] { '=' }, 2);
                WriteEnvironment(nv[0].Trim(), nv.Length < 2 ? String.Empty : nv[1].Trim());
            }

            string ValidJavaString(string s)
            {
                return(s.Replace("\"", "\\\""));
            }
        }
        protected override void WriteSymbols(StreamWriter output)
        {
            bool haveJavaToManaged = data.JavaToManagedMap != null && data.JavaToManagedMap.Count > 0;
            bool haveManagedToJava = data.ManagedToJavaMap != null && data.ManagedToJavaMap.Count > 0;

            using (var sharedOutput = MemoryStreamPool.Shared.CreateStreamWriter(output.Encoding)) {
                WriteSharedBits(sharedOutput, haveJavaToManaged, haveManagedToJava);
                sharedOutput.Flush();
                MonoAndroidHelper.CopyIfStreamChanged(sharedOutput.BaseStream, SharedIncludeFile);
            }

            if (haveJavaToManaged || haveManagedToJava)
            {
                output.Write(Indent);
                output.Write(".include");
                output.Write(Indent);
                output.Write('"');
                output.Write(Path.GetFileName(SharedIncludeFile));
                output.WriteLine('"');

                output.WriteLine();
            }

            uint size = 0;

            WriteCommentLine(output, "Managed to java map: START", indent: false);
            WriteSection(output, $".data.rel.{ManagedToJavaSymbol}", hasStrings: false, writable: true);
            WriteStructureSymbol(output, ManagedToJavaSymbol, alignBits: TargetProvider.DebugTypeMapAlignBits, isGlobal: false);
            if (haveManagedToJava)
            {
                foreach (TypeMapGenerator.TypeMapDebugEntry entry in data.ManagedToJavaMap)
                {
                    size += WritePointer(output, entry.ManagedLabel);
                    size += WritePointer(output, entry.JavaLabel);
                }
            }
            WriteStructureSize(output, ManagedToJavaSymbol, size, alwaysWriteSize: true);
            WriteCommentLine(output, "Managed to java map: END", indent: false);
            output.WriteLine();

            size = 0;
            WriteCommentLine(output, "Java to managed map: START", indent: false);
            WriteSection(output, $".data.rel.{JavaToManagedSymbol}", hasStrings: false, writable: true);
            WriteStructureSymbol(output, JavaToManagedSymbol, alignBits: TargetProvider.DebugTypeMapAlignBits, isGlobal: false);
            if (haveJavaToManaged)
            {
                foreach (TypeMapGenerator.TypeMapDebugEntry entry in data.JavaToManagedMap)
                {
                    size += WritePointer(output, entry.JavaLabel);

                    TypeMapGenerator.TypeMapDebugEntry managedEntry = entry.DuplicateForJavaToManaged != null ? entry.DuplicateForJavaToManaged : entry;
                    size += WritePointer(output, managedEntry.SkipInJavaToManaged ? null : managedEntry.ManagedLabel);
                }
            }
            WriteStructureSize(output, JavaToManagedSymbol, size, alwaysWriteSize: true);
            WriteCommentLine(output, "Java to managed map: END", indent: false);
            output.WriteLine();

            // MUST match src/monodroid/xamarin-app.hh
            WriteCommentLine(output, "TypeMap structure");
            WriteSection(output, $".data.rel.ro.{TypeMapSymbol}", hasStrings: false, writable: true);
            WriteStructureSymbol(output, TypeMapSymbol, alignBits: TargetProvider.DebugTypeMapAlignBits, isGlobal: true);

            size = WriteStructure(output, packed: false, structureWriter: () => WriteTypeMapStruct(output));

            WriteStructureSize(output, TypeMapSymbol, size);
        }
        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()));
        }
Пример #5
0
        public static bool CreateJavaSources(TaskLoggingHelper log, IEnumerable <TypeDefinition> javaTypes, string outputPath,
                                             string applicationJavaClass, string androidSdkPlatform, bool useSharedRuntime, bool generateOnCreateOverrides, bool hasExportReference)
        {
            string monoInit = GetMonoInitSource(androidSdkPlatform, useSharedRuntime);

            bool ok = true;

            using (var memoryStream = new MemoryStream())
                using (var writer = new StreamWriter(memoryStream)) {
                    foreach (var t in javaTypes)
                    {
                        //Reset for reuse
                        memoryStream.SetLength(0);

                        try {
                            var jti = new JavaCallableWrapperGenerator(t, log.LogWarning)
                            {
                                GenerateOnCreateOverrides = generateOnCreateOverrides,
                                ApplicationJavaClass      = applicationJavaClass,
                                MonoRuntimeInitialization = monoInit,
                            };

                            jti.Generate(writer);
                            writer.Flush();

                            var path = jti.GetDestinationPath(outputPath);
                            MonoAndroidHelper.CopyIfStreamChanged(memoryStream, path);
                            if (jti.HasExport && !hasExportReference)
                            {
                                Diagnostic.Error(4210, "You need to add a reference to Mono.Android.Export.dll when you use ExportAttribute or ExportFieldAttribute.");
                            }
                        } catch (XamarinAndroidException xae) {
                            ok = false;
                            log.LogError(
                                subcategory: "",
                                errorCode: "XA" + xae.Code,
                                helpKeyword: string.Empty,
                                file: xae.SourceFile,
                                lineNumber: xae.SourceLine,
                                columnNumber: 0,
                                endLineNumber: 0,
                                endColumnNumber: 0,
                                message: xae.MessageWithoutCode,
                                messageArgs: new object [0]
                                );
                        } catch (DirectoryNotFoundException ex) {
                            ok = false;
                            if (OS.IsWindows)
                            {
                                Diagnostic.Error(5301, "Failed to create JavaTypeInfo for class: {0} due to MAX_PATH: {1}", t.FullName, ex);
                            }
                            else
                            {
                                Diagnostic.Error(4209, "Failed to create JavaTypeInfo for class: {0} due to {1}", t.FullName, ex);
                            }
                        } catch (Exception ex) {
                            ok = false;
                            Diagnostic.Error(4209, "Failed to create JavaTypeInfo for class: {0} due to {1}", t.FullName, ex);
                        }
                    }
                }
            return(ok);
        }
Пример #6
0
        // Extracts library project contents under e.g. obj/Debug/[__library_projects__/*.jar | res/*/*]
        // Extracts library project contents under e.g. obj/Debug/[lp/*.jar | res/*/*]
        void Extract(
            IDictionary <string, ITaskItem> jars,
            ICollection <ITaskItem> resolvedResourceDirectories,
            ICollection <ITaskItem> resolvedAssetDirectories,
            ICollection <ITaskItem> resolvedEnvironments)
        {
            // lets "upgrade" the old directory.
            string oldPath = Path.GetFullPath(Path.Combine(OutputImportDirectory, "..", "__library_projects__"));

            if (!OutputImportDirectory.Contains("__library_projects__") && Directory.Exists(oldPath))
            {
                MonoAndroidHelper.SetDirectoryWriteable(Path.Combine(oldPath, ".."));
                Directory.Delete(oldPath, recursive: true);
            }
            var outdir = Path.GetFullPath(OutputImportDirectory);

            Directory.CreateDirectory(outdir);

            bool skip;

            foreach (var assemblyItem in Assemblies)
            {
                var assemblyPath = assemblyItem.ItemSpec;
                var fileName     = Path.GetFileName(assemblyPath);
                if (MonoAndroidHelper.IsFrameworkAssembly(fileName) &&
                    !MonoAndroidHelper.FrameworkEmbeddedJarLookupTargets.Contains(fileName) &&
                    !MonoAndroidHelper.FrameworkEmbeddedNativeLibraryAssemblies.Contains(fileName))
                {
                    Log.LogDebugMessage($"Skipping framework assembly '{fileName}'.");
                    continue;
                }
                if (!File.Exists(assemblyPath))
                {
                    Log.LogDebugMessage($"Skipping non-existent dependency '{assemblyPath}'.");
                    continue;
                }
                if (bool.TryParse(assemblyItem.GetMetadata(AndroidSkipResourceExtraction), out skip) && skip)
                {
                    Log.LogDebugMessage("Skipping resource extraction for '{0}' .", assemblyPath);
                    continue;
                }
                string assemblyFileName  = Path.GetFileNameWithoutExtension(assemblyPath);
                string assemblyIdentName = assemblyMap.GetLibraryImportDirectoryNameForAssembly(assemblyFileName);
                string outDirForDll      = Path.Combine(OutputImportDirectory, assemblyIdentName);
                string importsDir        = Path.Combine(outDirForDll, ImportsDirectory);
                string nativeimportsDir  = Path.Combine(outDirForDll, NativeImportsDirectory);
                string resDir            = Path.Combine(importsDir, "res");
                string assetsDir         = Path.Combine(importsDir, "assets");

                // Skip already-extracted resources.
                bool   updated      = false;
                string assemblyHash = MonoAndroidHelper.HashFile(assemblyPath);
                string stamp        = Path.Combine(outdir, assemblyIdentName + ".stamp");
                string stampHash    = File.Exists(stamp) ? File.ReadAllText(stamp) : null;
                if (assemblyHash == stampHash)
                {
                    Log.LogDebugMessage("Skipped resource lookup for {0}: extracted files are up to date", assemblyPath);
                    if (Directory.Exists(importsDir))
                    {
                        foreach (var file in Directory.EnumerateFiles(importsDir, "*.jar", SearchOption.AllDirectories))
                        {
                            AddJar(jars, Path.GetFullPath(file));
                        }
                    }
                    if (Directory.Exists(resDir))
                    {
                        var taskItem = new TaskItem(Path.GetFullPath(resDir), new Dictionary <string, string> {
                            { OriginalFile, assemblyPath },
                        });
                        if (bool.TryParse(assemblyItem.GetMetadata(AndroidSkipResourceProcessing), out skip) && skip)
                        {
                            taskItem.SetMetadata(AndroidSkipResourceProcessing, "True");
                        }
                        resolvedResourceDirectories.Add(taskItem);
                    }
                    if (Directory.Exists(assetsDir))
                    {
                        resolvedAssetDirectories.Add(new TaskItem(Path.GetFullPath(assetsDir), new Dictionary <string, string> {
                            { OriginalFile, assemblyPath }
                        }));
                    }
                    foreach (var env in Directory.EnumerateFiles(outDirForDll, "__AndroidEnvironment__*", SearchOption.TopDirectoryOnly))
                    {
                        resolvedEnvironments.Add(new TaskItem(env, new Dictionary <string, string> {
                            { OriginalFile, assemblyPath }
                        }));
                    }
                    continue;
                }

                Log.LogDebugMessage($"Refreshing {assemblyFileName}.dll");

                using (var pe = new PEReader(File.OpenRead(assemblyPath))) {
                    var reader = pe.GetMetadataReader();
                    foreach (var handle in reader.ManifestResources)
                    {
                        var    resource = reader.GetManifestResource(handle);
                        string name     = reader.GetString(resource.Name);

                        // android environment files
                        if (name.StartsWith("__AndroidEnvironment__", StringComparison.OrdinalIgnoreCase))
                        {
                            var outFile = Path.Combine(outDirForDll, name);
                            using (var stream = pe.GetEmbeddedResourceStream(resource)) {
                                updated |= MonoAndroidHelper.CopyIfStreamChanged(stream, outFile);
                            }
                            resolvedEnvironments.Add(new TaskItem(Path.GetFullPath(outFile), new Dictionary <string, string> {
                                { OriginalFile, assemblyPath }
                            }));
                        }
                        // embedded jars (EmbeddedJar, EmbeddedReferenceJar)
                        else if (name.EndsWith(".jar", StringComparison.InvariantCultureIgnoreCase))
                        {
                            using (var stream = pe.GetEmbeddedResourceStream(resource)) {
                                AddJar(jars, importsDir, name, assemblyPath);
                                updated |= MonoAndroidHelper.CopyIfStreamChanged(stream, Path.Combine(importsDir, name));
                            }
                        }
                        // embedded native libraries
                        else if (name == "__AndroidNativeLibraries__.zip")
                        {
                            List <string> files = new List <string> ();
                            using (var stream = pe.GetEmbeddedResourceStream(resource))
                                using (var zip = Xamarin.Tools.Zip.ZipArchive.Open(stream)) {
                                    try {
                                        updated |= Files.ExtractAll(zip, nativeimportsDir, modifyCallback: (entryFullName) => {
                                            files.Add(Path.GetFullPath(Path.Combine(nativeimportsDir, entryFullName)));
                                            return(entryFullName
                                                   .Replace("native_library_imports\\", "")
                                                   .Replace("native_library_imports/", ""));
                                        }, deleteCallback: (fileToDelete) => {
                                            return(!files.Contains(fileToDelete));
                                        });
                                    } catch (PathTooLongException ex) {
                                        Log.LogCodedError("XA4303", Properties.Resources.XA4303, assemblyPath, ex);
                                        return;
                                    } catch (NotSupportedException ex) {
                                        Log.LogCodedError("XA4303", Properties.Resources.XA4303, assemblyPath, ex);
                                        return;
                                    }
                                }
                        }
                        // embedded AndroidResourceLibrary archive
                        else if (name == "__AndroidLibraryProjects__.zip")
                        {
                            // temporarily extracted directory will look like:
                            //    __library_projects__/[dllname]/[library_project_imports | jlibs]/bin
                            using (var stream = pe.GetEmbeddedResourceStream(resource))
                                using (var zip = Xamarin.Tools.Zip.ZipArchive.Open(stream)) {
                                    try {
                                        updated |= Files.ExtractAll(zip, importsDir, modifyCallback: (entryFullName) => {
                                            var path = entryFullName
                                                       .Replace("library_project_imports\\", "")
                                                       .Replace("library_project_imports/", "");
                                            if (path.EndsWith(".jar", StringComparison.OrdinalIgnoreCase))
                                            {
                                                AddJar(jars, importsDir, path, assemblyPath);
                                            }
                                            return(path);
                                        }, deleteCallback: (fileToDelete) => {
                                            return(!jars.ContainsKey(fileToDelete));
                                        });
                                    } catch (PathTooLongException ex) {
                                        Log.LogCodedError("XA4303", Properties.Resources.XA4303, assemblyPath, ex);
                                        return;
                                    } catch (NotSupportedException ex) {
                                        Log.LogCodedError("XA4303", Properties.Resources.XA4303, assemblyPath, ex);
                                        return;
                                    }
                                }

                            // We used to *copy* the resources to overwrite other resources,
                            // which resulted in missing resource issue.
                            // Here we replaced copy with use of '-S' option and made it to work.
                            if (Directory.Exists(resDir))
                            {
                                var taskItem = new TaskItem(Path.GetFullPath(resDir), new Dictionary <string, string> {
                                    { OriginalFile, assemblyPath }
                                });
                                if (bool.TryParse(assemblyItem.GetMetadata(AndroidSkipResourceProcessing), out skip) && skip)
                                {
                                    taskItem.SetMetadata(AndroidSkipResourceProcessing, "True");
                                }
                                resolvedResourceDirectories.Add(taskItem);
                            }
                            if (Directory.Exists(assetsDir))
                            {
                                resolvedAssetDirectories.Add(new TaskItem(Path.GetFullPath(assetsDir), new Dictionary <string, string> {
                                    { OriginalFile, assemblyPath }
                                }));
                            }
                        }
                    }
                }

                if (Directory.Exists(importsDir))
                {
                    // Delete unknown files in the top directory of importsDir
                    foreach (var file in Directory.EnumerateFiles(importsDir, "*"))
                    {
                        var fullPath = Path.GetFullPath(file);
                        if (file.StartsWith("__AndroidEnvironment__", StringComparison.OrdinalIgnoreCase) && !resolvedEnvironments.Any(x => x.ItemSpec == fullPath))
                        {
                            Log.LogDebugMessage($"Deleting unknown AndroidEnvironment file: {Path.GetFileName (file)}");
                            File.Delete(fullPath);
                            updated = true;
                        }
                        else if (file.EndsWith(".jar", StringComparison.OrdinalIgnoreCase) && !jars.ContainsKey(fullPath))
                        {
                            Log.LogDebugMessage($"Deleting unknown jar: {Path.GetFileName (file)}");
                            File.Delete(fullPath);
                            updated = true;
                        }
                    }
                    if (assemblyHash != stampHash)
                    {
                        Log.LogDebugMessage($"Saving hash to {stamp}, changes: {updated}");
                        //NOTE: if the hash is different we always want to write the file, but preserve the timestamp if no changes
                        WriteAllText(stamp, assemblyHash, preserveTimestamp: !updated);
                    }
                }
            }
            foreach (var aarFile in AarLibraries ?? new ITaskItem[0])
            {
                if (!File.Exists(aarFile.ItemSpec))
                {
                    continue;
                }
                string aarIdentityName = Path.GetFileNameWithoutExtension(aarFile.ItemSpec);
                aarIdentityName = assemblyMap.GetLibraryImportDirectoryNameForAssembly(aarIdentityName);
                string outDirForDll = Path.Combine(OutputImportDirectory, aarIdentityName);
                string importsDir   = Path.Combine(outDirForDll, ImportsDirectory);
                string resDir       = Path.Combine(importsDir, "res");
                string assetsDir    = Path.Combine(importsDir, "assets");

                bool   updated     = false;
                string aarHash     = MonoAndroidHelper.HashFile(aarFile.ItemSpec);
                string stamp       = Path.Combine(outdir, aarIdentityName + ".stamp");
                string stampHash   = File.Exists(stamp) ? File.ReadAllText(stamp) : null;
                var    aarFullPath = Path.GetFullPath(aarFile.ItemSpec);
                if (aarHash == stampHash)
                {
                    Log.LogDebugMessage("Skipped {0}: extracted files are up to date", aarFile.ItemSpec);
                    if (Directory.Exists(importsDir))
                    {
                        foreach (var file in Directory.EnumerateFiles(importsDir, "*.jar", SearchOption.AllDirectories))
                        {
                            AddJar(jars, Path.GetFullPath(file));
                        }
                    }
                    if (Directory.Exists(resDir))
                    {
                        resolvedResourceDirectories.Add(new TaskItem(Path.GetFullPath(resDir), new Dictionary <string, string> {
                            { OriginalFile, Path.GetFullPath(aarFile.ItemSpec) },
                            { AndroidSkipResourceProcessing, "True" },
                        }));
                    }
                    if (Directory.Exists(assetsDir))
                    {
                        resolvedAssetDirectories.Add(new TaskItem(Path.GetFullPath(assetsDir), new Dictionary <string, string> {
                            { OriginalFile, aarFullPath },
                        }));
                    }
                    continue;
                }

                Log.LogDebugMessage($"Refreshing {aarFile.ItemSpec}");

                // temporarily extracted directory will look like:
                // _lp_/[aarFile]

                using (var zip = MonoAndroidHelper.ReadZipFile(aarFile.ItemSpec)) {
                    try {
                        updated |= Files.ExtractAll(zip, importsDir, modifyCallback: (entryFullName) => {
                            var entryFileName = Path.GetFileName(entryFullName);
                            var entryPath     = Path.GetDirectoryName(entryFullName);
                            if (entryFileName.StartsWith("internal_impl", StringComparison.InvariantCulture))
                            {
                                var hash = Files.HashString(entryFileName);
                                var jar  = Path.Combine(entryPath, $"internal_impl-{hash}.jar");
                                AddJar(jars, importsDir, jar, aarFullPath);
                                return(jar);
                            }
                            if (entryFullName.EndsWith(".jar", StringComparison.OrdinalIgnoreCase))
                            {
                                AddJar(jars, importsDir, entryFullName, aarFullPath);
                            }
                            return(entryFullName);
                        }, deleteCallback: (fileToDelete) => {
                            return(!jars.ContainsKey(fileToDelete));
                        }, skipCallback: (entryFullName) => {
                            // AAR files may contain other jars not needed for compilation
                            // See: https://developer.android.com/studio/projects/android-library.html#aar-contents
                            if (!entryFullName.EndsWith(".jar", StringComparison.OrdinalIgnoreCase))
                            {
                                return(false);
                            }
                            if (entryFullName == "classes.jar" ||
                                entryFullName.StartsWith("libs/", StringComparison.OrdinalIgnoreCase) ||
                                entryFullName.StartsWith("libs\\", StringComparison.OrdinalIgnoreCase))
                            {
                                return(false);
                            }
                            // This could be `lint.jar` or `api.jar`, etc.
                            return(true);
                        });

                        if (Directory.Exists(importsDir) && aarHash != stampHash)
                        {
                            Log.LogDebugMessage($"Saving hash to {stamp}, changes: {updated}");
                            //NOTE: if the hash is different we always want to write the file, but preserve the timestamp if no changes
                            WriteAllText(stamp, aarHash, preserveTimestamp: !updated);
                        }
                    } catch (PathTooLongException ex) {
                        Log.LogErrorFromException(new PathTooLongException($"Error extracting resources from \"{aarFile.ItemSpec}\"", ex));
                    }
                }
                if (Directory.Exists(resDir))
                {
                    resolvedResourceDirectories.Add(new TaskItem(Path.GetFullPath(resDir), new Dictionary <string, string> {
                        { OriginalFile, aarFullPath },
                        { AndroidSkipResourceProcessing, "True" },
                    }));
                }
                if (Directory.Exists(assetsDir))
                {
                    resolvedAssetDirectories.Add(new TaskItem(Path.GetFullPath(assetsDir), new Dictionary <string, string> {
                        { OriginalFile, aarFullPath },
                    }));
                }
            }
        }
        void GenerateCompressedAssemblySources()
        {
            if (Debug || !EnableCompression)
            {
                Generate(null);
                return;
            }

            var assemblies = new SortedDictionary <string, CompressedAssemblyInfo> (StringComparer.Ordinal);

            foreach (ITaskItem assembly in ResolvedAssemblies)
            {
                if (bool.TryParse(assembly.GetMetadata("AndroidSkipAddToPackage"), out bool value) && value)
                {
                    continue;
                }

                if (assemblies.ContainsKey(assembly.ItemSpec))
                {
                    continue;
                }

                var fi = new FileInfo(assembly.ItemSpec);
                if (!fi.Exists)
                {
                    Log.LogError($"Assembly {assembly.ItemSpec} does not exist");
                    continue;
                }

                assemblies.Add(CompressedAssemblyInfo.GetDictionaryKey(assembly),
                               new CompressedAssemblyInfo(checked ((uint)fi.Length)));
            }

            uint index = 0;

            foreach (var kvp in assemblies)
            {
                kvp.Value.DescriptorIndex = index++;
            }

            string key = CompressedAssemblyInfo.GetKey(ProjectFullPath);

            Log.LogDebugMessage($"Storing compression assemblies info with key '{key}'");
            BuildEngine4.RegisterTaskObjectAssemblyLocal(key, assemblies, RegisteredTaskObjectLifetime.Build);
            Generate(assemblies);

            void Generate(IDictionary <string, CompressedAssemblyInfo> dict)
            {
                foreach (string abi in SupportedAbis)
                {
                    NativeAssemblerTargetProvider asmTargetProvider = GeneratePackageManagerJava.GetAssemblyTargetProvider(abi);
                    string baseAsmFilePath = Path.Combine(EnvironmentOutputDirectory, $"compressed_assemblies.{abi.ToLowerInvariant ()}");
                    string asmFilePath     = $"{baseAsmFilePath}.s";
                    var    asmgen          = new CompressedAssembliesNativeAssemblyGenerator(dict, asmTargetProvider, baseAsmFilePath);

                    using (var sw = MemoryStreamPool.Shared.CreateStreamWriter()) {
                        asmgen.Write(sw);
                        sw.Flush();
                        if (MonoAndroidHelper.CopyIfStreamChanged(sw.BaseStream, asmFilePath))
                        {
                            Log.LogDebugMessage($"File {asmFilePath} was regenerated");
                        }
                    }
                }
            }
        }
Пример #8
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
            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()));
        }
        void AddEnvironment()
        {
            bool   usesMonoAOT          = false;
            bool   usesAssemblyPreload  = EnablePreloadAssembliesDefault;
            uint   monoAOTMode          = 0;
            string androidPackageName   = null;
            var    environmentVariables = new Dictionary <string, string> (StringComparer.Ordinal);
            var    systemProperties     = new Dictionary <string, string> (StringComparer.Ordinal);

            AotMode aotMode;

            if (AndroidAotMode != null && Aot.GetAndroidAotMode(AndroidAotMode, out aotMode))
            {
                usesMonoAOT = true;
                monoAOTMode = (uint)aotMode;
            }

            bool haveLogLevel           = false;
            bool haveMonoDebug          = false;
            bool havebuildId            = false;
            bool haveHttpMessageHandler = false;
            bool haveTlsProvider        = false;
            bool haveMonoGCParams       = false;

            SequencePointsMode sequencePointsMode;

            if (!Aot.TryGetSequencePointsMode(AndroidSequencePointsMode, out sequencePointsMode))
            {
                sequencePointsMode = SequencePointsMode.None;
            }

            foreach (ITaskItem env in Environments ?? new TaskItem[0])
            {
                foreach (string line in File.ReadLines(env.ItemSpec))
                {
                    var lineToWrite = line;
                    if (lineToWrite.StartsWith("MONO_LOG_LEVEL=", StringComparison.Ordinal))
                    {
                        haveLogLevel = true;
                    }
                    if (lineToWrite.StartsWith("MONO_GC_PARAMS=", StringComparison.Ordinal))
                    {
                        haveMonoGCParams = true;
                    }
                    if (lineToWrite.StartsWith("XAMARIN_BUILD_ID=", StringComparison.Ordinal))
                    {
                        havebuildId = true;
                    }
                    if (lineToWrite.StartsWith("MONO_DEBUG=", StringComparison.Ordinal))
                    {
                        haveMonoDebug = true;
                        if (sequencePointsMode != SequencePointsMode.None && !lineToWrite.Contains("gen-compact-seq-points"))
                        {
                            lineToWrite = line + ",gen-compact-seq-points";
                        }
                    }
                    if (lineToWrite.StartsWith("XA_HTTP_CLIENT_HANDLER_TYPE=", StringComparison.Ordinal))
                    {
                        haveHttpMessageHandler = true;
                    }
                    if (lineToWrite.StartsWith("XA_TLS_PROVIDER=", StringComparison.Ordinal))
                    {
                        haveTlsProvider = true;
                    }
                    if (lineToWrite.StartsWith("mono.enable_assembly_preload=", StringComparison.Ordinal))
                    {
                        int  idx = lineToWrite.IndexOf('=');
                        uint val;
                        if (idx < lineToWrite.Length - 1 && UInt32.TryParse(lineToWrite.Substring(idx + 1), out val))
                        {
                            usesAssemblyPreload = idx == 1;
                        }
                        continue;
                    }

                    AddEnvironmentVariableLine(lineToWrite);
                }
            }

            if (_Debug && !haveLogLevel)
            {
                AddEnvironmentVariable(defaultLogLevel[0], defaultLogLevel[1]);
            }

            if (sequencePointsMode != SequencePointsMode.None && !haveMonoDebug)
            {
                AddEnvironmentVariable(defaultMonoDebug[0], defaultMonoDebug[1]);
            }

            if (!havebuildId)
            {
                AddEnvironmentVariable("XAMARIN_BUILD_ID", BuildId);
            }

            if (!haveHttpMessageHandler)
            {
                if (HttpClientHandlerType == null)
                {
                    AddEnvironmentVariable(defaultHttpMessageHandler[0], defaultHttpMessageHandler[1]);
                }
                else
                {
                    AddEnvironmentVariable("XA_HTTP_CLIENT_HANDLER_TYPE", HttpClientHandlerType.Trim());
                }
            }

            if (!haveTlsProvider)
            {
                if (TlsProvider == null)
                {
                    AddEnvironmentVariable(defaultTlsProvider[0], defaultTlsProvider[1]);
                }
                else
                {
                    AddEnvironmentVariable("XA_TLS_PROVIDER", TlsProvider.Trim());
                }
            }

            if (!haveMonoGCParams)
            {
                if (EnableSGenConcurrent)
                {
                    AddEnvironmentVariable("MONO_GC_PARAMS", "major=marksweep-conc");
                }
                else
                {
                    AddEnvironmentVariable("MONO_GC_PARAMS", "major=marksweep");
                }
            }

            using (var ms = new MemoryStream()) {
                var utf8Encoding = new UTF8Encoding(false);
                foreach (string abi in SupportedAbis)
                {
                    ms.SetLength(0);
                    NativeAssemblerTargetProvider asmTargetProvider;
                    string asmFileName = Path.Combine(EnvironmentOutputDirectory, $"environment.{abi.ToLowerInvariant ()}.s");
                    switch (abi.Trim())
                    {
                    case "armeabi-v7a":
                        asmTargetProvider = new ARMNativeAssemblerTargetProvider(false);
                        break;

                    case "arm64-v8a":
                        asmTargetProvider = new ARMNativeAssemblerTargetProvider(true);
                        break;

                    case "x86":
                        asmTargetProvider = new X86NativeAssemblerTargetProvider(false);
                        break;

                    case "x86_64":
                        asmTargetProvider = new X86NativeAssemblerTargetProvider(true);
                        break;

                    default:
                        throw new InvalidOperationException($"Unknown ABI {abi}");
                    }

                    var asmgen = new ApplicationConfigNativeAssemblyGenerator(asmTargetProvider, environmentVariables, systemProperties)
                    {
                        IsBundledApp        = IsBundledApplication,
                        UsesMonoAOT         = usesMonoAOT,
                        UsesMonoLLVM        = EnableLLVM,
                        UsesAssemblyPreload = usesAssemblyPreload,
                        MonoAOTMode         = monoAOTMode.ToString().ToLowerInvariant(),
                        AndroidPackageName  = AndroidPackageName,
                    };

                    using (var sw = new StreamWriter(ms, utf8Encoding, bufferSize: 8192, leaveOpen: true)) {
                        asmgen.Write(sw, asmFileName);
                        MonoAndroidHelper.CopyIfStreamChanged(ms, asmFileName);
                    }
                }
            }

            void AddEnvironmentVariable(string name, string value)
            {
                if (Char.IsUpper(name [0]) || !Char.IsLetter(name [0]))
                {
                    environmentVariables [ValidAssemblerString(name)] = ValidAssemblerString(value);
                }
                else
                {
                    systemProperties [ValidAssemblerString(name)] = ValidAssemblerString(value);
                }
            }

            void AddEnvironmentVariableLine(string l)
            {
                string line = l?.Trim();

                if (String.IsNullOrEmpty(line) || line [0] == '#')
                {
                    return;
                }

                string[] nv = line.Split(new char[] { '=' }, 2);
                AddEnvironmentVariable(nv[0].Trim(), nv.Length < 2 ? String.Empty : nv[1].Trim());
            }

            string ValidAssemblerString(string s)
            {
                return(s.Replace("\"", "\\\""));
            }
        }
        bool GenerateDebugFiles(bool skipJniAddNativeMethodRegistrationAttributeScan, List <TypeDefinition> javaTypes, string outputDirectory, ApplicationConfigTaskState appConfState)
        {
            var modules = new Dictionary <string, ModuleDebugData> (StringComparer.Ordinal);
            int maxModuleFileNameWidth = 0;
            int maxModuleNameWidth     = 0;

            foreach (TypeDefinition td in javaTypes)
            {
                UpdateApplicationConfig(td, appConfState);
                string          moduleName = td.Module.Assembly.Name.Name;
                ModuleDebugData module;

                if (!modules.TryGetValue(moduleName, out module))
                {
                    string outputFileName = $"{moduleName}{TypemapExtension}";
                    module = new ModuleDebugData {
                        EntryCount       = 0,
                        JavaNameWidth    = 0,
                        ManagedNameWidth = 0,
                        JavaToManagedMap = new List <TypeMapDebugEntry> (),
                        ManagedToJavaMap = new List <TypeMapDebugEntry> (),
                        OutputFilePath   = Path.Combine(outputDirectory, outputFileName),
                        ModuleNameBytes  = outputEncoding.GetBytes(moduleName)
                    };

                    if (module.ModuleNameBytes.Length > maxModuleNameWidth)
                    {
                        maxModuleNameWidth = module.ModuleNameBytes.Length;
                    }

                    if (outputFileName.Length > maxModuleFileNameWidth)
                    {
                        maxModuleFileNameWidth = outputFileName.Length;
                    }

                    modules.Add(moduleName, module);
                }

                TypeMapDebugEntry entry = GetDebugEntry(td);
                if (entry.JavaName.Length > module.JavaNameWidth)
                {
                    module.JavaNameWidth = (uint)entry.JavaName.Length + 1;
                }

                if (entry.ManagedName.Length > module.ManagedNameWidth)
                {
                    module.ManagedNameWidth = (uint)entry.ManagedName.Length + 1;
                }

                module.JavaToManagedMap.Add(entry);
                module.ManagedToJavaMap.Add(entry);
            }

            foreach (ModuleDebugData module in modules.Values)
            {
                PrepareDebugMaps(module);
            }

            string typeMapIndexPath = Path.Combine(outputDirectory, "typemap.index");

            using (var indexWriter = MemoryStreamPool.Shared.CreateBinaryWriter()) {
                OutputModules(modules, indexWriter, maxModuleFileNameWidth + 1);
                indexWriter.Flush();
                MonoAndroidHelper.CopyIfStreamChanged(indexWriter.BaseStream, typeMapIndexPath);
            }
            GeneratedBinaryTypeMaps.Add(typeMapIndexPath);

            GenerateNativeAssembly(
                (NativeAssemblerTargetProvider asmTargetProvider, bool sharedBitsWritten, bool sharedIncludeUsesAbiPrefix) => {
                return(new TypeMappingDebugNativeAssemblyGenerator(asmTargetProvider, new ModuleDebugData(), outputDirectory, sharedBitsWritten));
            }
                );

            return(true);
        }
Пример #11
0
        void AddEnvironment()
        {
            bool usesMonoAOT                = false;
            bool usesAssemblyPreload        = EnablePreloadAssembliesDefault;
            bool brokenExceptionTransitions = false;
            var  environmentVariables       = new Dictionary <string, string> (StringComparer.Ordinal);
            var  systemProperties           = new Dictionary <string, string> (StringComparer.Ordinal);

            if (!Enum.TryParse(PackageNamingPolicy, out PackageNamingPolicy pnp))
            {
                pnp = PackageNamingPolicyEnum.LowercaseCrc64;
            }

            AotMode aotMode = AotMode.None;

            if (!string.IsNullOrEmpty(AndroidAotMode) && Aot.GetAndroidAotMode(AndroidAotMode, out aotMode) && aotMode != AotMode.None)
            {
                usesMonoAOT = true;
            }

            bool haveLogLevel           = false;
            bool haveMonoDebug          = false;
            bool havebuildId            = false;
            bool haveHttpMessageHandler = false;
            bool haveTlsProvider        = false;
            bool haveMonoGCParams       = false;

            SequencePointsMode sequencePointsMode;

            if (!Aot.TryGetSequencePointsMode(AndroidSequencePointsMode, out sequencePointsMode))
            {
                sequencePointsMode = SequencePointsMode.None;
            }

            foreach (ITaskItem env in Environments ?? new TaskItem[0])
            {
                foreach (string line in File.ReadLines(env.ItemSpec))
                {
                    var lineToWrite = line;
                    if (lineToWrite.StartsWith("MONO_LOG_LEVEL=", StringComparison.Ordinal))
                    {
                        haveLogLevel = true;
                    }
                    if (lineToWrite.StartsWith("MONO_GC_PARAMS=", StringComparison.Ordinal))
                    {
                        haveMonoGCParams = true;
                    }
                    if (lineToWrite.StartsWith("XAMARIN_BUILD_ID=", StringComparison.Ordinal))
                    {
                        havebuildId = true;
                    }
                    if (lineToWrite.StartsWith("MONO_DEBUG=", StringComparison.Ordinal))
                    {
                        haveMonoDebug = true;
                        if (sequencePointsMode != SequencePointsMode.None && !lineToWrite.Contains("gen-compact-seq-points"))
                        {
                            lineToWrite = line + ",gen-compact-seq-points";
                        }
                    }
                    if (lineToWrite.StartsWith("XA_HTTP_CLIENT_HANDLER_TYPE=", StringComparison.Ordinal))
                    {
                        haveHttpMessageHandler = true;
                    }
                    if (lineToWrite.StartsWith("XA_TLS_PROVIDER=", StringComparison.Ordinal))
                    {
                        haveTlsProvider = true;
                    }
                    if (lineToWrite.StartsWith("mono.enable_assembly_preload=", StringComparison.Ordinal))
                    {
                        int  idx = lineToWrite.IndexOf('=');
                        uint val;
                        if (idx < lineToWrite.Length - 1 && UInt32.TryParse(lineToWrite.Substring(idx + 1), out val))
                        {
                            usesAssemblyPreload = idx == 1;
                        }
                        continue;
                    }
                    if (lineToWrite.StartsWith("XA_BROKEN_EXCEPTION_TRANSITIONS="))
                    {
                        brokenExceptionTransitions = true;
                        continue;
                    }

                    AddEnvironmentVariableLine(lineToWrite);
                }
            }

            if (_Debug && !haveLogLevel)
            {
                AddEnvironmentVariable(defaultLogLevel[0], defaultLogLevel[1]);
            }

            if (sequencePointsMode != SequencePointsMode.None && !haveMonoDebug)
            {
                AddEnvironmentVariable(defaultMonoDebug[0], defaultMonoDebug[1]);
            }

            if (!havebuildId)
            {
                AddEnvironmentVariable("XAMARIN_BUILD_ID", BuildId);
            }

            if (!haveHttpMessageHandler)
            {
                if (HttpClientHandlerType == null)
                {
                    AddEnvironmentVariable(defaultHttpMessageHandler[0], defaultHttpMessageHandler[1]);
                }
                else
                {
                    AddEnvironmentVariable("XA_HTTP_CLIENT_HANDLER_TYPE", HttpClientHandlerType.Trim());
                }
            }

            if (!haveTlsProvider)
            {
                if (TlsProvider == null)
                {
                    AddEnvironmentVariable(defaultTlsProvider[0], defaultTlsProvider[1]);
                }
                else
                {
                    AddEnvironmentVariable("XA_TLS_PROVIDER", TlsProvider.Trim());
                }
            }

            if (!haveMonoGCParams)
            {
                if (EnableSGenConcurrent)
                {
                    AddEnvironmentVariable("MONO_GC_PARAMS", "major=marksweep-conc");
                }
                else
                {
                    AddEnvironmentVariable("MONO_GC_PARAMS", "major=marksweep");
                }
            }

            global::Android.Runtime.BoundExceptionType boundExceptionType;
            if (String.IsNullOrEmpty(BoundExceptionType) || String.Compare(BoundExceptionType, "System", StringComparison.OrdinalIgnoreCase) == 0)
            {
                boundExceptionType = global::Android.Runtime.BoundExceptionType.System;
            }
            else if (String.Compare(BoundExceptionType, "Java", StringComparison.OrdinalIgnoreCase) == 0)
            {
                boundExceptionType = global::Android.Runtime.BoundExceptionType.Java;
            }
            else
            {
                throw new InvalidOperationException($"Unsupported BoundExceptionType value '{BoundExceptionType}'");
            }

            var appConfState = BuildEngine4.GetRegisteredTaskObject(ApplicationConfigTaskState.RegisterTaskObjectKey, RegisteredTaskObjectLifetime.Build) as ApplicationConfigTaskState;

            foreach (string abi in SupportedAbis)
            {
                NativeAssemblerTargetProvider asmTargetProvider = GetAssemblyTargetProvider(abi);
                string baseAsmFilePath = Path.Combine(EnvironmentOutputDirectory, $"environment.{abi.ToLowerInvariant ()}");
                string asmFilePath     = $"{baseAsmFilePath}.s";

                var asmgen = new ApplicationConfigNativeAssemblyGenerator(asmTargetProvider, baseAsmFilePath, environmentVariables, systemProperties)
                {
                    IsBundledApp               = IsBundledApplication,
                    UsesMonoAOT                = usesMonoAOT,
                    UsesMonoLLVM               = EnableLLVM,
                    UsesAssemblyPreload        = usesAssemblyPreload,
                    MonoAOTMode                = aotMode.ToString().ToLowerInvariant(),
                    AndroidPackageName         = AndroidPackageName,
                    BrokenExceptionTransitions = brokenExceptionTransitions,
                    PackageNamingPolicy        = pnp,
                    BoundExceptionType         = boundExceptionType,
                    InstantRunEnabled          = InstantRunEnabled,
                    JniAddNativeMethodRegistrationAttributePresent = appConfState != null ? appConfState.JniAddNativeMethodRegistrationAttributePresent : false,
                };

                using (var sw = MemoryStreamPool.Shared.CreateStreamWriter()) {
                    asmgen.Write(sw);
                    sw.Flush();
                    MonoAndroidHelper.CopyIfStreamChanged(sw.BaseStream, asmFilePath);
                }
            }

            void AddEnvironmentVariable(string name, string value)
            {
                if (Char.IsUpper(name [0]) || !Char.IsLetter(name [0]))
                {
                    environmentVariables [ValidAssemblerString(name)] = ValidAssemblerString(value);
                }
                else
                {
                    systemProperties [ValidAssemblerString(name)] = ValidAssemblerString(value);
                }
            }

            void AddEnvironmentVariableLine(string l)
            {
                string line = l?.Trim();

                if (String.IsNullOrEmpty(line) || line [0] == '#')
                {
                    return;
                }

                string[] nv = line.Split(new char[] { '=' }, 2);
                AddEnvironmentVariable(nv[0].Trim(), nv.Length < 2 ? String.Empty : nv[1].Trim());
            }

            string ValidAssemblerString(string s)
            {
                return(s.Replace("\"", "\\\""));
            }
        }
        protected override void WriteSymbols(StreamWriter output)
        {
            output.WriteLine();
            WriteHeaderField(output, "map_module_count", mappingData.MapModuleCount);

            output.WriteLine();
            WriteHeaderField(output, "java_type_count", mappingData.JavaTypeCount);

            output.WriteLine();
            WriteHeaderField(output, "java_name_width", mappingData.JavaNameWidth);

            bool haveAssemblyNames = mappingData.AssemblyNames.Count > 0;
            bool haveModules       = mappingData.Modules.Length > 0;

            output.WriteLine();
            if (haveAssemblyNames)
            {
                output.Write(Indent);
                output.Write(".include");
                output.Write(Indent);
                output.Write('"');
                output.Write(Path.GetFileName(SharedIncludeFile));
                output.WriteLine('"');
            }
            else
            {
                WriteCommentLine(output, $"No shared data present, {Path.GetFileName (SharedIncludeFile)} not generated");
            }

            if (haveModules)
            {
                output.Write(Indent);
                output.Write(".include");
                output.Write(Indent);
                output.Write('"');
                output.Write(Path.GetFileName(TypemapsIncludeFile));
                output.WriteLine('"');
            }
            else
            {
                WriteCommentLine(output, $"No modules defined, {Path.GetFileName (TypemapsIncludeFile)} not generated");
            }
            output.WriteLine();

            if (!sharedBitsWritten && haveAssemblyNames)
            {
                using (var sharedOutput = MemoryStreamPool.Shared.CreateStreamWriter(output.Encoding)) {
                    WriteAssemblyNames(sharedOutput);
                    sharedOutput.Flush();
                    MonoAndroidHelper.CopyIfStreamChanged(sharedOutput.BaseStream, SharedIncludeFile);
                }
            }

            if (haveModules)
            {
                using (var mapOutput = MemoryStreamPool.Shared.CreateStreamWriter(output.Encoding)) {
                    WriteMapModules(output, mapOutput, "map_modules");
                    mapOutput.Flush();
                    MonoAndroidHelper.CopyIfStreamChanged(mapOutput.BaseStream, TypemapsIncludeFile);
                }
            }
            else
            {
                WriteMapModules(output, null, "map_modules");
            }

            WriteJavaMap(output, "map_java");
        }
Пример #13
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 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);
        }
Пример #14
0
        public override bool Execute()
        {
            BuildId = buildId.ToString();
            Log.LogDebugMessage("  [Output] BuildId: {0}", BuildId);

            var shared_runtime = string.Compare(UseSharedRuntime, "true", true) == 0;
            var doc            = AndroidAppManifest.Load(Manifest, MonoAndroidHelper.SupportedVersions);
            int minApiVersion  = doc.MinSdkVersion == null ? 4 : (int)doc.MinSdkVersion;
            // We need to include any special assemblies in the Assemblies list
            var assemblies = ResolvedUserAssemblies.Select(p => p.ItemSpec)
                             .Concat(MonoAndroidHelper.GetFrameworkAssembliesToTreatAsUserAssemblies(ResolvedAssemblies))
                             .ToList();
            var mainFileName = Path.GetFileName(MainAssembly);
            Func <string, string, bool> fileNameEq = (a, b) => a.Equals(b, StringComparison.OrdinalIgnoreCase);

            assemblies = assemblies.Where(a => fileNameEq(a, mainFileName)).Concat(assemblies.Where(a => !fileNameEq(a, mainFileName))).ToList();

            using (var stream = new MemoryStream())
                using (var pkgmgr = new StreamWriter(stream)) {
                    // Write the boilerplate from the MonoPackageManager.java resource
                    var packageManagerResource = minApiVersion < 9 ? "MonoPackageManager.api4.java" : "MonoPackageManager.java";
                    using (var template = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream(packageManagerResource))) {
                        string line;
                        while ((line = template.ReadLine()) != null)
                        {
                            pkgmgr.WriteLine(line);
                        }
                    }

                    // Write all the user assemblies
                    pkgmgr.WriteLine("class MonoPackageManager_Resources {");
                    pkgmgr.WriteLine("\tpublic static final String[] Assemblies = new String[]{");

                    pkgmgr.WriteLine("\t\t/* We need to ensure that \"{0}\" comes first in this list. */", mainFileName);
                    foreach (var assembly in assemblies)
                    {
                        pkgmgr.WriteLine("\t\t\"" + Path.GetFileName(assembly) + "\",");
                    }

                    // Write the assembly dependencies
                    pkgmgr.WriteLine("\t};");
                    pkgmgr.WriteLine("\tpublic static final String[] Dependencies = new String[]{");

                    //foreach (var assembly in assemblies.Except (args.Assemblies)) {
                    //        if (args.SharedRuntime && !Toolbox.IsInSharedRuntime (assembly))
                    //                pkgmgr.WriteLine ("\t\t\"" + Path.GetFileName (assembly) + "\",");
                    //}

                    pkgmgr.WriteLine("\t};");

                    // Write the platform api apk we need
                    pkgmgr.WriteLine("\tpublic static final String ApiPackageName = {0};", shared_runtime
                                                ? string.Format("\"Mono.Android.Platform.ApiLevel_{0}\"",
                                                                MonoAndroidHelper.SupportedVersions.GetApiLevelFromFrameworkVersion(TargetFrameworkVersion))
                                                : "null");
                    pkgmgr.WriteLine("}");
                    pkgmgr.Flush();

                    // Only copy to the real location if the contents actually changed
                    var dest = Path.GetFullPath(Path.Combine(OutputDirectory, "MonoPackageManager.java"));

                    MonoAndroidHelper.CopyIfStreamChanged(stream, dest);
                }

            AddEnvironment();

            return(!Log.HasLoggedErrors);
        }
Пример #15
0
        /// <summary>
        /// NOTE: all file paths used in this method should be full paths. (Or use AsyncTask.WorkingDirectory)
        /// </summary>
        void GenerateJava(Library library)
        {
            // In some cases (such as ancient support libraries), R.txt does not exist.
            // We can just use the main app's R.txt file and write *all fields* in this case.
            bool using_main_r_txt = false;
            var  r_txt            = library.TextFile;

            if (!File.Exists(r_txt))
            {
                LogDebugMessage($"Using main R.txt, R.txt does not exist: {r_txt}");
                using_main_r_txt = true;
                r_txt            = main_r_txt;
            }

            var manifestFile = library.ManifestFile;

            if (!File.Exists(manifestFile))
            {
                LogDebugMessage($"Skipping, AndroidManifest.xml does not exist: {manifestFile}");
                return;
            }

            var manifest = AndroidAppManifest.Load(manifestFile, MonoAndroidHelper.SupportedVersions);

            using (var memory = new MemoryStream())
                using (var writer = new StreamWriter(memory, Encoding)) {
                    // This code is based on the Android gradle plugin
                    // https://android.googlesource.com/platform/tools/base/+/908b391a9c006af569dfaff08b37f8fdd6c4da89/build-system/builder/src/main/java/com/android/builder/internal/SymbolWriter.java

                    writer.WriteLine("/* AUTO-GENERATED FILE. DO NOT MODIFY.");
                    writer.WriteLine(" *");
                    writer.WriteLine(" * This class was automatically generated by");
                    writer.WriteLine(" * Xamarin.Android from the resource data it found.");
                    writer.WriteLine(" * It should not be modified by hand.");
                    writer.WriteLine(" */");

                    writer.Write("package ");
                    writer.Write(manifest.PackageName);
                    writer.WriteLine(';');
                    writer.WriteLine();
                    writer.WriteLine("public final class R {");

                    using (var reader = File.OpenText(r_txt)) {
                        string currentClass = null;
                        foreach (var line in ParseFile(reader))
                        {
                            var type  = line [Index.Type];
                            var clazz = line [Index.Class];
                            var name  = line [Index.Name];
                            if (GetValue(clazz, name, line, using_main_r_txt, out string value))
                            {
                                if (clazz != currentClass)
                                {
                                    // If not the first inner class
                                    if (currentClass != null)
                                    {
                                        writer.WriteLine("\t}");
                                    }

                                    currentClass = clazz;
                                    writer.Write("\tpublic static final class ");
                                    writer.Write(currentClass);
                                    writer.WriteLine(" {");
                                }

                                writer.Write("\t\tpublic static final ");
                                writer.Write(type);
                                writer.Write(' ');
                                writer.Write(name);
                                writer.Write(" = ");
                                // It may be an int[]
                                if (value.StartsWith("{", StringComparison.Ordinal))
                                {
                                    writer.Write("new ");
                                    writer.Write(type);
                                    writer.Write(' ');
                                }
                                writer.Write(value);
                                writer.WriteLine(';');
                            }
                            else
                            {
                                LogDebugMessage($"{r_txt}: `{type} {clazz} {name}` value not found");
                            }
                        }

                        // If we wrote at least one inner class
                        if (currentClass != null)
                        {
                            writer.WriteLine("\t}");
                        }
                        writer.WriteLine('}');
                    }

                    writer.Flush();
                    var r_java = Path.Combine(output_directory, manifest.PackageName.Replace('.', Path.DirectorySeparatorChar), "R.java");
                    if (MonoAndroidHelper.CopyIfStreamChanged(memory, r_java))
                    {
                        LogDebugMessage($"Writing: {r_java}");
                    }
                    else
                    {
                        LogDebugMessage($"Up to date: {r_java}");
                    }
                }
        }
        // Extracts library project contents under e.g. obj/Debug/[__library_projects__/*.jar | res/*/*]
        // Extracts library project contents under e.g. obj/Debug/[lp/*.jar | res/*/*]
        void Extract(
            DirectoryAssemblyResolver res,
            IDictionary <string, ITaskItem> jars,
            ICollection <ITaskItem> resolvedResourceDirectories,
            ICollection <ITaskItem> resolvedAssetDirectories,
            ICollection <ITaskItem> resolvedEnvironments)
        {
            // lets "upgrade" the old directory.
            string oldPath = Path.GetFullPath(Path.Combine(OutputImportDirectory, "..", "__library_projects__"));

            if (!OutputImportDirectory.Contains("__library_projects__") && Directory.Exists(oldPath))
            {
                MonoAndroidHelper.SetDirectoryWriteable(Path.Combine(oldPath, ".."));
                Directory.Delete(oldPath, recursive: true);
            }
            var outdir = Path.GetFullPath(OutputImportDirectory);

            Directory.CreateDirectory(outdir);

            foreach (var assembly in Assemblies)
            {
                res.Load(assembly.ItemSpec);
            }

            // FIXME: reorder references by import priority (not sure how to do that yet)
            foreach (var assemblyPath in Assemblies
                     .Select(a => a.ItemSpec)
                     .Distinct())
            {
                var fileName = Path.GetFileName(assemblyPath);
                if (MonoAndroidHelper.IsFrameworkAssembly(fileName) &&
                    !MonoAndroidHelper.FrameworkEmbeddedJarLookupTargets.Contains(fileName) &&
                    !MonoAndroidHelper.FrameworkEmbeddedNativeLibraryAssemblies.Contains(fileName))
                {
                    Log.LogDebugMessage($"Skipping framework assembly '{fileName}'.");
                    continue;
                }
                if (DesignTimeBuild && !File.Exists(assemblyPath))
                {
                    Log.LogDebugMessage($"Skipping non-existent dependency '{assemblyPath}' during a design-time build.");
                    continue;
                }
                if (assembliestoSkipExtraction.Contains(assemblyPath))
                {
                    Log.LogDebugMessage("Skipping resource extraction for '{0}' .", assemblyPath);
                    continue;
                }
                string assemblyFileName  = Path.GetFileNameWithoutExtension(assemblyPath);
                string assemblyIdentName = assemblyFileName;
                if (UseShortFileNames)
                {
                    assemblyIdentName = assemblyMap.GetLibraryImportDirectoryNameForAssembly(assemblyFileName);
                }
                string outDirForDll     = Path.Combine(OutputImportDirectory, assemblyIdentName);
                string importsDir       = Path.Combine(outDirForDll, ImportsDirectory);
                string nativeimportsDir = Path.Combine(outDirForDll, NativeImportsDirectory);
                string resDir           = Path.Combine(importsDir, "res");
                string assetsDir        = Path.Combine(importsDir, "assets");

                // Skip already-extracted resources.
                bool   updated      = false;
                string assemblyHash = MonoAndroidHelper.HashFile(assemblyPath);
                string stamp        = Path.Combine(outdir, assemblyIdentName + ".stamp");
                string stampHash    = File.Exists(stamp) ? File.ReadAllText(stamp) : null;
                if (assemblyHash == stampHash)
                {
                    Log.LogDebugMessage("Skipped resource lookup for {0}: extracted files are up to date", assemblyPath);
                    if (Directory.Exists(importsDir))
                    {
                        foreach (var file in Directory.EnumerateFiles(importsDir, "*.jar", SearchOption.AllDirectories))
                        {
                            AddJar(jars, Path.GetFullPath(file));
                        }
                    }
                    if (Directory.Exists(resDir))
                    {
                        var taskItem = new TaskItem(Path.GetFullPath(resDir), new Dictionary <string, string> {
                            { OriginalFile, assemblyPath },
                        });
                        if (assembliesToSkipCaseFixup.Contains(assemblyPath))
                        {
                            taskItem.SetMetadata(AndroidSkipResourceProcessing, "True");
                        }
                        resolvedResourceDirectories.Add(taskItem);
                    }
                    if (Directory.Exists(assetsDir))
                    {
                        resolvedAssetDirectories.Add(new TaskItem(Path.GetFullPath(assetsDir), new Dictionary <string, string> {
                            { OriginalFile, assemblyPath }
                        }));
                    }
                    foreach (var env in Directory.EnumerateFiles(outDirForDll, "__AndroidEnvironment__*", SearchOption.TopDirectoryOnly))
                    {
                        resolvedEnvironments.Add(new TaskItem(env, new Dictionary <string, string> {
                            { OriginalFile, assemblyPath }
                        }));
                    }
                    continue;
                }

                Log.LogDebugMessage($"Refreshing {assemblyFileName}.dll");

                var assembly = res.GetAssembly(assemblyPath);
                foreach (var mod in assembly.Modules)
                {
                    // android environment files
                    foreach (var envtxt in mod.Resources
                             .Where(r => r.Name.StartsWith("__AndroidEnvironment__", StringComparison.OrdinalIgnoreCase))
                             .Where(r => r is EmbeddedResource)
                             .Cast <EmbeddedResource> ())
                    {
                        var outFile = Path.Combine(outDirForDll, envtxt.Name);
                        using (var stream = envtxt.GetResourceStream()) {
                            updated |= MonoAndroidHelper.CopyIfStreamChanged(stream, outFile);
                        }
                        resolvedEnvironments.Add(new TaskItem(Path.GetFullPath(outFile), new Dictionary <string, string> {
                            { OriginalFile, assemblyPath }
                        }));
                    }

                    // embedded jars (EmbeddedJar, EmbeddedReferenceJar)
                    var resjars = mod.Resources
                                  .Where(r => r.Name.EndsWith(".jar", StringComparison.InvariantCultureIgnoreCase))
                                  .Select(r => (EmbeddedResource)r);
                    foreach (var resjar in resjars)
                    {
                        using (var stream = resjar.GetResourceStream()) {
                            AddJar(jars, importsDir, resjar.Name, assemblyPath);
                            updated |= MonoAndroidHelper.CopyIfStreamChanged(stream, Path.Combine(importsDir, resjar.Name));
                        }
                    }

                    var libzip = mod.Resources.FirstOrDefault(r => r.Name == "__AndroidNativeLibraries__.zip") as EmbeddedResource;
                    if (libzip != null)
                    {
                        List <string> files = new List <string> ();
                        using (var stream = libzip.GetResourceStream())
                            using (var zip = Xamarin.Tools.Zip.ZipArchive.Open(stream)) {
                                try {
                                    updated |= Files.ExtractAll(zip, nativeimportsDir, modifyCallback: (entryFullName) => {
                                        files.Add(Path.GetFullPath(Path.Combine(nativeimportsDir, entryFullName)));
                                        return(entryFullName
                                               .Replace("native_library_imports\\", "")
                                               .Replace("native_library_imports/", ""));
                                    }, deleteCallback: (fileToDelete) => {
                                        return(!files.Contains(fileToDelete));
                                    });
                                } catch (PathTooLongException ex) {
                                    Log.LogCodedError("XA4303", $"Error extracting resources from \"{assemblyPath}\": {ex}");
                                    return;
                                } catch (NotSupportedException ex) {
                                    Log.LogCodedError("XA4303", $"Error extracting resources from \"{assemblyPath}\": {ex}");
                                    return;
                                }
                            }
                    }

                    // embedded AndroidResourceLibrary archive
                    var reszip = mod.Resources.FirstOrDefault(r => r.Name == "__AndroidLibraryProjects__.zip") as EmbeddedResource;
                    if (reszip != null)
                    {
                        // temporarily extracted directory will look like:
                        //    __library_projects__/[dllname]/[library_project_imports | jlibs]/bin
                        using (var stream = reszip.GetResourceStream())
                            using (var zip = Xamarin.Tools.Zip.ZipArchive.Open(stream)) {
                                try {
                                    updated |= Files.ExtractAll(zip, importsDir, modifyCallback: (entryFullName) => {
                                        var path = entryFullName
                                                   .Replace("library_project_imports\\", "")
                                                   .Replace("library_project_imports/", "");
                                        if (path.EndsWith(".jar", StringComparison.OrdinalIgnoreCase))
                                        {
                                            AddJar(jars, importsDir, path, assemblyPath);
                                        }
                                        return(path);
                                    }, deleteCallback: (fileToDelete) => {
                                        return(!jars.ContainsKey(fileToDelete));
                                    });
                                } catch (PathTooLongException ex) {
                                    Log.LogCodedError("XA4303", $"Error extracting resources from \"{assemblyPath}\": {ex}");
                                    return;
                                } catch (NotSupportedException ex) {
                                    Log.LogCodedError("XA4303", $"Error extracting resources from \"{assemblyPath}\": {ex}");
                                    return;
                                }
                            }

                        // We used to *copy* the resources to overwrite other resources,
                        // which resulted in missing resource issue.
                        // Here we replaced copy with use of '-S' option and made it to work.
                        if (Directory.Exists(resDir))
                        {
                            var taskItem = new TaskItem(Path.GetFullPath(resDir), new Dictionary <string, string> {
                                { OriginalFile, assemblyPath }
                            });
                            if (assembliesToSkipCaseFixup.Contains(assemblyPath))
                            {
                                taskItem.SetMetadata(AndroidSkipResourceProcessing, "True");
                            }
                            resolvedResourceDirectories.Add(taskItem);
                        }
                        if (Directory.Exists(assetsDir))
                        {
                            resolvedAssetDirectories.Add(new TaskItem(Path.GetFullPath(assetsDir), new Dictionary <string, string> {
                                { OriginalFile, assemblyPath }
                            }));
                        }
                    }
                }

                if (Directory.Exists(importsDir))
                {
                    // Delete unknown files in the top directory of importsDir
                    foreach (var file in Directory.EnumerateFiles(importsDir, "*"))
                    {
                        var fullPath = Path.GetFullPath(file);
                        if (file.StartsWith("__AndroidEnvironment__", StringComparison.OrdinalIgnoreCase) && !resolvedEnvironments.Any(x => x.ItemSpec == fullPath))
                        {
                            Log.LogDebugMessage($"Deleting unknown AndroidEnvironment file: {Path.GetFileName (file)}");
                            File.Delete(fullPath);
                            updated = true;
                        }
                        else if (file.EndsWith(".jar", StringComparison.OrdinalIgnoreCase) && !jars.ContainsKey(fullPath))
                        {
                            Log.LogDebugMessage($"Deleting unknown jar: {Path.GetFileName (file)}");
                            File.Delete(fullPath);
                            updated = true;
                        }
                    }
                    if (assemblyHash != stampHash)
                    {
                        Log.LogDebugMessage($"Saving hash to {stamp}, changes: {updated}");
                        //NOTE: if the hash is different we always want to write the file, but preserve the timestamp if no changes
                        WriteAllText(stamp, assemblyHash, preserveTimestamp: !updated);
                    }
                }
            }
            foreach (var aarFile in AarLibraries ?? new ITaskItem[0])
            {
                if (!File.Exists(aarFile.ItemSpec))
                {
                    continue;
                }
                string aarIdentityName = Path.GetFileNameWithoutExtension(aarFile.ItemSpec);
                if (UseShortFileNames)
                {
                    aarIdentityName = assemblyMap.GetLibraryImportDirectoryNameForAssembly(aarIdentityName);
                }
                string outDirForDll = Path.Combine(OutputImportDirectory, aarIdentityName);
                string importsDir   = Path.Combine(outDirForDll, ImportsDirectory);
                string resDir       = Path.Combine(importsDir, "res");
                string assetsDir    = Path.Combine(importsDir, "assets");

                bool   updated     = false;
                string aarHash     = MonoAndroidHelper.HashFile(aarFile.ItemSpec);
                string stamp       = Path.Combine(outdir, aarIdentityName + ".stamp");
                string stampHash   = File.Exists(stamp) ? File.ReadAllText(stamp) : null;
                var    aarFullPath = Path.GetFullPath(aarFile.ItemSpec);
                if (aarHash == stampHash)
                {
                    Log.LogDebugMessage("Skipped {0}: extracted files are up to date", aarFile.ItemSpec);
                    if (Directory.Exists(importsDir))
                    {
                        foreach (var file in Directory.EnumerateFiles(importsDir, "*.jar", SearchOption.AllDirectories))
                        {
                            AddJar(jars, Path.GetFullPath(file));
                        }
                    }
                    if (Directory.Exists(resDir))
                    {
                        resolvedResourceDirectories.Add(new TaskItem(Path.GetFullPath(resDir), new Dictionary <string, string> {
                            { OriginalFile, Path.GetFullPath(aarFile.ItemSpec) },
                            { AndroidSkipResourceProcessing, "True" },
                        }));
                    }
                    if (Directory.Exists(assetsDir))
                    {
                        resolvedAssetDirectories.Add(new TaskItem(Path.GetFullPath(assetsDir), new Dictionary <string, string> {
                            { OriginalFile, aarFullPath },
                        }));
                    }
                    continue;
                }

                Log.LogDebugMessage($"Refreshing {aarFile.ItemSpec}");

                // temporarily extracted directory will look like:
                // _lp_/[aarFile]

                using (var zip = MonoAndroidHelper.ReadZipFile(aarFile.ItemSpec)) {
                    try {
                        updated |= Files.ExtractAll(zip, importsDir, modifyCallback: (entryFullName) => {
                            var entryFileName = Path.GetFileName(entryFullName);
                            var entryPath     = Path.GetDirectoryName(entryFullName);
                            if (entryFileName.StartsWith("internal_impl", StringComparison.InvariantCulture))
                            {
                                var hash = Files.HashString(entryFileName);
                                var jar  = Path.Combine(entryPath, $"internal_impl-{hash}.jar");
                                AddJar(jars, importsDir, jar, aarFullPath);
                                return(jar);
                            }
                            if (entryFullName.EndsWith(".jar", StringComparison.OrdinalIgnoreCase))
                            {
                                AddJar(jars, importsDir, entryFullName, aarFullPath);
                            }
                            return(entryFullName);
                        }, deleteCallback: (fileToDelete) => {
                            return(!jars.ContainsKey(fileToDelete));
                        });

                        if (Directory.Exists(importsDir) && aarHash != stampHash)
                        {
                            Log.LogDebugMessage($"Saving hash to {stamp}, changes: {updated}");
                            //NOTE: if the hash is different we always want to write the file, but preserve the timestamp if no changes
                            WriteAllText(stamp, aarHash, preserveTimestamp: !updated);
                        }
                    } catch (PathTooLongException ex) {
                        Log.LogErrorFromException(new PathTooLongException($"Error extracting resources from \"{aarFile.ItemSpec}\"", ex));
                    }
                }
                if (Directory.Exists(resDir))
                {
                    resolvedResourceDirectories.Add(new TaskItem(Path.GetFullPath(resDir), new Dictionary <string, string> {
                        { OriginalFile, aarFullPath },
                        { AndroidSkipResourceProcessing, "True" },
                    }));
                }
                if (Directory.Exists(assetsDir))
                {
                    resolvedAssetDirectories.Add(new TaskItem(Path.GetFullPath(assetsDir), new Dictionary <string, string> {
                        { OriginalFile, aarFullPath },
                    }));
                }
            }
        }
Пример #17
0
        /// <summary>
        /// NOTE: all file paths used in this method should be full paths. (Or use AsyncTask.WorkingDirectory)
        /// </summary>
        void GenerateJava(Package package)
        {
            // In some cases (such as ancient support libraries), R.txt does not exist.
            // We can just use the main app's R.txt file and write *all fields* in this case.
            foreach (var r_txt in package.TextFiles)
            {
                if (!File.Exists(r_txt))
                {
                    LogDebugMessage($"Using main R.txt, R.txt does not exist: {r_txt}");
                    package.TextFiles.Clear();
                    package.TextFiles.Add(main_r_txt);
                    break;
                }
            }

            var lines = LoadValues(package);

            using (var writer = MemoryStreamPool.Shared.CreateStreamWriter()) {
                // This code is based on the Android gradle plugin
                // https://android.googlesource.com/platform/tools/base/+/908b391a9c006af569dfaff08b37f8fdd6c4da89/build-system/builder/src/main/java/com/android/builder/internal/SymbolWriter.java

                writer.WriteLine("/* AUTO-GENERATED FILE. DO NOT MODIFY.");
                writer.WriteLine(" *");
                writer.WriteLine(" * This class was automatically generated by");
                writer.WriteLine(" * Xamarin.Android from the resource data it found.");
                writer.WriteLine(" * It should not be modified by hand.");
                writer.WriteLine(" */");

                writer.Write("package ");
                writer.Write(package.Name);
                writer.WriteLine(';');
                writer.WriteLine();
                writer.WriteLine("public final class R {");

                string currentClass = null;
                foreach (var line in lines)
                {
                    var type  = line [Index.Type];
                    var clazz = line [Index.Class];
                    var name  = line [Index.Name];
                    var value = line [Index.Value];
                    if (clazz != currentClass)
                    {
                        // If not the first inner class
                        if (currentClass != null)
                        {
                            writer.WriteLine("\t}");
                        }

                        currentClass = clazz;
                        writer.Write("\tpublic static final class ");
                        writer.Write(currentClass);
                        writer.WriteLine(" {");
                    }

                    writer.Write("\t\tpublic static final ");
                    writer.Write(type);
                    writer.Write(' ');
                    writer.Write(name);
                    writer.Write(" = ");
                    // It may be an int[]
                    if (value.StartsWith("{", StringComparison.Ordinal))
                    {
                        writer.Write("new ");
                        writer.Write(type);
                        writer.Write(' ');
                    }
                    writer.Write(value);
                    writer.WriteLine(';');
                }

                // If we wrote at least one inner class
                if (currentClass != null)
                {
                    writer.WriteLine("\t}");
                }
                writer.WriteLine('}');

                writer.Flush();
                var r_java = Path.Combine(output_directory, package.Name.Replace('.', Path.DirectorySeparatorChar), "R.java");
                if (MonoAndroidHelper.CopyIfStreamChanged(writer.BaseStream, r_java))
                {
                    LogDebugMessage($"Writing: {r_java}");
                }
                else
                {
                    LogDebugMessage($"Up to date: {r_java}");
                }
            }
        }
Пример #18
0
        void UpdateWhenChanged(string path, string type, MemoryStream ms, Action <Stream> generator)
        {
            if (!EmbedAssemblies)
            {
                ms.SetLength(0);
                generator(ms);
                MonoAndroidHelper.CopyIfStreamChanged(ms, path);
            }

            string dataFilePath = $"{path}.inc";

            using (var stream = new NativeAssemblyDataStream()) {
                if (EmbedAssemblies)
                {
                    generator(stream);
                    stream.EndOfFile();
                    MonoAndroidHelper.CopyIfStreamChanged(stream, dataFilePath);
                }
                else
                {
                    stream.EmptyFile();
                }

                var    generatedFiles   = new List <ITaskItem> ();
                string mappingFieldName = $"{type}_typemap";
                string dataFileName     = Path.GetFileName(dataFilePath);
                NativeAssemblerTargetProvider asmTargetProvider;
                var utf8Encoding = new UTF8Encoding(false);
                foreach (string abi in SupportedAbis)
                {
                    ms.SetLength(0);
                    switch (abi.Trim())
                    {
                    case "armeabi-v7a":
                        asmTargetProvider = new ARMNativeAssemblerTargetProvider(is64Bit: false);
                        break;

                    case "arm64-v8a":
                        asmTargetProvider = new ARMNativeAssemblerTargetProvider(is64Bit: true);
                        break;

                    case "x86":
                        asmTargetProvider = new X86NativeAssemblerTargetProvider(is64Bit: false);
                        break;

                    case "x86_64":
                        asmTargetProvider = new X86NativeAssemblerTargetProvider(is64Bit: true);
                        break;

                    default:
                        throw new InvalidOperationException($"Unknown ABI {abi}");
                    }

                    var asmgen = new TypeMappingNativeAssemblyGenerator(asmTargetProvider, stream, dataFileName, stream.MapByteCount, mappingFieldName);
                    asmgen.EmbedAssemblies = EmbedAssemblies;
                    string asmFileName = $"{path}.{abi.Trim ()}.s";
                    using (var sw = new StreamWriter(ms, utf8Encoding, bufferSize: 8192, leaveOpen: true)) {
                        asmgen.Write(sw, dataFileName);
                        MonoAndroidHelper.CopyIfStreamChanged(ms, asmFileName);
                    }
                }
            }
        }
Пример #19
0
        public bool Generate(List <TypeDefinition> javaTypes, string outputDirectory, bool generateNativeAssembly)
        {
            if (String.IsNullOrEmpty(outputDirectory))
            {
                throw new ArgumentException("must not be null or empty", nameof(outputDirectory));
            }

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

            int assemblyId              = 0;
            int maxJavaNameLength       = 0;
            int maxModuleFileNameLength = 0;
            var knownAssemblies         = new Dictionary <string, int> (StringComparer.Ordinal);
            var tempModules             = new Dictionary <byte[], ModuleData> ();
            Dictionary <AssemblyDefinition, int> moduleCounter = null;
            var mvidCache = new Dictionary <Guid, byte[]> ();

            foreach (TypeDefinition td in javaTypes)
            {
                string assemblyName = td.Module.Assembly.FullName;

                if (!knownAssemblies.ContainsKey(assemblyName))
                {
                    assemblyId++;
                    knownAssemblies.Add(assemblyName, assemblyId);
                }

                // We must NOT use Guid here! The reason is that Guid sort order is different than its corresponding
                // byte array representation and on the runtime we need the latter in order to be able to binary search
                // through the module array.
                byte[] moduleUUID;
                if (!mvidCache.TryGetValue(td.Module.Mvid, out moduleUUID))
                {
                    moduleUUID = td.Module.Mvid.ToByteArray();
                    mvidCache.Add(td.Module.Mvid, moduleUUID);
                }

                ModuleData moduleData;
                if (!tempModules.TryGetValue(moduleUUID, out moduleData))
                {
                    if (moduleCounter == null)
                    {
                        moduleCounter = new Dictionary <AssemblyDefinition, int> ();
                    }

                    moduleData = new ModuleData {
                        Mvid           = td.Module.Mvid,
                        MvidBytes      = moduleUUID,
                        Assembly       = td.Module.Assembly,
                        AssemblyName   = td.Module.Assembly.Name.Name,
                        TypesScratch   = new Dictionary <string, TypeMapEntry> (StringComparer.Ordinal),
                        DuplicateTypes = new Dictionary <uint, TypeMapEntry> (),
                    };
                    tempModules.Add(moduleUUID, moduleData);

                    if (!generateNativeAssembly)
                    {
                        int moduleNum;
                        if (!moduleCounter.TryGetValue(moduleData.Assembly, out moduleNum))
                        {
                            moduleNum = 0;
                            moduleCounter [moduleData.Assembly] = 0;
                        }
                        else
                        {
                            moduleNum++;
                            moduleCounter [moduleData.Assembly] = moduleNum;
                        }

                        string fileName = $"{moduleData.Assembly.Name.Name}.{moduleNum}.typemap";
                        moduleData.OutputFilePath = Path.Combine(outputDirectory, fileName);
                        if (maxModuleFileNameLength < fileName.Length)
                        {
                            maxModuleFileNameLength = fileName.Length;
                        }
                    }
                }

                string javaName = Java.Interop.Tools.TypeNameMappings.JavaNativeTypeManager.ToJniName(td);
                var    entry    = new TypeMapEntry {
                    JavaName          = javaName,
                    JavaNameLength    = outputEncoding.GetByteCount(javaName),
                    ManagedTypeName   = td.FullName,
                    Token             = td.MetadataToken.ToUInt32(),
                    AssemblyNameIndex = knownAssemblies [assemblyName]
                };

                if (generateNativeAssembly)
                {
                    if (entry.JavaNameLength > maxJavaNameLength)
                    {
                        maxJavaNameLength = entry.JavaNameLength;
                    }
                }

                if (moduleData.TypesScratch.ContainsKey(entry.JavaName))
                {
                    // This is disabled because it costs a lot of time (around 150ms per standard XF Integration app
                    // build) and has no value for the end user. The message is left here because it may be useful to us
                    // in our devloop at some point.
                    //logger ($"Warning: duplicate Java type name '{entry.JavaName}' in assembly '{moduleData.AssemblyName}' (new token: {entry.Token}).");
                    moduleData.DuplicateTypes.Add(entry.Token, entry);
                }
                else
                {
                    moduleData.TypesScratch.Add(entry.JavaName, entry);
                }
            }

            var modules = tempModules.Values.ToArray();

            Array.Sort(modules, new ModuleUUIDArrayComparer());

            var typeMapEntryComparer = new TypeMapEntryArrayComparer();

            foreach (ModuleData module in modules)
            {
                if (module.TypesScratch.Count == 0)
                {
                    module.Types = new TypeMapEntry[0];
                    continue;
                }

                module.Types = module.TypesScratch.Values.ToArray();
                Array.Sort(module.Types, typeMapEntryComparer);
            }

            NativeTypeMappingData data;

            if (!generateNativeAssembly)
            {
                string typeMapIndexPath = Path.Combine(outputDirectory, "typemap.index");
                // Try to approximate the index size:
                //   16 bytes for the header
                //   16 bytes (UUID) + filename length per each entry
                using (var ms = new MemoryStream(16 + (modules.Length * (16 + 128)))) {
                    using (var indexWriter = new BinaryWriter(ms)) {
                        OutputModules(outputDirectory, modules, indexWriter, maxModuleFileNameLength + 1);
                        indexWriter.Flush();
                        MonoAndroidHelper.CopyIfStreamChanged(ms, typeMapIndexPath);
                    }
                }
                GeneratedBinaryTypeMaps.Add(typeMapIndexPath);

                data = new NativeTypeMappingData(logger, new ModuleData[0], 0);
            }
            else
            {
                data = new NativeTypeMappingData(logger, modules, maxJavaNameLength + 1);
            }

            NativeAssemblerTargetProvider asmTargetProvider;
            bool sharedBitsWritten = false;
            bool sharedIncludeUsesAbiPrefix;

            foreach (string abi in supportedAbis)
            {
                sharedIncludeUsesAbiPrefix = false;
                switch (abi.Trim())
                {
                case "armeabi-v7a":
                    asmTargetProvider          = new ARMNativeAssemblerTargetProvider(is64Bit: false);
                    sharedIncludeUsesAbiPrefix = true;                             // ARMv7a is "special", it uses different directive prefix
                    // than the others and the "shared" code won't build for it
                    break;

                case "arm64-v8a":
                    asmTargetProvider = new ARMNativeAssemblerTargetProvider(is64Bit: true);
                    break;

                case "x86":
                    asmTargetProvider = new X86NativeAssemblerTargetProvider(is64Bit: false);
                    break;

                case "x86_64":
                    asmTargetProvider = new X86NativeAssemblerTargetProvider(is64Bit: true);
                    break;

                default:
                    throw new InvalidOperationException($"Unknown ABI {abi}");
                }

                var generator = new TypeMappingNativeAssemblyGenerator(asmTargetProvider, data, Path.Combine(outputDirectory, "typemaps"), sharedBitsWritten, sharedIncludeUsesAbiPrefix);

                using (var sw = MemoryStreamPool.Shared.CreateStreamWriter(outputEncoding)) {
                    generator.Write(sw);
                    sw.Flush();
                    MonoAndroidHelper.CopyIfStreamChanged(sw.BaseStream, generator.MainSourceFile);
                    if (!sharedIncludeUsesAbiPrefix)
                    {
                        sharedBitsWritten = true;
                    }
                }
            }
            return(true);
        }