Esempio n. 1
0
        /// <summary>
        /// This is called on F5 to return the list of debug targets. What we return depends on the type
        /// of project.
        /// </summary>
        private async Task <DebugLaunchSettings> GetConsoleTargetForProfile(ILaunchProfile resolvedProfile, DebugLaunchOptions launchOptions, bool validateSettings)
        {
            var settings = new DebugLaunchSettings(launchOptions);

            string?executable, arguments;

            string            projectFolder     = Path.GetDirectoryName(_project.UnconfiguredProject.FullPath);
            ConfiguredProject?configuredProject = await GetConfiguredProjectForDebugAsync();

            Assumes.NotNull(configuredProject);

            // If no working directory specified in the profile, we default to output directory. If for some reason the output directory
            // is not specified, fall back to the project folder.
            string defaultWorkingDir = await GetOutputDirectoryAsync(configuredProject);

            if (string.IsNullOrEmpty(defaultWorkingDir))
            {
                defaultWorkingDir = projectFolder;
            }
            else
            {
                if (!Path.IsPathRooted(defaultWorkingDir))
                {
                    defaultWorkingDir = _fileSystem.GetFullPath(Path.Combine(projectFolder, defaultWorkingDir));
                }

                // If the directory at OutDir doesn't exist, fall back to the project folder
                if (!_fileSystem.DirectoryExists(defaultWorkingDir))
                {
                    defaultWorkingDir = projectFolder;
                }
            }

            // Is this profile just running the project? If so we ignore the exe
            if (IsRunProjectCommand(resolvedProfile))
            {
                // Get the executable to run, the arguments and the default working directory
                string workingDirectory;
                (executable, arguments, workingDirectory) = await GetRunnableProjectInformationAsync(configuredProject, validateSettings);

                if (!string.IsNullOrWhiteSpace(workingDirectory))
                {
                    defaultWorkingDir = workingDirectory;
                }

                if (!string.IsNullOrWhiteSpace(resolvedProfile.CommandLineArgs))
                {
                    arguments = arguments + " " + resolvedProfile.CommandLineArgs;
                }
            }
            else
            {
                executable = resolvedProfile.ExecutablePath;
                arguments  = resolvedProfile.CommandLineArgs;
            }

            string workingDir;

            if (Strings.IsNullOrWhiteSpace(resolvedProfile.WorkingDirectory))
            {
                workingDir = defaultWorkingDir;
            }
            else
            {
                // If the working directory is not rooted we assume it is relative to the project directory
                workingDir = _fileSystem.GetFullPath(Path.Combine(projectFolder, resolvedProfile.WorkingDirectory.Replace("/", "\\")));
            }

            // IF the executable is not rooted, we want to make is relative to the workingDir unless is doesn't contain
            // any path elements. In that case we are going to assume it is in the current directory of the VS process, or on
            // the environment path. If we can't find it, we just launch it as before.
            if (!Strings.IsNullOrWhiteSpace(executable))
            {
                executable = executable.Replace("/", "\\");
                if (Path.GetPathRoot(executable) == "\\")
                {
                    // Root of current drive
                    executable = _fileSystem.GetFullPath(executable);
                }
                else if (!Path.IsPathRooted(executable))
                {
                    if (executable.Contains("\\"))
                    {
                        // Combine with the working directory used by the profile
                        executable = _fileSystem.GetFullPath(Path.Combine(workingDir, executable));
                    }
                    else
                    {
                        // Try to resolve against the current working directory (for compat) and failing that, the environment path.
                        string exeName  = executable.EndsWith(".exe", StringComparisons.Paths) ? executable : executable + ".exe";
                        string fullPath = _fileSystem.GetFullPath(exeName);
                        if (_fileSystem.FileExists(fullPath))
                        {
                            executable = fullPath;
                        }
                        else
                        {
                            string?fullPathFromEnv = GetFullPathOfExeFromEnvironmentPath(exeName);
                            if (fullPathFromEnv != null)
                            {
                                executable = fullPathFromEnv;
                            }
                        }
                    }
                }
            }

            if (validateSettings)
            {
                ValidateSettings(executable, workingDir, resolvedProfile.Name);
            }

            // Apply environment variables.
            if (resolvedProfile.EnvironmentVariables?.IsEmpty == false)
            {
                foreach ((string key, string value) in resolvedProfile.EnvironmentVariables)
                {
                    settings.Environment[key] = value;
                }
            }

            settings.LaunchOperation       = DebugLaunchOperation.CreateProcess;
            settings.LaunchDebugEngineGuid = await GetDebuggingEngineAsync(configuredProject);

            if (resolvedProfile.IsNativeDebuggingEnabled())
            {
                settings.AdditionalDebugEngines.Add(DebuggerEngines.NativeOnlyEngine);
            }

            if (resolvedProfile.IsSqlDebuggingEnabled())
            {
                settings.AdditionalDebugEngines.Add(DebuggerEngines.SqlEngine);
            }

            if (settings.Environment.Count > 0)
            {
                settings.LaunchOptions |= DebugLaunchOptions.MergeEnvironment;
            }

            bool useCmdShell = false;

            if (await IsConsoleAppAsync())
            {
                if (await IsIntegratedConsoleEnabledAsync())
                {
                    settings.LaunchOptions |= DebugLaunchOptions.IntegratedConsole;
                }

                useCmdShell = UseCmdShellForConsoleLaunch(resolvedProfile, settings.LaunchOptions);
            }

            GetExeAndArguments(useCmdShell, executable, arguments, out string?finalExecutable, out string?finalArguments);

            settings.Executable       = finalExecutable;
            settings.Arguments        = finalArguments;
            settings.CurrentDirectory = workingDir;
            settings.Project          = _unconfiguredProjectVsServices.VsHierarchy;

            if (resolvedProfile.IsRemoteDebugEnabled())
            {
                settings.RemoteMachine = resolvedProfile.RemoteDebugMachine();

                string?remoteAuthenticationMode = resolvedProfile.RemoteAuthenticationMode();
                if (!Strings.IsNullOrEmpty(remoteAuthenticationMode))
                {
                    IRemoteAuthenticationProvider?remoteAuthenticationProvider = _remoteDebuggerAuthenticationService.FindProviderForAuthenticationMode(remoteAuthenticationMode);
                    if (remoteAuthenticationProvider != null)
                    {
                        settings.PortSupplierGuid = remoteAuthenticationProvider.PortSupplierGuid;
                    }
                }
            }

            return(settings);
        }
        /// <summary>
        /// This is called on F5 to return the list of debug targets. What we return depends on the type
        /// of project.
        /// </summary>
        /// <returns><see langword="null"/> if the runnable project information is <see langword="null"/>. Otherwise, the debug launch settings.</returns>
        private async Task <DebugLaunchSettings?> GetConsoleTargetForProfileAsync(ILaunchProfile resolvedProfile, DebugLaunchOptions launchOptions, bool validateSettings)
        {
            var settings = new DebugLaunchSettings(launchOptions);

            string?executable, arguments;

            string            projectFolder     = Path.GetDirectoryName(_project.UnconfiguredProject.FullPath) ?? string.Empty;
            ConfiguredProject?configuredProject = await GetConfiguredProjectForDebugAsync();

            Assumes.NotNull(configuredProject);

            // If no working directory specified in the profile, we default to output directory. If for some reason the output directory
            // is not specified, fall back to the project folder.
            string defaultWorkingDir = await GetOutputDirectoryAsync(configuredProject);

            if (string.IsNullOrEmpty(defaultWorkingDir))
            {
                defaultWorkingDir = projectFolder;
            }
            else
            {
                if (!Path.IsPathRooted(defaultWorkingDir))
                {
                    defaultWorkingDir = _fileSystem.GetFullPath(Path.Combine(projectFolder, defaultWorkingDir));
                }

                // If the directory at OutDir doesn't exist, fall back to the project folder
                if (!_fileSystem.DirectoryExists(defaultWorkingDir))
                {
                    defaultWorkingDir = projectFolder;
                }
            }

            // Is this profile just running the project? If so we ignore the exe
            if (IsRunProjectCommand(resolvedProfile))
            {
                // Get the executable to run, the arguments and the default working directory
                (string Command, string Arguments, string WorkingDirectory)? runnableProjectInfo = await GetRunnableProjectInformationAsync(configuredProject, validateSettings);

                if (runnableProjectInfo == null)
                {
                    return(null);
                }
                string workingDirectory;
                (executable, arguments, workingDirectory) = runnableProjectInfo.Value;

                if (!string.IsNullOrWhiteSpace(workingDirectory))
                {
                    defaultWorkingDir = workingDirectory;
                }

                if (!string.IsNullOrWhiteSpace(resolvedProfile.CommandLineArgs))
                {
                    arguments = arguments + " " + resolvedProfile.CommandLineArgs;
                }
            }
            else
            {
                executable = resolvedProfile.ExecutablePath;
                arguments  = resolvedProfile.CommandLineArgs;
            }

            string workingDir;

            if (Strings.IsNullOrWhiteSpace(resolvedProfile.WorkingDirectory))
            {
                workingDir = defaultWorkingDir;
            }
            else
            {
                // If the working directory is not rooted we assume it is relative to the project directory
                workingDir = _fileSystem.GetFullPath(Path.Combine(projectFolder, resolvedProfile.WorkingDirectory.Replace("/", "\\")));
            }

            // IF the executable is not rooted, we want to make is relative to the workingDir unless is doesn't contain
            // any path elements. In that case we are going to assume it is in the current directory of the VS process, or on
            // the environment path. If we can't find it, we just launch it as before.
            if (!Strings.IsNullOrWhiteSpace(executable))
            {
                executable = executable.Replace("/", "\\");
                if (Path.GetPathRoot(executable) == "\\")
                {
                    // Root of current drive
                    executable = _fileSystem.GetFullPath(executable);
                }
                else if (!Path.IsPathRooted(executable))
                {
                    if (executable.Contains("\\"))
                    {
                        // Combine with the working directory used by the profile
                        executable = _fileSystem.GetFullPath(Path.Combine(workingDir, executable));
                    }
                    else
                    {
                        // Try to resolve against the current working directory (for compat) and failing that, the environment path.
                        string exeName  = executable.EndsWith(".exe", StringComparisons.Paths) ? executable : executable + ".exe";
                        string fullPath = _fileSystem.GetFullPath(exeName);
                        if (_fileSystem.FileExists(fullPath))
                        {
                            executable = fullPath;
                        }
                        else
                        {
                            string?fullPathFromEnv = GetFullPathOfExeFromEnvironmentPath(exeName);
                            if (fullPathFromEnv != null)
                            {
                                executable = fullPathFromEnv;
                            }
                        }
                    }
                }
            }

            if (validateSettings)
            {
                ValidateSettings(executable, workingDir, resolvedProfile.Name);
            }

            // Apply environment variables.
            if (resolvedProfile.EnvironmentVariables?.IsEmpty == false)
            {
                foreach ((string key, string value) in resolvedProfile.EnvironmentVariables)
                {
                    settings.Environment[key] = value;
                }
            }

            settings.LaunchOperation       = DebugLaunchOperation.CreateProcess;
            settings.LaunchDebugEngineGuid = await GetDebuggingEngineAsync(configuredProject);

            if (resolvedProfile.IsNativeDebuggingEnabled())
            {
                settings.AdditionalDebugEngines.Add(DebuggerEngines.NativeOnlyEngine);
            }

            if (resolvedProfile.IsSqlDebuggingEnabled())
            {
                settings.AdditionalDebugEngines.Add(DebuggerEngines.SqlEngine);
            }

            bool useCmdShell = false;

            if (await _outputTypeChecker.IsConsoleAsync())
            {
                if (await IsIntegratedConsoleEnabledAsync())
                {
                    settings.LaunchOptions |= DebugLaunchOptions.IntegratedConsole;
                }

                useCmdShell = UseCmdShellForConsoleLaunch(resolvedProfile, settings.LaunchOptions);
            }

            GetExeAndArguments(useCmdShell, executable, arguments, out string?finalExecutable, out string?finalArguments);

            settings.Executable       = finalExecutable;
            settings.Arguments        = finalArguments;
            settings.CurrentDirectory = workingDir;
            settings.Project          = _unconfiguredProjectVsServices.VsHierarchy;

            if (resolvedProfile.IsRemoteDebugEnabled())
            {
                settings.RemoteMachine = resolvedProfile.RemoteDebugMachine();

                string?remoteAuthenticationMode = resolvedProfile.RemoteAuthenticationMode();
                if (!Strings.IsNullOrEmpty(remoteAuthenticationMode))
                {
                    IRemoteAuthenticationProvider?remoteAuthenticationProvider = _remoteDebuggerAuthenticationService.FindProviderForAuthenticationMode(remoteAuthenticationMode);
                    if (remoteAuthenticationProvider != null)
                    {
                        settings.PortSupplierGuid = remoteAuthenticationProvider.PortSupplierGuid;
                    }
                }
            }

            // WebView2 debugging is only supported for Project and Executable commands
            if (resolvedProfile.IsJSWebView2DebuggingEnabled() && (IsRunExecutableCommand(resolvedProfile) || IsRunProjectCommand(resolvedProfile)))
            {
                // If JS Debugger is selected, we would need to change the launch debugger to that one
                settings.LaunchDebugEngineGuid = DebuggerEngines.JavaScriptForWebView2Engine;

                // Create the launch params needed for the JS debugger
                var debuggerLaunchOptions = new JObject(
                    new JProperty("type", "pwa-msedge"),
                    new JProperty("runtimeExecutable", finalExecutable),
                    new JProperty("webRoot", workingDir),     // We use the Working Directory debugging option as the WebRoot, to map the urls to files on disk
                    new JProperty("useWebView", true),
                    new JProperty("runtimeArgs", finalArguments)
                    );

                settings.Options = JsonConvert.SerializeObject(debuggerLaunchOptions);
            }

            if (await HotReloadShouldBeEnabledAsync(resolvedProfile, launchOptions) &&
                await _hotReloadSessionManager.Value.TryCreatePendingSessionAsync(settings.Environment))
            {
                // Enable XAML Hot Reload
                settings.Environment["ENABLE_XAML_DIAGNOSTICS_SOURCE_INFO"] = "1";
            }

            if (settings.Environment.Count > 0)
            {
                settings.LaunchOptions |= DebugLaunchOptions.MergeEnvironment;
            }

            return(settings);
        }