Beispiel #1
0
        private void AddNativeLibraries(ArchiveFileList files, string [] supportedAbis)
        {
            var frameworkLibs = FrameworkNativeLibraries.Select(v => new LibInfo {
                Path            = v.ItemSpec,
                Link            = v.GetMetadata("Link"),
                Abi             = GetNativeLibraryAbi(v),
                ArchiveFileName = GetArchiveFileName(v)
            });

            AddNativeLibraries(files, supportedAbis, frameworkLibs);

            var libs = NativeLibraries.Concat(BundleNativeLibraries ?? Enumerable.Empty <ITaskItem> ())
                       .Where(v => IncludeNativeLibrary(v))
                       .Select(v => new LibInfo {
                Path            = v.ItemSpec,
                Link            = v.GetMetadata("Link"),
                Abi             = GetNativeLibraryAbi(v),
                ArchiveFileName = GetArchiveFileName(v)
            }
                               );

            AddNativeLibraries(files, supportedAbis, libs);

            if (String.IsNullOrWhiteSpace(CheckedBuild))
            {
                return;
            }

            string mode = CheckedBuild;
            string sanitizerName;

            if (String.Compare("asan", mode, StringComparison.Ordinal) == 0)
            {
                sanitizerName = "asan";
            }
            else if (String.Compare("ubsan", mode, StringComparison.Ordinal) == 0)
            {
                sanitizerName = "ubsan_standalone";
            }
            else
            {
                LogSanitizerWarning($"Unknown checked build mode '{CheckedBuild}'");
                return;
            }

            if (!IncludeWrapSh)
            {
                LogSanitizerError("Checked builds require the wrapper script to be packaged. Please set the `$(AndroidIncludeWrapSh)` MSBuild property to `true` in your project.");
                return;
            }

            if (!libs.Any(info => IsWrapperScript(info.Path, info.Link)))
            {
                LogSanitizerError($"Checked builds require the wrapper script to be packaged. Please add `wrap.sh` appropriate for the {CheckedBuild} checker to your project.");
                return;
            }

            NdkUtil.Init(AndroidNdkDirectory);
            string clangDir = NdkUtil.GetClangDeviceLibraryPath(AndroidNdkDirectory);

            if (String.IsNullOrEmpty(clangDir))
            {
                LogSanitizerError($"Unable to find the clang compiler directory. Is NDK installed?");
                return;
            }

            foreach (string abi in supportedAbis)
            {
                string clangAbi = MonoAndroidHelper.MapAndroidAbiToClang(abi);
                if (String.IsNullOrEmpty(clangAbi))
                {
                    LogSanitizerError($"Unable to map Android ABI {abi} to clang ABI");
                    return;
                }

                string sanitizerLib     = $"libclang_rt.{sanitizerName}-{clangAbi}-android.so";
                string sanitizerLibPath = Path.Combine(clangDir, sanitizerLib);
                if (!File.Exists(sanitizerLibPath))
                {
                    LogSanitizerError($"Unable to find sanitizer runtime for the {CheckedBuild} checker at {sanitizerLibPath}");
                    return;
                }

                AddNativeLibrary(files, sanitizerLibPath, abi, sanitizerLib);
            }
        }
        bool DoExecute()
        {
            var    results    = new List <ITaskItem> ();
            string bundlepath = Path.Combine(TempOutputPath, "bundles");

            if (!Directory.Exists(bundlepath))
            {
                Directory.CreateDirectory(bundlepath);
            }
            else
            {
                Directory.Delete(bundlepath, true);
            }
            foreach (var abi in SupportedAbis)
            {
                AndroidTargetArch arch = AndroidTargetArch.Other;
                switch (abi)
                {
                case "arm64":
                case "arm64-v8a":
                case "aarch64":
                    arch = AndroidTargetArch.Arm64;
                    break;

                case "armeabi-v7a":
                    arch = AndroidTargetArch.Arm;
                    break;

                case "x86":
                    arch = AndroidTargetArch.X86;
                    break;

                case "x86_64":
                    arch = AndroidTargetArch.X86_64;
                    break;

                case "mips":
                    arch = AndroidTargetArch.Mips;
                    break;
                }

                if (!NdkUtil.ValidateNdkPlatform(Log, AndroidNdkDirectory, arch, enableLLVM: false))
                {
                    return(false);
                }

                int level   = NdkUtil.GetMinimumApiLevelFor(arch, AndroidNdkDirectory);
                var outpath = Path.Combine(bundlepath, abi);
                if (!Directory.Exists(outpath))
                {
                    Directory.CreateDirectory(outpath);
                }

                var clb = new CommandLineBuilder();
                clb.AppendSwitch("--dos2unix=false");
                clb.AppendSwitch("--nomain");
                clb.AppendSwitch("--i18n none");
                clb.AppendSwitch("--bundled-header");
                clb.AppendSwitch("--mono-api-struct-path");
                clb.AppendFileNameIfNotNull(BundleApiPath);
                clb.AppendSwitch("--style");
                clb.AppendSwitch("linux");
                clb.AppendSwitch("-c");
                clb.AppendSwitch("-o");
                clb.AppendFileNameIfNotNull(Path.Combine(outpath, "temp.c"));
                clb.AppendSwitch("-oo");
                clb.AppendFileNameIfNotNull(Path.Combine(outpath, "assemblies.o"));
                if (AutoDeps)
                {
                    clb.AppendSwitch("--autodeps");
                }
                if (KeepTemp)
                {
                    clb.AppendSwitch("--keeptemp");
                }
                clb.AppendSwitch("-z");                  // Compress
                clb.AppendFileNamesIfNotNull(Assemblies, " ");
                var psi = new ProcessStartInfo()
                {
                    FileName               = MkbundlePath,
                    Arguments              = clb.ToString(),
                    UseShellExecute        = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError  = true,
                    CreateNoWindow         = true,
                    WindowStyle            = ProcessWindowStyle.Hidden,
                };
                string windowsCompilerSwitches = NdkUtil.GetCompilerTargetParameters(AndroidNdkDirectory, arch, level);
                var    compilerNoQuotes        = NdkUtil.GetNdkTool(AndroidNdkDirectory, arch, "gcc", level);
                var    compiler = $"\"{compilerNoQuotes}\" {windowsCompilerSwitches}".Trim();
                var    gas      = '"' + NdkUtil.GetNdkTool(AndroidNdkDirectory, arch, "as", level) + '"';
                psi.EnvironmentVariables ["CC"] = compiler;
                psi.EnvironmentVariables ["AS"] = gas;
                Log.LogDebugMessage("CC=" + compiler);
                Log.LogDebugMessage("AS=" + gas);
                //psi.EnvironmentVariables ["PKG_CONFIG_PATH"] = Path.Combine (Path.GetDirectoryName (MonoDroidSdk.MandroidTool), "lib", abi);
                Log.LogDebugMessage("[mkbundle] " + psi.FileName + " " + clb);
                var proc = new Process();
                proc.OutputDataReceived += OnMkbundleOutputData;
                proc.ErrorDataReceived  += OnMkbundleErrorData;
                proc.StartInfo           = psi;
                proc.Start();
                proc.BeginOutputReadLine();
                proc.BeginErrorReadLine();
                proc.WaitForExit();
                if (proc.ExitCode != 0)
                {
                    Log.LogCodedError("XA5102", "Conversion from assembly to native code failed. Exit code {0}", proc.ExitCode);
                    return(false);
                }

                // then compile temp.c into temp.o and ...

                clb = new CommandLineBuilder();

                // See NdkUtils.GetNdkTool for reasons why
                if (!String.IsNullOrEmpty(windowsCompilerSwitches))
                {
                    clb.AppendTextUnquoted(windowsCompilerSwitches);
                }

                clb.AppendSwitch("-c");

                // This is necessary only when unified headers are in use but it won't hurt to have it
                // defined even if we don't use them
                clb.AppendSwitch($"-D__ANDROID_API__={level}");

                // This is necessary because of the injected code, which is reused between libmonodroid
                // and the bundle
                clb.AppendSwitch("-DANDROID");

                clb.AppendSwitch("-o");
                clb.AppendFileNameIfNotNull(Path.Combine(outpath, "temp.o"));
                if (!string.IsNullOrWhiteSpace(IncludePath))
                {
                    clb.AppendSwitch("-I");
                    clb.AppendFileNameIfNotNull(IncludePath);
                }

                string asmIncludePath = NdkUtil.GetNdkAsmIncludePath(AndroidNdkDirectory, arch, level);
                if (!String.IsNullOrEmpty(asmIncludePath))
                {
                    clb.AppendSwitch("-I");
                    clb.AppendFileNameIfNotNull(asmIncludePath);
                }

                clb.AppendSwitch("-I");
                clb.AppendFileNameIfNotNull(NdkUtil.GetNdkPlatformIncludePath(AndroidNdkDirectory, arch, level));
                clb.AppendFileNameIfNotNull(Path.Combine(outpath, "temp.c"));
                Log.LogDebugMessage("[CC] " + compiler + " " + clb);
                if (MonoAndroidHelper.RunProcess(compilerNoQuotes, clb.ToString(), OnCcOutputData, OnCcErrorData) != 0)
                {
                    Log.LogCodedError("XA5103", "NDK C compiler resulted in an error. Exit code {0}", proc.ExitCode);
                    return(false);
                }

                // ... link temp.o and assemblies.o into app.so

                clb = new CommandLineBuilder();
                clb.AppendSwitch("--shared");
                clb.AppendFileNameIfNotNull(Path.Combine(outpath, "temp.o"));
                clb.AppendFileNameIfNotNull(Path.Combine(outpath, "assemblies.o"));

                // API23+ requires that the shared library has its soname set or it won't load
                clb.AppendSwitch("-soname");
                clb.AppendSwitch(BundleSharedLibraryName);
                clb.AppendSwitch("-o");
                clb.AppendFileNameIfNotNull(Path.Combine(outpath, BundleSharedLibraryName));
                clb.AppendSwitch("-L");
                clb.AppendFileNameIfNotNull(NdkUtil.GetNdkPlatformLibPath(AndroidNdkDirectory, arch, level));
                clb.AppendSwitch("-lc");
                clb.AppendSwitch("-lm");
                clb.AppendSwitch("-ldl");
                clb.AppendSwitch("-llog");
                clb.AppendSwitch("-lz");                  // Compress
                string ld = NdkUtil.GetNdkTool(AndroidNdkDirectory, arch, "ld", level);
                Log.LogMessage(MessageImportance.Normal, "[LD] " + ld + " " + clb);
                if (MonoAndroidHelper.RunProcess(ld, clb.ToString(), OnLdOutputData, OnLdErrorData) != 0)
                {
                    Log.LogCodedError("XA5201", "NDK Linker resulted in an error. Exit code {0}", proc.ExitCode);
                    return(false);
                }
                results.Add(new TaskItem(Path.Combine(outpath, "libmonodroid_bundle_app.so")));
            }
            OutputNativeLibraries = results.ToArray();
            return(true);
        }
Beispiel #3
0
        public override bool Execute()
        {
            Log.LogDebugMessage("JarToXml Task");
            Log.LogDebugMessage("  JavaOptions: {0}", JavaOptions);
            Log.LogDebugMessage("  JavaMaximumHeapSize: {0}", JavaMaximumHeapSize);
            Log.LogDebugMessage("  AndroidSdkDirectory: {0}", AndroidSdkDirectory);
            Log.LogDebugMessage("  AndroidApiLevel: {0}", AndroidApiLevel);
            Log.LogDebugMessage("  MonoAndroidToolsDirectory: {0}", MonoAndroidToolsDirectory);
            Log.LogDebugMessage("  JavaSdkDirectory: {0}", JavaSdkDirectory);
            Log.LogDebugMessage("  OutputFile: {0}", OutputFile);
            Log.LogDebugMessage("  DroidDocPaths: {0}", DroidDocPaths);
            Log.LogDebugMessage("  JavaDocPaths: {0}", JavaDocPaths);
            Log.LogDebugMessage("  Java7DocPaths: {0}", Java7DocPaths);
            Log.LogDebugMessage("  Java8DocPaths: {0}", Java8DocPaths);
            Log.LogDebugTaskItems("  JavaDocs: {0}", JavaDocs);
            Log.LogDebugMessage("  AndroidApiLevel: {0}", AndroidApiLevel);
            Log.LogDebugTaskItems("  LibraryProjectJars:", LibraryProjectJars);
            Log.LogDebugTaskItems("  SourceJars:", SourceJars);
            Log.LogDebugTaskItems("  ReferenceJars:", ReferenceJars);

            if (SourceJars == null || SourceJars.Count() == 0)
            {
                Log.LogError("At least one Java library is required for binding, this must be either 'EmbeddedJar', 'InputJar' (for jar), 'LibraryProjectZip' (for aar or zip) or 'LibraryProjectProperties' (project.properties) build action.");
                return(false);
            }

            // Ensure that the user has the platform they are targeting installed
            var jarpath = Path.Combine(AndroidSdkDirectory, "platforms", "android-" + MonoAndroidHelper.GetPlatformApiLevelName(AndroidApiLevel), "android.jar");

            if (!File.Exists(jarpath))
            {
                Log.LogError("Could not find android.jar for API Level {0}.  This means the Android SDK platform for API Level {0} is not installed.  Either install it in the Android SDK Manager, or change your Android Bindings project to target an API version that is installed. ({1} missing.)", AndroidApiLevel, jarpath);
                return(false);
            }

            // Ensure that all requested jars exist
            foreach (var jar in SourceJars)
            {
                if (!File.Exists(jar.ItemSpec))
                {
                    Log.LogError("Specified source jar not found: {0}", jar.ItemSpec);
                }
            }

            if (ReferenceJars != null)
            {
                foreach (var jar in ReferenceJars)
                {
                    if (!File.Exists(jar.ItemSpec))
                    {
                        Log.LogError("Specified reference jar not found: {0}", jar.ItemSpec);
                    }
                }
            }

            if (Log.HasLoggedErrors)
            {
                return(false);
            }

            // Ensure our output directory exists
            Directory.CreateDirectory(Path.GetDirectoryName(OutputFile));

            return(base.Execute());
        }
        IEnumerable <Config> GetLinkerConfigs()
        {
            var abis = new Dictionary <string, InputFiles> (StringComparer.Ordinal);

            ITaskItem[] dsos = ApplicationSharedLibraries;
            foreach (ITaskItem item in dsos)
            {
                string abi = item.GetMetadata("abi");
                abis [abi] = GatherFilesForABI(item.ItemSpec, abi, ObjectFiles);
            }

            foreach (var kvp in abis)
            {
                string            abi    = kvp.Key;
                InputFiles        inputs = kvp.Value;
                AndroidTargetArch arch;

                var linkerArgs = new List <string> {
                    "--unresolved-symbols=ignore-in-shared-libs",
                    "--export-dynamic",
                    "-soname", "libxamarin-app.so",
                    "-z", "relro",
                    "-z", "noexecstack",
                    "--enable-new-dtags",
                    "--eh-frame-hdr",
                    "-shared",
                    "--build-id",
                    "--warn-shared-textrel",
                    "--fatal-warnings",
                    "-o", QuoteFileName(inputs.OutputSharedLibrary),
                };

                string elf_arch;
                switch (abi)
                {
                case "armeabi-v7a":
                    arch = AndroidTargetArch.Arm;
                    linkerArgs.Add("-X");
                    elf_arch = "armelf_linux_eabi";
                    break;

                case "arm64":
                case "arm64-v8a":
                case "aarch64":
                    arch = AndroidTargetArch.Arm64;
                    linkerArgs.Add("--fix-cortex-a53-843419");
                    elf_arch = "aarch64linux";
                    break;

                case "x86":
                    arch     = AndroidTargetArch.X86;
                    elf_arch = "elf_i386";
                    break;

                case "x86_64":
                    arch     = AndroidTargetArch.X86_64;
                    elf_arch = "elf_x86_64";
                    break;

                default:
                    throw new NotSupportedException($"Unsupported Android target architecture ABI: {abi}");
                }

                linkerArgs.Add("-m");
                linkerArgs.Add(elf_arch);

                foreach (string file in inputs.ObjectFiles)
                {
                    linkerArgs.Add(QuoteFileName(file));
                }

                string ld = MonoAndroidHelper.GetExecutablePath(AndroidBinUtilsDirectory, $"{NdkUtil.GetNdkToolchainPrefix (arch, false)}ld");
                yield return(new Config {
                    LinkerPath = Path.Combine(AndroidBinUtilsDirectory, ld),
                    LinkerOptions = String.Join(" ", linkerArgs),
                    OutputSharedLibrary = inputs.OutputSharedLibrary,
                });
            }
        }
        private void AddAssemblies(ZipArchiveEx apk)
        {
            bool debug = _Debug;
            bool use_shared_runtime = String.Equals(UseSharedRuntime, "true", StringComparison.OrdinalIgnoreCase);

            int count = 0;

            foreach (ITaskItem assembly in ResolvedUserAssemblies)
            {
                if (bool.TryParse(assembly.GetMetadata("AndroidSkipAddToPackage"), out bool value) && value)
                {
                    Log.LogDebugMessage($"Skipping {assembly.ItemSpec} due to 'AndroidSkipAddToPackage' == 'true' ");
                    continue;
                }
                if (MonoAndroidHelper.IsReferenceAssembly(assembly.ItemSpec))
                {
                    Log.LogCodedWarning("XA0107", assembly.ItemSpec, 0, "{0} is a Reference Assembly!", assembly.ItemSpec);
                }
                // Add assembly
                AddFileToArchiveIfNewer(apk, assembly.ItemSpec, GetTargetDirectory(assembly.ItemSpec) + "/" + Path.GetFileName(assembly.ItemSpec), compressionMethod: UncompressedMethod);

                // Try to add config if exists
                var config = Path.ChangeExtension(assembly.ItemSpec, "dll.config");
                AddAssemblyConfigEntry(apk, config);

                // Try to add symbols if Debug
                if (debug)
                {
                    var symbols = Path.ChangeExtension(assembly.ItemSpec, "dll.mdb");

                    if (File.Exists(symbols))
                    {
                        AddFileToArchiveIfNewer(apk, symbols, AssembliesPath + Path.GetFileName(symbols), compressionMethod: UncompressedMethod);
                    }

                    symbols = Path.ChangeExtension(assembly.ItemSpec, "pdb");

                    if (File.Exists(symbols))
                    {
                        AddFileToArchiveIfNewer(apk, symbols, AssembliesPath + Path.GetFileName(symbols), compressionMethod: UncompressedMethod);
                    }
                }
                count++;
                if (count == ZipArchiveEx.ZipFlushLimit)
                {
                    apk.Flush();
                    count = 0;
                }
            }

            if (use_shared_runtime)
            {
                return;
            }

            count = 0;
            // Add framework assemblies
            foreach (ITaskItem assembly in ResolvedFrameworkAssemblies)
            {
                if (bool.TryParse(assembly.GetMetadata("AndroidSkipAddToPackage"), out bool value) && value)
                {
                    Log.LogDebugMessage($"Skipping {assembly.ItemSpec} due to 'AndroidSkipAddToPackage' == 'true' ");
                    continue;
                }
                if (MonoAndroidHelper.IsReferenceAssembly(assembly.ItemSpec))
                {
                    Log.LogCodedWarning("XA0107", assembly.ItemSpec, 0, "{0} is a Reference Assembly!", assembly.ItemSpec);
                }
                AddFileToArchiveIfNewer(apk, assembly.ItemSpec, AssembliesPath + Path.GetFileName(assembly.ItemSpec), compressionMethod: UncompressedMethod);
                var config = Path.ChangeExtension(assembly.ItemSpec, "dll.config");
                AddAssemblyConfigEntry(apk, config);
                // Try to add symbols if Debug
                if (debug)
                {
                    var symbols = Path.ChangeExtension(assembly.ItemSpec, "dll.mdb");

                    if (File.Exists(symbols))
                    {
                        AddFileToArchiveIfNewer(apk, symbols, AssembliesPath + Path.GetFileName(symbols), compressionMethod: UncompressedMethod);
                    }

                    symbols = Path.ChangeExtension(assembly.ItemSpec, "pdb");

                    if (File.Exists(symbols))
                    {
                        AddFileToArchiveIfNewer(apk, symbols, AssembliesPath + Path.GetFileName(symbols), compressionMethod: UncompressedMethod);
                    }
                }
                count++;
                if (count == ZipArchiveEx.ZipFlushLimit)
                {
                    apk.Flush();
                    count = 0;
                }
            }
        }
