string GetDestinationDir(AndroidToolchainComponent component, string sdkRoot)
        {
            string path = component.DestDir;

            if (!Path.IsPathRooted(path))
            {
                return(Path.Combine(sdkRoot, path));
            }
            return(path);
        }
        Uri GetPackageUrl(AndroidToolchainComponent component, string packageName)
        {
            Uri packageUrl;

            if (component.RelativeUrl != null)
            {
                packageUrl = new Uri(AndroidToolchain.AndroidUri, component.RelativeUrl);
            }
            else
            {
                packageUrl = AndroidToolchain.AndroidUri;
            }

            return(new Uri(packageUrl, packageName));
        }
        void Check(Context context, string packageCacheDir, string sdkRoot, AndroidToolchainComponent component, List <AndroidPackage> toInstall, int padLeft)
        {
            Log.StatusLine($"  {context.Characters.Bullet} Checking ", component.Name, tailColor: ConsoleColor.White);

            const string statusMissing   = "missing";
            const string statusOutdated  = "outdated";
            const string statusInstalled = "installed";

            string path = GetDestinationDir(component, sdkRoot);

            Log.DebugLine($"Checking if {component.Name} exists in {path}");
            bool missing;

            if (IsInstalled(component, path, out missing))
            {
                LogStatus(statusInstalled, padLeft, Log.InfoColor);
                return;
            }

            if (missing)
            {
                LogStatus(statusMissing, padLeft, ConsoleColor.Magenta);
            }
            else
            {
                LogStatus(statusOutdated, padLeft, ConsoleColor.DarkYellow);
            }

            string packageName = $"{component.Name}.zip";
            var    pkg         = new AndroidPackage {
                Component        = component,
                PackageName      = packageName,
                Url              = GetPackageUrl(component, packageName),
                LocalPackagePath = Path.Combine(packageCacheDir, packageName),
                DestinationDir   = GetDestinationDir(component, sdkRoot),
            };

            toInstall.Add(pkg);
        }
 bool IsNdk(AndroidToolchainComponent component)
 {
     return(component.Name.StartsWith("android-ndk", StringComparison.OrdinalIgnoreCase));
 }
        bool IsInstalled(AndroidToolchainComponent component, string path, out bool missing)
        {
            missing = true;
            if (!Directory.Exists(path))
            {
                Log.DebugLine($"Component '{component.Name}' directory does not exist: {path}");
                return(false);
            }

            // This is just a cursory check, we might want to check versions
            string propsFile = Path.Combine(path, "source.properties");

            if (!File.Exists(propsFile))
            {
                Log.DebugLine($"Component '{component.Name}' properties file does not exist: {propsFile}");
                return(false);
            }

            missing = false;
            if ((RefreshSdk && !IsNdk(component)) || (RefreshNdk && IsNdk(component)))
            {
                Log.DebugLine($"A reinstall has been requested for component '{component.Name}'");
                return(false);
            }

            if (String.IsNullOrEmpty(component.PkgRevision))
            {
                Log.DebugLine($"Component '{component.Name}' does not specify required Pkg.Revision, assuming it's valid");
                return(true);
            }

            Log.DebugLine($"Component '{component.Name}' requires Pkg.Revision to be '{component.PkgRevision}', verifying");
            var props = new JavaProperties();

            try {
                using (var fs = File.OpenRead(propsFile)) {
                    props.Load(fs);
                }
            } catch (Exception ex) {
                Log.DebugLine($"Failed to read '{component.Name}' source.properties. Assuming invalid version, component will be reinstalled.");
                Log.DebugLine(ex.ToString());
                return(false);
            }

            string pkgRevision = props.GetProperty("Pkg.Revision", String.Empty);

            if (String.IsNullOrEmpty(pkgRevision))
            {
                Log.DebugLine($"Component '{component.Name}' does not have Pkg.Revision in its source.properties file, it will be reinstalled.");
                return(false);
            }

            if (!ParseVersion(pkgRevision, out Version pkgVer))
            {
                Log.DebugLine($"Failed to parse a valid version from Pkg.Revision ({pkgRevision}) for component '{component.Name}'. Component will be reinstalled.");
                return(false);
            }

            if (!ParseVersion(component.PkgRevision, out Version expectedPkgVer))
            {
                throw new InvalidOperationException($"Invalid expected package version for component '{component.Name}': {component.PkgRevision}");
            }

            bool equal = pkgVer == expectedPkgVer;

            if (!equal)
            {
                Log.DebugLine($"Installed version of '{component.Name}' ({pkgVer}) is different than the required one ({expectedPkgVer})");
            }

            return(equal);
        }