Example #1
0
        public override bool Execute()
        {
            Log.LogDebugMessage("BuildApk Task");
            Log.LogDebugMessage("  ApkInputPath: {0}", ApkInputPath);
            Log.LogDebugMessage("  ApkOutputPath: {0}", ApkOutputPath);
            Log.LogDebugMessage("  BundleAssemblies: {0}", BundleAssemblies);
            Log.LogDebugTaskItems("  DalvikClasses:", DalvikClasses);
            Log.LogDebugMessage("  SupportedAbis: {0}", SupportedAbis);
            Log.LogDebugMessage("  UseSharedRuntime: {0}", UseSharedRuntime);
            Log.LogDebugMessage("  Debug: {0}", Debug ?? "no");
            Log.LogDebugMessage("  PreferNativeLibrariesWithDebugSymbols: {0}", PreferNativeLibrariesWithDebugSymbols);
            Log.LogDebugMessage("  EmbedAssemblies: {0}", EmbedAssemblies);
            Log.LogDebugMessage("  AndroidSequencePointsMode: {0}", AndroidSequencePointsMode);
            Log.LogDebugMessage("  CreatePackagePerAbi: {0}", CreatePackagePerAbi);
            Log.LogDebugMessage("  UncompressedFileExtensions: {0}", UncompressedFileExtensions);
            Log.LogDebugTaskItems("  ResolvedUserAssemblies:", ResolvedUserAssemblies);
            Log.LogDebugTaskItems("  ResolvedFrameworkAssemblies:", ResolvedFrameworkAssemblies);
            Log.LogDebugTaskItems("  NativeLibraries:", NativeLibraries);
            Log.LogDebugTaskItems("  AdditionalNativeLibraryReferences:", AdditionalNativeLibraryReferences);
            Log.LogDebugTaskItems("  BundleNativeLibraries:", BundleNativeLibraries);
            Log.LogDebugTaskItems("  JavaSourceFiles:", JavaSourceFiles);
            Log.LogDebugTaskItems("  JavaLibraries:", JavaLibraries);
            Log.LogDebugTaskItems("  LibraryProjectJars:", LibraryProjectJars);
            Log.LogDebugTaskItems("  AdditionalNativeLibraryReferences:", AdditionalNativeLibraryReferences);

            Aot.TryGetSequencePointsMode(AndroidSequencePointsMode, out sequencePointsMode);

            if (string.IsNullOrEmpty(AndroidEmbedProfilers) && _Debug)
            {
                AndroidEmbedProfilers = "log";
            }

            var outputFiles = new List <string> ();

            uncompressedFileExtensions = UncompressedFileExtensions?.Split(new char [] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries) ?? new string [0];

            ExecuteWithAbi(SupportedAbis, ApkInputPath, ApkOutputPath);
            outputFiles.Add(ApkOutputPath);
            if (CreatePackagePerAbi)
            {
                var abis = SupportedAbis.Split(new char[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
                if (abis.Length > 1)
                {
                    foreach (var abi in abis)
                    {
                        var path = Path.GetDirectoryName(ApkOutputPath);
                        var apk  = Path.GetFileNameWithoutExtension(ApkOutputPath);
                        ExecuteWithAbi(abi, String.Format("{0}-{1}", ApkInputPath, abi),
                                       Path.Combine(path, String.Format("{0}-{1}.apk", apk, abi)));
                        outputFiles.Add(Path.Combine(path, String.Format("{0}-{1}.apk", apk, abi)));
                    }
                }
            }

            OutputFiles = outputFiles.Select(a => new TaskItem(a)).ToArray();

            Log.LogDebugTaskItems("  [Output] OutputFiles :", OutputFiles);

            return(!Log.HasLoggedErrors);
        }
Example #2
0
        void UpdateWhenChanged(string path, string type, MemoryStream ms, Action <Stream> generator)
        {
            if (InstantRunEnabled)
            {
                ms.SetLength(0);
                generator(ms);
                MonoAndroidHelper.CopyIfStreamChanged(ms, path);
            }

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

            using (var stream = new NativeAssemblyDataStream()) {
                generator(stream);
                stream.EndOfFile();
                MonoAndroidHelper.CopyIfStreamChanged(stream, dataFilePath);

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

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

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

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

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

                    var    asmgen      = new TypeMappingNativeAssemblyGenerator(asmTargetProvider, stream, dataFileName, stream.MapByteCount, mappingFieldName);
                    string asmFileName = $"{path}.{abi.Trim ()}.s";
                    using (var sw = new StreamWriter(ms, utf8Encoding, bufferSize: 8192, leaveOpen: true)) {
                        asmgen.Write(sw, dataFileName);
                        MonoAndroidHelper.CopyIfStreamChanged(ms, asmFileName);
                    }
                }
            }
        }
Example #3
0
        void ProcessManifest(ITaskItem manifestFile)
        {
            var manifest = Path.IsPathRooted(manifestFile.ItemSpec) ? manifestFile.ItemSpec : Path.Combine(WorkingDirectory, manifestFile.ItemSpec);

            if (!File.Exists(manifest))
            {
                LogDebugMessage("{0} does not exists. Skipping", manifest);
                return;
            }

            bool upToDate = ManifestIsUpToDate(manifest);

            if (AdditionalAndroidResourcePaths != null)
            {
                foreach (var dir in AdditionalAndroidResourcePaths)
                {
                    if (!string.IsNullOrEmpty(dir.ItemSpec))
                    {
                        upToDate = upToDate && ManifestIsUpToDate(string.Format("{0}{1}{2}{3}{4}", dir, Path.DirectorySeparatorChar, "manifest", Path.DirectorySeparatorChar, "AndroidManifest.xml"));
                    }
                }
            }

            if (upToDate)
            {
                LogMessage("  Additional Android Resources manifsets files are unchanged. Skipping.");
                return;
            }

            var defaultAbi = new string [] { null };
            var abis       = SupportedAbis?.Split(new char [] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
            var outputFile = string.IsNullOrEmpty(OutputFile) ? Path.GetTempFileName() : OutputFile;

            foreach (var abi in (CreatePackagePerAbi && abis?.Length > 1) ? defaultAbi.Concat(abis) : defaultAbi)
            {
                var currentResourceOutputFile = abi != null?string.Format("{0}-{1}", outputFile, abi) : outputFile;

                if (!string.IsNullOrEmpty(currentResourceOutputFile) && !Path.IsPathRooted(currentResourceOutputFile))
                {
                    currentResourceOutputFile = Path.Combine(WorkingDirectory, currentResourceOutputFile);
                }
                if (!ExecuteForAbi(GenerateCommandLineCommands(manifest, abi, currentResourceOutputFile), currentResourceOutputFile))
                {
                    Cancel();
                }
            }

            return;
        }
Example #4
0
        int DoExecute(ITaskItem manifestFile, ThreadingTasks.ParallelLoopState state, int loop)
        {
            if (!File.Exists(manifestFile.ItemSpec))
            {
                LogDebugMessage("{0} does not exists. Skipping", manifestFile.ItemSpec);
                return(0);
            }

            bool upToDate = ManifestIsUpToDate(manifestFile.ItemSpec);

            if (AdditionalAndroidResourcePaths != null)
            {
                foreach (var dir in AdditionalAndroidResourcePaths)
                {
                    if (!string.IsNullOrEmpty(dir.ItemSpec))
                    {
                        upToDate = upToDate && ManifestIsUpToDate(string.Format("{0}{1}{2}{3}{4}", dir, Path.DirectorySeparatorChar, "manifest", Path.DirectorySeparatorChar, "AndroidManifest.xml"));
                    }
                }
            }

            if (upToDate)
            {
                LogMessage("  Additional Android Resources manifsets files are unchanged. Skipping.");
                return(0);
            }

            var defaultAbi = new string [] { null };
            var abis       = SupportedAbis?.Split(new char [] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);

            foreach (var abi in (CreatePackagePerAbi && abis?.Length > 1) ? defaultAbi.Concat(abis) : defaultAbi)
            {
                var currentResourceOutputFile = abi != null?string.Format("{0}-{1}", ResourceOutputFile, abi) : ResourceOutputFile;

                if (!ExecuteForAbi(GenerateCommandLineCommands(manifestFile.ItemSpec, abi, currentResourceOutputFile), currentResourceOutputFile))
                {
                    Cancel();
                }
            }

            return(0);
        }
Example #5
0
        public override bool Execute()
        {
            Aot.TryGetSequencePointsMode(AndroidSequencePointsMode, out sequencePointsMode);

            if (string.IsNullOrEmpty(AndroidEmbedProfilers) && _Debug)
            {
                AndroidEmbedProfilers = "log";
            }

            var outputFiles = new List <string> ();

            uncompressedFileExtensions = UncompressedFileExtensions?.Split(new char [] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries) ?? new string [0];

            ExecuteWithAbi(SupportedAbis, ApkInputPath, ApkOutputPath);
            outputFiles.Add(ApkOutputPath);
            if (CreatePackagePerAbi)
            {
                var abis = SupportedAbis.Split(new char[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
                if (abis.Length > 1)
                {
                    foreach (var abi in abis)
                    {
                        var path = Path.GetDirectoryName(ApkOutputPath);
                        var apk  = Path.GetFileNameWithoutExtension(ApkOutputPath);
                        ExecuteWithAbi(abi, String.Format("{0}-{1}", ApkInputPath, abi),
                                       Path.Combine(path, String.Format("{0}-{1}.apk", apk, abi)));
                        outputFiles.Add(Path.Combine(path, String.Format("{0}-{1}.apk", apk, abi)));
                    }
                }
            }

            OutputFiles = outputFiles.Select(a => new TaskItem(a)).ToArray();

            Log.LogDebugTaskItems("  [Output] OutputFiles :", OutputFiles);

            return(!Log.HasLoggedErrors);
        }
Example #6
0
        IEnumerable <Config> GetAotConfigs()
        {
            if (!Directory.Exists(AotOutputDirectory))
            {
                Directory.CreateDirectory(AotOutputDirectory);
            }

            // Check that we have a compatible NDK version for the targeted ABIs.
            NdkUtil.NdkVersion ndkVersion;
            bool hasNdkVersion = NdkUtil.GetNdkToolchainRelease(AndroidNdkDirectory, out ndkVersion);

            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":
                    aotCompiler = Path.Combine(sdkBinDirectory, "cross-arm");
                    outdir      = Path.Combine(AotOutputDirectory, "armeabi");
                    mtriple     = "armv5-linux-gnueabi";
                    arch        = AndroidTargetArch.Arm;
                    break;

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

                var toolPrefix    = NdkUtil.GetNdkToolPrefix(AndroidNdkDirectory, arch);
                var toolchainPath = toolPrefix.Substring(0, toolPrefix.LastIndexOf(Path.DirectorySeparatorChar));
                var ldFlags       = string.Empty;
                if (EnableLLVM)
                {
                    int level = GetNdkApiLevel(AndroidNdkDirectory, AndroidApiLevel, arch);

                    string androidLibPath = string.Empty;
                    try {
                        androidLibPath = NdkUtil.GetNdkPlatformLibPath(AndroidNdkDirectory, arch, level);
                    } catch (InvalidOperationException ex) {
                        Diagnostic.Error(5101, ex.Message);
                    }
                    var libs = new List <string>()
                    {
                        GetShortPath(Path.Combine(GetNdkToolchainLibraryDir(toolchainPath), "libgcc.a")),
                        GetShortPath(Path.Combine(androidLibPath, "libc.so")),
                        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 (!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));
                }
            }
        }
Example #7
0
        public override bool Execute()
        {
            Log.LogDebugMessage("BuildApk Task");
            Log.LogDebugMessage("  ApkInputPath: {0}", ApkInputPath);
            Log.LogDebugMessage("  ApkOutputPath: {0}", ApkOutputPath);
            Log.LogDebugMessage("  BundleAssemblies: {0}", BundleAssemblies);
            Log.LogDebugTaskItems("  DalvikClasses:", DalvikClasses);
            Log.LogDebugMessage("  SupportedAbis: {0}", SupportedAbis);
            Log.LogDebugMessage("  UseSharedRuntime: {0}", UseSharedRuntime);
            Log.LogDebugMessage("  Debug: {0}", Debug ?? "no");
            Log.LogDebugMessage("  EmbedAssemblies: {0}", EmbedAssemblies);
            Log.LogDebugMessage("  AndroidAotMode: {0}", AndroidAotMode);
            Log.LogDebugMessage("  AndroidSequencePointsMode: {0}", AndroidSequencePointsMode);
            Log.LogDebugMessage("  CreatePackagePerAbi: {0}", CreatePackagePerAbi);
            Log.LogDebugTaskItems("  Environments:", Environments);
            Log.LogDebugTaskItems("  ResolvedUserAssemblies:", ResolvedUserAssemblies);
            Log.LogDebugTaskItems("  ResolvedFrameworkAssemblies:", ResolvedFrameworkAssemblies);
            Log.LogDebugTaskItems("  NativeLibraries:", NativeLibraries);
            Log.LogDebugTaskItems("  AdditionalNativeLibraryReferences:", AdditionalNativeLibraryReferences);
            Log.LogDebugTaskItems("  BundleNativeLibraries:", BundleNativeLibraries);
            Log.LogDebugTaskItems("  JavaSourceFiles:", JavaSourceFiles);
            Log.LogDebugTaskItems("  JavaLibraries:", JavaLibraries);
            Log.LogDebugTaskItems("  LibraryProjectJars:", LibraryProjectJars);
            Log.LogDebugTaskItems("  AdditionalNativeLibraryReferences:", AdditionalNativeLibraryReferences);
            Log.LogDebugTaskItems("  HttpClientHandlerType:", HttpClientHandlerType);

            Aot.TryGetSequencePointsMode(AndroidSequencePointsMode, out sequencePointsMode);

            var androidDebugServer = GdbPaths.GetAndroidDebugServer(AndroidGdbDebugServer);

            if (!androidDebugServer.HasValue)
            {
                Log.LogError("Unable to determine debug server variant: {0}", AndroidGdbDebugServer);
                return(false);
            }
            debugServer = androidDebugServer.Value;

            if (string.IsNullOrEmpty(AndroidEmbedProfilers) && _Debug)
            {
                AndroidEmbedProfilers = "log";
            }

            var outputFiles = new List <string> ();

            ExecuteWithAbi(SupportedAbis, ApkInputPath, ApkOutputPath);
            outputFiles.Add(ApkOutputPath);
            if (CreatePackagePerAbi)
            {
                var abis = SupportedAbis.Split(new char[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
                if (abis.Length > 1)
                {
                    foreach (var abi in abis)
                    {
                        var path = Path.GetDirectoryName(ApkOutputPath);
                        var apk  = Path.GetFileNameWithoutExtension(ApkOutputPath);
                        ExecuteWithAbi(abi, String.Format("{0}-{1}", ApkInputPath, abi),
                                       Path.Combine(path, String.Format("{0}-{1}.apk", apk, abi)));
                        outputFiles.Add(Path.Combine(path, String.Format("{0}-{1}.apk", apk, abi)));
                    }
                }
            }

            BuildId = buildId.ToString();

            Log.LogDebugMessage("  [Output] BuildId: {0}", BuildId);

            OutputFiles = outputFiles.Select(a => new TaskItem(a)).ToArray();

            Log.LogDebugTaskItems("  [Output] OutputFiles :", OutputFiles);

            return(!Log.HasLoggedErrors);
        }
        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, requireLibm:true))
                {
                    return(false);
                }

                // FIXME: it is kind of hacky, we should have something like NdkUtil.GetMinimumApiLevelFor(arch).
                int level   = NdkUtil.IsNdk64BitArch(arch) ? 21 : arch == AndroidTargetArch.Arm ? 4 : 9;
                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("--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,
                };
                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);
        }
        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-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,
                };
                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);
                }

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

                clb = new CommandLineBuilder();
                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] " + 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"));

                // 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");
                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);
        }