Beispiel #6
0
        // Extracts library project contents under e.g. obj/Debug/[__library_projects__/*.jar | res/*/*]
        void Extract(
            ICollection <string> jars,
            ICollection <string> resolvedResourceDirectories,
            ICollection <string> resolvedAssetDirectories,
            ICollection <string> resolvedEnvironments)
        {
            var outdir = new DirectoryInfo(OutputImportDirectory);

            if (!outdir.Exists)
            {
                outdir.Create();
            }

            var res = new DirectoryAssemblyResolver(Log.LogWarning, loadDebugSymbols: false);

            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 => GetTargetAssembly(a))
                     .Where(a => a != null)
                     .Distinct())
            {
                foreach (var imp in new string [] { imports_dir, "library_project_imports" }.Distinct())
                {
                    string assemblyIdentName = Path.GetFileNameWithoutExtension(assemblyPath);
                    if (UseShortFileNames)
                    {
                        assemblyIdentName = Xamarin.Android.Tasks.MonoAndroidHelper.GetLibraryImportDirectoryNameForAssembly(assemblyIdentName);
                    }
                    string outDirForDll = Path.Combine(OutputImportDirectory, assemblyIdentName);
                    string importsDir   = Path.Combine(outDirForDll, imp);
#if SEPARATE_CRUNCH
                    // FIXME: review these binResDir thing and enable this. Eclipse does this.
                    // Enabling these blindly causes build failure on ActionBarSherlock.
                    //string binResDir = Path.Combine (importsDir, "bin", "res");
                    //string binAssemblyDir = Path.Combine (importsDir, "bin", "assets");
#endif
                    string resDir      = Path.Combine(importsDir, "res");
                    string assemblyDir = Path.Combine(importsDir, "assets");

                    // Skip already-extracted resources.
                    var stamp = new FileInfo(Path.Combine(outdir.FullName, assemblyIdentName + ".stamp"));
                    if (stamp.Exists && stamp.LastWriteTime > new FileInfo(assemblyPath).LastWriteTime)
                    {
                        Log.LogDebugMessage("Skipped resource lookup for {0}: extracted files are up to date", assemblyPath);
#if SEPARATE_CRUNCH
                        // FIXME: review these binResDir/binAssemblyDir thing and enable this. Eclipse does this.
                        // Enabling these blindly causes build failure on ActionBarSherlock.
                        if (Directory.Exists(binResDir))
                        {
                            resolvedResourceDirectories.Add(binResDir);
                        }
                        if (Directory.Exists(binAssemblyDir))
                        {
                            resolvedAssetDirectories.Add(binAssemblyDir);
                        }
#endif
                        if (Directory.Exists(resDir))
                        {
                            resolvedResourceDirectories.Add(resDir);
                        }
                        if (Directory.Exists(assemblyDir))
                        {
                            resolvedAssetDirectories.Add(assemblyDir);
                        }
                        continue;
                    }

                    if (Directory.Exists(outDirForDll))
                    {
                        Directory.Delete(outDirForDll, true);
                    }

                    Directory.CreateDirectory(importsDir);

                    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> ())
                        {
                            if (!Directory.Exists(outDirForDll))
                            {
                                Directory.CreateDirectory(outDirForDll);
                            }
                            var finfo = new FileInfo(Path.Combine(outDirForDll, envtxt.Name));
                            using (var fs = finfo.Create()) {
                                var data = envtxt.GetResourceData();
                                fs.Write(data, 0, data.Length);
                            }
                            resolvedEnvironments.Add(finfo.FullName);
                        }

                        // 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)
                        {
                            var data = resjar.GetResourceData();
                            using (var outfs = File.Create(Path.Combine(importsDir, resjar.Name)))
                                outfs.Write(data, 0, data.Length);
                        }

                        // embedded AndroidResourceLibrary archive
                        var reszip = mod.Resources.FirstOrDefault(r => r.Name == "__AndroidLibraryProjects__.zip") as EmbeddedResource;
                        if (reszip != null)
                        {
                            if (!Directory.Exists(outDirForDll))
                            {
                                Directory.CreateDirectory(outDirForDll);
                            }
                            var finfo = new FileInfo(Path.Combine(outDirForDll, reszip.Name));
                            using (var fs = finfo.Create()) {
                                var data = reszip.GetResourceData();
                                fs.Write(data, 0, data.Length);
                            }

                            // temporarily extracted directory will look like:
                            //    __library_projects__/[dllname]/[library_project_imports | jlibs]/bin
                            using (var zip = MonoAndroidHelper.ReadZipFile(finfo.FullName))
                                Files.ExtractAll(zip, outDirForDll);

                            // 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 SEPARATE_CRUNCH
                            // FIXME: review these binResDir/binAssemblyDir thing and enable this. Eclipse does this.
                            // Enabling these blindly causes build failure on ActionBarSherlock.
                            if (Directory.Exists(binResDir))
                            {
                                resolvedResourceDirectories.Add(binResDir);
                            }
                            if (Directory.Exists(binAssemblyDir))
                            {
                                resolvedAssetDirectories.Add(binAssemblyDir);
                            }
#endif
                            if (Directory.Exists(resDir))
                            {
                                resolvedResourceDirectories.Add(resDir);
                            }
                            if (Directory.Exists(assemblyDir))
                            {
                                resolvedAssetDirectories.Add(assemblyDir);
                            }

                            finfo.Delete();
                        }
                    }

                    stamp.Create().Close();
                }
            }

            foreach (var f in outdir.GetFiles("*.jar")
                     .Select(fi => fi.FullName))
            {
                jars.Add(f);
            }
        }
