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    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", Properties.Resources.XA5102, 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", Properties.Resources.XA5103, 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", Properties.Resources.XA5201, proc.ExitCode);
                    return(false);
                }
                results.Add(new TaskItem(Path.Combine(outpath, "libmonodroid_bundle_app.so")));
            }
            OutputNativeLibraries = results.ToArray();
            return(true);
        }
Пример #3
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);
                }

                string ldName = String.Empty;
                if (EnableLLVM)
                {
                    ldName = NdkUtil.GetNdkTool(AndroidNdkDirectory, arch, "ld", level);
                    if (!String.IsNullOrEmpty(ldName))
                    {
                        ldName = Path.GetFileName(ldName);
                        if (ldName.IndexOf('-') >= 0)
                        {
                            ldName = ldName.Substring(ldName.LastIndexOf("-") + 1);
                        }
                    }
                }
                else
                {
                    ldName = "ld";
                }

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

                    if (!String.IsNullOrEmpty(ldName))
                    {
                        // MUST be before `ld-flags`, otherwise Mono fails to parse it on Windows
                        aotOptions.Add($"ld-name={ldName}");
                    }

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