public override Task <DiagnosticResult> Examine(SharedState history)
        {
            var xamJdks = new List <OpenJdkInfo>();

            try
            {
                var xamSdkInfo = new AndroidSdkInfo((traceLevel, msg) => Util.Log(msg), null, null, null);

                if (!string.IsNullOrEmpty(xamSdkInfo.JavaSdkPath))
                {
                    SearchDirectoryForJdks(xamJdks, xamSdkInfo.JavaSdkPath);
                }
            }
            catch (Exception ex)
            {
                Util.Exception(ex);
            }

            var jdks = xamJdks.Concat(FindJdks())
                       .GroupBy(j => j.Directory.FullName)
                       .Select(g => g.First());

            var ok = false;

            foreach (var jdk in jdks)
            {
                if ((jdk.JavaC.FullName.Contains("microsoft", StringComparison.OrdinalIgnoreCase) || jdk.JavaC.FullName.Contains("openjdk", StringComparison.OrdinalIgnoreCase)) &&
                    jdk.Version.IsCompatible(Version, RequireExact ? Version : null))
                {
                    ok = true;
                    ReportStatus($"{jdk.Version} ({jdk.Directory})", Status.Ok);
                    history.SetEnvironmentVariable("JAVA_HOME", jdk.Directory.FullName);

                    // Try and set the global env var on windows if it's not set
                    if (Util.IsWindows && string.IsNullOrEmpty(Environment.GetEnvironmentVariable("JAVA_HOME")))
                    {
                        try
                        {
                            Environment.SetEnvironmentVariable("JAVA_HOME", jdk.Directory.FullName, EnvironmentVariableTarget.Machine);
                            ReportStatus($"Set Environment Variable: JAVA_HOME={jdk.Directory.FullName}", Status.Ok);
                        } catch { }
                    }
                }
                else
                {
                    ReportStatus($"{jdk.Version} ({jdk.Directory.FullName})", null);
                }
            }

            if (ok)
            {
                return(Task.FromResult(DiagnosticResult.Ok(this)));
            }

            return(Task.FromResult(new DiagnosticResult(Status.Error, this,
                                                        new Suggestion("Install OpenJDK11",
                                                                       new BootsSolution(Manifest?.Check?.OpenJdk?.Url, "Download and Install Microsoft OpenJDK 11")))));
        }
Beispiel #2
0
        public override Task <DiagnosticResult> Examine(SharedState history)
        {
            var xamJdks = new List <OpenJdkInfo>();

            try
            {
                var xamSdkInfo = new AndroidSdkInfo((traceLevel, msg) => Util.Log(msg), null, null, null);

                if (!string.IsNullOrEmpty(xamSdkInfo.JavaSdkPath))
                {
                    SearchDirectoryForJdks(xamJdks, xamSdkInfo.JavaSdkPath);
                }
            }
            catch (Exception ex)
            {
                Util.Exception(ex);
            }

            var jdks = xamJdks.Concat(FindJdks());

            var ok = false;

            foreach (var jdk in jdks)
            {
                if ((jdk.JavaC.FullName.Contains("microsoft") || jdk.JavaC.FullName.Contains("openjdk")) &&
                    jdk.Version.IsCompatible(MinimumVersion, ExactVersion))
                {
                    ok = true;
                    ReportStatus($"{jdk.Version} ({jdk.Directory})", Status.Ok);
                    history.SetEnvironmentVariable("JAVA_HOME", jdk.Directory.FullName);

                    // Try and set the global env var on windows if it's not set
                    if (Util.IsWindows && string.IsNullOrEmpty(Environment.GetEnvironmentVariable("JAVA_HOME")))
                    {
                        try
                        {
                            Environment.SetEnvironmentVariable("JAVA_HOME", jdk.Directory.FullName, EnvironmentVariableTarget.Machine);
                            ReportStatus($"Set Environemnt Variable: JAVA_HOME={jdk.Directory.FullName}", Status.Ok);
                        } catch { }
                    }
                }
                else
                {
                    ReportStatus($"{jdk.Version} ({jdk.Directory})", null);
                }
            }

            if (ok)
            {
                return(Task.FromResult(DiagnosticResult.Ok(this)));
            }

            return(Task.FromResult(new DiagnosticResult(Status.Error, this)));
        }
