public void TestNdkUtil()
        {
            var log = new TaskLoggingHelper(engine, TestName);

            using (var builder = new Builder()) {
                var ndkDir = AndroidNdkPath;
                var sdkDir = AndroidSdkPath;
                MonoAndroidHelper.AndroidSdk = new AndroidSdkInfo((arg1, arg2) => { }, sdkDir, ndkDir, AndroidSdkResolver.GetJavaSdkPath());
                NdkUtil.Init(log, ndkDir);
                var platforms = NdkUtil.GetSupportedPlatforms(log, ndkDir);
                Assert.AreNotEqual(0, platforms.Count(), "No platforms found");
                var arch = AndroidTargetArch.X86;
                Assert.IsTrue(NdkUtil.ValidateNdkPlatform(log, ndkDir, arch, enableLLVM: false));
                Assert.AreEqual(0, errors.Count, "NdkUtil.ValidateNdkPlatform should not have returned false.");
                int level    = NdkUtil.GetMinimumApiLevelFor(arch, ndkDir);
                int expected = 16;
                Assert.AreEqual(expected, level, $"Min Api Level for {arch} should be {expected}.");
                var compilerNoQuotes = NdkUtil.GetNdkTool(ndkDir, arch, "gcc", level);
                Assert.AreEqual(0, errors.Count, "NdkUtil.GetNdkTool should not have errored.");
                Assert.NotNull(compilerNoQuotes, "NdkUtil.GetNdkTool returned null.");
                var gas = NdkUtil.GetNdkTool(ndkDir, arch, "as", level);
                Assert.AreEqual(0, errors.Count, "NdkUtil.GetNdkTool should not have errored.");
                Assert.NotNull(gas, "NdkUtil.GetNdkTool returned null.");
                var inc = NdkUtil.GetNdkPlatformIncludePath(ndkDir, arch, level);
                Assert.NotNull(inc, " NdkUtil.GetNdkPlatformIncludePath should not return null");
                var libPath = NdkUtil.GetNdkPlatformLibPath(ndkDir, arch, level);
                Assert.NotNull(libPath, "NdkUtil.GetNdkPlatformLibPath  should not return null");
                string ld = NdkUtil.GetNdkTool(ndkDir, arch, "ld", level);
                Assert.AreEqual(0, errors.Count, "NdkUtil.GetNdkTool should not have errored.");
                Assert.NotNull(ld, "NdkUtil.GetNdkTool returned null.");
            }
        }
Exemple #2
0
        public bool RunTask()
        {
            MonoAndroidHelper.RefreshAndroidSdk(string.Empty, AndroidNdkDirectory, string.Empty, Log);
            if (!NdkUtil.Init(Log, AndroidNdkDirectory))
            {
                return(false);
            }

            try
            {
                return(DoExecute());
            }
            catch (XamarinAndroidException e)
            {
                Log.LogCodedError(string.Format("XA{0:0000}", e.Code), e.MessageWithoutCode);
                if (MonoAndroidHelper.LogInternalExceptions)
                {
                    Log.LogMessage(e.ToString());
                }
            }
            catch (Exception ex)
            {
                Log.LogErrorFromException(ex);
                return(false);
            }
            return(!Log.HasLoggedErrors);
        }
