Beispiel #1
0
        public static int GetMinimumApiLevelFor(AndroidTargetArch arch, string androidNdkPath)
        {
            var minValue  = NdkUtil.IsNdk64BitArch(arch) ? 21 : arch == AndroidTargetArch.Arm ? 4 : 9;
            var platforms = GetSupportedPlatforms(androidNdkPath).OrderBy(x => x).Where(x => x >= minValue);

            return(platforms.First(x => Directory.Exists(Path.Combine(androidNdkPath, "platforms", $"android-{x}", $"arch-{archPathMap[arch]}"))));
        }
Beispiel #2
0
        public static bool ValidateNdkPlatform(TaskLoggingHelper log, string ndkPath, AndroidTargetArch arch, bool enableLLVM)
        {
            // Check that we have a compatible NDK version for the targeted ABIs.
            NdkUtil.NdkVersion ndkVersion;
            bool hasNdkVersion = NdkUtil.GetNdkToolchainRelease(ndkPath, out ndkVersion);

            if (NdkUtil.IsNdk64BitArch(arch) && hasNdkVersion && ndkVersion.Version < 10)
            {
                log.LogMessage(MessageImportance.High,
                               "The detected Android NDK version is incompatible with the targeted 64-bit architecture, " +
                               "please upgrade to NDK r10 or newer.");
            }

            // NDK r10d is buggy and cannot link x86_64 ABI shared libraries because they are 32-bits.
            // See https://code.google.com/p/android/issues/detail?id=161421
            if (enableLLVM && ndkVersion.Version == 10 && ndkVersion.Revision == "d" && arch == AndroidTargetArch.X86_64)
            {
                log.LogCodedError("XA3004", "Android NDK r10d is buggy and provides an incompatible x86_64 libm.so. " +
                                  "See https://code.google.com/p/android/issues/detail?id=161422.");
                return(false);
            }

            if (enableLLVM && (ndkVersion.Version < 10 || (ndkVersion.Version == 10 && ndkVersion.Revision[0] < 'd')))
            {
                log.LogCodedError("XA3005",
                                  "The detected Android NDK version is incompatible with the targeted LLVM configuration, " +
                                  "please upgrade to NDK r10d or newer.");
            }

            return(true);
        }