Beispiel #7
0
        bool DoExecute()
        {
            var    abis       = SupportedAbis.Split(new char[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
            var    results    = new List <ITaskItem> ();
            string bundlepath = Path.Combine(TempOutputPath, "bundles");

            if (!Directory.Exists(bundlepath))
            {
                Directory.CreateDirectory(bundlepath);
            }
            else
            {
                Directory.Delete(bundlepath, true);
            }
            foreach (var abi in abis)
            {
                AndroidTargetArch arch = AndroidTargetArch.Other;
                switch (abi)
                {
                case "arm64":
                case "arm64-v8a":
                case "aarch64":
                    arch = AndroidTargetArch.Arm64;
                    break;

                case "armeabi":
                case "armeabi-v7a":
                    arch = AndroidTargetArch.Arm;
                    break;

                case "x86":
                    arch = AndroidTargetArch.X86;
                    break;

                case "x86_64":
                    arch = AndroidTargetArch.X86_64;
                    break;

                case "mips":
                    arch = AndroidTargetArch.Mips;
                    break;
                }

                if (!NdkUtil.ValidateNdkPlatform(Log, AndroidNdkDirectory, arch, enableLLVM: false))
                {
                    return(false);
                }

                int level   = NdkUtil.GetMinimumApiLevelFor(arch, AndroidNdkDirectory);
                var outpath = Path.Combine(bundlepath, abi);
                if (!Directory.Exists(outpath))
                {
                    Directory.CreateDirectory(outpath);
                }

                var clb = new CommandLineBuilder();
                clb.AppendSwitch("--dos2unix=false");
                clb.AppendSwitch("--nomain");
                clb.AppendSwitch("--i18n none");
                clb.AppendSwitch("--style");
                clb.AppendSwitch("linux");
                clb.AppendSwitch("-c");
                clb.AppendSwitch("-o");
                clb.AppendFileNameIfNotNull(Path.Combine(outpath, "temp.c"));
                clb.AppendSwitch("-oo");
                clb.AppendFileNameIfNotNull(Path.Combine(outpath, "assemblies.o"));
                if (AutoDeps)
                {
                    clb.AppendSwitch("--autodeps");
                }
                if (KeepTemp)
                {
                    clb.AppendSwitch("--keeptemp");
                }
                clb.AppendSwitch("-z");                  // Compress
                clb.AppendFileNamesIfNotNull(Assemblies, " ");
                var psi = new ProcessStartInfo()
                {
                    FileName               = MkbundlePath,
                    Arguments              = clb.ToString(),
                    UseShellExecute        = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError  = true,
                    CreateNoWindow         = true,
                    WindowStyle            = ProcessWindowStyle.Hidden,
                };
                var gccNoQuotes = NdkUtil.GetNdkTool(AndroidNdkDirectory, arch, "gcc");
                var gcc         = '"' + gccNoQuotes + '"';
                var gas         = '"' + NdkUtil.GetNdkTool(AndroidNdkDirectory, arch, "as") + '"';
                psi.EnvironmentVariables ["CC"] = gcc;
                psi.EnvironmentVariables ["AS"] = gas;
                Log.LogDebugMessage("CC=" + gcc);
                Log.LogDebugMessage("AS=" + gas);
                //psi.EnvironmentVariables ["PKG_CONFIG_PATH"] = Path.Combine (Path.GetDirectoryName (MonoDroidSdk.MandroidTool), "lib", abi);
                Log.LogDebugMessage("[mkbundle] " + psi.FileName + " " + clb);
                var proc = new Process();
                proc.OutputDataReceived += OnMkbundleOutputData;
                proc.ErrorDataReceived  += OnMkbundleErrorData;
                proc.StartInfo           = psi;
                proc.Start();
                proc.BeginOutputReadLine();
                proc.BeginErrorReadLine();
                proc.WaitForExit();
                if (proc.ExitCode != 0)
                {
                    Log.LogCodedError("XA5102", "Conversion from assembly to native code failed. Exit code {0}", proc.ExitCode);
                    return(false);
                }

                // make some changes in the mkbundle output so that it does not require libmonodroid.so
                var mkbundleOutput = File.ReadAllText(Path.Combine(outpath, "temp.c"));
                mkbundleOutput = mkbundleOutput.Replace("void mono_mkbundle_init ()", "void mono_mkbundle_init (void (register_bundled_assemblies_func)(const MonoBundledAssembly **), void (register_config_for_assembly_func)(const char *, const char *))")
                                 .Replace("mono_register_config_for_assembly (\"", "register_config_for_assembly_func (\"")
                                 .Replace("install_dll_config_files (void)", "install_dll_config_files (void (register_config_for_assembly_func)(const char *, const char *))")
                                 .Replace("install_dll_config_files ()", "install_dll_config_files (register_config_for_assembly_func)")
                                 .Replace("mono_register_bundled_assemblies(", "register_bundled_assemblies_func(");
                File.WriteAllText(Path.Combine(outpath, "temp.c"), mkbundleOutput);

                // then compile temp.c into temp.o and ...

                clb = new CommandLineBuilder();
                clb.AppendSwitch("-c");
                clb.AppendSwitch("-o");
                clb.AppendFileNameIfNotNull(Path.Combine(outpath, "temp.o"));
                if (!string.IsNullOrWhiteSpace(IncludePath))
                {
                    clb.AppendSwitch("-I");
                    clb.AppendFileNameIfNotNull(IncludePath);
                }
                clb.AppendSwitch("-I");
                clb.AppendFileNameIfNotNull(NdkUtil.GetNdkPlatformIncludePath(AndroidNdkDirectory, arch, level));
                clb.AppendFileNameIfNotNull(Path.Combine(outpath, "temp.c"));
                Log.LogDebugMessage("[CC] " + gcc + " " + clb);
                if (MonoAndroidHelper.RunProcess(gccNoQuotes, clb.ToString(), OnCcOutputData, OnCcErrorData) != 0)
                {
                    Log.LogCodedError("XA5103", "NDK C compiler resulted in an error. Exit code {0}", proc.ExitCode);
                    return(false);
                }

                // ... link temp.o and assemblies.o into app.so

                clb = new CommandLineBuilder();
                clb.AppendSwitch("--shared");
                clb.AppendFileNameIfNotNull(Path.Combine(outpath, "temp.o"));
                clb.AppendFileNameIfNotNull(Path.Combine(outpath, "assemblies.o"));
                clb.AppendSwitch("-o");
                clb.AppendFileNameIfNotNull(Path.Combine(outpath, "libmonodroid_bundle_app.so"));
                clb.AppendSwitch("-L");
                clb.AppendFileNameIfNotNull(NdkUtil.GetNdkPlatformLibPath(AndroidNdkDirectory, arch, level));
                clb.AppendSwitch("-lc");
                clb.AppendSwitch("-lm");
                clb.AppendSwitch("-ldl");
                clb.AppendSwitch("-llog");
                clb.AppendSwitch("-lz");                  // Compress
                string ld = NdkUtil.GetNdkTool(AndroidNdkDirectory, arch, "ld");
                Log.LogMessage(MessageImportance.Normal, "[LD] " + ld + " " + clb);
                if (MonoAndroidHelper.RunProcess(ld, clb.ToString(), OnLdOutputData, OnLdErrorData) != 0)
                {
                    Log.LogCodedError("XA5201", "NDK Linker resulted in an error. Exit code {0}", proc.ExitCode);
                    return(false);
                }
                results.Add(new TaskItem(Path.Combine(outpath, "libmonodroid_bundle_app.so")));
            }
            OutputNativeLibraries = results.ToArray();
            return(true);
        }
Beispiel #8
0
        protected bool LogAapt2EventsFromOutput(string singleLine, MessageImportance messageImportance, bool apptResult)
        {
            if (string.IsNullOrEmpty(singleLine))
            {
                return(true);
            }

            var match = AndroidRunToolTask.AndroidErrorRegex.Match(singleLine.Trim());

            if (match.Success)
            {
                var file = match.Groups ["file"].Value;
                int line = 0;
                if (!string.IsNullOrEmpty(match.Groups ["line"]?.Value))
                {
                    line = int.Parse(match.Groups ["line"].Value.Trim()) + 1;
                }
                var level   = match.Groups ["level"].Value.ToLowerInvariant();
                var message = match.Groups ["message"].Value;

                // Handle the following which is NOT an error :(
                // W/ResourceType(23681): For resource 0x0101053d, entry index(1341) is beyond type entryCount(733)
                // W/ResourceType( 3681): For resource 0x0101053d, entry index(1341) is beyond type entryCount(733)
                if (file.StartsWith("W/", StringComparison.OrdinalIgnoreCase))
                {
                    LogCodedWarning(GetErrorCode(singleLine), singleLine);
                    return(true);
                }
                if (message.StartsWith("unknown option", StringComparison.OrdinalIgnoreCase))
                {
                    // we need to filter out the remailing help lines somehow.
                    LogCodedError("APT0001", $"{message}. This is the result of using `aapt` command line arguments with `aapt2`. The arguments are not compatible.");
                    return(false);
                }
                if (message.Contains("in APK") && message.Contains("is compressed."))
                {
                    LogMessage(singleLine, messageImportance);
                    return(true);
                }
                if (message.Contains("fakeLogOpen"))
                {
                    LogMessage(singleLine, messageImportance);
                    return(true);
                }
                if (message.Contains("note:"))
                {
                    LogMessage(singleLine, messageImportance);
                    return(true);
                }
                if (message.Contains("warn:"))
                {
                    LogCodedWarning(GetErrorCode(singleLine), singleLine);
                    return(true);
                }
                if (level.Contains("note"))
                {
                    LogMessage(message, messageImportance);
                    return(true);
                }
                if (level.Contains("warning") || level.StartsWith($"{ToolName} W", StringComparison.OrdinalIgnoreCase))
                {
                    LogCodedWarning(GetErrorCode(singleLine), singleLine);
                    return(true);
                }

                // Try to map back to the original resource file, so when the user
                // double clicks the error, it won't take them to the obj/Debug copy
                if (ResourceDirectories != null)
                {
                    foreach (var dir in ResourceDirectories)
                    {
                        var resourceDirectory         = dir.ItemSpec;
                        var resourceDirectoryFullPath = ResourceDirectoryFullPath(resourceDirectory);

                        string newfile = MonoAndroidHelper.FixUpAndroidResourcePath(file, resourceDirectory, resourceDirectoryFullPath, resource_name_case_map);
                        if (!string.IsNullOrEmpty(newfile))
                        {
                            file = newfile;
                            break;
                        }
                    }
                }

                // Strip any "Error:" text from aapt's output
                if (message.StartsWith("error: ", StringComparison.InvariantCultureIgnoreCase))
                {
                    message = message.Substring("error: ".Length);
                }

                if (level.Contains("error") || (line != 0 && !string.IsNullOrEmpty(file)))
                {
                    LogCodedError(GetErrorCode(message), message, file, line);
                    return(true);
                }
            }

            if (!apptResult)
            {
                var message = string.Format("{0} \"{1}\".", singleLine.Trim(), singleLine.Substring(singleLine.LastIndexOfAny(new char [] { '\\', '/' }) + 1));
                LogCodedError(GetErrorCode(message), message, ToolName);
            }
            else
            {
                LogCodedWarning(GetErrorCode(singleLine), singleLine);
            }
            return(true);
        }
Beispiel #9
0
 protected void LoadResourceCaseMap()
 {
     resource_name_case_map = MonoAndroidHelper.LoadResourceCaseMap(ResourceNameCaseMap);
 }
        public override bool RunTask()
        {
            if (SourceFiles.Length != DestinationFiles.Length)
            {
                throw new ArgumentException("source and destination count mismatch");
            }

            var readerParameters = new ReaderParameters {
                ReadSymbols = true,
            };
            var writerParameters = new WriterParameters {
                DeterministicMvid = Deterministic,
            };

            using (var resolver = new DirectoryAssemblyResolver(this.CreateTaskLogger(), loadDebugSymbols: true, loadReaderParameters: readerParameters)) {
                // Add SearchDirectories with ResolvedAssemblies
                foreach (var assembly in ResolvedAssemblies)
                {
                    var path = Path.GetFullPath(Path.GetDirectoryName(assembly.ItemSpec));
                    if (!resolver.SearchDirectories.Contains(path))
                    {
                        resolver.SearchDirectories.Add(path);
                    }
                }

                // Set up the FixAbstractMethodsStep
                var step1 = new FixAbstractMethodsStep(resolver, new TypeDefinitionCache(), Log);
                // Set up the AddKeepAlivesStep
                var step2 = new MonoDroid.Tuner.AddKeepAlivesStep(new TypeDefinitionCache());
                for (int i = 0; i < SourceFiles.Length; i++)
                {
                    var source      = SourceFiles [i];
                    var destination = DestinationFiles [i];
                    AssemblyDefinition assemblyDefinition = null;

                    var assemblyName = Path.GetFileNameWithoutExtension(source.ItemSpec);
                    if (!MTProfile.IsSdkAssembly(assemblyName) && !MTProfile.IsProductAssembly(assemblyName))
                    {
                        assemblyDefinition = resolver.GetAssembly(source.ItemSpec);
                        step1.CheckAppDomainUsage(assemblyDefinition, (string msg) => Log.LogMessageFromText(msg, MessageImportance.High));
                    }

                    // Only run the step on "MonoAndroid" assemblies
                    if (MonoAndroidHelper.IsMonoAndroidAssembly(source) && !MonoAndroidHelper.IsSharedRuntimeAssembly(source.ItemSpec))
                    {
                        if (assemblyDefinition == null)
                        {
                            assemblyDefinition = resolver.GetAssembly(source.ItemSpec);
                        }

                        if (step1.FixAbstractMethods(assemblyDefinition) ||
                            (AddKeepAlives && step2.AddKeepAlives(assemblyDefinition)))
                        {
                            Log.LogDebugMessage($"Saving modified assembly: {destination.ItemSpec}");
                            writerParameters.WriteSymbols = assemblyDefinition.MainModule.HasSymbols;
                            assemblyDefinition.Write(destination.ItemSpec, writerParameters);
                            continue;
                        }
                    }

                    if (MonoAndroidHelper.CopyAssemblyAndSymbols(source.ItemSpec, destination.ItemSpec))
                    {
                        Log.LogDebugMessage($"Copied: {destination.ItemSpec}");
                    }
                    else
                    {
                        Log.LogDebugMessage($"Skipped unchanged file: {destination.ItemSpec}");

                        // NOTE: We still need to update the timestamp on this file, or this target would run again
                        File.SetLastWriteTimeUtc(destination.ItemSpec, DateTime.UtcNow);
                    }
                }
            }

            return(!Log.HasLoggedErrors);
        }
Beispiel #11
0
        public override bool RunTask()
        {
            if (InputAssemblies == null)
            {
                return(true);
            }

            var output = new List <ITaskItem> (InputAssemblies.Length);

            foreach (var assemblyItem in InputAssemblies)
            {
                if (!File.Exists(assemblyItem.ItemSpec))
                {
                    Log.LogDebugMessage($"Skipping non-existent dependency '{assemblyItem.ItemSpec}'.");
                    continue;
                }
                using (var pe = new PEReader(File.OpenRead(assemblyItem.ItemSpec))) {
                    var reader = pe.GetMetadataReader();
                    // Check in-memory cache
                    var module = reader.GetModuleDefinition();
                    var key    = (nameof(FilterAssemblies), reader.GetGuid(module.Mvid));
                    var value  = BuildEngine4.GetRegisteredTaskObject(key, Lifetime);
                    if (value is bool isMonoAndroidAssembly)
                    {
                        if (isMonoAndroidAssembly)
                        {
                            Log.LogDebugMessage($"Cached: {assemblyItem.ItemSpec}");
                            output.Add(assemblyItem);
                        }
                        continue;
                    }
                    // Check assembly definition
                    var assemblyDefinition        = reader.GetAssemblyDefinition();
                    var targetFrameworkIdentifier = GetTargetFrameworkIdentifier(assemblyDefinition, reader);
                    if (string.Compare(targetFrameworkIdentifier, TargetFrameworkIdentifier, StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        output.Add(assemblyItem);
                        BuildEngine4.RegisterTaskObject(key, true, Lifetime, AllowEarlyCollection);
                        continue;
                    }
                    // Fallback to looking for a Mono.Android reference
                    if (MonoAndroidHelper.HasMonoAndroidReference(reader))
                    {
                        Log.LogDebugMessage($"Mono.Android reference found: {assemblyItem.ItemSpec}");
                        output.Add(assemblyItem);
                        BuildEngine4.RegisterTaskObject(key, true, Lifetime, AllowEarlyCollection);
                        continue;
                    }
                    // Fallback to looking for *.jar or __Android EmbeddedResource files
                    if (HasEmbeddedResource(reader))
                    {
                        Log.LogDebugMessage($"EmbeddedResource found: {assemblyItem.ItemSpec}");
                        output.Add(assemblyItem);
                        BuildEngine4.RegisterTaskObject(key, true, Lifetime, AllowEarlyCollection);
                        continue;
                    }
                    // Not a MonoAndroid assembly, store false
                    BuildEngine4.RegisterTaskObject(key, false, Lifetime, AllowEarlyCollection);
                }
            }
            OutputAssemblies = output.ToArray();

            return(!Log.HasLoggedErrors);
        }
Beispiel #12
0
        public static string GetNdkTool(string androidNdkPath, AndroidTargetArch arch, string tool, int apiLevel)
        {
            if (!UsingClangNDK)
            {
                return(NdkUtilOld.GetNdkTool(androidNdkPath, arch, tool));
            }

            string toolchainDir = Path.Combine(androidNdkPath, "toolchains", "llvm", "prebuilt", MonoAndroidHelper.AndroidSdk.AndroidNdkHostPlatform);
            string toolName;
            bool   forCompiler = false;

            if (String.Compare(tool, "gcc", StringComparison.Ordinal) == 0 ||
                String.Compare(tool, "clang", StringComparison.Ordinal) == 0)
            {
                forCompiler = true;
                toolName    = "clang";
            }
            else if (String.Compare(tool, "g++", StringComparison.Ordinal) == 0 ||
                     String.Compare(tool, "clang++", StringComparison.Ordinal) == 0)
            {
                forCompiler = true;
                toolName    = "clang++";
            }
            else
            {
                toolName = tool;
            }

            //
            // NDK r19 bug.
            //
            // The llvm toolchain directory contains a selection of shell scripts (both Unix and Windows)
            // which call `clang/clang++` with different `-target` parameters depending on both the target
            // architecture and API level. For instance, the clang/clang++ compilers targetting aarch64 on API level
            // 28 will have the following Unix shell scripts present in the toolchain `bin` directory:
            //
            //   aarch64-linux-android28-clang
            //   aarch64-linux-android28-clang++
            //
            // However, the Windows version of the NDK has a bug where there is only one  Windows
            // counterpart to the above Unix scripts:
            //
            //   aarch64-linux-android28-clang.cmd
            //
            // This script, despite its name suggesting that it calls `clang.exe` in fact calls
            // `clang++.exe` which breaks compilation of some C programs (including the code generated by
            // Mono's mkbundle utility) because `clang++` treats the input as C++. There is no corresponding
            // `aarch64-linux-android28-clang++.cmd` and so invocation of `clang.exe` becomes harder and,
            // most certainly, non-standard as far as cross-platform NDK compatibility is concerned.
            //
            // The code below tries to rectify the situation by special-casing the compiler tool handling to
            // return path to the actual .exe instead of the CMD. Unfortunately, the caller of this code
            // will need to provide the correct parameters for the compilers.
            //
            string toolchainPrefix;

            if (forCompiler)
            {
                if (!OS.IsWindows)
                {
                    toolchainPrefix = $"{GetNdkToolchainPrefix (arch, true)}{apiLevel}";
                }
                else
                {
                    toolchainPrefix = String.Empty;
                }
            }
            else
            {
                toolchainPrefix = GetNdkToolchainPrefix(arch, false);
            }

            string extension = OS.IsWindows ? ".exe" : String.Empty;

            if (forCompiler && OS.IsWindows)
            {
                toolName = $"{toolName}{extension}";
            }
            else
            {
                toolName = $"{toolchainPrefix}-{toolName}{extension}";
            }

            string binDir   = Path.Combine(toolchainDir, "bin");
            string toolExe  = MonoAndroidHelper.GetExecutablePath(binDir, toolName);
            string toolPath = Path.Combine(binDir, toolExe);

            if (File.Exists(toolPath))
            {
                return(toolPath);
            }

            Diagnostic.Error(5101,
                             $"Toolchain utility '{toolName}' for target {arch} was not found. Tried in path: \"{toolchainDir}\"");
            return(null);
        }
        public override bool Execute()
        {
            Log.LogDebugMessage("ResolveSdksTask:");
            Log.LogDebugMessage("  AndroidApiLevel: {0}", AndroidApiLevel);
            Log.LogDebugMessage("  AndroidSdkBuildToolsVersion: {0}", AndroidSdkBuildToolsVersion);
            Log.LogDebugTaskItems("  ReferenceAssemblyPaths: ", ReferenceAssemblyPaths);
            Log.LogDebugMessage("  TargetFrameworkVersion: {0}", TargetFrameworkVersion);
            Log.LogDebugMessage("  UseLatestAndroidPlatformSdk: {0}", UseLatestAndroidPlatformSdk);
            Log.LogDebugMessage("  SequencePointsMode: {0}", SequencePointsMode);
            Log.LogDebugMessage("  MonoAndroidToolsPath: {0}", MonoAndroidToolsPath);
            Log.LogDebugMessage("  MonoAndroidBinPath: {0}", MonoAndroidBinPath);

            MonoAndroidHelper.InitializeAndroidLogger(Log);

            MonoAndroidHelper.RefreshAndroidSdk(AndroidSdkPath, AndroidNdkPath, JavaSdkPath);
            MonoAndroidHelper.RefreshMonoDroidSdk(MonoAndroidToolsPath, MonoAndroidBinPath, ReferenceAssemblyPaths);

            // OS X:    $prefix/lib/mandroid
            // Windows: %ProgramFiles(x86)%\MSBuild\Xamarin\Android
            this.MonoAndroidToolsPath = MonoDroidSdk.RuntimePath;

            // OS X:    $prefix/bin
            // Windows: %ProgramFiles(x86)%\MSBuild\Xamarin\Android
            this.MonoAndroidBinPath = MonoDroidSdk.BinPath;

            if (this.MonoAndroidBinPath == null)
            {
                Log.LogCodedError("XA0020", "Could not find mandroid!");
                return(false);
            }

            string include;

            if (MonoAndroidToolsPath != null &&
                Directory.Exists(include = Path.Combine(MonoAndroidToolsPath, "include")))
            {
                MonoAndroidIncludePath = include;
            }

            this.AndroidNdkPath = AndroidSdk.AndroidNdkPath;
            this.AndroidSdkPath = AndroidSdk.AndroidSdkPath;
            this.JavaSdkPath    = AndroidSdk.JavaSdkPath;

            if (string.IsNullOrEmpty(AndroidSdkPath))
            {
                Log.LogCodedError("XA5205", "The Android SDK Directory could not be found. Please set via /p:AndroidSdkDirectory.");
                return(false);
            }

            string toolsZipAlignPath = Path.Combine(AndroidSdkPath, "tools", ZipAlign);
            bool   findZipAlign      = (string.IsNullOrEmpty(ZipAlignPath) || !Directory.Exists(ZipAlignPath)) && !File.Exists(toolsZipAlignPath);

            foreach (var dir in AndroidSdk.GetBuildToolsPaths(AndroidSdkBuildToolsVersion))
            {
                Log.LogDebugMessage("Trying build-tools path: {0}", dir);
                if (dir == null || !Directory.Exists(dir))
                {
                    continue;
                }

                var toolsPaths = new string[] {
                    Path.Combine(dir),
                    Path.Combine(dir, "bin"),
                };

                string aapt = toolsPaths.FirstOrDefault(x => File.Exists(Path.Combine(x, Aapt)));
                if (string.IsNullOrEmpty(aapt))
                {
                    Log.LogDebugMessage("Could not find `{0}`; tried: {1}", Aapt,
                                        string.Join(";", toolsPaths.Select(x => Path.Combine(x, Aapt))));
                    continue;
                }
                AndroidSdkBuildToolsPath    = Path.GetFullPath(dir);
                AndroidSdkBuildToolsBinPath = Path.GetFullPath(aapt);

                string zipalign = toolsPaths.FirstOrDefault(x => File.Exists(Path.Combine(x, ZipAlign)));
                if (findZipAlign && string.IsNullOrEmpty(zipalign))
                {
                    Log.LogDebugMessage("Could not find `{0}`; tried: {1}", ZipAlign,
                                        string.Join(";", toolsPaths.Select(x => Path.Combine(x, ZipAlign))));
                    continue;
                }
                else
                {
                    break;
                }
            }

            if (string.IsNullOrEmpty(AndroidSdkBuildToolsPath))
            {
                Log.LogCodedError("XA5205",
                                  string.Format(
                                      "Cannot find `{0}`. Please install the Android SDK Build-tools package with the `{1}{2}tools{2}{3}` program.",
                                      Aapt, AndroidSdkPath, Path.DirectorySeparatorChar, Android));
                return(false);
            }

            if (string.IsNullOrEmpty(ZipAlignPath) || !Directory.Exists(ZipAlignPath))
            {
                ZipAlignPath = new[] {
                    Path.Combine(AndroidSdkBuildToolsPath),
                    Path.Combine(AndroidSdkBuildToolsBinPath),
                    Path.Combine(AndroidSdkPath, "tools"),
                }
                .Where(p => File.Exists(Path.Combine(p, ZipAlign)))
                .FirstOrDefault();
            }
            if (string.IsNullOrEmpty(ZipAlignPath))
            {
                Log.LogCodedError("XA5205",
                                  string.Format(
                                      "Cannot find `{0}`. Please install the Android SDK Build-tools package with the `{1}{2}tools{2}{3}` program.",
                                      ZipAlign, AndroidSdkPath, Path.DirectorySeparatorChar, Android));
                return(false);
            }

            if (!ValidateApiLevels())
            {
                return(false);
            }

            string frameworksPath = Path.GetDirectoryName(MonoDroidSdk.FrameworkPath);

            if (!Directory.Exists(Path.Combine(frameworksPath, TargetFrameworkVersion)))
            {
                Log.LogError(
                    subcategory:      string.Empty,
                    errorCode:        "XA0001",
                    helpKeyword:      string.Empty,
                    file:             ProjectFilePath,
                    lineNumber:       0,
                    columnNumber:     0,
                    endLineNumber:    0,
                    endColumnNumber:  0,
                    message:          "Unsupported or invalid $(TargetFrameworkVersion) value of '{0}'. Please update your Project Options.",
                    messageArgs:      new[] {
                    TargetFrameworkVersion,
                }
                    );
                return(false);
            }

            SequencePointsMode mode;

            if (!Aot.TryGetSequencePointsMode(SequencePointsMode ?? "None", out mode))
            {
                Log.LogCodedError("XA0104", "Invalid Sequence Point mode: {0}", SequencePointsMode);
            }
            AndroidSequencePointsMode = mode.ToString();


            AndroidApiLevelName = MonoAndroidHelper.GetPlatformApiLevelName(AndroidApiLevel);

            Log.LogDebugMessage("ResolveSdksTask Outputs:");
            Log.LogDebugMessage("  AndroidApiLevel: {0}", AndroidApiLevel);
            Log.LogDebugMessage("  AndroidApiLevelName: {0}", AndroidApiLevelName);
            Log.LogDebugMessage("  AndroidNdkPath: {0}", AndroidNdkPath);
            Log.LogDebugMessage("  AndroidSdkBuildToolsPath: {0}", AndroidSdkBuildToolsPath);
            Log.LogDebugMessage("  AndroidSdkBuildToolsBinPath: {0}", AndroidSdkBuildToolsBinPath);
            Log.LogDebugMessage("  AndroidSdkPath: {0}", AndroidSdkPath);
            Log.LogDebugMessage("  JavaSdkPath: {0}", JavaSdkPath);
            Log.LogDebugMessage("  MonoAndroidBinPath: {0}", MonoAndroidBinPath);
            Log.LogDebugMessage("  MonoAndroidToolsPath: {0}", MonoAndroidToolsPath);
            Log.LogDebugMessage("  MonoAndroidIncludePath: {0}", MonoAndroidIncludePath);
            Log.LogDebugMessage("  TargetFrameworkVersion: {0}", TargetFrameworkVersion);
            Log.LogDebugMessage("  ZipAlignPath: {0}", ZipAlignPath);
            Log.LogDebugMessage("  SupportedApiLevel: {0}", SupportedApiLevel);
            Log.LogDebugMessage("  AndroidSequencePointMode: {0}", AndroidSequencePointsMode);

            if (!string.IsNullOrEmpty(CacheFile))
            {
                Directory.CreateDirectory(Path.GetDirectoryName(CacheFile));

                var document = new XDocument(
                    new XDeclaration("1.0", "UTF-8", null),
                    new XElement("Sdk",
                                 new XElement("AndroidApiLevel", AndroidApiLevel),
                                 new XElement("AndroidApiLevelName", AndroidApiLevelName),
                                 new XElement("AndroidNdkPath", AndroidNdkPath),
                                 new XElement("AndroidSdkBuildToolsPath", AndroidSdkBuildToolsPath),
                                 new XElement("AndroidSdkBuildToolsBinPath", AndroidSdkBuildToolsBinPath),
                                 new XElement("AndroidSdkPath", AndroidSdkPath),
                                 new XElement("JavaSdkPath", JavaSdkPath),
                                 new XElement("MonoAndroidBinPath", MonoAndroidBinPath),
                                 new XElement("MonoAndroidToolsPath", MonoAndroidToolsPath),
                                 new XElement("ReferenceAssemblyPaths",
                                              (ReferenceAssemblyPaths ?? new string [0])
                                              .Select(e => new XElement("ReferenceAssemblyPath", e))),
                                 new XElement("TargetFrameworkVersion", TargetFrameworkVersion),
                                 new XElement("ZipAlignPath", ZipAlignPath),
                                 new XElement("MonoAndroidIncludePath", MonoAndroidIncludePath),
                                 new XElement("SupportedApiLevel", SupportedApiLevel),
                                 new XElement("AndroidSequencePointsMode", AndroidSequencePointsMode.ToString())
                                 ));
                document.Save(CacheFile);
            }

            //note: this task does not error out if it doesn't find all things. that's the job of the targets
            return(!Log.HasLoggedErrors);
        }
        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("\"", "\\\""));
            }
        }