Example #10
0
        bool DoExecute()
        {
            Log.LogDebugMessage("Aot:", AndroidAotMode);
            Log.LogDebugMessage("  AndroidApiLevel: {0}", AndroidApiLevel);
            Log.LogDebugMessage("  AndroidAotMode: {0}", AndroidAotMode);
            Log.LogDebugMessage("  AndroidSequencePointsMode: {0}", AndroidSequencePointsMode);
            Log.LogDebugMessage("  AndroidNdkDirectory: {0}", AndroidNdkDirectory);
            Log.LogDebugMessage("  AotOutputDirectory: {0}", AotOutputDirectory);
            Log.LogDebugMessage("  EnableLLVM: {0}", EnableLLVM);
            Log.LogDebugMessage("  IntermediateAssemblyDir: {0}", IntermediateAssemblyDir);
            Log.LogDebugMessage("  LinkMode: {0}", LinkMode);
            Log.LogDebugMessage("  SdkBinDirectory: {0}", SdkBinDirectory);
            Log.LogDebugMessage("  SupportedAbis: {0}", SupportedAbis);
            Log.LogDebugTaskItems("  ResolvedAssemblies:", ResolvedAssemblies);
            Log.LogDebugTaskItems("  AdditionalNativeLibraryReferences:", AdditionalNativeLibraryReferences);

            bool hasValidAotMode = GetAndroidAotMode(AndroidAotMode, out AotMode);

            if (!hasValidAotMode)
            {
                Log.LogCodedError("XA3001", "Invalid AOT mode: {0}", AndroidAotMode);
                return(false);
            }

            TryGetSequencePointsMode(AndroidSequencePointsMode, out sequencePointsMode);

            var nativeLibs = new List <string> ();

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

            // Check that we have a compatible NDK version for the targeted ABIs.
            NdkUtil.NdkVersion ndkVersion;
            bool hasNdkVersion = NdkUtil.GetNdkToolchainRelease(AndroidNdkDirectory, out ndkVersion);

            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":
                    aotCompiler = Path.Combine(SdkBinDirectory, "cross-arm");
                    outdir      = Path.Combine(AotOutputDirectory, "armeabi");
                    mtriple     = "armv5-linux-gnueabi";
                    arch        = AndroidTargetArch.Arm;
                    break;

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

                if (!ValidateAotConfiguration(Log, arch, EnableLLVM))
                {
                    return(false);
                }

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

                var toolPrefix    = NdkUtil.GetNdkToolPrefix(AndroidNdkDirectory, arch);
                var toolchainPath = toolPrefix.Substring(0, toolPrefix.LastIndexOf(Path.DirectorySeparatorChar));
                var ldFlags       = string.Empty;
                if (EnableLLVM)
                {
                    int level = GetNdkApiLevel(AndroidNdkDirectory, AndroidApiLevel, arch);

                    string androidLibPath = string.Empty;
                    try {
                        androidLibPath = NdkUtil.GetNdkPlatformLibPath(AndroidNdkDirectory, arch, level);
                    } catch (InvalidOperationException ex) {
                        Diagnostic.Error(5101, ex.Message);
                    }
                    var libs = new List <string>()
                    {
                        QuoteFileName(Path.Combine(GetNdkToolchainLibraryDir(toolchainPath), "libgcc.a")),
                        QuoteFileName(Path.Combine(androidLibPath, "libc.so")),
                        QuoteFileName(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 aotOptions = string.Format(
                        "{0}--aot={8}{1}outfile={2},asmwriter,mtriple={3},tool-prefix={4},ld-flags={5},llvm-path={6},temp-path={7}",
                        EnableLLVM ? "--llvm " : string.Empty,
                        AotMode != AotMode.Normal ? string.Format("{0},", AotMode.ToString().ToLowerInvariant()) : string.Empty,
                        QuoteFileName(outputFile),
                        mtriple,
                        QuoteFileName(toolPrefix),
                        ldFlags,
                        QuoteFileName(SdkBinDirectory),
                        QuoteFileName(outdir),
                        sequencePointsMode == SequencePointsMode.Offline ? string.Format("gen-seq-points-file={0},", seqpointsFile) : string.Empty
                        );

                    // 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));

                    if (!RunAotCompiler(assembliesPath, aotCompiler, aotOptions, assemblyPath))
                    {
                        Log.LogCodedError("XA3001", "Could not AOT the assembly: {0}", assembly.ItemSpec);
                        return(false);
                    }
                    nativeLibs.Add(outputFile);
                }
            }

            NativeLibrariesReferences = nativeLibs.ToArray();

            Log.LogDebugTaskItems("Aot Outputs:");
            Log.LogDebugTaskItems("  NativeLibrariesReferences: ", NativeLibrariesReferences);

            return(true);
        }
Example #11
0
        public override bool Execute()
        {
            Log.LogDebugMessage("Aapt Task");
            Log.LogDebugMessage("  AssetDirectory: {0}", AssetDirectory);
            Log.LogDebugMessage("  ManifestFile: {0}", ManifestFile);
            Log.LogDebugMessage("  ResourceDirectory: {0}", ResourceDirectory);
            Log.LogDebugMessage("  JavaDesignerOutputDirectory: {0}", JavaDesignerOutputDirectory);
            Log.LogDebugMessage("  PackageName: {0}", PackageName);
            Log.LogDebugMessage("  UncompressedFileExtensions: {0}", UncompressedFileExtensions);
            Log.LogDebugMessage("  ExtraPackages: {0}", ExtraPackages);
            Log.LogDebugTaskItems("  AdditionalResourceDirectories: ", AdditionalResourceDirectories);
            Log.LogDebugTaskItems("  AdditionalAndroidResourcePaths: ", AdditionalAndroidResourcePaths);
            Log.LogDebugTaskItems("  LibraryProjectJars: ", LibraryProjectJars);
            Log.LogDebugMessage("  ExtraArgs: {0}", ExtraArgs);
            Log.LogDebugMessage("  CreatePackagePerAbi: {0}", CreatePackagePerAbi);
            Log.LogDebugMessage("  ResourceNameCaseMap: {0}", ResourceNameCaseMap);
            if (CreatePackagePerAbi)
            {
                Log.LogDebugMessage("  SupportedAbis: {0}", SupportedAbis);
            }

            bool upToDate = ManifestIsUpToDate(ManifestFile);

            if (ResourceNameCaseMap != null)
            {
                foreach (var arr in ResourceNameCaseMap.Split(';').Select(l => l.Split('|')).Where(a => a.Length == 2))
                {
                    resource_name_case_map [arr [1]] = arr [0];                     // lowercase -> original
                }
            }
            if (AdditionalAndroidResourcePaths != null)
            {
                foreach (var dir in AdditionalAndroidResourcePaths)
                {
                    if (!string.IsNullOrEmpty(dir.ItemSpec))
                    {
                        upToDate = upToDate && ManifestIsUpToDate(string.Format("{0}{1}{2}", dir, Path.DirectorySeparatorChar, "manifest", Path.DirectorySeparatorChar, "AndroidManifest.xml"));
                    }
                }
            }

            if (upToDate)
            {
                Log.LogMessage(MessageImportance.Normal, "  Additional Android Resources manifsets files are unchanged. Skipping.");
                return(true);
            }

            ExecuteForAbi(null);

            if (CreatePackagePerAbi)
            {
                var abis = SupportedAbis.Split(new char[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
                if (abis.Length > 1)
                {
                    foreach (var abi in abis)
                    {
                        ExecuteForAbi(abi);
                    }
                }
            }
            return(!Log.HasLoggedErrors);
        }
        void AddEnvironment()
        {
            bool   usesEmbeddedDSOs     = false;
            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("__XA_DSO_IN_APK", StringComparison.Ordinal))
                    {
                        usesEmbeddedDSOs = true;
                        continue;
                    }
                    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.Split(new char[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries))
                {
                    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,
                        UsesEmbeddedDSOs    = usesEmbeddedDSOs,
                        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("\"", "\\\""));
            }
        }