#pragma warning disable CS1998
        protected override async Task <bool> Execute(Context context)
        {
            // Ignore copying if not installing the NDK
            if (!dependencyTypeToInstall.HasFlag(AndroidToolchainComponentType.BuildDependency))
            {
                Log.DebugLine("NDK is not being installed, binutils installation skipped.");
                return(true);
            }

            string ndkRoot = context.Properties.GetRequiredValue(KnownProperties.AndroidNdkDirectory);

            string sourceDirectory      = Configurables.Paths.AndroidToolchainBinDirectory;
            string destinationDirectory = Configurables.Paths.HostBinutilsInstallDir;

            Log.StatusLine("Copying host binutils:");
            foreach (var kvp in Configurables.Defaults.AndroidToolchainPrefixes)
            {
                string archPrefix = kvp.Value;
                foreach (NDKTool ndkTool in Configurables.Defaults.NDKTools)
                {
                    string sourcePath = context.OS.AppendExecutableExtension(Path.Combine(sourceDirectory, $"{archPrefix}-{ndkTool.Name}"));
                    string destName   = ndkTool.DestinationName.Length == 0 ? ndkTool.Name : ndkTool.DestinationName;
                    string destPath   = context.OS.AppendExecutableExtension(Path.Combine(destinationDirectory, $"{archPrefix}-{destName}"));

                    Log.Status($"  {context.Characters.Bullet} {Path.GetFileName (sourcePath)} ");
                    Log.Status($"{context.Characters.RightArrow}", ConsoleColor.Cyan);
                    Log.StatusLine($" {Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, destPath)}");
                    Utilities.CopyFile(sourcePath, destPath);
                }
            }

            return(true);
        }
        public async Task <bool> Run(string logTag, string sourceDirectory, string workingDirectory, List <string> arguments)
        {
            if (String.IsNullOrEmpty(logTag))
            {
                throw new ArgumentException("must not be null or empty", nameof(logTag));
            }

            if (String.IsNullOrEmpty(sourceDirectory))
            {
                throw new ArgumentException("must not be null or empty", nameof(sourceDirectory));
            }

            if (String.IsNullOrEmpty(workingDirectory))
            {
                throw new ArgumentException("must not be null or empty", nameof(workingDirectory));
            }

            var runner = CreateProcessRunner();

            AddArguments(runner, arguments);
            runner.AddQuotedArgument(Utilities.GetRelativePath(workingDirectory, sourceDirectory));

            try {
                return(await RunTool(() => {
                    using (var outputSink = (OutputSink)SetupOutputSink(runner, $"cmake.{logTag}")) {
                        runner.WorkingDirectory = workingDirectory;
                        StartTwiddler();
                        return runner.Run();
                    }
                }
                                     ));
            } finally {
                StopTwiddler();
            }
        }
#pragma warning disable CS1998
        protected override async Task <bool> Execute(Context context)
        {
            List <GeneratedFile>?filesToGenerate = GetFilesToGenerate(context);

            if (filesToGenerate != null && filesToGenerate.Count > 0)
            {
                foreach (GeneratedFile gf in filesToGenerate)
                {
                    if (gf == null)
                    {
                        continue;
                    }

                    Log.Status("Generating ");
                    Log.Status(Utilities.GetRelativePath(BuildPaths.XamarinAndroidSourceRoot, gf.OutputPath), ConsoleColor.White);
                    if (!String.IsNullOrEmpty(gf.InputPath))
                    {
                        Log.StatusLine($" {context.Characters.LeftArrow} ", Utilities.GetRelativePath(BuildPaths.XamarinAndroidSourceRoot, gf.InputPath), leadColor: ConsoleColor.Cyan, tailColor: ConsoleColor.White);
                    }
                    else
                    {
                        Log.StatusLine();
                    }

                    gf.Generate(context);
                }
            }

            return(true);
        }
        public async Task <bool> Restore(string solutionFilePath)
        {
            if (String.IsNullOrEmpty(solutionFilePath))
            {
                throw new ArgumentException("must not be null or empty", nameof(solutionFilePath));
            }

            if (!File.Exists(solutionFilePath))
            {
                throw new InvalidOperationException($"Solution file '{solutionFilePath}' does not exist");
            }

            ProcessRunner runner = CreateProcessRunner("restore");

            runner.AddArgument("-Verbosity").AddArgument("detailed");
            runner.AddArgument("-NonInteractive");
            runner.AddArgument("-ForceEnglishOutput");
            runner.AddQuotedArgument(solutionFilePath);

            try {
                return(await RunTool(() => {
                    using (TextWriter outputSink = SetupOutputSink(runner, $"nuget-restore.{Path.GetFileName (solutionFilePath)}", "restoring NuGet packages")) {
                        Log.StatusLine($"Solution file: {Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, solutionFilePath)}", ConsoleColor.White);
                        runner.WorkingDirectory = Path.GetDirectoryName(solutionFilePath);
                        StartTwiddler();
                        return runner.Run();
                    }
                }
                                     ));
            } finally {
                StopTwiddler();
            }
        }