Beispiel #15
0
        IEnumerable <Config> GetAotConfigs()
        {
            if (!Directory.Exists(AotOutputDirectory))
            {
                Directory.CreateDirectory(AotOutputDirectory);
            }

            var sdkBinDirectory = MonoAndroidHelper.GetOSBinPath();

            foreach (var abi in SupportedAbis)
            {
                string            aotCompiler = "";
                string            outdir      = "";
                string            mtriple     = "";
                AndroidTargetArch arch;

                switch (abi)
                {
                case "armeabi-v7a":
                    aotCompiler = Path.Combine(sdkBinDirectory, "cross-arm");
                    outdir      = Path.Combine(AotOutputDirectory, "armeabi-v7a");
                    mtriple     = "armv7-linux-gnueabi";
                    arch        = AndroidTargetArch.Arm;
                    break;

                case "arm64":
                case "arm64-v8a":
                case "aarch64":
                    aotCompiler = Path.Combine(sdkBinDirectory, "cross-arm64");
                    outdir      = Path.Combine(AotOutputDirectory, "arm64-v8a");
                    mtriple     = "aarch64-linux-android";
                    arch        = AndroidTargetArch.Arm64;
                    break;

                case "x86":
                    aotCompiler = Path.Combine(sdkBinDirectory, "cross-x86");
                    outdir      = Path.Combine(AotOutputDirectory, "x86");
                    mtriple     = "i686-linux-android";
                    arch        = AndroidTargetArch.X86;
                    break;

                case "x86_64":
                    aotCompiler = Path.Combine(sdkBinDirectory, "cross-x86_64");
                    outdir      = Path.Combine(AotOutputDirectory, "x86_64");
                    mtriple     = "x86_64-linux-android";
                    arch        = AndroidTargetArch.X86_64;
                    break;

                // case "mips":
                default:
                    throw new Exception("Unsupported Android target architecture ABI: " + abi);
                }

                if (EnableLLVM && !NdkUtil.ValidateNdkPlatform(LogMessage, LogCodedError, AndroidNdkDirectory, arch, enableLLVM:EnableLLVM))
                {
                    yield return(Config.Invalid);

                    yield break;
                }

                outdir = Path.GetFullPath(outdir);
                if (!Directory.Exists(outdir))
                {
                    Directory.CreateDirectory(outdir);
                }

                // dont use a full path if the outdir is withing the WorkingDirectory.
                if (outdir.StartsWith(WorkingDirectory, StringComparison.InvariantCultureIgnoreCase))
                {
                    outdir = outdir.Replace(WorkingDirectory + Path.DirectorySeparatorChar, string.Empty);
                }

                int    level      = 0;
                string toolPrefix = EnableLLVM
                                        ? NdkUtil.GetNdkToolPrefix(AndroidNdkDirectory, arch, level = GetNdkApiLevel(AndroidNdkDirectory, AndroidApiLevel, arch))
                                        : Path.Combine(AndroidBinUtilsDirectory, $"{NdkUtil.GetArchDirName (arch)}-");
                var toolchainPath = toolPrefix.Substring(0, toolPrefix.LastIndexOf(Path.DirectorySeparatorChar));
                var ldFlags       = string.Empty;
                if (EnableLLVM)
                {
                    if (string.IsNullOrEmpty(AndroidNdkDirectory))
                    {
                        yield return(Config.Invalid);

                        yield break;
                    }

                    string androidLibPath = string.Empty;
                    try {
                        androidLibPath = NdkUtil.GetNdkPlatformLibPath(AndroidNdkDirectory, arch, level);
                    } catch (InvalidOperationException ex) {
                        Diagnostic.Error(5101, ex.Message);
                    }

                    string toolchainLibDir;
                    if (NdkUtil.UsingClangNDK)
                    {
                        toolchainLibDir = GetNdkToolchainLibraryDir(toolchainPath, arch);
                    }
                    else
                    {
                        toolchainLibDir = GetNdkToolchainLibraryDir(toolchainPath);
                    }

                    var libs = new List <string>();
                    if (NdkUtil.UsingClangNDK)
                    {
                        libs.Add($"-L{toolchainLibDir}");
                        libs.Add($"-L{androidLibPath}");

                        if (arch == AndroidTargetArch.Arm)
                        {
                            // Needed for -lunwind to work
                            string compilerLibDir = Path.Combine(toolchainPath, "..", "sysroot", "usr", "lib", NdkUtil.GetArchDirName(arch));
                            libs.Add($"-L{compilerLibDir}");
                        }
                    }

                    libs.Add($"\\\"{Path.Combine (toolchainLibDir, "libgcc.a")}\\\"");
                    libs.Add($"\\\"{Path.Combine (androidLibPath, "libc.so")}\\\"");
                    libs.Add($"\\\"{Path.Combine (androidLibPath, "libm.so")}\\\"");

                    ldFlags = string.Join(";", libs);
                }

                foreach (var assembly in ResolvedAssemblies)
                {
                    string outputFile = Path.Combine(outdir, string.Format("libaot-{0}.so",
                                                                           Path.GetFileName(assembly.ItemSpec)));

                    string seqpointsFile = Path.Combine(outdir, string.Format("{0}.msym",
                                                                              Path.GetFileName(assembly.ItemSpec)));

                    string tempDir = Path.Combine(outdir, Path.GetFileName(assembly.ItemSpec));
                    if (!Directory.Exists(tempDir))
                    {
                        Directory.CreateDirectory(tempDir);
                    }

                    List <string> aotOptions = new List <string> ();

                    if (Profiles != null && Profiles.Length > 0)
                    {
                        aotOptions.Add("profile-only");
                        foreach (var p in Profiles)
                        {
                            var fp = Path.GetFullPath(p.ItemSpec);
                            aotOptions.Add($"profile={fp}");
                        }
                    }
                    if (!string.IsNullOrEmpty(AotAdditionalArguments))
                    {
                        aotOptions.Add(AotAdditionalArguments);
                    }
                    if (sequencePointsMode == SequencePointsMode.Offline)
                    {
                        aotOptions.Add($"msym-dir={outdir}");
                    }
                    if (AotMode != AotMode.Normal)
                    {
                        aotOptions.Add(AotMode.ToString().ToLowerInvariant());
                    }

                    aotOptions.Add($"outfile={outputFile}");
                    aotOptions.Add("asmwriter");
                    aotOptions.Add($"mtriple={mtriple}");
                    aotOptions.Add($"tool-prefix={toolPrefix}");
                    aotOptions.Add($"llvm-path={sdkBinDirectory}");
                    aotOptions.Add($"temp-path={tempDir}");
                    aotOptions.Add($"ld-flags={ldFlags}");

                    // we need to quote the entire --aot arguments here to make sure it is parsed
                    // on windows as one argument. Otherwise it will be split up into multiple
                    // values, which wont work.
                    string aotOptionsStr = (EnableLLVM ? "--llvm " : "") + $"\"--aot={string.Join (",", aotOptions)}\"";

                    if (!string.IsNullOrEmpty(ExtraAotOptions))
                    {
                        aotOptionsStr += (aotOptions.Count > 0 ? " " : "") + ExtraAotOptions;
                    }

                    // Due to a Monodroid MSBuild bug we can end up with paths to assemblies that are not in the intermediate
                    // assembly directory (typically obj/assemblies). This can lead to problems with the Mono loader not being
                    // able to find their dependency laters, since framework assemblies are stored in different directories.
                    // This can happen when linking is disabled (AndroidLinkMode=None). Workaround this problem by resolving
                    // the paths to the right assemblies manually.
                    var resolvedPath             = Path.GetFullPath(assembly.ItemSpec);
                    var intermediateAssemblyPath = Path.Combine(IntermediateAssemblyDir, Path.GetFileName(assembly.ItemSpec));

                    if (LinkMode.ToLowerInvariant() == "none")
                    {
                        if (!resolvedPath.Contains(IntermediateAssemblyDir) && File.Exists(intermediateAssemblyPath))
                        {
                            resolvedPath = intermediateAssemblyPath;
                        }
                    }

                    var assembliesPath = Path.GetFullPath(Path.GetDirectoryName(resolvedPath));
                    var assemblyPath   = Path.GetFullPath(resolvedPath);

                    yield return(new Config(assembliesPath, aotCompiler, aotOptionsStr, assemblyPath, outputFile, Path.Combine(tempDir, "response.txt")));
                }
            }
        }
