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

            string toolchainDir = GetToolchainDir(androidNdkPath);
            string toolName;
            bool   forCompiler = false;

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

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

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

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

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

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

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

            Diagnostic.Error(5105, Properties.Resources.XA5105, toolName, arch, toolchainDir);
            return(null);
        }
Exemple #2
0
        IEnumerable <Config> GetLinkerConfigs()
        {
            var abis = new Dictionary <string, InputFiles> (StringComparer.Ordinal);

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

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

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

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

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

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

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

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

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

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

                string ld = MonoAndroidHelper.GetExecutablePath(AndroidBinUtilsDirectory, $"{NdkUtil.GetNdkToolchainPrefix (arch, false)}ld");
                yield return(new Config {
                    LinkerPath = Path.Combine(AndroidBinUtilsDirectory, ld),
                    LinkerOptions = String.Join(" ", linkerArgs),
                    OutputSharedLibrary = inputs.OutputSharedLibrary,
                });
            }
        }
        IEnumerable <Config> GetAssemblerConfigs()
        {
            foreach (ITaskItem item in Sources)
            {
                string            abi    = item.GetMetadata("abi")?.ToLowerInvariant();
                string            prefix = String.Empty;
                AndroidTargetArch arch;

                switch (abi)
                {
                case "armeabi-v7a":
                    prefix = Path.Combine(AndroidBinUtilsDirectory, "arm-linux-androideabi");
                    arch   = AndroidTargetArch.Arm;
                    break;

                case "arm64":
                case "arm64-v8a":
                case "aarch64":
                    prefix = Path.Combine(AndroidBinUtilsDirectory, "aarch64-linux-android");
                    arch   = AndroidTargetArch.Arm64;
                    break;

                case "x86":
                    prefix = Path.Combine(AndroidBinUtilsDirectory, "i686-linux-android");
                    arch   = AndroidTargetArch.X86;
                    break;

                case "x86_64":
                    prefix = Path.Combine(AndroidBinUtilsDirectory, "x86_64-linux-android");
                    arch   = AndroidTargetArch.X86_64;
                    break;

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

                // We don't need the directory since our WorkingDirectory is (and must be) where all the
                // sources are (because of the typemap.inc file being included by the other sources with
                // a relative path of `.`)
                string sourceFile       = Path.GetFileName(item.ItemSpec);
                var    assemblerOptions = new List <string> {
                    "--warn",
                    "-o",
                    QuoteFileName(sourceFile.Replace(".s", ".o"))
                };

                if (DebugBuild)
                {
                    assemblerOptions.Add("-g");
                }

                assemblerOptions.Add(QuoteFileName(sourceFile));

                string baseExecutablePath = $"{prefix}-as";
                string executableDir      = Path.GetDirectoryName(baseExecutablePath);
                string executableName     = MonoAndroidHelper.GetExecutablePath(executableDir, Path.GetFileName(baseExecutablePath));

                yield return(new Config {
                    InputSource = item.ItemSpec,
                    AssemblerPath = Path.Combine(executableDir, executableName),
                    AssemblerOptions = String.Join(" ", assemblerOptions),
                });
            }
        }
Exemple #4
0
        public bool RunTask()
        {
            Log.LogDebugMessage("ResolveSdksTask:");
            Log.LogDebugMessage("  AndroidApiLevel: {0}", AndroidApiLevel);
            Log.LogDebugMessage("  AndroidSdkBuildToolsVersion: {0}", AndroidSdkBuildToolsVersion);
            Log.LogDebugMessage($"  {nameof (AndroidSdkPath)}: {AndroidSdkPath}");
            Log.LogDebugMessage($"  {nameof (AndroidNdkPath)}: {AndroidNdkPath}");
            Log.LogDebugTaskItems("  ReferenceAssemblyPaths: ", ReferenceAssemblyPaths);
            Log.LogDebugMessage("  TargetFrameworkVersion: {0}", TargetFrameworkVersion);
            Log.LogDebugMessage("  UseLatestAndroidPlatformSdk: {0}", UseLatestAndroidPlatformSdk);
            Log.LogDebugMessage("  SequencePointsMode: {0}", SequencePointsMode);
            Log.LogDebugMessage("  LintToolPath: {0}", LintToolPath);

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

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

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

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

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

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

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

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

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

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

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

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

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

            ApkSignerToolExe    = MonoAndroidHelper.GetExecutablePath(AndroidSdkBuildToolsBinPath, ApkSigner);
            AndroidUseApkSigner = File.Exists(Path.Combine(AndroidSdkBuildToolsBinPath, ApkSignerToolExe));

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

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

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

            SequencePointsMode mode;

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

            MonoAndroidHelper.TargetFrameworkDirectories = ReferenceAssemblyPaths;

            AndroidApiLevelName = MonoAndroidHelper.SupportedVersions.GetIdFromApiLevel(AndroidApiLevel);

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

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

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

            //note: this task does not error out if it doesn't find all things. that's the job of the targets
            return(!Log.HasLoggedErrors);
        }