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