Beispiel #16
0
        void Execute(DirectoryAssemblyResolver resolver)
        {
            foreach (var dir in ReferenceAssembliesDirectory.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
            {
                resolver.SearchDirectories.Add(dir);
            }

            var assemblies = new Dictionary <string, ITaskItem> ();

            var topAssemblyReferences = new List <AssemblyDefinition> ();
            var logger = new NuGetLogger((s) => {
                LogDebugMessage("{0}", s);
            });

            LockFile lockFile = null;

            if (!string.IsNullOrEmpty(ProjectAssetFile) && File.Exists(ProjectAssetFile))
            {
                lockFile = LockFileUtilities.GetLockFile(ProjectAssetFile, logger);
            }

            try {
                foreach (var assembly in Assemblies)
                {
                    var assembly_path = Path.GetDirectoryName(assembly.ItemSpec);

                    if (!resolver.SearchDirectories.Contains(assembly_path))
                    {
                        resolver.SearchDirectories.Add(assembly_path);
                    }

                    // Add each user assembly and all referenced assemblies (recursive)
                    var assemblyDef = resolver.Load(assembly.ItemSpec);
                    if (assemblyDef == null)
                    {
                        throw new InvalidOperationException("Failed to load assembly " + assembly.ItemSpec);
                    }
                    if (MonoAndroidHelper.IsReferenceAssembly(assemblyDef))
                    {
                        // Resolve "runtime" library
                        var asmFullPath = Path.GetFullPath(assembly.ItemSpec);
                        if (lockFile != null)
                        {
                            assemblyDef = ResolveRuntimeAssemblyForReferenceAssembly(lockFile, resolver, asmFullPath);
                        }
                        if (lockFile == null || assemblyDef == null)
                        {
                            LogCodedWarning("XA0107", asmFullPath, 0, "Ignoring {0} as it is a Reference Assembly", asmFullPath);
                            continue;
                        }
                    }
                    topAssemblyReferences.Add(assemblyDef);
                    var taskItem = new TaskItem(assembly)
                    {
                        ItemSpec = Path.GetFullPath(assemblyDef.MainModule.FileName),
                    };
                    if (string.IsNullOrEmpty(taskItem.GetMetadata("ReferenceAssembly")))
                    {
                        taskItem.SetMetadata("ReferenceAssembly", taskItem.ItemSpec);
                    }
                    assemblies [assemblyDef.Name.Name] = taskItem;
                }
            } catch (Exception ex) {
                LogError("Exception while loading assemblies: {0}", ex);
                return;
            }
            try {
                foreach (var assembly in topAssemblyReferences)
                {
                    AddAssemblyReferences(resolver, assemblies, assembly, null);
                }
            } catch (Exception ex) {
                LogError("Exception while loading assemblies: {0}", ex);
                return;
            }

            // Add I18N assemblies if needed
            AddI18nAssemblies(resolver, assemblies);

            var mainapiLevel = MonoAndroidHelper.SupportedVersions.GetApiLevelFromFrameworkVersion(TargetFrameworkVersion);

            foreach (var item in api_levels.Where(x => mainapiLevel < x.Value))
            {
                var itemOSVersion = MonoAndroidHelper.SupportedVersions.GetFrameworkVersionFromApiLevel(item.Value);
                Log.LogCodedWarning("XA0105", ProjectFile, 0,
                                    "The $(TargetFrameworkVersion) for {0} ({1}) is greater than the $(TargetFrameworkVersion) for your project ({2}). " +
                                    "You need to increase the $(TargetFrameworkVersion) for your project.", Path.GetFileName(item.Key), itemOSVersion, TargetFrameworkVersion);
            }

            var resolvedAssemblies          = new List <ITaskItem> (assemblies.Count);
            var resolvedSymbols             = new List <ITaskItem> (assemblies.Count);
            var resolvedFrameworkAssemblies = new List <ITaskItem> (assemblies.Count);
            var resolvedUserAssemblies      = new List <ITaskItem> (assemblies.Count);

            foreach (var assembly in assemblies.Values)
            {
                var mdb = assembly + ".mdb";
                var pdb = Path.ChangeExtension(assembly.ItemSpec, "pdb");
                if (File.Exists(mdb))
                {
                    resolvedSymbols.Add(new TaskItem(mdb));
                }
                if (File.Exists(pdb) && Files.IsPortablePdb(pdb))
                {
                    resolvedSymbols.Add(new TaskItem(pdb));
                }
                resolvedAssemblies.Add(assembly);
                if (MonoAndroidHelper.IsFrameworkAssembly(assembly.ItemSpec, checkSdkPath: true))
                {
                    resolvedFrameworkAssemblies.Add(assembly);
                }
                else
                {
                    resolvedUserAssemblies.Add(assembly);
                }
            }
            ResolvedAssemblies             = resolvedAssemblies.ToArray();
            ResolvedSymbols                = resolvedSymbols.ToArray();
            ResolvedFrameworkAssemblies    = resolvedFrameworkAssemblies.ToArray();
            ResolvedUserAssemblies         = resolvedUserAssemblies.ToArray();
            ResolvedDoNotPackageAttributes = do_not_package_atts.ToArray();
        }
Beispiel #17
0
        bool Execute(DirectoryAssemblyResolver res)
        {
            // Put every assembly we'll need in the resolver
            foreach (var assembly in ResolvedAssemblies)
            {
                res.Load(Path.GetFullPath(assembly.ItemSpec));
            }

            var resolver = new AssemblyResolver(res.ToResolverCache());

            // Set up for linking
            var options = new LinkerOptions();

            options.MainAssembly     = res.GetAssembly(MainAssembly);
            options.OutputDirectory  = Path.GetFullPath(OutputDirectory);
            options.LinkSdkOnly      = string.Compare(LinkMode, "SdkOnly", true) == 0;
            options.LinkNone         = string.Compare(LinkMode, "None", true) == 0;
            options.Resolver         = resolver;
            options.LinkDescriptions = LinkDescriptions.Select(item => Path.GetFullPath(item.ItemSpec)).ToArray();
            options.I18nAssemblies   = Linker.ParseI18nAssemblies(I18nAssemblies);
            if (!options.LinkSdkOnly)
            {
                options.RetainAssemblies = GetRetainAssemblies(res);
            }
            options.DumpDependencies      = DumpDependencies;
            options.HttpClientHandlerType = HttpClientHandlerType;
            options.TlsProvider           = TlsProvider;

            var skiplist = new List <string> ();

            if (string.Compare(UseSharedRuntime, "true", true) == 0)
            {
                skiplist.AddRange(Profile.SharedRuntimeAssemblies.Where(a => a.EndsWith(".dll")).Select(a => Path.GetFileNameWithoutExtension(a)));
            }
            if (!string.IsNullOrWhiteSpace(LinkOnlyNewerThan) && File.Exists(LinkOnlyNewerThan))
            {
                var newerThan   = File.GetLastWriteTime(LinkOnlyNewerThan);
                var skipOldOnes = ResolvedAssemblies.Where(a => File.GetLastWriteTime(a.ItemSpec) < newerThan);
                foreach (var old in skipOldOnes)
                {
                    Log.LogMessage(MessageImportance.Low, "  Skip linking unchanged file: " + old.ItemSpec);
                }
                skiplist = skipOldOnes.Select(a => Path.GetFileNameWithoutExtension(a.ItemSpec)).Concat(skiplist).ToList();
            }

            // Add LinkSkip options
            if (!string.IsNullOrWhiteSpace(LinkSkip))
            {
                foreach (var assembly in LinkSkip.Split(',', ';'))
                {
                    skiplist.Add(assembly);
                }
            }

            options.SkippedAssemblies = skiplist;

            if (EnableProguard)
            {
                options.ProguardConfiguration = ProguardConfiguration;
            }

            // Link!
            try {
                LinkContext link_context;
                Linker.Process(options, out link_context);

                var copydst = OptionalDestinationDirectory ?? OutputDirectory;

                foreach (var assembly in ResolvedAssemblies)
                {
                    var copysrc  = assembly.ItemSpec;
                    var filename = Path.GetFileName(assembly.ItemSpec);

                    if (options.LinkNone)
                    {
                        if (skiplist.Any(s => Path.GetFileNameWithoutExtension(filename) == s))
                        {
                            // For skipped assemblies, skip if there is existing file in the destination.
                            // We cannot just copy the linker output from *current* run output, because
                            // it always renew the assemblies, in *different* binary values, whereas
                            // the dll in the OptionalDestinationDirectory must retain old and unchanged.
                            if (File.Exists(Path.Combine(copydst, filename)))
                            {
                                continue;
                            }
                            copysrc = assembly.ItemSpec;
                        }
                        else
                        {
                            // Prefer fixup assemblies if exists, otherwise just copy the original.
                            copysrc = Path.Combine(OutputDirectory, filename);
                            copysrc = File.Exists(copysrc) ? copysrc : assembly.ItemSpec;
                        }
                    }
                    else if (!MonoAndroidHelper.IsForceRetainedAssembly(filename))
                    {
                        continue;
                    }

                    MonoAndroidHelper.CopyIfChanged(copysrc, Path.Combine(copydst, filename));
                    try {
                        MonoAndroidHelper.CopyIfChanged(assembly.ItemSpec + ".mdb", Path.Combine(copydst, filename + ".mdb"));
                    } catch (Exception) {                     // skip it, mdb sometimes fails to read and it's optional
                    }
                }
            } catch (ResolutionException ex) {
                Diagnostic.Error(2006, ex, "Could not resolve reference to '{0}' (defined in assembly '{1}') with scope '{2}'. When the scope is different from the defining assembly, it usually means that the type is forwarded.", ex.Member, ex.Member.Module.Assembly, ex.Scope);
            }

            return(true);
        }
Beispiel #18
0
        void AddAssemblyReferences(DirectoryAssemblyResolver resolver, Dictionary <string, ITaskItem> assemblies, AssemblyDefinition assembly, List <string> resolutionPath)
        {
            var assemblyName = assembly.Name.Name;
            var fullPath     = Path.GetFullPath(assembly.MainModule.FileName);

            // Don't repeat assemblies we've already done
            bool topLevel = resolutionPath == null;

            if (!topLevel && assemblies.ContainsKey(assemblyName))
            {
                return;
            }

            if (resolutionPath == null)
            {
                resolutionPath = new List <string>();
            }

            CheckAssemblyAttributes(assembly);

            LogMessage("{0}Adding assembly reference for {1}, recursively...", new string (' ', indent), assembly.Name);
            resolutionPath.Add(assembly.Name.Name);
            indent += 2;

            // Add this assembly
            if (!topLevel)
            {
                assemblies [assemblyName] = CreateAssemblyTaskItem(fullPath);
            }

            // Recurse into each referenced assembly
            foreach (AssemblyNameReference reference in assembly.MainModule.AssemblyReferences)
            {
                AssemblyDefinition reference_assembly;
                try {
                    reference_assembly = resolver.Resolve(reference);
                } catch (FileNotFoundException ex) {
                    var references = new StringBuilder();
                    for (int i = 0; i < resolutionPath.Count; i++)
                    {
                        if (i != 0)
                        {
                            references.Append(" > ");
                        }
                        references.Append('`');
                        references.Append(resolutionPath [i]);
                        references.Append('`');
                    }

                    string missingAssembly = Path.GetFileNameWithoutExtension(ex.FileName);
                    string message         = $"Can not resolve reference: `{missingAssembly}`, referenced by {references}.";
                    if (MonoAndroidHelper.IsFrameworkAssembly(ex.FileName))
                    {
                        LogCodedError("XA2002", $"{message} Perhaps it doesn't exist in the Mono for Android profile?");
                    }
                    else
                    {
                        LogCodedError("XA2002", $"{message} Please add a NuGet package or assembly reference for `{missingAssembly}`, or remove the reference to `{resolutionPath [0]}`.");
                    }
                    return;
                }
                AddAssemblyReferences(resolver, assemblies, reference_assembly, resolutionPath);
            }

            indent -= 2;
            resolutionPath.RemoveAt(resolutionPath.Count - 1);
        }
Beispiel #19
0
        // Extracts library project contents under e.g. obj/Debug/[__library_projects__/*.jar | res/*/*]
        public override bool Execute()
        {
            Log.LogDebugMessage("ResolveLibraryProjectImports Task");
            Log.LogDebugMessage("  ImportsDirectory: {0}", ImportsDirectory);
            Log.LogDebugMessage("  OutputDirectory: {0}", OutputDirectory);
            Log.LogDebugMessage("  OutputImportDirectory: {0}", OutputImportDirectory);
            Log.LogDebugMessage("  UseShortFileNames: {0}", UseShortFileNames);
            Log.LogDebugTaskItems("  Assemblies: ", Assemblies);

            var jars = new List <string> ();
            var resolvedResourceDirectories = new List <string> ();
            var resolvedAssetDirectories    = new List <string> ();
            var resolvedEnvironmentFiles    = new List <string> ();

            Extract(jars, resolvedResourceDirectories, resolvedAssetDirectories, resolvedEnvironmentFiles);

            Jars = jars.ToArray();
            ResolvedResourceDirectories = resolvedResourceDirectories
                                          .Select(s => new TaskItem(Path.GetFullPath(s)))
                                          .ToArray();
            ResolvedAssetDirectories = resolvedAssetDirectories.ToArray();
            ResolvedEnvironmentFiles = resolvedEnvironmentFiles.ToArray();

            ResolvedResourceDirectoryStamps = ResolvedResourceDirectories
                                              .Select(s => new TaskItem(Path.GetFullPath(Path.Combine(s.ItemSpec, "../..")) + ".stamp"))
                                              .ToArray();

            foreach (var directory in ResolvedResourceDirectories)
            {
                MonoAndroidHelper.SetDirectoryWriteable(directory.ItemSpec);
            }

            foreach (var directory in ResolvedAssetDirectories)
            {
                MonoAndroidHelper.SetDirectoryWriteable(directory);
            }

            if (!string.IsNullOrEmpty(CacheFile))
            {
                var document = new XDocument(
                    new XDeclaration("1.0", "UTF-8", null),
                    new XElement("Paths",
                                 new XElement("Jars", string.Join(";", Jars)),
                                 new XElement("ResolvedResourceDirectories",
                                              ResolvedResourceDirectories.Select(e => new XElement("ResolvedResourceDirectory", e))),
                                 new XElement("ResolvedAssetDirectories",
                                              ResolvedAssetDirectories.Select(e => new XElement("ResolvedAssetDirectory", e))),
                                 new XElement("ResolvedEnvironmentFiles",
                                              ResolvedEnvironmentFiles.Select(e => new XElement("ResolvedEnvironmentFile", e))),
                                 new XElement("ResolvedResourceDirectoryStamps",
                                              ResolvedResourceDirectoryStamps.Select(e => new XElement("ResolvedResourceDirectoryStamp", e)))
                                 ));
                document.Save(CacheFile);
            }

            Log.LogDebugTaskItems("  Jars: ", Jars.Select(s => new TaskItem(s)).ToArray());
            Log.LogDebugTaskItems("  ResolvedResourceDirectories: ", ResolvedResourceDirectories.Select(s => new TaskItem(s)).ToArray());
            Log.LogDebugTaskItems("  ResolvedAssetDirectories: ", ResolvedAssetDirectories.Select(s => new TaskItem(s)).ToArray());
            Log.LogDebugTaskItems("  ResolvedEnvironmentFiles: ", ResolvedEnvironmentFiles.Select(s => new TaskItem(s)).ToArray());
            Log.LogDebugTaskItems("  ResolvedResourceDirectoryStamps: ", ResolvedResourceDirectoryStamps);

            return(!Log.HasLoggedErrors);
        }
        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()));
        }