Exemple #3
0
        public static void AssertValidEnvironmentSharedLibrary(string outputDirectoryRoot, string sdkDirectory, string ndkDirectory, string supportedAbis)
        {
            NdkUtil.Init(ndkDirectory);
            MonoAndroidHelper.AndroidSdk = new AndroidSdkInfo((arg1, arg2) => {}, sdkDirectory, ndkDirectory);

            AndroidTargetArch arch;

            foreach (string abi in supportedAbis.Split(';'))
            {
                switch (abi)
                {
                case "armeabi-v7a":
                    arch = AndroidTargetArch.Arm;
                    break;

                case "arm64":
                case "arm64-v8a":
                case "aarch64":
                    arch = AndroidTargetArch.Arm64;
                    break;

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

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

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

                string envSharedLibrary = Path.Combine(outputDirectoryRoot, "app_shared_libraries", abi, "libxamarin-app.so");
                Assert.IsTrue(File.Exists(envSharedLibrary), $"Application environment SharedLibrary '{envSharedLibrary}' must exist");

                // API level doesn't matter in this case
                AssertSharedLibraryHasRequiredSymbols(envSharedLibrary, NdkUtil.GetNdkTool(ndkDirectory, arch, "readelf", 0));
            }
        }
Exemple #4
0
        bool CompileNDK(IEnumerable <string> files)
        {
            var monoPath = Path.Combine(MonoSdkPath, "include", "mono-2.0");
            var name     = Path.GetFileNameWithoutExtension(Project.Assemblies[0]);
            var libName  = $"lib{name}.so";
            var ndkPath  = AndroidSdk.AndroidNdkPath;

            foreach (var abi in new[] { "armeabi", "armeabi-v7a", "arm64-v8a", "x86", "x86_64" })
            {
                string            extra = string.Empty;
                AndroidTargetArch targetArch;
                switch (abi)
                {
                case "armeabi":
                    targetArch = AndroidTargetArch.Arm;
                    break;

                case "armeabi-v7a":
                    targetArch = AndroidTargetArch.Arm;
                    extra      = " -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16";
                    break;

                case "arm64-v8a":
                    targetArch = AndroidTargetArch.Arm64;
                    break;

                case "x86":
                    targetArch = AndroidTargetArch.X86;
                    extra      = " -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32";
                    break;

                case "x86_64":
                    targetArch = AndroidTargetArch.X86_64;
                    extra      = " -march=x86-64 -mtune=intel -msse4.2 -mpopcnt -m64";
                    break;

                default:
                    throw new NotImplementedException();
                }

                var clangBin      = NdkUtil.GetNdkClangBin(Path.Combine(ndkPath, "toolchains"), targetArch);
                var systemInclude = NdkUtil.GetNdkPlatformIncludePath(ndkPath, targetArch, XamarinAndroid.ApiLevel);
                var monoDroidPath = Path.Combine(XamarinAndroid.LibraryPath, abi);
                var abiDir        = Path.Combine(Options.OutputDir, "android", "jni", abi);
                var outputPath    = Path.Combine(abiDir, libName);

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

                var args = new List <string> {
                    $"--sysroot=\"{systemInclude}\"{extra}",
                    "-fdiagnostics-color",
                    $"-D{DLLExportDefine}",
                    $"-I\"{monoPath}\"",
                    $"-L\"{monoDroidPath}\" -lmonosgen-2.0 -lmono-android.release",
                    string.Join(" ", files.ToList()),
                    "--std=c99",
                    $"-shared -o {outputPath}",
                };

                var invocation = string.Join(" ", args);
                var output     = Invoke(clangBin, invocation);
                if (output.ExitCode != 0)
                {
                    return(false);
                }
            }
            return(true);
        }
        void CompileNDK(IEnumerable <string> files)
        {
            RefreshAndroidSdk();

            var monoPath = ManagedToolchain.FindMonoPath();
            var name     = Path.GetFileNameWithoutExtension(Project.Assemblies[0]);
            var libName  = $"lib{name}.so";
            var ndkPath  = AndroidSdk.AndroidNdkPath;

            //NOTE: "arm64-v8a" doesn't compile at the moment
            foreach (var abi in new[] { "armeabi", "armeabi-v7a", "x86", "x86_64" })
            {
                string            extra = string.Empty;
                AndroidTargetArch targetArch;
                switch (abi)
                {
                case "armeabi":
                    targetArch = AndroidTargetArch.Arm;
                    break;

                case "armeabi-v7a":
                    targetArch = AndroidTargetArch.Arm;
                    extra      = " -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16";
                    break;

                case "arm64-v8a":
                    targetArch = AndroidTargetArch.Arm64;
                    break;

                case "x86":
                    targetArch = AndroidTargetArch.X86;
                    extra      = " -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32";
                    break;

                case "x86_64":
                    targetArch = AndroidTargetArch.X86_64;
                    extra      = " -march=x86-64 -mtune=intel -msse4.2 -mpopcnt -m64";
                    break;

                default:
                    throw new NotImplementedException();
                }

                var clangBin      = NdkUtil.GetNdkClangBin(Path.Combine(ndkPath, "toolchains"), targetArch);
                var systemInclude = NdkUtil.GetNdkPlatformIncludePath(ndkPath, targetArch, 24); //NOTE: 24 should be an option?
                var monoDroidPath = Path.Combine(MonoDroidSdk.BinPath, "..", "lib", "xbuild", "Xamarin", "Android", "lib", abi);
                var abiDir        = Path.Combine(Options.OutputDir, "android", "jni", abi);
                var output        = Path.Combine(abiDir, libName);

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

                var args = new List <string> {
                    $"--sysroot=\"{systemInclude}\"{extra}",
                    $"-D{DLLExportDefine}",
                    $"-I\"{monoPath}/include/mono-2.0\"",
                    $"-L\"{monoDroidPath}\" -lmonosgen-2.0",
                    string.Join(" ", files.ToList()),
                    "--std=c99",
                    $"-shared -o {output}",
                };

                var invocation = string.Join(" ", args);
                Invoke(clangBin, invocation);
            }
        }
Exemple #6
0
        bool CompileNDK(IEnumerable <string> files)
        {
            var monoPath = Path.Combine(MonoSdkPath, "include", "mono-2.0");
            var name     = Path.GetFileNameWithoutExtension(Project.Assemblies[0]);
            var libName  = $"lib{name}.so";
            var ndkPath  = XamarinAndroid.AndroidSdk.AndroidNdkPath;

            foreach (var abi in new[] { "armeabi-v7a", "arm64-v8a", "x86", "x86_64" })
            {
                string            extra = string.Empty;
                AndroidTargetArch targetArch;
                switch (abi)
                {
                case "armeabi-v7a":
                    targetArch = AndroidTargetArch.Arm;
                    extra      = " -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16";
                    break;

                case "arm64-v8a":
                    targetArch = AndroidTargetArch.Arm64;
                    break;

                case "x86":
                    targetArch = AndroidTargetArch.X86;
                    extra      = " -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32";
                    break;

                case "x86_64":
                    targetArch = AndroidTargetArch.X86_64;
                    extra      = " -march=x86-64 -mtune=intel -msse4.2 -mpopcnt -m64";
                    break;

                default:
                    throw new NotImplementedException();
                }

                bool isLLVM   = false;
                var  clangBin = NdkUtil.GetNdkClangBin(Path.Combine(ndkPath, "toolchains"), targetArch);
                if (string.IsNullOrEmpty(clangBin))
                {
                    clangBin = NdkUtil.GetNdkClangBin(Path.Combine(ndkPath, "toolchains", "llvm"), targetArch);
                    isLLVM   = true;
                }
                if (string.IsNullOrEmpty(clangBin))
                {
                    throw new Exception($"Unable to find NDK toolchain for {abi}!");
                }
                var systemInclude = NdkUtil.GetNdkPlatformIncludePath(ndkPath, targetArch, XamarinAndroid.ApiLevel, isLLVM);
                //var newPath = "D:\\dev\\githubMP\\flutter\\embeddinatorfork\\external\\Xamarin.Android\\lib\\xbuild\\Xamarin\\Android\\lib";
                //var monoDroidPath = Path.Combine(newPath, abi); // XamarinAndroid.LibraryPath, abi);
                var monoDroidPath = Path.Combine(XamarinAndroid.LibraryPath, abi);
                var abiDir        = Path.Combine(Options.OutputDir, "android", "jni", abi);
                var outputPath    = Path.Combine(abiDir, libName);

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

                var args = new List <string> {
                    $"--sysroot=\"{systemInclude}\"{extra}",
                    "-fdiagnostics-color",
                    $"-D{DLLExportDefine}",
                    $"-I\"{monoPath}\"",
                    $"-L\"{monoDroidPath}\" -lmonosgen-2.0 -lmono-android.release",
                    string.Join(" ", files.ToList()),
                    "--std=c11",
                    "-fPIC",
                    $"-shared -o {outputPath}",
                };
                if (isLLVM)
                {
                    args.Add($"--target={NdkUtil.GetLlvmToolchainTarget(targetArch, XamarinAndroid.ApiLevel)}");
                }

                var invocation = string.Join(" ", args);
                var output     = Invoke(clangBin, invocation);
                if (output.ExitCode != 0)
                {
                    return(false);
                }
            }
            return(true);
        }
Exemple #7
0
        bool DoExecute()
        {
            var    results    = new List <ITaskItem>();
            string bundlepath = Path.Combine(TempOutputPath, "bundles");

            if (!Directory.Exists(bundlepath))
            {
                Directory.CreateDirectory(bundlepath);
            }
            else if (ProduceStub && Link)
            {
                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);
                }

                var    clb     = new CommandLineBuilder();
                var    proc    = new Process();
                var    outpath = Path.Combine(bundlepath, abi);
                int    level   = NdkUtil.GetMinimumApiLevelFor(arch, AndroidNdkDirectory);
                string windowsCompilerSwitches = NdkUtil.GetCompilerTargetParameters(AndroidNdkDirectory, arch, level);
                var    compilerNoQuotes        = NdkUtil.GetNdkTool(AndroidNdkDirectory, arch, "gcc", level);
                var    compiler = $"\"{compilerNoQuotes}\" {windowsCompilerSwitches}".Trim();
                if (ProduceStub)
                {
                    if (!Directory.Exists(outpath))
                    {
                        Directory.CreateDirectory(outpath);
                    }

                    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("-v");
                    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 gas = '"' + NdkUtil.GetNdkTool(AndroidNdkDirectory, arch, "as", level) + '"';
                    psi.EnvironmentVariables["CC"] = compiler;
                    psi.EnvironmentVariables["AS"] = gas;
                    Log.LogDebugMessage("CC=" + compiler);
                    Log.LogDebugMessage("AS=" + gas);
                    Log.LogDebugMessage("[mkbundle] " + psi.FileName + " " + clb);
                    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 ...

                if (Link)
                {
                    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);
        }