Beispiel #3
0
        public override bool Execute()
        {
            Log.LogDebugMessage("Assemblies: {0}", Assemblies.Length);
            Log.LogDebugMessage("SupportedAbis: {0}", SupportedAbis);
            Log.LogDebugMessage("AutoDeps: {0}", AutoDeps);

            NdkUtil.Init(AndroidNdkDirectory);

            try {
                if (String.IsNullOrEmpty(AndroidNdkDirectory))
                {
                    Log.LogCodedError("XA5101", "Could not locate Android NDK. Please make sure to configure path to NDK in SDK Locations or set via /p:AndroidNdkDirectory in the MSBuild/xbuild argument.");
                    return(false);
                }
                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(!Log.HasLoggedErrors);
        }
Beispiel #4
0
        public override bool RunTask()
        {
            if (EnableLLVM && !NdkUtil.Init(LogCodedError, AndroidNdkDirectory))
            {
                return(false);
            }

            return(base.RunTask());
        }
Beispiel #5
0
        public override bool RunTask()
        {
            // NdkUtil must always be initialized - once per thread
            if (!NdkUtil.Init(LogCodedError, AndroidNdkDirectory))
            {
                return(false);
            }

            return(base.RunTask());
        }
Beispiel #6
0
        public async override System.Threading.Tasks.Task RunTaskAsync()
        {
            // NdkUtil must always be initialized - once per thread
            if (!NdkUtil.Init(LogCodedError, AndroidNdkDirectory))
            {
                LogDebugMessage("Failed to initialize NdkUtil");
                return;
            }

            bool hasValidAotMode = GetAndroidAotMode(AndroidAotMode, out AotMode);

            if (!hasValidAotMode)
            {
                LogCodedError("XA3002", Properties.Resources.XA3002, AndroidAotMode);
                return;
            }

            if (AotMode == AotMode.Interp)
            {
                LogDebugMessage("Interpreter AOT mode enabled");
                return;
            }

            TryGetSequencePointsMode(AndroidSequencePointsMode, out sequencePointsMode);

            var nativeLibs = new List <string> ();

            await this.WhenAllWithLock(GetAotConfigs(),
                                       (config, lockObject) => {
                if (!config.Valid)
                {
                    Cancel();
                    return;
                }

                if (!RunAotCompiler(config.AssembliesPath, config.AotCompiler, config.AotOptions, config.AssemblyPath, config.ResponseFile))
                {
                    LogCodedError("XA3001", Properties.Resources.XA3001, Path.GetFileName(config.AssemblyPath));
                    Cancel();
                    return;
                }

                File.Delete(config.ResponseFile);

                lock (lockObject)
                    nativeLibs.Add(config.OutputFile);
            }
                                       );

            NativeLibrariesReferences = nativeLibs.ToArray();

            LogDebugMessage("Aot Outputs:");
            LogDebugTaskItems("  NativeLibrariesReferences: ", NativeLibrariesReferences);
        }
Beispiel #7
0
        public override bool Execute()
        {
            NdkUtil.Init(AndroidNdkDirectory);

            try {
                return(DoExecute());
            } catch (Exception e) {
                Log.LogCodedError("XA3001", "{0}", e);
                return(false);
            }
        }
Beispiel #8
0
        static int GetNdkApiLevel(string androidNdkPath, string androidApiLevel, AndroidTargetArch arch)
        {
            int level = int.Parse(androidApiLevel);

            // Some Android API levels do not exist on the NDK level. Workaround this my mapping them to the
            // most appropriate API level that does exist.
            if (level == 6 || level == 7)
            {
                level = 5;
            }
            else if (level == 10)
            {
                level = 9;
            }
            else if (level == 11)
            {
                level = 12;
            }
            else if (level == 20)
            {
                level = 19;
            }
            else if (level == 22)
            {
                level = 21;
            }
            else if (level == 23)
            {
                level = 21;
            }

            // API levels below level 21 do not provide support for 64-bit architectures.
            if (NdkUtil.IsNdk64BitArch(arch) && level < 21)
            {
                level = 21;
            }

            // We perform a downwards API level lookup search since we might not have hardcoded the correct API
            // mapping above and we do not want to crash needlessly.
            for (; level >= 5; level--)
            {
                try {
                    NdkUtil.GetNdkPlatformLibPath(androidNdkPath, arch, level);
                    break;
                } catch (InvalidOperationException ex) {
                    // Path not found, continue searching...
                    continue;
                }
            }

            return(level);
        }
Beispiel #9
0
        public override bool RunTask()
        {
            if (EnableLLVM && !NdkUtil.Init(Log, AndroidNdkDirectory))
            {
                return(false);
            }

            try {
                return(DoExecute());
            } catch (Exception e) {
                Log.LogCodedError("XA3001", "{0}", e);
                return(false);
            }
        }
Beispiel #10
0
		public override bool RunTask ()
		{
			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 !Log.HasLoggedErrors;
		}
        public override bool Execute()
        {
            Log.LogDebugMessage("Assemblies: {0}", Assemblies.Length);
            Log.LogDebugMessage("SupportedAbis: {0}", SupportedAbis);
            Log.LogDebugMessage("AutoDeps: {0}", AutoDeps);

            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(!Log.HasLoggedErrors);
        }
Beispiel #12
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);
        }
Beispiel #13
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));
                }
            }
        }
Beispiel #14
0
        int GetNdkApiLevel(string androidNdkPath, string androidApiLevel, AndroidTargetArch arch)
        {
            var manifest = AndroidAppManifest.Load(ManifestFile.ItemSpec, MonoAndroidHelper.SupportedVersions);

            int level;

            if (manifest.MinSdkVersion.HasValue)
            {
                level = manifest.MinSdkVersion.Value;
            }
            else if (int.TryParse(androidApiLevel, out level))
            {
                // level already set
            }
            else
            {
                // Probably not ideal!
                level = MonoAndroidHelper.SupportedVersions.MaxStableVersion.ApiLevel;
            }

            // Some Android API levels do not exist on the NDK level. Workaround this my mapping them to the
            // most appropriate API level that does exist.
            if (level == 6 || level == 7)
            {
                level = 5;
            }
            else if (level == 10)
            {
                level = 9;
            }
            else if (level == 11)
            {
                level = 12;
            }
            else if (level == 20)
            {
                level = 19;
            }
            else if (level == 22)
            {
                level = 21;
            }
            else if (level == 23)
            {
                level = 21;
            }

            // API levels below level 21 do not provide support for 64-bit architectures.
            if (NdkUtil.IsNdk64BitArch(arch) && level < 21)
            {
                level = 21;
            }

            // We perform a downwards API level lookup search since we might not have hardcoded the correct API
            // mapping above and we do not want to crash needlessly.
            for (; level >= 5; level--)
            {
                try {
                    NdkUtil.GetNdkPlatformLibPath(androidNdkPath, arch, level);
                    break;
                } catch (InvalidOperationException ex) {
                    // Path not found, continue searching...
                    continue;
                }
            }

            return(level);
        }