Exemple #5
0
        bool InstallUtilities(Context context)
        {
            string destDir = MonoRuntimesHelpers.UtilitiesDestinationDir;

            Utilities.CreateDirectory(destDir);

            string managedRuntime     = context.Properties.GetRequiredValue(KnownProperties.ManagedRuntime);
            bool   haveManagedRuntime = !String.IsNullOrEmpty(managedRuntime);
            string remapper           = Utilities.GetRelativePath(BuildPaths.XamarinAndroidSourceRoot, context.Properties.GetRequiredValue(KnownProperties.RemapAssemblyRefToolExecutable));
            string targetCecil        = Utilities.GetRelativePath(BuildPaths.XamarinAndroidSourceRoot, Path.Combine(Configurables.Paths.BuildBinDir, "Xamarin.Android.Cecil.dll"));

            StatusStep(context, "Installing runtime utilities");
            foreach (MonoUtilityFile muf in allRuntimes.UtilityFilesToInstall)
            {
                (string destFilePath, string debugSymbolsDestPath) = MonoRuntimesHelpers.GetDestinationPaths(muf);
                Utilities.CopyFile(muf.SourcePath, destFilePath);
                if (!muf.IgnoreDebugInfo)
                {
                    if (!String.IsNullOrEmpty(debugSymbolsDestPath))
                    {
                        Utilities.CopyFile(muf.DebugSymbolsPath, debugSymbolsDestPath);
                    }
                    else
                    {
                        Log.DebugLine($"Debug symbols not found for utility file {Path.GetFileName (muf.SourcePath)}");
                    }
                }

                if (!muf.RemapCecil)
                {
                    continue;
                }

                string relDestFilePath = Utilities.GetRelativePath(BuildPaths.XamarinAndroidSourceRoot, destFilePath);
                StatusSubStep(context, $"Remapping Cecil references for {relDestFilePath}");
                bool result = Utilities.RunCommand(
                    haveManagedRuntime ? managedRuntime : remapper, // command
                    BuildPaths.XamarinAndroidSourceRoot,            // workingDirectory
                    true,                                           // ignoreEmptyArguments

                    // arguments
                    haveManagedRuntime ? remapper : String.Empty,
                    Utilities.GetRelativePath(BuildPaths.XamarinAndroidSourceRoot, muf.SourcePath),
                    relDestFilePath,
                    "Mono.Cecil",
                    targetCecil);

                if (result)
                {
                    continue;
                }

                Log.ErrorLine($"Failed to remap cecil reference for {destFilePath}");
                return(false);
            }

            return(true);
        }
        void EnsureAllSDKHeadersAreIncluded(Context context, Runtimes allRuntimes)
        {
            string topDirTail = Configurables.Paths.MonoSDKRelativeIncludeSourceDir;

            if (!topDirTail.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
            {
                topDirTail += Path.DirectorySeparatorChar;
            }

            // Find first enabled runtime - all headers are the same across all runtimes, so we don't care
            // where they come from.
            Runtime runtime = MonoRuntimesHelpers.GetEnabledRuntimes(allRuntimes, enableLogging: false)?.FirstOrDefault();

            if (runtime == null)
            {
                Log.WarningLine("No enabled runtimes (?!)");
                return;
            }

            string runtimeIncludeDirRoot        = Path.Combine(Configurables.Paths.MonoSourceFullPath, MonoRuntimesHelpers.GetRootDir(runtime), Configurables.Paths.MonoSDKRelativeIncludeSourceDir);
            IEnumerable <string> sourceIncludes = Directory.EnumerateFiles(runtimeIncludeDirRoot, "*", SearchOption.AllDirectories);
            var destinationIncludes             = new List <string> ();

            foreach (RuntimeFile rf in allRuntimes.RuntimeFilesToInstall.Where(rf => rf.Type == RuntimeFileType.SdkHeader))
            {
                destinationIncludes.Add(Path.Combine(Configurables.Paths.MonoSourceFullPath, rf.Source(runtime)));
            }

            bool haveDifference = false;

            haveDifference &= ReportDifference(sourceIncludes.Except(destinationIncludes).ToList(), "runtime", "bundle");
            haveDifference &= ReportDifference(destinationIncludes.Except(sourceIncludes).ToList(), "bundle", "runtime");

            if (haveDifference)
            {
                throw new InvalidOperationException("Differences found between the Mono SDK header files shipped in Mono archive and included in Xamarin.Android bundle");
            }

            bool ReportDifference(List <string> diff, string foundIn, string notFoundIn)
            {
                if (diff.Count == 0)
                {
                    return(false);
                }

                Log.ErrorLine($"There are files found in the {foundIn} but not in the {notFoundIn}:");
                foreach (string f in diff)
                {
                    Log.ErrorLine($"  {context.Characters.Bullet} {Utilities.GetRelativePath (runtimeIncludeDirRoot, f)}");
                }
                Log.ErrorLine();
                return(true);
            }
        }
Exemple #7
0
#pragma warning disable CS1998
        protected override async Task <bool> Execute(Context context)
        {
            var runner = new BrewRunner(context);

            if (!runner.List("libzip", out List <string> lines) || lines == null || lines.Count == 0)
            {
                Log.ErrorLine("Failed to retrieve libzip package contents");
                return(false);
            }

            string libZipPath = null;

            foreach (string line in lines)
            {
                Match match = libZipDylib.Match(line);
                if (!match.Success)
                {
                    continue;
                }
                libZipPath = line;
                break;
            }

            if (String.IsNullOrEmpty(libZipPath))
            {
                Log.ErrorLine("`libzip` package does not contain the dynamic library");
                return(false);
            }

            if (!File.Exists(libZipPath))
            {
                Log.ErrorLine($"`libzip` package lists the dynamic library at {libZipPath} but the file does not exist");
                return(false);
            }

            Log.DebugLine($"`libzip` library found at {libZipPath}");
            string destFile = Path.Combine(Configurables.Paths.InstallMSBuildDir, Path.GetFileName(libZipPath));

            Log.Status("Installing ");
            Log.Status(Utilities.GetRelativePath(BuildPaths.XamarinAndroidSourceRoot, destFile), ConsoleColor.White);
            Log.StatusLine($" {context.Characters.LeftArrow} ", libZipPath, leadColor: ConsoleColor.Cyan, tailColor: ConsoleColor.White);

            Utilities.CopyFile(libZipPath, destFile);

            if (!File.Exists(destFile))
            {
                Log.ErrorLine("Failed to copy the libzip dynamic library.");
                return(false);
            }

            return(true);
        }
        bool FileSetsDiffer(IEnumerable <TestAssembly> assemblies, string directoryPath, string batchName, HashSet <string>?ignoreFiles = null)
        {
            List <string> tests = FilesFromDir(directoryPath, ignoreFiles).ToList();

            tests.AddRange(
                FilesFromDir(
                    directoryPath,
                    globPattern: "*.resources.dll",
                    stripPath: false,
                    searchSubdirs: true
                    ).Select(f => Utilities.GetRelativePath(directoryPath, f))
                );

            return(FileSetsDiffer(ToStringSet(assemblies), tests, batchName));
        }
Exemple #9
0
        void GenerateMonodroidTargets(Context context, StreamWriter sw)
        {
            string sourceDir = Utilities.GetRelativePath(Configurables.Paths.BuildBinDir, Configurables.Paths.MonodroidSourceDir);

            var commonReplacements = new Dictionary <string, string> (StringComparer.Ordinal)
            {
                { "@BUILD_TYPE@", "" },
                { "@CONFIGURATION@", "" },
                { "@SOURCE_DIRECTORY@", $"$(MSBuildThisFileDirectory){sourceDir}" },
            };

            var androidRuntimeReplacements = new Dictionary <string, string> (StringComparer.Ordinal)
            {
                { "@CmakeAndroidFlags@", "$(_CmakeAndroidFlags)" },
                { "@NATIVE_API_LEVEL@", "" },
                { "@ABI@", "%(AndroidSupportedTargetJitAbi.Identity)" },
                { "@OUTPUT_DIRECTORY@", "" },
            };

            var hostRuntimeReplacements = new Dictionary <string, string> (StringComparer.Ordinal)
            {
                { "@CmakeHostFlags@", "%(_HostRuntime.CmakeFlags)" },
                { "@JdkIncludePath@", "@(JdkIncludePath->'%(Identity)', ' ')" },
                { "@OUTPUT_DIRECTORY@", "" },
            };

            AddReplacements(commonReplacements, androidRuntimeReplacements);
            AddReplacements(commonReplacements, hostRuntimeReplacements);

            WriteMSBuildProjectStart(sw);
            sw.WriteLine("  <Target Name=\"_PrepareConfigureRuntimeCommands\" DependsOnTargets=\"_GetBuildHostRuntimes\">");

            string indent = "    ";

            foreach (CmakeBuilds.RuntimeCommand rc in CmakeBuilds.AndroidRuntimeCommands)
            {
                WriteMSBuildConfigureAndroidRuntimeCommands(sw, indent, rc, androidRuntimeReplacements);
            }

            foreach (CmakeBuilds.RuntimeCommand rc in CmakeBuilds.HostRuntimeCommands)
            {
                WriteMSBuildConfigureHostRuntimeCommands(sw, indent, rc, hostRuntimeReplacements);
            }
            ;

            sw.WriteLine("  </Target>");
            WriteMSBuildProjectEnd(sw);
        }
        protected override async Task <bool> Execute(Context context)
        {
            var    allRuntimes = new Runtimes();
            string binRoot     = Configurables.Paths.BinDir;
            string bundlePath  = Path.Combine(binRoot, Configurables.Paths.XABundleFileName);

            Log.StatusLine("Generating bundle archive: ", Utilities.GetRelativePath(BuildPaths.XamarinAndroidSourceRoot, bundlePath), tailColor: ConsoleColor.White);
            Utilities.DeleteFileSilent(bundlePath);

            var sevenZip         = new SevenZipRunner(context);
            CompressionFormat cf = context.CompressionFormat;
            Func <string, string, List <string>, Task <bool> > compressor;

            if (String.Compare(cf.Name, Configurables.Defaults.ZipCompressionFormatName, StringComparison.OrdinalIgnoreCase) == 0)
            {
                compressor = sevenZip.Zip;
            }
            else if (String.Compare(cf.Name, Configurables.Defaults.SevenZipCompressionFormatName, StringComparison.OrdinalIgnoreCase) == 0)
            {
                compressor = sevenZip.SevenZip;
            }
            else
            {
                throw new InvalidOperationException($"Unsupported compression type: {cf.Description}");
            }

            EnsureAllSDKHeadersAreIncluded(context, allRuntimes);
            List <string> items = allRuntimes.BundleItems.Where(item => item.ShouldInclude == null || item.ShouldInclude(context)).Select(
                item => {
                string relPath = Utilities.GetRelativePath(binRoot, item.SourcePath);
                Log.DebugLine($"Bundle item: {item.SourcePath} (archive path: {relPath})");
                return(relPath);
            }
                ).Distinct().ToList();

            items.Sort();

            if (!await compressor(bundlePath, binRoot, items))
            {
                Log.ErrorLine("Bundle archive creation failed, see the log files for details.");
                return(false);
            }

            return(true);
        }
Exemple #11
0
        public async Task <bool> Run(string projectPath, string logTag, List <string> arguments = null, string binlogName = null, string workingDirectory = null)
        {
            if (String.IsNullOrEmpty(logTag))
            {
                throw new ArgumentException("must not be null or empty", nameof(logTag));
            }

            if (String.IsNullOrEmpty(workingDirectory))
            {
                workingDirectory = BuildPaths.XamarinAndroidSourceRoot;
            }

            ProcessRunner runner = CreateProcessRunner();

            AddArguments(runner, StandardArguments);
            if (!String.IsNullOrEmpty(binlogName))
            {
                string logPath = Utilities.GetRelativePath(workingDirectory, Path.Combine(Configurables.Paths.BuildBinDir, $"msbuild-{Context.BuildTimeStamp}-{binlogName}.binlog"));
                runner.AddArgument("/v:normal");
                runner.AddQuotedArgument($"/bl:{logPath}");
            }
            AddArguments(runner, arguments);
            runner.AddQuotedArgument(Utilities.GetRelativePath(workingDirectory, projectPath));

            string message = GetLogMessage(runner);

            Log.Info(message, CommandMessageColor);
            Log.StatusLine();

            try {
                return(await RunTool(
                           () => {
                    using (var outputSink = (OutputSink)SetupOutputSink(runner, $"msbuild.{logTag}")) {
                        runner.WorkingDirectory = workingDirectory;
                        StartTwiddler();
                        return runner.Run();
                    }
                }
                           ));
            } finally {
                StopTwiddler();
            }
        }
Exemple #12
0
        public async Task <bool> ReSign(string snkPath, string assemblyPath, string logTag, string workingDirectory = null)
        {
            if (String.IsNullOrEmpty(snkPath))
            {
                throw new ArgumentException("must not be null or empty", nameof(snkPath));
            }

            if (String.IsNullOrEmpty(assemblyPath))
            {
                throw new ArgumentException("must not be null or empty", nameof(assemblyPath));
            }

            if (String.IsNullOrEmpty(logTag))
            {
                throw new ArgumentException("must not be null or empty", nameof(logTag));
            }

            if (String.IsNullOrEmpty(workingDirectory))
            {
                workingDirectory = BuildPaths.XamarinAndroidSourceRoot;
            }

            ProcessRunner runner = CreateProcessRunner();

            runner.AddQuotedArgument("-R");
            runner.AddQuotedArgument(Utilities.GetRelativePath(workingDirectory, assemblyPath));
            runner.AddQuotedArgument(Utilities.GetRelativePath(workingDirectory, snkPath));

            string message = GetLogMessage(runner);

            Log.Info(message, CommandMessageColor);
            Log.StatusLine();

            return(await RunTool(
                       () => {
                using (var outputSink = (OutputSink)SetupOutputSink(runner, $"sn.{logTag}")) {
                    runner.WorkingDirectory = workingDirectory;
                    return runner.Run();
                }
            }
                       ));
        }
        void HashFiles(HashAlgorithm ha, List <string> globPatterns)
        {
            var block = new byte [4096];

            foreach (string glob in globPatterns)
            {
                string pattern = glob?.Trim();
                if (String.IsNullOrEmpty(pattern))
                {
                    continue;
                }

                foreach (string file in Directory.EnumerateFiles(Path.GetDirectoryName(pattern), Path.GetFileName(pattern)))
                {
                    Log.StatusLine("      file: ", Utilities.GetRelativePath(BuildPaths.XamarinAndroidSourceRoot, file), tailColor: ConsoleColor.Cyan);
                    HashFile(ha, file, block);
                }
            }
            ha.TransformFinalBlock(block, 0, 0);
        }
        async Task <bool> ReadEntry(HttpClient httpClient, Uri url, CDHeader cdh, BinaryReader br, string destinationDirectory)
        {
            Context context            = Context.Instance;
            string  destFilePath       = Path.Combine(destinationDirectory, Path.GetFileName(cdh.FileName));
            string  compressedFilePath = Path.Combine(destinationDirectory, $"{destFilePath}.deflated");

            Log.Status($"  {context.Characters.Bullet} {Path.GetFileName (cdh.FileName)} ");
            Log.Status($"{context.Characters.RightArrow}", ConsoleColor.Cyan);
            Log.StatusLine($" {Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, destFilePath)}");
            Log.DebugLine($" {cdh.FileName} (offset: {cdh.RelativeOffsetOfLocalHeader})");

            (bool success, Stream contentStream) = await ReadFileData(httpClient, url, cdh);

            if (!success)
            {
                Log.ErrorLine("Failed to read file data");
                return(false);
            }

            using (var destFile = new BinaryWriter(File.OpenWrite(compressedFilePath))) {
                using (var fbr = new BinaryReader(contentStream)) {
                    if (!await DownloadAndExtract(fbr, contentStream, destFile, compressedFilePath))
                    {
                        return(CleanupAndReturn(false));
                    }
                }
            }

            return(CleanupAndReturn(true));

            bool CleanupAndReturn(bool retval)
            {
                if (File.Exists(compressedFilePath))
                {
                    File.Delete(compressedFilePath);
                }

                return(retval);
            }
        }
        protected TextWriter SetupOutputSink(ProcessRunner runner, string tags = null, string messagePrefix = null)
        {
            string logFilePath = null;

            if (!String.IsNullOrEmpty(tags))
            {
                logFilePath = Context.GetLogFilePath(tags ?? String.Empty);
                if (String.IsNullOrEmpty(messagePrefix))
                {
                    messagePrefix = "running";
                }
                Log.StatusLine($"{LogMessageIndent}[{ToolName}] {messagePrefix}");
                Log.StatusLine($"[{ToolName}] log file: ", $"{Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, logFilePath)}", tailColor: Log.DestinationColor);
            }

            TextWriter ret = CreateLogSink(logFilePath);

            runner.AddStandardErrorSink(ret);
            runner.AddStandardOutputSink(ret);

            return(ret);
        }
        protected override async Task <bool> Execute(Context context)
        {
            string xcrun = context.OS.Which("xcrun");

            var libs = new string[] {
                Path.Combine(Configurables.Paths.HostRuntimeDir, Configurables.Paths.UnstrippedLibMonoSgenName),
                Path.Combine(Configurables.Paths.HostRuntimeDir, Configurables.Paths.StrippedLibMonoSgenName),
            };

            bool result = true;

            Log.StatusLine("Changing id for:");
            foreach (string libPath in libs)
            {
                if (!Utilities.FileExists(libPath))
                {
                    Log.StatusLine("    not found", ConsoleColor.Magenta);
                    continue;
                }

                if (!ChangeID(libPath))
                {
                    Log.StatusLine("    failed", ConsoleColor.Magenta);
                    result = false;
                }
            }

            return(result);

            bool ChangeID(string path)
            {
                Log.DebugLine($"Changing dylib id for {path}");
                Log.StatusLine($"  {context.Characters.Bullet} {Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, path)}");
                var runner = new ProcessRunner(xcrun, "install_name_tool", "-id", "@loader_path/libmonosgen-2.0.dylib", path);

                return(runner.Run());
            }
        }
Exemple #17
0
        List <string> GetCmakeArguments(Context context, string workingDirectory, bool sixtyFourBit)
        {
            string cmakeToolchainFile;
            string zlibRoot;

            if (sixtyFourBit)
            {
                cmakeToolchainFile = Configurables.Paths.Mingw64CmakePath;
                zlibRoot           = context.Properties.GetRequiredValue(KnownProperties.MingwZlibRootDirectory64);
            }
            else
            {
                cmakeToolchainFile = Configurables.Paths.Mingw32CmakePath;
                zlibRoot           = context.Properties.GetRequiredValue(KnownProperties.MingwZlibRootDirectory32);
            }

            if (!String.IsNullOrEmpty(zlibRootPrefix))
            {
                zlibRoot = Path.Combine(zlibRootPrefix, zlibRoot);
            }

            string zlibLibrary    = Path.Combine(zlibRoot, "lib", context.Properties.GetRequiredValue(KnownProperties.MingwZlibLibraryName));
            string zlibIncludeDir = Path.Combine(zlibRoot, "include");

            return(new List <string> {
                "-GNinja",
                "-DCMAKE_MAKE_PROGRAM=ninja",
                "-DCMAKE_POLICY_DEFAULT_CMP0074=NEW",
                "-DENABLE_GNUTLS=OFF",
                "-DENABLE_OPENSSL=OFF",
                "-DENABLE_COMMONCRYPTO=OFF",
                "-Wno-dev",                 // Hushes some warnings that are useless for us
                $"-DCMAKE_TOOLCHAIN_FILE={Utilities.GetRelativePath (workingDirectory, cmakeToolchainFile)}",
                $"-DZLIB_ROOT={zlibRoot}",
                $"-DZLIB_LIBRARY={zlibLibrary}",
                $"-DZLIB_INCLUDE_DIR={zlibIncludeDir}"
            });
        }
Exemple #18
0
        public static List <ExternalGitDependency> GetDependencies(Context context, string externalFilePath, bool quiet = false)
        {
            if (!quiet)
            {
                Log.Instance.StatusLine($"  {context.Characters.Bullet} Reading external dependencies from {Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, externalFilePath)}");
            }
            string[] unparsedExternals = File.ReadAllLines(externalFilePath);
            var      externals         = new List <ExternalGitDependency> (unparsedExternals.Length);
            bool     includeCommercial = context.CheckCondition(KnownConditions.IncludeCommercial);

            foreach (string external in unparsedExternals)
            {
                Match match = externalRegex.Match(external);
                if (match != null && match.Success)
                {
                    if (match.Groups["comment"].Success)
                    {
                        // Ignore matching lines which start with '#'.
                        continue;
                    }

                    string owner = match.Groups["owner"].Value;
                    string repo  = match.Groups["repo"].Value;
                    if (!includeCommercial && Configurables.Defaults.CommercialExternalDependencies.Contains($"{owner}/{repo}"))
                    {
                        Log.Instance.DebugLine($"Ignoring external commercial dependency '{owner}/{repo}'");
                        continue;
                    }

                    var e = new ExternalGitDependency {
                        Branch = match.Groups["branch"].Value,
                        Commit = match.Groups["commit"].Value,
                        Name   = repo,
                        Owner  = owner,
                    };
                    externals.Add(e);
                    if (!quiet)
                    {
                        Log.Instance.StatusLine($"    {context.Characters.Bullet} {e.Owner}/{e.Name} ({e.Commit})");
                    }
                }
            }

            return(externals);
        }
Exemple #19
0
        protected override async Task <bool> Execute(Context context)
        {
            if (context.ForceRuntimesBuild)
            {
                if (osSupportsMonoBuild)
                {
                    Log.InfoLine("Rebuilding Mono runtimes as requested");
                    return(false);
                }

                Log.InfoLine($"Forced Mono runtimes rebuild requested but rebuilding on {context.OS.Type} is currently not supported.");
            }

            string localPackagePath = Configurables.Paths.BundleArchivePath;

            Log.DebugLine($"Local bundle path: {localPackagePath}");

            if (await Utilities.VerifyArchive(localPackagePath))
            {
                Log.StatusLine("Xamarin.Android Bundle archive already downloaded and valid");
            }
            else
            {
                if (!String.IsNullOrEmpty(context.XABundlePath))
                {
                    // User indicated they wanted to use a specific bundle that's supposed to be on disk. It's not (or
                    // it's invalid) and that means we have no way of getting it - we can't download the default one
                    // since that was not the intention behind overriding the location. Thus, we error out.
                    Log.DebugLine($"Bundle directory from command line: {context.XABundlePath}");
                    throw new InvalidOperationException($"Xamarin.Android bundle indicated on the command line does not exist ({context.XABundlePath})");
                }

                var bundleUrl = new Uri(BundleUriPrefix, BundleFileName);

                Log.StatusLine("Bundle URL: ", $"{bundleUrl}", tailColor: ConsoleColor.Cyan);

                HttpStatusCode status;
                bool           success;
                ulong          size;

                (success, size, status) = await Utilities.GetDownloadSizeWithStatus(bundleUrl);

                if (!success)
                {
                    if (status == HttpStatusCode.NotFound)
                    {
                        if (osSupportsMonoBuild)
                        {
                            Log.StatusLine("   not found, will need to rebuild");
                        }
                        else
                        {
                            Log.ErrorLine($"   not found, rebuilding on {context.OS.Type} is not currently supported");
                        }
                        return(false);
                    }

                    if (String.IsNullOrEmpty(bundle404Message))
                    {
                        throw new InvalidOperationException($"Failed to access bundle at {bundleUrl} (HTTP status: {status})");
                    }
                    else
                    {
                        throw new InvalidOperationException(bundle404Message);
                    }
                }

                DownloadStatus downloadStatus = Utilities.SetupDownloadStatus(context, size, context.InteractiveSession);
                Log.StatusLine($"  {context.Characters.Link} {bundleUrl}", ConsoleColor.White);
                await Download(context, bundleUrl, localPackagePath, "Xamarin.Android Bundle", Path.GetFileName(localPackagePath), downloadStatus);

                if (!File.Exists(localPackagePath))
                {
                    Log.ErrorLine($"Download of Xamarin.Android Bundle from {bundleUrl} failed.");
                    return(false);
                }
            }

            Log.StatusLine($"Unpacking bundle to {Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, Configurables.Paths.BundleInstallDir)}");
            string tempDir = $"{Configurables.Paths.BundleInstallDir}-bundle.temp";

            try {
                if (!await Utilities.Unpack(localPackagePath, tempDir, cleanDestinatioBeforeUnpacking: true))
                {
                    Log.WarningLine("Failed to unpack bundle, will need to rebuild");
                    return(false);
                }

                Log.DebugLine($"Moving unpacked bundle from {tempDir} to {Configurables.Paths.BundleInstallDir}");
                Utilities.MoveDirectoryContentsRecursively(tempDir, Configurables.Paths.BundleInstallDir, resetFileTimestamp: true, ignoreDeletionErrors: true);
            } finally {
                Utilities.DeleteDirectorySilent(tempDir);
            }

            string managedRuntime     = context.Properties.GetRequiredValue(KnownProperties.ManagedRuntime);
            bool   haveManagedRuntime = !String.IsNullOrEmpty(managedRuntime);

            if (!await Utilities.BuildRemapRef(context, haveManagedRuntime, managedRuntime))
            {
                return(false);
            }

            Utilities.PropagateXamarinAndroidCecil(context);

            if (String.IsNullOrEmpty(context.XABundleCopyDir))
            {
                return(HaveEverything());
            }

            string destPackagePath = Path.Combine(context.XABundleCopyDir, Path.GetFileName(localPackagePath));

            Log.DebugLine($"Copy of the XA bundle was requested to be created at {destPackagePath}");
            if (Utilities.FileExists(destPackagePath))
            {
                Log.DebugLine("Bundle copy already exists");
                return(HaveEverything());
            }

            // Utilities.FileExists above will return `false` for a dangling symlink at `destPackagePath`, doesn't hurt
            // to remove it here just in case
            Utilities.DeleteFileSilent(destPackagePath);
            Utilities.CopyFile(localPackagePath, destPackagePath);

            return(HaveEverything());

            bool HaveEverything()
            {
                bool ret = MonoRuntimesHelpers.AllBundleItemsPresent(new Runtimes());

                if (!ret)
                {
                    Log.Instance.StatusLine($"Some bundle files are missing, download/rebuild/reinstall forced");
                }
                return(ret);
            }
        }
#pragma warning restore CS1998

        void GenerateThirdPartyNotices(string outputPath, ThirdPartyLicenseType licenseType, bool includeExternalDeps, bool includeBuildDeps)
        {
            List <Type> types = Utilities.GetTypesWithCustomAttribute <TPNAttribute> ();

            if (types.Count == 0)
            {
                Log.StatusLine("No Third Party Notice entries found", ConsoleColor.Gray);
                return;
            }

            var licenses = new SortedDictionary <string, ThirdPartyNotice> (StringComparer.OrdinalIgnoreCase);

            foreach (Type type in types)
            {
                EnsureValidTPNType(type);

                if (type.IsSubclassOf(typeof(ThirdPartyNoticeGroup)))
                {
                    ProcessTPN(licenses, Activator.CreateInstance(type) as ThirdPartyNoticeGroup, includeExternalDeps, includeBuildDeps);
                    continue;
                }

                if (type.IsSubclassOf(typeof(ThirdPartyNotice)))
                {
                    ProcessTPN(licenses, Activator.CreateInstance(type) as ThirdPartyNotice, includeExternalDeps, includeBuildDeps);
                    continue;
                }

                throw new NotSupportedException($"ThirdPartyNotice type {type.FullName} not supported");
            }

            if (licenses.Count == 0)
            {
                return;
            }

            string blurb;

            if (!tpnBlurbs.TryGetValue(licenseType, out blurb))
            {
                throw new InvalidOperationException($"Unknown license type {licenseType}");
            }

            using (StreamWriter sw = Utilities.OpenStreamWriter(outputPath)) {
                Log.StatusLine($" Generating: {Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, outputPath)}", ConsoleColor.Gray);
                Log.DebugLine($"Full path: {outputPath}");

                sw.WriteLine(blurb);
                sw.WriteLine();

                uint i   = 0;
                int  pad = licenses.Count >= 10 ? 4 : 3;
                foreach (var kvp in licenses)
                {
                    string           name = kvp.Key;
                    ThirdPartyNotice tpn  = kvp.Value;

                    sw.Write($"{++i}.".PadRight(pad));
                    sw.WriteLine($"{name} ({tpn.SourceUrl})");
                }
                sw.WriteLine();

                foreach (string key in licenses.Keys)
                {
                    ThirdPartyNotice tpn = licenses [key];

                    string heading   = $"%% {tpn.Name} NOTICES AND INFORMATION BEGIN HERE";
                    string underline = "=".PadRight(heading.Length, '=');
                    sw.WriteLine(heading);
                    sw.WriteLine(underline);
                    if (tpn.LicenseText != null)
                    {
                        sw.WriteLine(tpn.LicenseText.TrimStart());
                    }
                    else
                    {
                        sw.WriteLine(FetchTPNLicense(tpn.LicenseFile));
                    }
                    sw.WriteLine();
                    sw.WriteLine(underline);
                    sw.WriteLine($"END OF {tpn.Name} NOTICES AND INFORMATION");
                    sw.WriteLine();
                }

                sw.Flush();
            }
        }
        public static List <ExternalGitDependency> GetDependencies(Context context, string externalFilePath)
        {
            Log.Instance.StatusLine($"  {context.Characters.Bullet} Reading external dependencies from {Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, externalFilePath)}");
            string[] unparsedExternals = File.ReadAllLines(externalFilePath);
            var      externals         = new List <ExternalGitDependency> (unparsedExternals.Length);

            foreach (string external in unparsedExternals)
            {
                Match match = externalRegex.Match(external);
                if (match != null && match.Success)
                {
                    if (match.Groups["comment"].Success)
                    {
                        // Ignore matching lines which start with '#'.
                        continue;
                    }

                    var e = new ExternalGitDependency {
                        Branch = match.Groups["branch"].Value,
                        Commit = match.Groups["commit"].Value,
                        Name   = match.Groups["repo"].Value,
                        Owner  = match.Groups["owner"].Value,
                    };
                    externals.Add(e);
                    Log.Instance.StatusLine($"    {context.Characters.Bullet} {e.Owner}/{e.Name} ({e.Commit})");
                }
            }

            return(externals);
        }
        void Generate(Context context, StreamWriter sw)
        {
            string myPath = Path.Combine(BuildPaths.XAPrepareSourceDir, "Application", "GeneratedMakeRulesFile.cs");

            sw.WriteLine("#");
            sw.WriteLine($"# Generated by {myPath}");
            sw.WriteLine("#");
            sw.WriteLine();

            WriteVariable("export OS_NAME", context.OS.Type);
            WriteVariable("export OS_ARCH", context.OS.Architecture);
            WriteVariable("export OS_ARCH_TRANSLATED", context.OS.ProcessIsTranslated ? "true" : "false");
            WriteVariable("PRODUCT_VERSION", context.ProductVersion);
            WriteVariable("MONO_SOURCE_FULL_PATH", Configurables.Paths.MonoSourceFullPath);

            // These must remain dynamic since the developer may change branches without re-running `prepare`
            string getGitBranchScript = Utilities.GetRelativePath(BuildPaths.XamarinAndroidSourceRoot, Path.Combine(Configurables.Paths.BuildToolsScriptsDir, "get-git-branch.sh"));

            WriteVariable("GIT_BRANCH", $"$(shell LANG=C \"{getGitBranchScript}\" | tr -d '[[:space:]]' | tr -C a-zA-Z0-9- _)");
            WriteVariable("GIT_COMMIT", $"$(shell LANG=C git log --no-color --first-parent -n1 --pretty=format:%h)");
            WriteVariable("-num-commits-since-version-change", $"$(shell LANG=C git log {context.BuildInfo.CommitOfLastVersionChange}..HEAD --oneline 2>/dev/null | wc -l | sed 's/ //g')");

            WriteVariable("ZIP_EXTENSION", context.OS.ZipExtension);
            WriteVariable("ZIP_OUTPUT_BASENAME", GetOutputFileName(context, "xamarin.android-oss"));
            WriteVariable("_TEST_RESULTS_BASENAME", GetOutputFileName(context, "xa-test-results"));
            WriteVariable("_BUILD_STATUS_BASENAME", GetOutputFileName(context, "xa-build-status"));

            WriteVariable("ZIP_OUTPUT", "$(ZIP_OUTPUT_BASENAME).$(ZIP_EXTENSION)");
            WriteVariable("_BUILD_STATUS_ZIP_OUTPUT", "$(_BUILD_STATUS_BASENAME).$(ZIP_EXTENSION)");
            WriteVariable("_TEST_RESULTS_ZIP_OUTPUT", "$(_TEST_RESULTS_BASENAME).$(ZIP_EXTENSION)");

            var allApiLevels     = new List <string> ();
            var allPlatformIDs   = new List <string> ();
            var allFrameworks    = new List <string> ();
            var apiLevels        = new List <string> ();
            var stableApiLevels  = new List <string> ();
            var frameworks       = new List <string> ();
            var stableFrameworks = new List <string> ();
            var platformIds      = new List <string> ();

            foreach (AndroidPlatform ap in BuildAndroidPlatforms.AllPlatforms)
            {
                string api = ap.ApiLevel.ToString();

                allApiLevels.Add(api);
                allPlatformIDs.Add(ap.PlatformID);
                if (!String.IsNullOrEmpty(ap.Framework))
                {
                    allFrameworks.Add(ap.Framework);
                    frameworks.Add(ap.Framework);
                    if (ap.Stable)
                    {
                        stableFrameworks.Add(ap.Framework);
                    }
                }
                else
                {
                    allFrameworks.Add("-");
                }

                if (!ap.Supported)
                {
                    continue;
                }

                apiLevels.Add(api);
                platformIds.Add(ap.PlatformID);
                if (ap.Stable)
                {
                    stableApiLevels.Add(api);
                }
            }

            var enabledJitAbis  = AbiNames.AllJitAbis.Where(a => context.IsTargetJitAbiEnabled(a));
            var enabledHostAbis = AbiNames.AllHostAbis.Where(a => context.IsHostJitAbiEnabled(a));
            var enabledAotAbis  = AbiNames.AllAotAbis.Where(a => context.IsTargetAotAbiEnabled(a));

            WriteVariable("ALL_API_LEVELS", ToValue(allApiLevels));
            WriteVariable("ALL_PLATFORM_IDS", ToValue(allPlatformIDs));
            WriteVariable("ALL_FRAMEWORKS", ToValue(allFrameworks));
            WriteVariable("API_LEVELS", ToValue(apiLevels));
            WriteVariable("STABLE_API_LEVELS", ToValue(stableApiLevels));
            WriteVariable("FRAMEWORKS", ToValue(frameworks));
            WriteVariable("STABLE_FRAMEWORKS", ToValue(stableFrameworks));
            WriteVariable("ALL_JIT_ABIS", ToValue(enabledJitAbis.ToList()));
            WriteVariable("ALL_HOST_ABIS", ToValue(enabledHostAbis.ToList()));
            WriteVariable("ALL_AOT_ABIS", ToValue(enabledAotAbis.ToList()));
            WriteVariable("ANDROID_TOOLCHAIN_DIR", context.Properties.GetRequiredValue(KnownProperties.AndroidToolchainDirectory));
            if (context.MonoOptions != null && context.MonoOptions.Count > 0)
            {
                WriteVariable("MONO_OPTIONS", ToValue(context.MonoOptions));
                sw.WriteLine("export MONO_OPTIONS");
            }

            sw.WriteLine("_MSBUILD_ARGS = \\");
            sw.WriteLine($"\t/p:{KnownProperties.AndroidSupportedTargetJitAbis}={context.Properties.GetRequiredValue (KnownProperties.AndroidSupportedTargetJitAbis)} \\");
            sw.WriteLine($"\t/p:{KnownProperties.AndroidSupportedHostJitAbis}={context.Properties.GetRequiredValue (KnownProperties.AndroidSupportedHostJitAbis)} \\");
            sw.WriteLine($"\t/p:{KnownProperties.AndroidSupportedTargetAotAbis}={context.Properties.GetRequiredValue (KnownProperties.AndroidSupportedTargetAotAbis)}");

            OutputOSVariables(context, sw);

            WriteListVariable("_BUNDLE_ZIPS_INCLUDE", Configurables.Defaults.BundleZipsInclude);
            WriteListVariable("_BUNDLE_ZIPS_EXCLUDE", Configurables.Defaults.BundleZipsExclude);
            WriteListVariable("_TEST_RESULTS_BUNDLE_INCLUDE", Configurables.Defaults.TestResultsBundleInclude);
            WriteListVariable("_TEST_RESULTS_BUNDLE_EXCLUDE", Configurables.Defaults.TestResultsBundleExclude);
            WriteListVariable("_BUILD_STATUS_BUNDLE_INCLUDE", Configurables.Defaults.BuildStatusBundleInclude);
            WriteListVariable("_BUILD_STATUS_BUNDLE_INCLUDE", Configurables.Defaults.BuildStatusBundleIncludeConditional, true);
            WriteListVariable("_BUILD_STATUS_BUNDLE_EXCLUDE", Configurables.Defaults.BuildStatusBundleExclude);

            sw.WriteLine();
            sw.WriteLine(".PHONY: framework-assemblies");
            sw.WriteLine("framework-assemblies:");

            string prevVersion        = "v1.0";
            string monoFrameworksRoot = Path.Combine("bin", "$(CONFIGURATION)", context.Properties.GetRequiredValue(KnownProperties.XABinRelativeInstallPrefix), Configurables.Paths.MonoAndroidFrameworksSubDir);

            for (int i = 0; i < apiLevels.Count; i++)
            {
                string curVersion = frameworks [i];
                string apiLevel   = apiLevels [i];
                string platformId = platformIds [i];
                string redistFile = Path.Combine(monoFrameworksRoot, curVersion, "RedistList", "FrameworkList.xml");
                WriteRuleLine($"grep -q {prevVersion} {redistFile}; \\");
                WriteRuleLine("if [ $$? -ne 0 ] ; then \\");
                WriteRuleLine($"\trm -f {redistFile}; \\");
                WriteRuleLine("fi; \\");
                WriteRuleLine("$(call MSBUILD_BINLOG,Mono.Android,$(_SLN_BUILD)) src/Mono.Android/Mono.Android.csproj \\");
                WriteRuleLine("\t/p:Configuration=$(CONFIGURATION) $(_MSBUILD_ARGS) \\");
                WriteRuleLine($"\t/p:AndroidApiLevel={apiLevel} /p:AndroidPlatformId={platformId} /p:AndroidFrameworkVersion={curVersion} \\");
                WriteRuleLine($"\t/p:AndroidPreviousFrameworkVersion={prevVersion} || exit 1;");

                prevVersion = curVersion;
            }

            string firstApiLevel         = apiLevels [0];
            string firstPlatformId       = platformIds [0];
            string firstFramework        = frameworks [0];
            string latestStableFramework = stableFrameworks [stableFrameworks.Count - 1];

            WriteMSBuildCall(
                fileToRemovePath: Path.Combine(monoFrameworksRoot, "v1.0", "Xamarin.Android.NUnitLite.dll"),
                projectPath: "src/Xamarin.Android.NUnitLite/Xamarin.Android.NUnitLite.csproj"
                );

            WriteMSBuildCall(
                fileToRemovePath: $"{monoFrameworksRoot}/{latestStableFramework}/Mono.Android.Export.*",
                projectPath: "src/Mono.Android.Export/Mono.Android.Export.csproj"
                );

            WriteMSBuildCall(
                fileToRemovePath: $"{monoFrameworksRoot}/{latestStableFramework}/OpenTK-1.0.*",
                projectPath: "src/OpenTK-1.0/OpenTK.csproj"
                );
            sw.WriteLine();

            if (context.RuleGenerators == null || context.RuleGenerators.Count == 0)
            {
                return;
            }

            foreach (RuleGenerator rg in context.RuleGenerators)
            {
                if (rg == null)
                {
                    continue;
                }
                rg(this, sw);
            }

            void WriteMSBuildCall(string fileToRemovePath, string projectPath)
            {
                WriteRuleLine($"rm -f {fileToRemovePath}");
                WriteRuleLine($"$(call MSBUILD_BINLOG,NUnitLite,$(_SLN_BUILD)) $(MSBUILD_FLAGS) {projectPath} \\");
                WriteRuleLine("\t/p:Configuration=$(CONFIGURATION) $(_MSBUILD_ARGS) \\");
                WriteRuleLine($"\t/p:AndroidApiLevel={firstApiLevel} /p:AndroidPlatformId={firstPlatformId} \\");
                WriteRuleLine($"\t/p:AndroidFrameworkVersion={firstFramework} || exit 1;");
            }

            string ToValue(ICollection <string> list, string?separator = null)
            {
                return(String.Join(separator ?? " ", list));
            }

            void WriteRuleLine(string line)
            {
                sw.Write('\t');
                sw.WriteLine(line);
            }

            void WriteVariable(string name, string value)
            {
                sw.WriteLine($"{name} = {value}");
            }

            void WriteListVariable(string name, ICollection <string> list, bool conditional = false)
            {
                if (list.Count == 0)
                {
                    return;
                }

                if (!conditional)
                {
                    sw.Write($"{name} =");
                }

                foreach (string i in list)
                {
                    string item = i.Trim();
                    if (String.IsNullOrEmpty(item))
                    {
                        continue;
                    }

                    if (conditional)
                    {
                        sw.WriteLine($"ifneq ($(wildcard {item}),)");
                        sw.WriteLine($"{name} += {item}");
                        sw.WriteLine("endif");
                        continue;
                    }

                    sw.WriteLine(" \\");
                    sw.Write($"\t{item}");
                }

                if (!conditional)
                {
                    sw.WriteLine();
                }
            }
        }
Exemple #23
0
        protected override async Task <bool> Execute(Context context)
        {
            if (context.ForceRuntimesBuild)
            {
                if (osSupportsMonoBuild)
                {
                    Log.InfoLine("Rebuilding Mono runtimes as requested");
                    return(false);
                }

                Log.InfoLine($"Forced Mono runtimes rebuild requested but rebuilding on {context.OS.Type} is currently not supported.");
            }

            string localPackagePath = Path.Combine(Configurables.Paths.BundleArchivePath);

            if (await Utilities.VerifyArchive(localPackagePath))
            {
                Log.StatusLine("Xamarin.Android Bundle archive already downloaded and valid");
            }
            else
            {
                if (!String.IsNullOrEmpty(context.XABundlePath))
                {
                    // User indicated they wanted to use a specific bundle that's supposed to be on disk. It's not (or
                    // it's invalid) and that means we have no way of getting it - we can't download the default one
                    // since that was not the intention behind overriding the location. Thus, we error out.
                    throw new InvalidOperationException($"Xamarin.Android bundle indicated on the command line does not exist ({context.XABundlePath})");
                }

                var bundleUrl = new Uri(BundleUriPrefix, BundleFileName);

                Log.StatusLine("Bundle URL: ", $"{bundleUrl}", tailColor: ConsoleColor.Cyan);

                HttpStatusCode status;
                bool           success;
                ulong          size;

                (success, size, status) = await Utilities.GetDownloadSizeWithStatus(bundleUrl);

                if (!success)
                {
                    if (status == HttpStatusCode.NotFound)
                    {
                        if (osSupportsMonoBuild)
                        {
                            Log.StatusLine("   not found, will need to rebuild");
                        }
                        else
                        {
                            Log.ErrorLine($"   not found, rebuilding on {context.OS.Type} is not currently supported");
                        }
                        return(false);
                    }

                    if (String.IsNullOrEmpty(bundle404Message))
                    {
                        throw new InvalidOperationException($"Failed to access bundle at {bundleUrl} (HTTP status: {status})");
                    }
                    else
                    {
                        throw new InvalidOperationException(bundle404Message);
                    }
                }

                DownloadStatus downloadStatus = Utilities.SetupDownloadStatus(context, size, context.InteractiveSession);
                Log.StatusLine($"  {context.Characters.Link} {bundleUrl}", ConsoleColor.White);
                await Download(context, bundleUrl, localPackagePath, "Xamarin.Android Bundle", Path.GetFileName(localPackagePath), downloadStatus);

                if (!File.Exists(localPackagePath))
                {
                    Log.ErrorLine($"Download of Xamarin.Android Bundle from {bundleUrl} failed.");
                    return(false);
                }
            }

            Log.StatusLine($"Unpacking bundle to {Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, Configurables.Paths.BundleInstallDir)}");
            string tempDir = $"{Configurables.Paths.BundleInstallDir}-bundle.temp";

            try {
                if (!await Utilities.Unpack(localPackagePath, tempDir, cleanDestinatioBeforeUnpacking: true))
                {
                    Log.WarningLine("Failed to unpack bundle, will need to rebuild");
                    return(false);
                }

                Log.DebugLine("Moving unpacked bundle from {tempDir} to {Configurables.Paths.Bundle_InstallDir}");
                Utilities.MoveDirectoryContentsRecursively(tempDir, Configurables.Paths.BundleInstallDir, resetFileTimestamp: true);
            } finally {
                Utilities.DeleteDirectorySilent(tempDir);
            }

            return(true);
        }
        protected override async Task <bool> Execute(Context context)
        {
            List <ExternalGitDependency> externalDependencies = ExternalGitDependency.GetDependencies(context, Configurables.Paths.ExternalGitDepsFilePath);

            bool failed = false;

            Log.StatusLine();
            Log.StatusLine("Updating external repositories");
            var git = new GitRunner(context)
            {
                EchoCmdAndArguments = false
            };

            foreach (ExternalGitDependency egd in externalDependencies)
            {
                Log.StatusLine($"  {context.Characters.Bullet} {egd.Name}");

                if (egd.Owner == "mono" && egd.Name == "mono")
                {
                    var genFilesStep     = new Step_GenerateFiles(false, true);
                    var monogitPropsFile = genFilesStep.Get_MonoGitHash_props(context);
                    Log.Status("Generating ");
                    Log.Status(Utilities.GetRelativePath(BuildPaths.XamarinAndroidSourceRoot, monogitPropsFile.OutputPath), ConsoleColor.White);
                    monogitPropsFile.Generate(context);
                    continue;
                }

                string destDir = Path.Combine(Configurables.Paths.ExternalGitDepsDestDir, egd.Name);
                if (!Directory.Exists(destDir))
                {
                    var egdUrl = await GetGitHubURL(egd, git);

                    Log.StatusLine($"    {context.Characters.Link} cloning from {egd.Owner}/{egd.Name}");
                    if (!await git.Clone(egdUrl, destDir))
                    {
                        Log.ErrorLine($"Failed to clone {egd.Name}");
                        failed = true;
                        continue;
                    }
                }

                Log.StatusLine($"    {context.Characters.Link} fetching changes from {egd.Owner}/{egd.Name}");
                if (!await git.Fetch(destDir))
                {
                    Log.ErrorLine($"Failed to fetch changes for {egd.Name}");
                    failed = true;
                    continue;
                }

                Log.StatusLine($"    {context.Characters.Bullet} checking out commit {egd.Commit}");
                if (!await git.CheckoutCommit(destDir, egd.Commit))
                {
                    Log.ErrorLine($"Failed to checkout commit {egd.Commit} for {egd.Name}");
                    failed = true;
                    continue;
                }

                //
                // Commented out for now because we only have monodroid in .external and its submodules are updated
                // elsewhere and there's no need to duplicate the (time-consuming) work. However, it might be a good
                // idea to re-enable this code for the benefit of future .external additions (and, possibly, monodroid
                // itself after its integration code is updated to not initialize submodules)
                //

                // string gitModules = Path.Combine (destDir, ".gitmodules");
                // if (!Utilities.FileExists (gitModules))
                //  continue;

                // Log.StatusLine ($"    {context.Characters.Bullet} updating submodules");
                // if (!await git.SubmoduleUpdate (destDir)) {
                //  Log.ErrorLine ($"Failed to update submodules for {egd.Name}");
                //  failed = true;
                // }
            }

            return(!failed);
        }