Beispiel #21
0
        public bool RunTask()
        {
            Log.LogDebugMessage("ResolveSdksTask:");
            Log.LogDebugMessage("  AndroidApiLevel: {0}", AndroidApiLevel);
            Log.LogDebugMessage("  AndroidSdkBuildToolsVersion: {0}", AndroidSdkBuildToolsVersion);
            Log.LogDebugMessage($"  {nameof (AndroidSdkPath)}: {AndroidSdkPath}");
            Log.LogDebugMessage($"  {nameof (AndroidNdkPath)}: {AndroidNdkPath}");
            Log.LogDebugMessage($"  {nameof (JavaSdkPath)}: {JavaSdkPath}");
            Log.LogDebugTaskItems("  ReferenceAssemblyPaths: ", ReferenceAssemblyPaths);
            Log.LogDebugMessage("  TargetFrameworkVersion: {0}", TargetFrameworkVersion);
            Log.LogDebugMessage("  UseLatestAndroidPlatformSdk: {0}", UseLatestAndroidPlatformSdk);
            Log.LogDebugMessage("  SequencePointsMode: {0}", SequencePointsMode);
            Log.LogDebugMessage("  LintToolPath: {0}", LintToolPath);

            // OS X:    $prefix/lib/xamarin.android/xbuild/Xamarin/Android
            // Windows: %ProgramFiles(x86)%\MSBuild\Xamarin\Android
            if (string.IsNullOrEmpty(MonoAndroidToolsPath))
            {
                MonoAndroidToolsPath = Path.GetDirectoryName(typeof(ResolveSdks).Assembly.Location);
            }
            MonoAndroidBinPath = MonoAndroidHelper.GetOSBinPath() + Path.DirectorySeparatorChar;

            MonoAndroidHelper.RefreshSupportedVersions(ReferenceAssemblyPaths);
            MonoAndroidHelper.RefreshAndroidSdk(AndroidSdkPath, AndroidNdkPath, JavaSdkPath);

            this.AndroidNdkPath = MonoAndroidHelper.AndroidSdk.AndroidNdkPath;
            this.AndroidSdkPath = MonoAndroidHelper.AndroidSdk.AndroidSdkPath;
            this.JavaSdkPath    = MonoAndroidHelper.AndroidSdk.JavaSdkPath;

            if (!ValidateJavaVersion(TargetFrameworkVersion, AndroidSdkBuildToolsVersion))
            {
                return(false);
            }

            if (string.IsNullOrEmpty(AndroidSdkPath))
            {
                Log.LogCodedError("XA5205", "The Android SDK Directory could not be found. Please set via /p:AndroidSdkDirectory.");
                return(false);
            }

            string toolsZipAlignPath = Path.Combine(AndroidSdkPath, "tools", ZipAlign);
            bool   findZipAlign      = (string.IsNullOrEmpty(ZipAlignPath) || !Directory.Exists(ZipAlignPath)) && !File.Exists(toolsZipAlignPath);

            var lintPaths = new string [] {
                LintToolPath ?? string.Empty,
                Path.Combine(AndroidSdkPath, "tools"),
                Path.Combine(AndroidSdkPath, "tools", "bin"),
            };

            LintToolPath = null;
            foreach (var path in lintPaths)
            {
                if (File.Exists(Path.Combine(path, Lint)))
                {
                    LintToolPath = path;
                    break;
                }
            }

            foreach (var dir in MonoAndroidHelper.AndroidSdk.GetBuildToolsPaths(AndroidSdkBuildToolsVersion))
            {
                Log.LogDebugMessage("Trying build-tools path: {0}", dir);
                if (dir == null || !Directory.Exists(dir))
                {
                    continue;
                }

                var toolsPaths = new string[] {
                    Path.Combine(dir),
                    Path.Combine(dir, "bin"),
                };

                string aapt = toolsPaths.FirstOrDefault(x => File.Exists(Path.Combine(x, Aapt)));
                if (string.IsNullOrEmpty(aapt))
                {
                    Log.LogDebugMessage("Could not find `{0}`; tried: {1}", Aapt,
                                        string.Join(";", toolsPaths.Select(x => Path.Combine(x, Aapt))));
                    continue;
                }
                AndroidSdkBuildToolsPath    = Path.GetFullPath(dir);
                AndroidSdkBuildToolsBinPath = Path.GetFullPath(aapt);

                string zipalign = toolsPaths.FirstOrDefault(x => File.Exists(Path.Combine(x, ZipAlign)));
                if (findZipAlign && string.IsNullOrEmpty(zipalign))
                {
                    Log.LogDebugMessage("Could not find `{0}`; tried: {1}", ZipAlign,
                                        string.Join(";", toolsPaths.Select(x => Path.Combine(x, ZipAlign))));
                    continue;
                }
                else
                {
                    break;
                }
            }

            if (string.IsNullOrEmpty(AndroidSdkBuildToolsPath))
            {
                Log.LogCodedError("XA5205",
                                  string.Format(
                                      "Cannot find `{0}`. Please install the Android SDK Build-tools package with the `{1}{2}tools{2}{3}` program.",
                                      Aapt, AndroidSdkPath, Path.DirectorySeparatorChar, Android));
                return(false);
            }

            ApkSignerJar        = Path.Combine(AndroidSdkBuildToolsBinPath, "lib", ApkSigner);
            AndroidUseApkSigner = File.Exists(ApkSignerJar);

            if (string.IsNullOrEmpty(ZipAlignPath) || !Directory.Exists(ZipAlignPath))
            {
                ZipAlignPath = new[] {
                    Path.Combine(AndroidSdkBuildToolsPath),
                    Path.Combine(AndroidSdkBuildToolsBinPath),
                    Path.Combine(AndroidSdkPath, "tools"),
                }
                .Where(p => File.Exists(Path.Combine(p, ZipAlign)))
                .FirstOrDefault();
            }
            if (string.IsNullOrEmpty(ZipAlignPath))
            {
                Log.LogCodedError("XA5205",
                                  string.Format(
                                      "Cannot find `{0}`. Please install the Android SDK Build-tools package with the `{1}{2}tools{2}{3}` program.",
                                      ZipAlign, AndroidSdkPath, Path.DirectorySeparatorChar, Android));
                return(false);
            }

            if (!ValidateApiLevels())
            {
                return(false);
            }

            if (!MonoAndroidHelper.SupportedVersions.FrameworkDirectories.Any(p => Directory.Exists(Path.Combine(p, TargetFrameworkVersion))))
            {
                Log.LogError(
                    subcategory:      string.Empty,
                    errorCode:        "XA0001",
                    helpKeyword:      string.Empty,
                    file:             ProjectFilePath,
                    lineNumber:       0,
                    columnNumber:     0,
                    endLineNumber:    0,
                    endColumnNumber:  0,
                    message:          "Unsupported or invalid $(TargetFrameworkVersion) value of '{0}'. Please update your Project Options.",
                    messageArgs:      new[] {
                    TargetFrameworkVersion,
                }
                    );
                return(false);
            }

            SequencePointsMode mode;

            if (!Aot.TryGetSequencePointsMode(SequencePointsMode ?? "None", out mode))
            {
                Log.LogCodedError("XA0104", "Invalid Sequence Point mode: {0}", SequencePointsMode);
            }
            AndroidSequencePointsMode = mode.ToString();

            MonoAndroidHelper.TargetFrameworkDirectories = ReferenceAssemblyPaths;

            AndroidApiLevelName = MonoAndroidHelper.SupportedVersions.GetIdFromApiLevel(AndroidApiLevel);

            Log.LogDebugMessage("ResolveSdksTask Outputs:");
            Log.LogDebugMessage("  AndroidApiLevel: {0}", AndroidApiLevel);
            Log.LogDebugMessage("  AndroidApiLevelName: {0}", AndroidApiLevelName);
            Log.LogDebugMessage("  AndroidNdkPath: {0}", AndroidNdkPath);
            Log.LogDebugMessage("  AndroidSdkBuildToolsPath: {0}", AndroidSdkBuildToolsPath);
            Log.LogDebugMessage("  AndroidSdkBuildToolsBinPath: {0}", AndroidSdkBuildToolsBinPath);
            Log.LogDebugMessage("  AndroidSdkPath: {0}", AndroidSdkPath);
            Log.LogDebugMessage("  JavaSdkPath: {0}", JavaSdkPath);
            Log.LogDebugMessage("  JdkVersion: {0}", JdkVersion);
            Log.LogDebugMessage("  MinimumRequiredJdkVersion: {0}", MinimumRequiredJdkVersion);
            Log.LogDebugMessage("  MonoAndroidBinPath: {0}", MonoAndroidBinPath);
            Log.LogDebugMessage("  MonoAndroidToolsPath: {0}", MonoAndroidToolsPath);
            Log.LogDebugMessage("  TargetFrameworkVersion: {0}", TargetFrameworkVersion);
            Log.LogDebugMessage("  ZipAlignPath: {0}", ZipAlignPath);
            Log.LogDebugMessage("  SupportedApiLevel: {0}", SupportedApiLevel);
            Log.LogDebugMessage("  AndroidSequencePointMode: {0}", AndroidSequencePointsMode);
            Log.LogDebugMessage("  LintToolPath: {0}", LintToolPath);

            if (!string.IsNullOrEmpty(CacheFile))
            {
                Directory.CreateDirectory(Path.GetDirectoryName(CacheFile));

                var document = new XDocument(
                    new XDeclaration("1.0", "UTF-8", null),
                    new XElement("Sdk",
                                 new XElement("AndroidApiLevel", AndroidApiLevel),
                                 new XElement("AndroidApiLevelName", AndroidApiLevelName),
                                 new XElement("AndroidNdkPath", AndroidNdkPath),
                                 new XElement("AndroidSdkBuildToolsPath", AndroidSdkBuildToolsPath),
                                 new XElement("AndroidSdkBuildToolsBinPath", AndroidSdkBuildToolsBinPath),
                                 new XElement("AndroidSdkPath", AndroidSdkPath),
                                 new XElement("JavaSdkPath", JavaSdkPath),
                                 new XElement("MonoAndroidBinPath", MonoAndroidBinPath),
                                 new XElement("MonoAndroidToolsPath", MonoAndroidToolsPath),
                                 new XElement("ReferenceAssemblyPaths",
                                              (ReferenceAssemblyPaths ?? new string [0])
                                              .Select(e => new XElement("ReferenceAssemblyPath", e))),
                                 new XElement("TargetFrameworkVersion", TargetFrameworkVersion),
                                 new XElement("ZipAlignPath", ZipAlignPath),
                                 new XElement("MonoAndroidIncludePath", MonoAndroidIncludePath),
                                 new XElement("SupportedApiLevel", SupportedApiLevel),
                                 new XElement("AndroidSequencePointsMode", AndroidSequencePointsMode.ToString()),
                                 new XElement("LintToolPath", LintToolPath)
                                 ));
                document.Save(CacheFile);
            }

            //note: this task does not error out if it doesn't find all things. that's the job of the targets
            return(!Log.HasLoggedErrors);
        }
        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);
        }
        void ExecuteWithAbi(string [] supportedAbis, string apkInputPath, string apkOutputPath)
        {
            ArchiveFileList files   = new ArchiveFileList();
            bool            refresh = true;

            if (apkInputPath != null && File.Exists(apkInputPath) && !File.Exists(apkOutputPath))
            {
                Log.LogDebugMessage($"Copying {apkInputPath} to {apkInputPath}");
                File.Copy(apkInputPath, apkOutputPath, overwrite: true);
                refresh = false;
            }
            using (var notice = Assembly.GetExecutingAssembly().GetManifestResourceStream("NOTICE.txt"))
                using (var apk = new ZipArchiveEx(apkOutputPath, File.Exists(apkOutputPath) ? FileMode.Open : FileMode.Create)) {
                    if (refresh)
                    {
                        for (long i = 0; i < apk.Archive.EntryCount; i++)
                        {
                            ZipEntry e = apk.Archive.ReadEntry((ulong)i);
                            Log.LogDebugMessage($"Registering item {e.FullName}");
                            existingEntries.Add(e.FullName);
                        }
                    }
                    if (apkInputPath != null && File.Exists(apkInputPath) && refresh)
                    {
                        var lastWriteOutput = File.Exists(apkOutputPath) ? File.GetLastWriteTimeUtc(apkOutputPath) : DateTime.MinValue;
                        var lastWriteInput  = File.GetLastWriteTimeUtc(apkInputPath);
                        using (var packaged = new ZipArchiveEx(apkInputPath, FileMode.Open)) {
                            foreach (var entry in packaged.Archive)
                            {
                                Log.LogDebugMessage($"Deregistering item {entry.FullName}");
                                existingEntries.Remove(entry.FullName);
                                if (lastWriteInput <= lastWriteOutput)
                                {
                                    continue;
                                }
                                if (apk.Archive.ContainsEntry(entry.FullName))
                                {
                                    ZipEntry e = apk.Archive.ReadEntry(entry.FullName);
                                    // check the CRC values as the ModifiedDate is always 01/01/1980 in the aapt generated file.
                                    if (entry.CRC == e.CRC)
                                    {
                                        Log.LogDebugMessage($"Skipping {entry.FullName} from {apkInputPath} as its up to date.");
                                        continue;
                                    }
                                }
                                var ms = new MemoryStream();
                                entry.Extract(ms);
                                Log.LogDebugMessage($"Refreshing {entry.FullName} from {apkInputPath}");
                                apk.Archive.AddStream(ms, entry.FullName, compressionMethod: entry.CompressionMethod);
                            }
                        }
                    }
                    apk.FixupWindowsPathSeparators((a, b) => Log.LogDebugMessage($"Fixing up malformed entry `{a}` -> `{b}`"));
                    string noticeName = RootPath + "NOTICE";
                    existingEntries.Remove(noticeName);
                    if (!apk.Archive.ContainsEntry(noticeName))
                    {
                        apk.Archive.AddEntry(noticeName, notice);
                    }

                    // Add classes.dx
                    foreach (var dex in DalvikClasses)
                    {
                        string apkName = dex.GetMetadata("ApkName");
                        string dexPath = string.IsNullOrWhiteSpace(apkName) ? Path.GetFileName(dex.ItemSpec) : apkName;
                        AddFileToArchiveIfNewer(apk, dex.ItemSpec, DalvikPath + dexPath);
                    }

                    if (EmbedAssemblies && !BundleAssemblies)
                    {
                        AddAssemblies(apk);
                    }

                    AddRuntimeLibraries(apk, supportedAbis);
                    apk.Flush();
                    AddNativeLibraries(files, supportedAbis);
                    AddAdditionalNativeLibraries(files, supportedAbis);

                    if (TypeMappings != null)
                    {
                        foreach (ITaskItem typemap in TypeMappings)
                        {
                            AddFileToArchiveIfNewer(apk, typemap.ItemSpec, RootPath + Path.GetFileName(typemap.ItemSpec), compressionMethod: UncompressedMethod);
                        }
                    }

                    int count = 0;
                    foreach (var file in files)
                    {
                        var item = Path.Combine(file.archivePath, Path.GetFileName(file.filePath))
                                   .Replace(Path.DirectorySeparatorChar, '/');
                        existingEntries.Remove(item);
                        if (apk.SkipExistingFile(file.filePath, item))
                        {
                            Log.LogDebugMessage($"Skipping {file.filePath} as the archive file is up to date.");
                            continue;
                        }
                        Log.LogDebugMessage("\tAdding {0}", file.filePath);
                        apk.Archive.AddFile(file.filePath, item, compressionMethod: GetCompressionMethod(file.filePath));
                        count++;
                        if (count == ZipArchiveEx.ZipFlushLimit)
                        {
                            apk.Flush();
                            count = 0;
                        }
                    }

                    var jarFiles = (JavaSourceFiles != null) ? JavaSourceFiles.Where(f => f.ItemSpec.EndsWith(".jar")) : null;
                    if (jarFiles != null && JavaLibraries != null)
                    {
                        jarFiles = jarFiles.Concat(JavaLibraries);
                    }
                    else if (JavaLibraries != null)
                    {
                        jarFiles = JavaLibraries;
                    }

                    var libraryProjectJars = MonoAndroidHelper.ExpandFiles(LibraryProjectJars)
                                             .Where(jar => !MonoAndroidHelper.IsEmbeddedReferenceJar(jar));

                    var jarFilePaths = libraryProjectJars.Concat(jarFiles != null ? jarFiles.Select(j => j.ItemSpec) : Enumerable.Empty <string> ());
                    jarFilePaths = MonoAndroidHelper.DistinctFilesByContent(jarFilePaths);

                    count = 0;
                    foreach (var jarFile in jarFilePaths)
                    {
                        using (var jar = ZipArchive.Open(File.OpenRead(jarFile))) {
                            foreach (var jarItem in jar)
                            {
                                if (jarItem.IsDirectory)
                                {
                                    continue;
                                }
                                var name = jarItem.FullName;
                                if (!PackagingUtils.CheckEntryForPackaging(name))
                                {
                                    continue;
                                }
                                var path = RootPath + name;
                                existingEntries.Remove(path);
                                if (apk.SkipExistingEntry(jarItem, path))
                                {
                                    Log.LogDebugMessage($"Skipping {path} as the archive file is up to date.");
                                    continue;
                                }
                                byte [] data;
                                using (var d = new MemoryStream()) {
                                    jarItem.Extract(d);
                                    data = d.ToArray();
                                }
                                Log.LogDebugMessage($"Adding {path} as the archive file is out of date.");
                                apk.Archive.AddEntry(data, path);
                            }
                        }
                        count++;
                        if (count == ZipArchiveEx.ZipFlushLimit)
                        {
                            apk.Flush();
                            count = 0;
                        }
                    }
                    // Clean up Removed files.
                    foreach (var entry in existingEntries)
                    {
                        Log.LogDebugMessage($"Removing {entry} as it is not longer required.");
                        apk.Archive.DeleteEntry(entry);
                    }
                    apk.Flush();
                    FixupArchive(apk);
                }
        }