Beispiel #15
0
        private void AddNativeLibraries(ArchiveFileList files, string [] supportedAbis)
        {
            var frameworkLibs = FrameworkNativeLibraries.Select(v => new LibInfo {
                Path            = v.ItemSpec,
                Link            = v.GetMetadata("Link"),
                Abi             = GetNativeLibraryAbi(v),
                ArchiveFileName = GetArchiveFileName(v)
            });

            AddNativeLibraries(files, supportedAbis, frameworkLibs);

            var libs = NativeLibraries.Concat(BundleNativeLibraries ?? Enumerable.Empty <ITaskItem> ())
                       .Where(v => IncludeNativeLibrary(v))
                       .Select(v => new LibInfo {
                Path            = v.ItemSpec,
                Link            = v.GetMetadata("Link"),
                Abi             = GetNativeLibraryAbi(v),
                ArchiveFileName = GetArchiveFileName(v)
            }
                               );

            AddNativeLibraries(files, supportedAbis, libs);

            if (String.IsNullOrWhiteSpace(CheckedBuild))
            {
                return;
            }

            string mode = CheckedBuild;
            string sanitizerName;

            if (String.Compare("asan", mode, StringComparison.Ordinal) == 0)
            {
                sanitizerName = "asan";
            }
            else if (String.Compare("ubsan", mode, StringComparison.Ordinal) == 0)
            {
                sanitizerName = "ubsan_standalone";
            }
            else
            {
                LogSanitizerWarning($"Unknown checked build mode '{CheckedBuild}'");
                return;
            }

            if (!IncludeWrapSh)
            {
                LogSanitizerError("Checked builds require the wrapper script to be packaged. Please set the `$(AndroidIncludeWrapSh)` MSBuild property to `true` in your project.");
                return;
            }

            if (!libs.Any(info => IsWrapperScript(info.Path, info.Link)))
            {
                LogSanitizerError($"Checked builds require the wrapper script to be packaged. Please add `wrap.sh` appropriate for the {CheckedBuild} checker to your project.");
                return;
            }

            NdkUtil.Init(AndroidNdkDirectory);
            string clangDir = NdkUtil.GetClangDeviceLibraryPath(AndroidNdkDirectory);

            if (String.IsNullOrEmpty(clangDir))
            {
                LogSanitizerError($"Unable to find the clang compiler directory. Is NDK installed?");
                return;
            }

            foreach (string abi in supportedAbis)
            {
                string clangAbi = MonoAndroidHelper.MapAndroidAbiToClang(abi);
                if (String.IsNullOrEmpty(clangAbi))
                {
                    LogSanitizerError($"Unable to map Android ABI {abi} to clang ABI");
                    return;
                }

                string sanitizerLib     = $"libclang_rt.{sanitizerName}-{clangAbi}-android.so";
                string sanitizerLibPath = Path.Combine(clangDir, sanitizerLib);
                if (!File.Exists(sanitizerLibPath))
                {
                    LogSanitizerError($"Unable to find sanitizer runtime for the {CheckedBuild} checker at {sanitizerLibPath}");
                    return;
                }

                AddNativeLibrary(files, sanitizerLibPath, abi, sanitizerLib);
            }
        }
Beispiel #16
0
 static string GetNdkToolchainLibraryDir(string binDir, AndroidTargetArch arch)
 {
     return(GetNdkToolchainLibraryDir(binDir, NdkUtil.GetArchDirName(arch)));
 }
Beispiel #17
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,
                });
            }
        }
        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);
        }
Beispiel #20
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);
                }

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