Beispiel #3
0
        public override async Task <int> ExecuteAsync(CommandContext context, CheckSettings settings)
        {
            Util.Verbose = settings.Verbose;
            Util.CI      = settings.CI;
            if (settings.CI)
            {
                settings.NonInteractive = true;
            }

            Console.Title = ToolInfo.ToolName;

            AnsiConsole.Render(
                new FigletText(".NET MAUI").LeftAligned().Color(Color.Green));

            AnsiConsole.MarkupLine($"[underline bold green]{Icon.Ambulance} {ToolInfo.ToolName} {Icon.Recommend}[/]");
            AnsiConsole.Render(new Rule());

            AnsiConsole.MarkupLine("This tool will attempt to evaluate your .NET MAUI development environment.");
            AnsiConsole.MarkupLine("If problems are detected, this tool may offer the option to try and fix them for you, or suggest a way to fix them yourself.");
            AnsiConsole.WriteLine();
            AnsiConsole.MarkupLine("Thanks for choosing .NET MAUI!");
            AnsiConsole.Render(new Rule());

            if (!Util.IsAdmin() && Util.IsWindows)
            {
                var suTxt = Util.IsWindows ? "Administrator" : "Superuser (su)";

                AnsiConsole.MarkupLine($"[bold red]{Icon.Bell} {suTxt} is required to fix most issues.  Consider exiting and running the tool with {suTxt} permissions.[/]");

                AnsiConsole.Render(new Rule());

                if (!settings.NonInteractive)
                {
                    if (!AnsiConsole.Confirm("Would you still like to continue?", false))
                    {
                        return(1);
                    }
                }
            }

            var cts = new System.Threading.CancellationTokenSource();

            var checkupStatus = new Dictionary <string, Models.Status>();
            var sharedState   = new SharedState();

            var results       = new Dictionary <string, DiagnosticResult>();
            var consoleStatus = AnsiConsole.Status();

            var skippedChecks = new List <string>();

            AnsiConsole.Markup($"[bold blue]{Icon.Thinking} Synchronizing configuration...[/]");

            var manifest = await ToolInfo.LoadManifest(settings.Manifest, settings.Dev);

            if (!ToolInfo.Validate(manifest))
            {
                ToolInfo.ExitPrompt(settings.NonInteractive);
                return(-1);
            }

            AnsiConsole.MarkupLine(" ok");
            AnsiConsole.Markup($"[bold blue]{Icon.Thinking} Scheduling appointments...[/]");

            if (!string.IsNullOrEmpty(settings.DotNetSdkRoot))
            {
                sharedState.SetEnvironmentVariable("DOTNET_ROOT", settings.DotNetSdkRoot);
            }

            var checkups = CheckupManager.BuildCheckupGraph(manifest, sharedState);

            AnsiConsole.MarkupLine(" ok");

            var checkupId = string.Empty;

            for (int i = 0; i < checkups.Count(); i++)
            {
                var checkup = checkups.ElementAt(i);

                // Set the manifest
                checkup.Manifest = manifest;

                // If the ID is the same, it's a retry
                var isRetry = checkupId == checkup.Id;

                // Track the last used id so we can detect retry
                checkupId = checkup.Id;

                if (!checkup.ShouldExamine(sharedState))
                {
                    checkupStatus[checkup.Id] = Models.Status.Ok;
                    continue;
                }

                var skipCheckup = false;

                var dependencies = checkup.DeclareDependencies(checkups.Select(c => c.Id));

                // Make sure our dependencies succeeded first
                if (dependencies?.Any() ?? false)
                {
                    foreach (var dep in dependencies)
                    {
                        var depCheckup = checkups.FirstOrDefault(c => c.Id.StartsWith(dep.CheckupId, StringComparison.OrdinalIgnoreCase));

                        if (depCheckup != null && depCheckup.IsPlatformSupported(Util.Platform))
                        {
                            if (!checkupStatus.TryGetValue(dep.CheckupId, out var depStatus) || depStatus == Models.Status.Error)
                            {
                                skipCheckup = dep.IsRequired;
                                break;
                            }
                        }
                    }
                }

                // See if --skip was specified
                if (settings.Skip?.Any(s => s.Equals(checkup.Id, StringComparison.OrdinalIgnoreCase) ||
                                       s.Equals(checkup.GetType().Name, StringComparison.OrdinalIgnoreCase)) ?? false)
                {
                    skipCheckup = true;
                }

                if (skipCheckup)
                {
                    skippedChecks.Add(checkup.Id);
                    checkupStatus[checkup.Id] = Models.Status.Error;
                    AnsiConsole.WriteLine();
                    AnsiConsole.MarkupLine($"[bold red]{Icon.Error} Skipped: " + checkup.Title + "[/]");
                    continue;
                }

                checkup.OnStatusUpdated += checkupStatusUpdated;

                AnsiConsole.WriteLine();
                AnsiConsole.MarkupLine($"[bold]{Icon.Checking} " + checkup.Title + " Checkup[/]...");
                Console.Title = checkup.Title;

                DiagnosticResult diagnosis = null;

                try
                {
                    diagnosis = await checkup.Examine(sharedState);
                }
                catch (Exception ex)
                {
                    Util.Exception(ex);
                    diagnosis = new DiagnosticResult(Models.Status.Error, checkup, ex.Message);
                }

                results[checkup.Id] = diagnosis;

                // Cache the status for dependencies
                checkupStatus[checkup.Id] = diagnosis.Status;

                if (diagnosis.Status == Models.Status.Ok)
                {
                    continue;
                }

                var statusEmoji = diagnosis.Status == Models.Status.Error ? Icon.Error : Icon.Warning;
                var statusColor = diagnosis.Status == Models.Status.Error ? "red" : "darkorange3_1";

                var msg = !string.IsNullOrEmpty(diagnosis.Message) ? " - " + diagnosis.Message : string.Empty;

                if (diagnosis.HasSuggestion)
                {
                    Console.WriteLine();
                    AnsiConsole.Render(new Rule());
                    AnsiConsole.MarkupLine($"[bold blue]{Icon.Recommend} Recommendation:[/][blue] {diagnosis.Suggestion.Name}[/]");

                    if (!string.IsNullOrEmpty(diagnosis.Suggestion.Description))
                    {
                        AnsiConsole.MarkupLine("" + diagnosis.Suggestion.Description + "");
                    }

                    AnsiConsole.Render(new Rule());
                    Console.WriteLine();

                    // See if we should fix
                    // needs to have a remedy available to even bother asking/trying
                    var doFix = diagnosis.Suggestion.HasSolution &&
                                (
                        // --fix + --non-interactive == auto fix, no prompt
                        (settings.NonInteractive && settings.Fix)
                        // interactive (default) + prompt/confirm they want to fix
                        || (!settings.NonInteractive && AnsiConsole.Confirm($"[bold]{Icon.Bell} Attempt to fix?[/]"))
                                );

                    if (doFix && !isRetry)
                    {
                        var isAdmin = Util.IsAdmin();

                        var adminMsg = Util.IsWindows ?
                                       $"{Icon.Bell} [red]Administrator Permissions Required.  Try opening a new console as Administrator and running this tool again.[/]"
                                                        : $"{Icon.Bell} [red]Super User Permissions Required.  Try running this tool again with 'sudo'.[/]";

                        var didFix = false;

                        foreach (var remedy in diagnosis.Suggestion.Solutions)
                        {
                            try
                            {
                                remedy.OnStatusUpdated += remedyStatusUpdated;

                                AnsiConsole.MarkupLine($"{Icon.Thinking} Attempting to fix: " + checkup.Title);

                                await remedy.Implement(sharedState, cts.Token);

                                didFix = true;
                                AnsiConsole.MarkupLine($"[bold]Fix applied.  Checking again...[/]");
                            }
                            catch (Exception x) when(x is AccessViolationException || x is UnauthorizedAccessException)
                            {
                                Util.Exception(x);
                                AnsiConsole.Markup(adminMsg);
                            }
                            catch (Exception ex)
                            {
                                Util.Exception(ex);
                                AnsiConsole.MarkupLine("[bold red]Fix failed - " + ex.Message + "[/]");
                            }
                            finally
                            {
                                remedy.OnStatusUpdated -= remedyStatusUpdated;
                            }
                        }

                        // RETRY The check again
                        if (didFix)
                        {
                            i--;
                        }
                    }
                }

                checkup.OnStatusUpdated -= checkupStatusUpdated;
            }

            AnsiConsole.Render(new Rule());
            AnsiConsole.WriteLine();

            var erroredChecks = results.Values.Where(d => d.Status == Models.Status.Error && !skippedChecks.Contains(d.Checkup.Id));

            foreach (var ec in erroredChecks)
            {
                Util.Log($"Checkup had Error status: {ec.Checkup.Id}");
            }

            var hasErrors = erroredChecks.Any();

            if (hasErrors)
            {
                AnsiConsole.MarkupLine($"[bold red]{Icon.Bell} There were one or more problems detected.[/]");
                AnsiConsole.MarkupLine($"[bold red]Please review the errors and correct them and run {ToolInfo.ToolCommand} again.[/]");
            }
            else
            {
                AnsiConsole.MarkupLine($"[bold blue]{Icon.Success} Congratulations, everything looks great![/]");
            }

            Console.Title = ToolInfo.ToolName;

            ToolInfo.ExitPrompt(settings.NonInteractive);

            Util.Log($"Has Errors? {hasErrors}");
            var exitCode = hasErrors ? 1 : 0;

            Environment.ExitCode = exitCode;

            return(exitCode);
        }
        public DotNetSdk(SharedState sharedState)
        {
            KnownDotnetLocations = Util.Platform switch
            {
                Platform.Windows => new string[]
                {
                    Path.Combine(
                        Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
                        "dotnet",
                        DotNetExeName),
                    Path.Combine(
                        Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
                        "dotnet",
                        DotNetExeName),
                },
                Platform.OSX => new string[]
                {
                    "/usr/local/share/dotnet/dotnet",
                },
                Platform.Linux => new string[]
                {
                    // /home/user/share/dotnet/dotnet
                    Path.Combine(
                        Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
                        "share",
                        "dotnet",
                        DotNetExeName)
                },
                _ => new string[] { }
            };

            string sdkRoot = null;

            if (sharedState != null && sharedState.TryGetEnvironmentVariable("DOTNET_ROOT", out var envSdkRoot))
            {
                if (Directory.Exists(envSdkRoot))
                {
                    sdkRoot = envSdkRoot;
                }
            }

            if (string.IsNullOrEmpty(sdkRoot) || !Directory.Exists(sdkRoot))
            {
                sdkRoot = Microsoft.DotNet.NativeWrapper.EnvironmentProvider.GetDotnetExeDirectory();
            }

            if (string.IsNullOrEmpty(sdkRoot) || !Directory.Exists(sdkRoot))
            {
                var l = FindDotNetLocations();
                if (l != default)
                {
                    sdkRoot = l.sdkDir.FullName;
                }
            }

            sharedState.SetEnvironmentVariable("DOTNET_ROOT", sdkRoot);

            // First try and use the actual resolver logic
            DotNetSdkLocation = new DirectoryInfo(sdkRoot);
            DotNetExeLocation = new FileInfo(Path.Combine(DotNetSdkLocation.FullName, DotNetExeName));
        }
        public override async Task <DiagnosticResult> Examine(SharedState history)
        {
            var dn = new DotNetSdk(history);

            var missingDiagnosis = new DiagnosticResult(Status.Error, this, new Suggestion(".NET SDK not installed"));

            if (!dn.Exists)
            {
                return(missingDiagnosis);
            }

            var sdks = await dn.GetSdks();

            var missingSdks   = new List <Manifest.DotNetSdk>();
            var sentinelFiles = new List <string>();

            if (RequiredSdks?.Any() ?? false)
            {
                foreach (var rs in RequiredSdks)
                {
                    var rsVersion = NuGetVersion.Parse(rs.Version);

                    if (!sdks.Any(s => (rs.RequireExact && s.Version == rsVersion) || (!rs.RequireExact && s.Version >= rsVersion)))
                    {
                        missingSdks.Add(rs);
                    }
                }
            }

            DotNetSdkInfo bestSdk = null;

            foreach (var sdk in sdks)
            {
                // See if the sdk is one of the required sdk's
                var requiredSdk = RequiredSdks.FirstOrDefault(rs => sdk.Version == NuGetVersion.Parse(rs.Version));

                if (requiredSdk != null)
                {
                    if (bestSdk == null || sdk.Version > bestSdk.Version)
                    {
                        bestSdk = sdk;
                    }

                    if (requiredSdk.EnableWorkloadResolver)
                    {
                        var sentinelPath = Path.Combine(sdk.Directory.FullName, "EnableWorkloadResolver.sentinel");
                        sentinelFiles.Add(sentinelPath);
                    }

                    ReportStatus($"{sdk.Version} - {sdk.Directory}", Status.Ok);
                }
                else
                {
                    ReportStatus($"{sdk.Version} - {sdk.Directory}", null);
                }
            }

            // If we didn't get the exact one before, let's find a new enough one
            if (bestSdk == null)
            {
                bestSdk = sdks.OrderByDescending(s => s.Version)?.FirstOrDefault();
            }

            // Find newest compatible sdk
            if (bestSdk != null)
            {
                history.SetEnvironmentVariable("DOTNET_SDK", bestSdk.Directory.FullName);
                history.SetEnvironmentVariable("DOTNET_SDK_VERSION", bestSdk.Version.ToString());
            }

            // Add sentinel files that should be considered
            if (sentinelFiles.Any())
            {
                history.ContributeState(this, "sentinel_files", sentinelFiles.ToArray());
            }

            if (missingSdks.Any())
            {
                var str = SdkListToString();

                var remedies = new List <Solution>();

                if (Util.CI)
                {
                    remedies.AddRange(missingSdks
                                      .Select(ms => new DotNetSdkScriptInstallSolution(ms.Version)));
                }
                else
                {
                    remedies.AddRange(missingSdks
                                      .Where(ms => !ms.Url.AbsolutePath.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
                                      .Select(ms => new BootsSolution(ms.Url, ".NET SDK " + ms.Version) as Solution));

                    remedies.AddRange(missingSdks
                                      .Where(ms => ms.Url.AbsolutePath.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
                                      .Select(ms => new MsInstallerSolution(ms.Url, ".NET SDK " + ms.Version)));
                }

                return(new DiagnosticResult(Status.Error, this, $".NET SDK {str} not installed.",
                                            new Suggestion($"Download .NET SDK {str}",
                                                           remedies.ToArray())));
            }

            return(new DiagnosticResult(Status.Ok, this));
        }
Beispiel #6
0
        public override Task <DiagnosticResult> Examine(SharedState history)
        {
            var jdkPath = history.GetEnvironmentVariable("JAVA_HOME") ?? Environment.GetEnvironmentVariable("JAVA_HOME");

            string androidSdkPath = null;

            try
            {
                // Set the logger to override the default one that is set in this library
                // So we can catch output from failed path lookups that are otherwise swallowed
                var _ = new AndroidSdkInfo((traceLevel, msg) =>
                {
                    if (Util.Verbose || traceLevel == System.Diagnostics.TraceLevel.Error)
                    {
                        Util.LogAlways(msg);
                    }
                }, androidSdkPath, null, jdkPath);
            }
            catch (Exception ex)
            {
                Util.Exception(ex);
            }

            if (string.IsNullOrEmpty(androidSdkPath))
            {
                androidSdkPath = FindBestSdkLocation();
            }

            var missingPackages = new List <IAndroidComponent>();

            var installer = new AndroidSDKInstaller(new Helper(), AndroidManifestType.GoogleV2);

            installer.Discover(new List <string> {
                androidSdkPath
            });

            var sdkInstance = installer.FindInstance(androidSdkPath);

            if (string.IsNullOrEmpty(sdkInstance?.Path))
            {
                return(Task.FromResult(
                           new DiagnosticResult(
                               Status.Error,
                               this,
                               "Failed to find Android SDK.",
                               new Suggestion("Install the Android SDK",
                                              "For more information see: [underline]https://aka.ms/dotnet-androidsdk-help[/]"))));
            }

            history.SetEnvironmentVariable("ANDROID_SDK_ROOT", sdkInstance.Path);
            history.SetEnvironmentVariable("ANDROID_HOME", sdkInstance.Path);

            var installed = sdkInstance?.Components?.AllInstalled(true);

            foreach (var package in RequiredPackages)
            {
                var v = !string.IsNullOrWhiteSpace(package.Version) ? new AndroidRevision(package.Version) : null;

                var installedPkg = FindInstalledPackage(installed, package)
                                   ?? FindInstalledPackage(installed, package.Alternatives?.ToArray());

                if (installedPkg == null)
                {
                    var pkgToInstall = sdkInstance?.Components?.AllNotInstalled()?
                                       .FirstOrDefault(p => p.Path.Equals(package.Path.Trim(), StringComparison.OrdinalIgnoreCase) &&
                                                       p.Revision >= (v ?? p.Revision));

                    ReportStatus($"{package.Path} ({package.Version}) missing.", Status.Error);

                    if (pkgToInstall != null)
                    {
                        missingPackages.Add(pkgToInstall);
                    }
                }
                else
                {
                    if (!package.Path.Equals(installedPkg.Path) || v != (installedPkg.Revision ?? installedPkg.InstalledRevision))
                    {
                        ReportStatus($"{installedPkg.Path} ({installedPkg.InstalledRevision ?? installedPkg.Revision})", Status.Ok);
                    }
                    else
                    {
                        ReportStatus($"{package.Path} ({package.Version})", Status.Ok);
                    }
                }
            }

            if (!missingPackages.Any())
            {
                return(Task.FromResult(DiagnosticResult.Ok(this)));
            }


            var installationSet = installer.GetInstallationSet(sdkInstance, missingPackages);

            var desc =
                @$ "Your Android SDK has missing or outdated packages.
You can use the Android SDK Manager to install / update them.
For more information see: [underline]https://aka.ms/dotnet-androidsdk-help[/]";