Beispiel #24
0
        void ExecuteWithAbi(string supportedAbis, string apkInputPath, string apkOutputPath)
        {
            ArchiveFileList files = new ArchiveFileList();

            if (apkInputPath != null)
            {
                File.Copy(apkInputPath, apkOutputPath + "new", overwrite: true);
            }
            using (var apk = new ZipArchiveEx(apkOutputPath + "new", apkInputPath != null ? FileMode.Open : FileMode.Create)) {
                apk.Archive.AddEntry("NOTICE",
                                     Assembly.GetExecutingAssembly().GetManifestResourceStream("NOTICE.txt"));

                // Add classes.dx
                apk.Archive.AddFiles(DalvikClasses, useFileDirectories: false);

                if (EmbedAssemblies && !BundleAssemblies)
                {
                    AddAssemblies(apk);
                }

                AddEnvironment(apk);
                AddRuntimeLibraries(apk, supportedAbis);
                apk.Flush();
                AddNativeLibraries(files, supportedAbis);
                apk.Flush();
                AddAdditionalNativeLibraries(files, supportedAbis);
                apk.Flush();
                AddNativeLibrariesFromAssemblies(apk, supportedAbis);
                apk.Flush();

                foreach (ITaskItem typemap in TypeMappings)
                {
                    apk.Archive.AddFile(typemap.ItemSpec, Path.GetFileName(typemap.ItemSpec), compressionMethod: CompressionMethod.Store);
                }

                int count = 0;
                foreach (var file in files)
                {
                    var item = Path.Combine(file.Item2, Path.GetFileName(file.Item1))
                               .Replace(Path.DirectorySeparatorChar, '/');
                    if (apk.Archive.ContainsEntry(item))
                    {
                        Log.LogWarning(null, "XA4301", null, file.Item1, 0, 0, 0, 0, "Apk already contains the item {0}; ignoring.", item);
                        continue;
                    }
                    apk.Archive.AddFile(file.Item1, item);
                    count++;
                    if (count == ZipArchiveEx.ZipFlushLimit)
                    {
                        apk.Flush();
                        count = 0;
                    }
                }
                if (_Debug)
                {
                    AddGdbservers(apk, files, supportedAbis, debugServer);
                }

                var jarFiles = (JavaSourceFiles != null) ? JavaSourceFiles.Where(f => f.ItemSpec.EndsWith(".jar")) : null;
                if (jarFiles != null && JavaLibraries != null)
                {
                    jarFiles = jarFiles.Concat(JavaLibraries);
                }
                else if (JavaLibraries != null)
                {
                    jarFiles = JavaLibraries;
                }

                var libraryProjectJars = MonoAndroidHelper.ExpandFiles(LibraryProjectJars)
                                         .Where(jar => !MonoAndroidHelper.IsEmbeddedReferenceJar(jar));

                var jarFilePaths = libraryProjectJars.Concat(jarFiles != null ? jarFiles.Select(j => j.ItemSpec) : Enumerable.Empty <string> ());
                jarFilePaths = MonoAndroidHelper.DistinctFilesByContent(jarFilePaths);

                count = 0;
                foreach (var jarFile in jarFilePaths)
                {
                    using (var jar = ZipArchive.Open(File.OpenRead(jarFile))) {
                        foreach (var jarItem in jar.Where(ze => !ze.IsDirectory && !ze.FullName.StartsWith("META-INF") && !ze.FullName.EndsWith(".class") && !ze.FullName.EndsWith(".java") && !ze.FullName.EndsWith("MANIFEST.MF")))
                        {
                            byte [] data;
                            using (var d = new System.IO.MemoryStream()) {
                                jarItem.Extract(d);
                                data = d.ToArray();
                            }
                            if (apk.Archive.Any(e => e.FullName == jarItem.FullName))
                            {
                                Log.LogMessage("Warning: failed to add jar entry {0} from {1}: the same file already exists in the apk", jarItem.FullName, Path.GetFileName(jarFile));
                            }
                            else
                            {
                                apk.Archive.AddEntry(data, jarItem.FullName);
                            }
                        }
                    }
                    count++;
                    if (count == ZipArchiveEx.ZipFlushLimit)
                    {
                        apk.Flush();
                        count = 0;
                    }
                }
                if (StubApplicationDataFile != null && File.Exists(StubApplicationDataFile))
                {
                    apk.Archive.AddFile(StubApplicationDataFile, Path.GetFileName(StubApplicationDataFile));
                }
            }
            MonoAndroidHelper.CopyIfZipChanged(apkOutputPath + "new", apkOutputPath);
            File.Delete(apkOutputPath + "new");
        }
        bool Execute(DirectoryAssemblyResolver res)
        {
            foreach (var assembly in Assemblies)
            {
                res.Load(Path.GetFullPath(assembly.ItemSpec));
            }

            foreach (var assemblyName in Assemblies)
            {
                var    suffix   = assemblyName.ItemSpec.EndsWith(".dll") ? String.Empty : ".dll";
                string hintPath = assemblyName.GetMetadata("HintPath").Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
                string fileName = assemblyName.ItemSpec + suffix;
                if (!String.IsNullOrEmpty(hintPath) && !File.Exists(hintPath))                   // ignore invalid HintPath
                {
                    hintPath = null;
                }
                string assemblyPath = String.IsNullOrEmpty(hintPath) ? fileName : hintPath;
                if (MonoAndroidHelper.IsFrameworkAssembly(fileName) && !MonoAndroidHelper.FrameworkEmbeddedJarLookupTargets.Contains(Path.GetFileName(fileName)))
                {
                    continue;
                }

                var  assembly          = res.GetAssembly(assemblyPath);
                bool assembly_modified = false;
                foreach (var mod in assembly.Modules)
                {
                    // embedded jars
                    var resjars = mod.Resources.Where(r => r.Name.EndsWith(".jar", StringComparison.InvariantCultureIgnoreCase)).Select(r => (EmbeddedResource)r);
                    foreach (var resjar in resjars.ToArray())
                    {
                        Log.LogDebugMessage("    Stripped {0}", resjar.Name);
                        mod.Resources.Remove(resjar);
                        assembly_modified = true;
                    }
                    // embedded AndroidNativeLibrary archive
                    var nativezip = mod.Resources.FirstOrDefault(r => r.Name == "__AndroidNativeLibraries__.zip") as EmbeddedResource;
                    if (nativezip != null)
                    {
                        Log.LogDebugMessage("    Stripped {0}", nativezip.Name);
                        mod.Resources.Remove(nativezip);
                        assembly_modified = true;
                    }
                    // embedded AndroidResourceLibrary archive
                    var reszip = mod.Resources.FirstOrDefault(r => r.Name == "__AndroidLibraryProjects__.zip") as EmbeddedResource;
                    if (reszip != null)
                    {
                        Log.LogDebugMessage("    Stripped {0}", reszip.Name);
                        mod.Resources.Remove(reszip);
                        assembly_modified = true;
                    }
                }
                if (assembly_modified)
                {
                    Log.LogDebugMessage("    The stripped library is saved as {0}", assemblyPath);

                    // Output assembly needs to regenerate symbol file even if no IL/metadata was touched
                    // because Cecil still rewrites all assembly types in Cecil order (type A, nested types of A, type B, etc)
                    // and not in the original order causing symbols if original order doesn't match Cecil order
                    var wp = new WriterParameters()
                    {
                        WriteSymbols = assembly.MainModule.HasSymbols
                    };

                    assembly.Write(wp);
                }
            }
            return(true);
        }
Beispiel #26
0
        string MakeSureLibraryIsInPlace(string destinationBase, string url, string version, string embeddedArchive, string sha1)
        {
            if (string.IsNullOrEmpty(url))
            {
                return(null);
            }

            Log.LogDebugMessage("Making sure we have {0} downloaded and extracted {1} from it...", url, embeddedArchive);

            string destinationDir             = version == null ? destinationBase : Path.Combine(destinationBase, version);
            bool   createDestinationDirectory = !Directory.Exists(destinationDir);

            if (createDestinationDirectory)
            {
                Directory.CreateDirectory(destinationDir);
            }

            var hash = string.Concat(md5.ComputeHash(Encoding.UTF8.GetBytes(url)).Select(b => b.ToString("X02")));
            var uri  = new Uri(url);

            var extraPath = extraPaths.FirstOrDefault(x => File.Exists(Path.Combine(AndroidSdkDirectory, x, embeddedArchive ?? String.Empty)));

            string zipDir             = !uri.IsFile ? Path.Combine(CachePath, "zips") : destinationDir;
            bool   createZipDirectory = !Directory.Exists(zipDir);

            if (createZipDirectory)
            {
                Directory.CreateDirectory(zipDir);
            }

            string file = Path.Combine(zipDir, !uri.IsFile ? hash + ".zip" : Path.GetFileName(uri.AbsolutePath));

            if (string.IsNullOrEmpty(extraPath) && (!File.Exists(file) || !IsValidDownload(file, sha1) || !MonoAndroidHelper.IsValidZip(file)))
            {
                int progress        = -1;
                var downloadHandler = new Action <long, long, int>((r, t, p) => {
                    if (p % 10 != 0 || progress == p)
                    {
                        return;
                    }
                    progress = p;
                    LogMessage("\t({0}/{1}b), total {2:F1}%", r,
                               t, p);
                });
                LogMessage("  Downloading {0} into {1}", url, zipDir);
                try {
                    Download(file, uri, downloadHandler);
                    if (MonoAndroidHelper.IsValidZip(file))
                    {
                        LogMessage("  Downloading Complete");
                    }
                    else
                    {
                        LogCodedError("XA5208", "Download succeeded but the zip file was not valid. Please do a clean build and try again.");
                    }
                } catch (Exception e) {
                    LogCodedError("XA5208", "Download failed. Please build again.");
                    LogCodedError("XA5208", "Reason: {0}", e.GetBaseException().Message);
                    Log.LogMessage(MessageImportance.Low, e.ToString());
                }
            }
            else
            {
                if (string.IsNullOrEmpty(extraPath))
                {
                    LogDebugMessage("    reusing existing archive: {0}", file);
                }
                else
                {
                    LogDebugMessage("    found `{0}` in `{1}`", embeddedArchive, Path.Combine(AndroidSdkDirectory, extraPath));
                }
            }

            string contentDir = string.IsNullOrEmpty(extraPath) ? Path.Combine(destinationDir, "content") : Path.Combine(AndroidSdkDirectory, extraPath);

            int attempt = 0;

            while (attempt < 3 && !Log.HasLoggedErrors)
            {
                var success = ExtractArchive(url, file, contentDir);
                if (!success && Log.HasLoggedErrors)
                {
                    break;
                }

                if (!string.IsNullOrEmpty(embeddedArchive))
                {
                    string embeddedDir = Path.Combine(destinationDir, "embedded");
                    success = ExtractArchive(string.Format("{0}:{1}", url, embeddedArchive), Path.Combine(contentDir, embeddedArchive), embeddedDir);
                    if (success)
                    {
                        contentDir = embeddedDir;
                        break;
                    }
                    if (Log.HasLoggedErrors)
                    {
                        break;
                    }
                    if (!success)
                    {
                        Log.LogWarning("Expected File {0} does not exist. Trying to extract again.", Path.Combine(contentDir, embeddedArchive));
                        if (Directory.Exists(contentDir))
                        {
                            Directory.Delete(contentDir, recursive: true);
                        }
                    }
                }
                else
                {
                    break;
                }
                attempt++;
            }

            if (string.IsNullOrEmpty(contentDir) || !Directory.Exists(contentDir))
            {
                if (createZipDirectory)
                {
                    Directory.Delete(zipDir);
                }
                if (createDestinationDirectory)
                {
                    Directory.Delete(destinationDir);
                }
            }

            return(contentDir);
        }
Beispiel #27
0
        protected override string GenerateCommandLineCommands()
        {
            var cmd = new CommandLineBuilder();

            // Add the JavaOptions if they are not null
            // These could be any of the additional options
            if (!string.IsNullOrEmpty(JavaOptions))
            {
                cmd.AppendSwitch(JavaOptions);
            }

            // Add the specific -XmxN to override the default heap size for the JVM
            // N can be in the form of Nm or NGB (e.g 100m or 1GB )
            cmd.AppendSwitchIfNotNull("-Xmx", JavaMaximumHeapSize);

            // See https://bugzilla.xamarin.com/show_bug.cgi?id=21096
            cmd.AppendSwitch("-XX:-UseSplitVerifier");

            // Arguments sent to java.exe
            cmd.AppendSwitchIfNotNull("-jar ", Path.Combine(MonoAndroidToolsDirectory, "jar2xml.jar"));

            foreach (var jar in SourceJars)
            {
                cmd.AppendSwitchIfNotNull("--jar=", Path.GetFullPath(jar.ItemSpec));
            }

            var libraryProjectJars = MonoAndroidHelper.ExpandFiles(LibraryProjectJars);

            foreach (var jar in libraryProjectJars)
            {
                if (MonoAndroidHelper.IsEmbeddedReferenceJar(jar))
                {
                    cmd.AppendSwitchIfNotNull("--ref=", Path.GetFullPath(jar));
                }
                else
                {
                    cmd.AppendSwitchIfNotNull("--jar=", Path.GetFullPath(jar));
                }
            }

            // Arguments sent to jar2xml
            var jarpath = Path.Combine(AndroidSdkDirectory, "platforms", "android-" + MonoAndroidHelper.GetPlatformApiLevelName(AndroidApiLevel), "android.jar");

            cmd.AppendSwitchIfNotNull("--ref=", Path.GetFullPath(jarpath));

            cmd.AppendSwitchIfNotNull("--out=", Path.GetFullPath(OutputFile));

            if (ReferenceJars != null)
            {
                foreach (var jar in ReferenceJars)
                {
                    cmd.AppendSwitchIfNotNull("--ref=", Path.GetFullPath(jar.ItemSpec));
                }
            }

            if (DroidDocPaths != null)
            {
                foreach (var path in DroidDocPaths.Split(';'))
                {
                    cmd.AppendSwitchIfNotNull("--droiddocpath=", Path.GetFullPath(path));
                }
            }

            if (JavaDocPaths != null)
            {
                foreach (var path in JavaDocPaths.Split(';'))
                {
                    cmd.AppendSwitchIfNotNull("--javadocpath=", Path.GetFullPath(path));
                }
            }

            if (Java7DocPaths != null)
            {
                foreach (var path in Java7DocPaths.Split(';'))
                {
                    cmd.AppendSwitchIfNotNull("--java7docpath=", Path.GetFullPath(path));
                }
            }

            if (Java8DocPaths != null)
            {
                foreach (var path in Java8DocPaths.Split(';'))
                {
                    cmd.AppendSwitchIfNotNull("--java8docpath=", Path.GetFullPath(path));
                }
            }

            if (JavaDocs != null)
            {
                foreach (var doc in JavaDocs)
                {
                    var opt = GetJavadocOption(doc.ItemSpec);
                    if (opt != null)
                    {
                        cmd.AppendSwitchIfNotNull(opt, Path.GetFullPath(Path.GetDirectoryName(doc.ItemSpec)));
                    }
                }
            }
            return(cmd.ToString());
        }
Beispiel #28
0
        public override bool Execute()
        {
            LogDebugMessage("GetAdditionalResourcesFromAssemblies Task");
            LogDebugMessage("  AndroidSdkDirectory: {0}", AndroidSdkDirectory);
            LogDebugMessage("  AndroidNdkDirectory: {0}", AndroidNdkDirectory);
            LogDebugTaskItems("  Assemblies: ", Assemblies);

            if (Environment.GetEnvironmentVariable("XA_DL_IGNORE_CERT_ERRROS") == "yesyesyes")
            {
                ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
                LogDebugMessage("    Disabling download certificate validation callback.");
            }
            var androidResources = new HashSet <string> ();
            var javaLibraries    = new HashSet <string> ();
            var nativeLibraries  = new HashSet <string> ();
            var assemblies       = new HashSet <string> ();

            if (Assemblies == null)
            {
                return(true);
            }

            System.Threading.Tasks.Task.Run(() => {
                // The cache location can be overriden by the (to be documented) XAMARIN_CACHEPATH
                CachePath = Environment.ExpandEnvironmentVariables(CachePathEnvironmentVar);
                CachePath = CachePath != CachePathEnvironmentVar
                                ? CachePath
                                : Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), CacheBaseDir);

                using (var resolver = new DirectoryAssemblyResolver(Log.LogWarning, loadDebugSymbols: false)) {
                    foreach (var assemblyItem in Assemblies)
                    {
                        string fullPath = Path.GetFullPath(assemblyItem.ItemSpec);
                        if (assemblies.Contains(fullPath))
                        {
                            LogDebugMessage("  Skip assembly: {0}, it was already processed", fullPath);
                            continue;
                        }
                        assemblies.Add(fullPath);
                        resolver.Load(fullPath);
                        // Append source file name (without the Xamarin. prefix or extension) to the base folder
                        // This would help avoid potential collisions.
                        foreach (var ca in resolver.GetAssembly(assemblyItem.ItemSpec).CustomAttributes)
                        {
                            switch (ca.AttributeType.FullName)
                            {
                            case "Android.IncludeAndroidResourcesFromAttribute":
                                AddAttributeValue(androidResources, ca, "XA5206", "{0}. Android resource directory {1} doesn't exist.", true, fullPath);
                                break;

                            case "Java.Interop.JavaLibraryReferenceAttribute":
                                AddAttributeValue(javaLibraries, ca, "XA5207", "{0}. Java library file {1} doesn't exist.", false, fullPath);
                                break;

                            case "Android.NativeLibraryReferenceAttribute":
                                AddAttributeValue(nativeLibraries, ca, "XA5210", "{0}. Native library file {1} doesn't exist.", false, fullPath);
                                break;
                            }
                        }
                    }
                }
            }).ContinueWith((t) => {
                if (t.Exception != null)
                {
                    Log.LogErrorFromException(t.Exception.GetBaseException());
                }
                Complete();
            });

            var result = base.Execute();

            if (!result || Log.HasLoggedErrors)
            {
                if (File.Exists(CacheFile))
                {
                    File.Delete(CacheFile);
                }
                return(false);
            }

            var AdditionalAndroidResourcePaths    = androidResources.ToArray();
            var AdditionalJavaLibraryReferences   = javaLibraries.ToArray();
            var AdditionalNativeLibraryReferences = nativeLibraries
                                                    .Where(x => MonoAndroidHelper.GetNativeLibraryAbi(x) != null)
                                                    .ToArray();

            var document = new XDocument(
                new XDeclaration("1.0", "UTF-8", null),
                new XElement("Paths",
                             new XElement("AdditionalAndroidResourcePaths",
                                          AdditionalAndroidResourcePaths.Select(e => new XElement("AdditionalAndroidResourcePath", e))),
                             new XElement("AdditionalJavaLibraryReferences",
                                          AdditionalJavaLibraryReferences.Select(e => new XElement("AdditionalJavaLibraryReference", e))),
                             new XElement("AdditionalNativeLibraryReferences",
                                          AdditionalNativeLibraryReferences.Select(e => new XElement("AdditionalNativeLibraryReference", e)))
                             ));

            document.Save(CacheFile);

            LogDebugTaskItems("  AdditionalAndroidResourcePaths: ", AdditionalAndroidResourcePaths);
            LogDebugTaskItems("  AdditionalJavaLibraryReferences: ", AdditionalJavaLibraryReferences);
            LogDebugTaskItems("  AdditionalNativeLibraryReferences: ", AdditionalNativeLibraryReferences);

            return(result && !Log.HasLoggedErrors);
        }
