public override async Task <bool> Install()
        {
            Context context = Context.Instance;

            if (!context.AutoProvisionUsesSudo)
            {
                Log.ErrorLine("Installation of macOS packages requires sudo to be enabled (pass `--auto-provision-uses-sudo=yes` to the bootstrapper)");
                return(false);
            }

            if (PackageUrl == null)
            {
                Log.ErrorLine($"{Name} is not installed but no URL is provided to download it from. Please make sure to install it before continuing");
                return(false);
            }

            (bool success, ulong size) = await Utilities.GetDownloadSize(PackageUrl);

            if (!success)
            {
                Log.ErrorLine($"Failed to get download size of {PackageUrl}");
                return(false);
            }

            DownloadStatus downloadStatus = Utilities.SetupDownloadStatus(context, size, context.InteractiveSession);

            Log.StatusLine($"  {context.Characters.Link} {PackageUrl}", ConsoleColor.White);

            string localPath = Path.Combine(context.Properties.GetRequiredValue(KnownProperties.AndroidToolchainCacheDirectory), Path.GetFileName(PackageUrl.LocalPath));

            success = await Utilities.Download(PackageUrl, localPath, downloadStatus);

            if (!success)
            {
                Log.ErrorLine($"Failed to download {PackageUrl}");
                return(false);
            }

            var runner = new ProcessRunner("sudo")
            {
                EchoStandardError  = true,
                EchoStandardOutput = true,
                ProcessTimeout     = TimeSpan.FromMinutes(10)
            };

            runner.AddArgument("/usr/sbin/installer");
            runner.AddArgument("-verbose");
            runner.AddArgument("-pkg");
            runner.AddQuotedArgument(localPath);
            runner.AddArgument("-target");
            runner.AddArgument("/");

            return(await Task.Run(() => runner.Run()));
        }
        protected override async Task <bool> Execute(Context context)
        {
            string sdkRoot         = context.Properties.GetRequiredValue(KnownProperties.AndroidSdkDirectory);
            string ndkRoot         = context.Properties.GetRequiredValue(KnownProperties.AndroidNdkDirectory);
            string packageCacheDir = context.Properties.GetRequiredValue(KnownProperties.AndroidToolchainCacheDirectory);

            RefreshSdk = context.ComponentsToRefresh.HasFlag(RefreshableComponent.AndroidSDK);
            RefreshNdk = context.ComponentsToRefresh.HasFlag(RefreshableComponent.AndroidNDK);

            Log.StatusLine("Android SDK location: ", sdkRoot, tailColor: Log.DestinationColor);
            Log.StatusLine("Android NDK location: ", ndkRoot, tailColor: Log.DestinationColor);
            Log.DebugLine($"Toolchain cache directory: {packageCacheDir}");

            var toolchain = new AndroidToolchain();
            var toInstall = new List <AndroidPackage> ();

            toolchain.Components.ForEach(c => Check(context, packageCacheDir, sdkRoot, c, toInstall, 4));
            if (toInstall.Count == 0)
            {
                return(GatherNDKInfo(context, ndkRoot));
            }

            Log.MessageLine();
            toInstall.ForEach(p => Log.DebugLine($"Missing Android component: {p.Component.Name}"));

            string tempDir = Path.Combine(context.Properties.GetRequiredValue(KnownProperties.AndroidToolchainDirectory), "temp");

            Log.DebugLine($"Toolchain temporary directory: {tempDir}");

            if (Directory.Exists(tempDir))
            {
                Log.DebugLine("Temporary directory exists, cleaning it up");
                Utilities.DeleteDirectorySilent(tempDir);
            }
            Directory.CreateDirectory(tempDir);

            Log.MessageLine("Installing missing components");
            var toDownload = new List <AndroidPackage> ();

            toInstall.ForEach(c => CheckPackageStatus(context, packageCacheDir, c, toDownload));

            if (toDownload.Count > 0)
            {
                ulong totalDownloadSize = 0;
                foreach (AndroidPackage pkg in toDownload)
                {
                    Log.DebugLine($"Android component '{pkg.Component.Name}' will be downloaded from {pkg.Url}");
                    (bool success, ulong size) = await Utilities.GetDownloadSize(pkg.Url);

                    if (!success)
                    {
                        continue;
                    }
                    totalDownloadSize += size;
                }

                toDownload.ForEach(p => Log.StatusLine($"  {context.Characters.Link} {p.Url}", ConsoleColor.White));

                DownloadStatus downloadStatus = Utilities.SetupDownloadStatus(context, totalDownloadSize, context.InteractiveSession);
                await Task.WhenAll(toDownload.Select(p => Download(context, p.Url, p.LocalPackagePath, p.Component.Name, p.PackageName, downloadStatus)));
            }

            foreach (AndroidPackage p in toInstall)
            {
                await Unpack(context, tempDir, p);
            }

            if (!AcceptLicenses(context, sdkRoot))
            {
                Log.ErrorLine("Failed to accept Android SDK licenses");
                return(false);
            }

            return(GatherNDKInfo(context, ndkRoot));
        }