Beispiel #29
0
        IEnumerable <Config> GetAotConfigs()
        {
            if (!Directory.Exists(AotOutputDirectory))
            {
                Directory.CreateDirectory(AotOutputDirectory);
            }

            var sdkBinDirectory = MonoAndroidHelper.GetOSBinPath();
            var abis            = SupportedAbis.Split(new char[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);

            foreach (var abi in abis)
            {
                string            aotCompiler = "";
                string            outdir      = "";
                string            mtriple     = "";
                AndroidTargetArch arch;

                switch (abi)
                {
                case "armeabi-v7a":
                    aotCompiler = Path.Combine(sdkBinDirectory, "cross-arm");
                    outdir      = Path.Combine(AotOutputDirectory, "armeabi-v7a");
                    mtriple     = "armv7-linux-gnueabi";
                    arch        = AndroidTargetArch.Arm;
                    break;

                case "arm64":
                case "arm64-v8a":
                case "aarch64":
                    aotCompiler = Path.Combine(sdkBinDirectory, "cross-arm64");
                    outdir      = Path.Combine(AotOutputDirectory, "arm64-v8a");
                    mtriple     = "aarch64-linux-android";
                    arch        = AndroidTargetArch.Arm64;
                    break;

                case "x86":
                    aotCompiler = Path.Combine(sdkBinDirectory, "cross-x86");
                    outdir      = Path.Combine(AotOutputDirectory, "x86");
                    mtriple     = "i686-linux-android";
                    arch        = AndroidTargetArch.X86;
                    break;

                case "x86_64":
                    aotCompiler = Path.Combine(sdkBinDirectory, "cross-x86_64");
                    outdir      = Path.Combine(AotOutputDirectory, "x86_64");
                    mtriple     = "x86_64-linux-android";
                    arch        = AndroidTargetArch.X86_64;
                    break;

                // case "mips":
                default:
                    throw new Exception("Unsupported Android target architecture ABI: " + abi);
                }

                if (!NdkUtil.ValidateNdkPlatform(Log, AndroidNdkDirectory, arch, enableLLVM:EnableLLVM))
                {
                    yield return(Config.Invalid);

                    yield break;
                }

                if (!ValidateAotConfiguration(Log, arch, EnableLLVM))
                {
                    yield return(Config.Invalid);

                    yield break;
                }

                outdir = Path.GetFullPath(outdir);
                if (!Directory.Exists(outdir))
                {
                    Directory.CreateDirectory(outdir);
                }

                int    level         = GetNdkApiLevel(AndroidNdkDirectory, AndroidApiLevel, arch);
                string toolPrefix    = NdkUtil.GetNdkToolPrefix(AndroidNdkDirectory, arch, level);
                var    toolchainPath = toolPrefix.Substring(0, toolPrefix.LastIndexOf(Path.DirectorySeparatorChar));
                var    ldFlags       = string.Empty;
                if (EnableLLVM)
                {
                    string androidLibPath = string.Empty;
                    try {
                        androidLibPath = NdkUtil.GetNdkPlatformLibPath(AndroidNdkDirectory, arch, level);
                    } catch (InvalidOperationException ex) {
                        Diagnostic.Error(5101, ex.Message);
                    }

                    string toolchainLibDir;
                    if (NdkUtil.UsingClangNDK)
                    {
                        toolchainLibDir = GetNdkToolchainLibraryDir(toolchainPath, arch);
                    }
                    else
                    {
                        toolchainLibDir = GetNdkToolchainLibraryDir(toolchainPath);
                    }

                    var libs = new List <string>();
                    if (NdkUtil.UsingClangNDK)
                    {
                        libs.Add($"-L{GetShortPath (toolchainLibDir)}");
                        libs.Add($"-L{GetShortPath (androidLibPath)}");

                        if (arch == AndroidTargetArch.Arm)
                        {
                            // Needed for -lunwind to work
                            string compilerLibDir = Path.Combine(toolchainPath, "..", "sysroot", "usr", "lib", NdkUtil.GetArchDirName(arch));
                            libs.Add($"-L{GetShortPath (compilerLibDir)}");
                        }
                    }

                    libs.Add(GetShortPath(Path.Combine(toolchainLibDir, "libgcc.a")));
                    libs.Add(GetShortPath(Path.Combine(androidLibPath, "libc.so")));
                    libs.Add(GetShortPath(Path.Combine(androidLibPath, "libm.so")));

                    ldFlags = string.Join(";", libs);
                }

                foreach (var assembly in ResolvedAssemblies)
                {
                    string outputFile = Path.Combine(outdir, string.Format("libaot-{0}.so",
                                                                           Path.GetFileName(assembly.ItemSpec)));

                    string seqpointsFile = Path.Combine(outdir, string.Format("{0}.msym",
                                                                              Path.GetFileName(assembly.ItemSpec)));

                    string tempDir = Path.Combine(outdir, Path.GetFileName(assembly.ItemSpec));
                    if (!Directory.Exists(tempDir))
                    {
                        Directory.CreateDirectory(tempDir);
                    }

                    List <string> aotOptions = new List <string> ();

                    if (Profiles != null && Profiles.Length > 0)
                    {
                        aotOptions.Add("profile-only");
                        foreach (var p in Profiles)
                        {
                            var fp = Path.GetFullPath(p.ItemSpec);
                            aotOptions.Add($"profile={GetShortPath (fp)}");
                        }
                    }
                    if (!string.IsNullOrEmpty(AotAdditionalArguments))
                    {
                        aotOptions.Add(AotAdditionalArguments);
                    }
                    if (sequencePointsMode == SequencePointsMode.Offline)
                    {
                        aotOptions.Add("msym-dir=" + GetShortPath(outdir));
                    }
                    if (AotMode != AotMode.Normal)
                    {
                        aotOptions.Add(AotMode.ToString().ToLowerInvariant());
                    }

                    aotOptions.Add("outfile=" + GetShortPath(outputFile));
                    aotOptions.Add("asmwriter");
                    aotOptions.Add("mtriple=" + mtriple);
                    aotOptions.Add("tool-prefix=" + GetShortPath(toolPrefix));
                    aotOptions.Add("ld-flags=" + ldFlags);
                    aotOptions.Add("llvm-path=" + GetShortPath(sdkBinDirectory));
                    aotOptions.Add("temp-path=" + GetShortPath(tempDir));

                    string aotOptionsStr = (EnableLLVM ? "--llvm " : "") + "--aot=" + string.Join(",", aotOptions);

                    if (!string.IsNullOrEmpty(ExtraAotOptions))
                    {
                        aotOptionsStr += (aotOptions.Count > 0 ? "," : "") + ExtraAotOptions;
                    }

                    // Due to a Monodroid MSBuild bug we can end up with paths to assemblies that are not in the intermediate
                    // assembly directory (typically obj/assemblies). This can lead to problems with the Mono loader not being
                    // able to find their dependency laters, since framework assemblies are stored in different directories.
                    // This can happen when linking is disabled (AndroidLinkMode=None). Workaround this problem by resolving
                    // the paths to the right assemblies manually.
                    var resolvedPath             = Path.GetFullPath(assembly.ItemSpec);
                    var intermediateAssemblyPath = Path.Combine(IntermediateAssemblyDir, Path.GetFileName(assembly.ItemSpec));

                    if (LinkMode.ToLowerInvariant() == "none")
                    {
                        if (!resolvedPath.Contains(IntermediateAssemblyDir) && File.Exists(intermediateAssemblyPath))
                        {
                            resolvedPath = intermediateAssemblyPath;
                        }
                    }

                    var assembliesPath = Path.GetFullPath(Path.GetDirectoryName(resolvedPath));
                    var assemblyPath   = QuoteFileName(Path.GetFullPath(resolvedPath));

                    yield return(new Config(assembliesPath, QuoteFileName(aotCompiler), aotOptionsStr, assemblyPath, outputFile));
                }
            }
        }
Beispiel #30
0
        void AddAssemblies(ZipArchiveEx apk, bool debug, bool compress, IDictionary <string, CompressedAssemblyInfo> compressedAssembliesInfo)
        {
            string sourcePath;

            AssemblyCompression.AssemblyData compressedAssembly = null;
            string compressedOutputDir = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(ApkOutputPath), "..", "lz4"));

            int count = 0;

            foreach (ITaskItem assembly in ResolvedUserAssemblies)
            {
                if (bool.TryParse(assembly.GetMetadata("AndroidSkipAddToPackage"), out bool value) && value)
                {
                    Log.LogDebugMessage($"Skipping {assembly.ItemSpec} due to 'AndroidSkipAddToPackage' == 'true' ");
                    continue;
                }
                if (MonoAndroidHelper.IsReferenceAssembly(assembly.ItemSpec))
                {
                    Log.LogCodedWarning("XA0107", assembly.ItemSpec, 0, Properties.Resources.XA0107, assembly.ItemSpec);
                }

                sourcePath = CompressAssembly(assembly);

                // Add assembly
                var assemblyPath = GetAssemblyPath(assembly, frameworkAssembly: false);
                AddFileToArchiveIfNewer(apk, sourcePath, assemblyPath + Path.GetFileName(assembly.ItemSpec), compressionMethod: UncompressedMethod);

                // Try to add config if exists
                var config = Path.ChangeExtension(assembly.ItemSpec, "dll.config");
                AddAssemblyConfigEntry(apk, assemblyPath, config);

                // Try to add symbols if Debug
                if (debug)
                {
                    var symbols = Path.ChangeExtension(assembly.ItemSpec, "dll.mdb");

                    if (File.Exists(symbols))
                    {
                        AddFileToArchiveIfNewer(apk, symbols, assemblyPath + Path.GetFileName(symbols), compressionMethod: UncompressedMethod);
                    }

                    symbols = Path.ChangeExtension(assembly.ItemSpec, "pdb");

                    if (File.Exists(symbols))
                    {
                        AddFileToArchiveIfNewer(apk, symbols, assemblyPath + Path.GetFileName(symbols), compressionMethod: UncompressedMethod);
                    }
                }
                count++;
                if (count >= ZipArchiveEx.ZipFlushFilesLimit)
                {
                    apk.Flush();
                    count = 0;
                }
            }

            count = 0;
            // Add framework assemblies
            foreach (ITaskItem assembly in ResolvedFrameworkAssemblies)
            {
                if (bool.TryParse(assembly.GetMetadata("AndroidSkipAddToPackage"), out bool value) && value)
                {
                    Log.LogDebugMessage($"Skipping {assembly.ItemSpec} due to 'AndroidSkipAddToPackage' == 'true' ");
                    continue;
                }

                if (MonoAndroidHelper.IsReferenceAssembly(assembly.ItemSpec))
                {
                    Log.LogCodedWarning("XA0107", assembly.ItemSpec, 0, Properties.Resources.XA0107, assembly.ItemSpec);
                }

                sourcePath = CompressAssembly(assembly);
                var assemblyPath = GetAssemblyPath(assembly, frameworkAssembly: true);
                AddFileToArchiveIfNewer(apk, sourcePath, assemblyPath + Path.GetFileName(assembly.ItemSpec), compressionMethod: UncompressedMethod);
                var config = Path.ChangeExtension(assembly.ItemSpec, "dll.config");
                AddAssemblyConfigEntry(apk, assemblyPath, config);
                // Try to add symbols if Debug
                if (debug)
                {
                    var symbols = Path.ChangeExtension(assembly.ItemSpec, "dll.mdb");

                    if (File.Exists(symbols))
                    {
                        AddFileToArchiveIfNewer(apk, symbols, assemblyPath + Path.GetFileName(symbols), compressionMethod: UncompressedMethod);
                    }

                    symbols = Path.ChangeExtension(assembly.ItemSpec, "pdb");

                    if (File.Exists(symbols))
                    {
                        AddFileToArchiveIfNewer(apk, symbols, assemblyPath + Path.GetFileName(symbols), compressionMethod: UncompressedMethod);
                    }
                }
                count++;
                if (count >= ZipArchiveEx.ZipFlushFilesLimit)
                {
                    apk.Flush();
                    count = 0;
                }
            }

            void EnsureCompressedAssemblyData(string sourcePath, uint descriptorIndex)
            {
                if (compressedAssembly == null)
                {
                    compressedAssembly = new AssemblyCompression.AssemblyData(sourcePath, descriptorIndex);
                }
                else
                {
                    compressedAssembly.SetData(sourcePath, descriptorIndex);
                }
            }

            string CompressAssembly(ITaskItem assembly)
            {
                if (!compress)
                {
                    return(assembly.ItemSpec);
                }

                if (bool.TryParse(assembly.GetMetadata("AndroidSkipCompression"), out bool value) && value)
                {
                    Log.LogDebugMessage($"Skipping compression of {assembly.ItemSpec} due to 'AndroidSkipCompression' == 'true' ");
                    return(assembly.ItemSpec);
                }

                var key = CompressedAssemblyInfo.GetDictionaryKey(assembly);

                if (compressedAssembliesInfo.TryGetValue(key, out CompressedAssemblyInfo info) && info != null)
                {
                    EnsureCompressedAssemblyData(assembly.ItemSpec, info.DescriptorIndex);
                    string assemblyOutputDir;
                    string subDirectory = assembly.GetMetadata("DestinationSubDirectory");
                    if (!String.IsNullOrEmpty(subDirectory))
                    {
                        assemblyOutputDir = Path.Combine(compressedOutputDir, subDirectory);
                    }
                    else
                    {
                        assemblyOutputDir = compressedOutputDir;
                    }
                    AssemblyCompression.CompressionResult result = AssemblyCompression.Compress(compressedAssembly, assemblyOutputDir);
                    if (result != AssemblyCompression.CompressionResult.Success)
                    {
                        switch (result)
                        {
                        case AssemblyCompression.CompressionResult.EncodingFailed:
                            Log.LogMessage($"Failed to compress {assembly.ItemSpec}");
                            break;

                        case AssemblyCompression.CompressionResult.InputTooBig:
                            Log.LogMessage($"Input assembly {assembly.ItemSpec} exceeds maximum input size");
                            break;

                        default:
                            Log.LogMessage($"Unknown error compressing {assembly.ItemSpec}");
                            break;
                        }
                        return(assembly.ItemSpec);
                    }
                    return(compressedAssembly.DestinationPath);
                }
                else
                {
                    Log.LogDebugMessage($"Assembly missing from {nameof (CompressedAssemblyInfo)}: {key}");
                }

                return(assembly.ItemSpec);
            }
        }