public static void LaunchVisualStudio(string devEnvFullPath, bool useShellExecute, string solutionFileFullPath, ISlnGenLogger logger) { ProcessStartInfo processStartInfo; if (!string.IsNullOrWhiteSpace(devEnvFullPath)) { if (!File.Exists(devEnvFullPath)) { logger.LogError($"The specified path to Visual Studio ({devEnvFullPath}) does not exist or is inaccessible."); return; } processStartInfo = new ProcessStartInfo { FileName = devEnvFullPath, Arguments = $"\"{solutionFileFullPath}\"", }; } else if (!useShellExecute) { processStartInfo = new ProcessStartInfo { Arguments = $"/C start \"\" \"devenv.exe\" \"{solutionFileFullPath}\"", FileName = Environment.GetEnvironmentVariable("ComSpec"), WindowStyle = ProcessWindowStyle.Hidden, }; } else { processStartInfo = new ProcessStartInfo { FileName = solutionFileFullPath, UseShellExecute = true, }; } try { Process process = new Process { StartInfo = processStartInfo, }; logger.LogMessageHigh("Opening Visual Studio solution..."); logger.LogMessageLow(" FileName = {0}", processStartInfo.FileName); logger.LogMessageLow(" Arguments = {0}", processStartInfo.Arguments); logger.LogMessageLow(" UseShellExecute = {0}", processStartInfo.UseShellExecute); logger.LogMessageLow(" WindowStyle = {0}", processStartInfo.WindowStyle); if (!process.Start()) { logger.LogError("Failed to launch Visual Studio."); } } catch (Exception e) { logger.LogError($"Failed to launch Visual Studio. {e.Message}"); } }
/// <summary> /// Loads projects. /// </summary> /// <param name="msbuildExeFileInfo">The <see cref="FileInfo" /> of MSBuild.exe.</param> /// <param name="projectCollection">The <see cref="ProjectCollection" /> to use when loading projects.</param> /// <param name="entryProjects">The <see cref="IEnumerable{String}" /> of entry projects.</param> /// <param name="globalProperties">The <see cref="IDictionary{String,String}" /> of global properties to use.</param> /// <param name="logger">A <see cref="ISlnGenLogger" /> to use as a logger.</param> /// <returns>A <see cref="Tuple{TimeSpan, Int32}" /> with the amount of time it took to load projects and the total number of projects that were loaded.</returns> public static (TimeSpan projectEvaluation, int projectCount) LoadProjects(FileInfo msbuildExeFileInfo, ProjectCollection projectCollection, IEnumerable <string> entryProjects, IDictionary <string, string> globalProperties, ISlnGenLogger logger) { if (logger.HasLoggedErrors) { return(TimeSpan.Zero, 0); } logger.LogMessageHigh("Loading project references..."); Stopwatch sw = Stopwatch.StartNew(); IProjectLoader projectLoader = Create(msbuildExeFileInfo, logger); try { projectLoader.LoadProjects(entryProjects, projectCollection, globalProperties); } catch (InvalidProjectFileException) { return(TimeSpan.Zero, 0); } catch (Exception e) { logger.LogError(e.ToString()); return(TimeSpan.Zero, 0); } sw.Stop(); logger.LogMessageNormal($"Loaded {projectCollection.LoadedProjects.Count:N0} project(s) in {sw.ElapsedMilliseconds:N0}ms"); return(sw.Elapsed, projectCollection.LoadedProjects.Count); }
/// <summary> /// Attempts to load the specified project if it hasn't already been loaded. /// </summary> /// <param name="path">The path to the project to load.</param> /// <param name="toolsVersion">The ToolsVersion to use when loading the project.</param> /// <param name="projectCollection">The <see cref="ProjectCollection"/> to load the project into.</param> /// <param name="globalProperties">The <see cref="IDictionary{String,String}" /> to use when evaluating the project.</param> /// <param name="project">Contains the loaded <see cref="Project"/> if one was loaded.</param> /// <returns><code>true</code> if the project was loaded, otherwise <code>false</code>.</returns> private bool TryLoadProject(string path, string toolsVersion, ProjectCollection projectCollection, IDictionary <string, string> globalProperties, out Project project) { project = null; bool shouldLoadProject; string fullPath = path.ToFullPathInCorrectCase(); lock (_loadedProjects) { shouldLoadProject = _loadedProjects.Add(fullPath); } if (!shouldLoadProject) { return(false); } long now = DateTime.Now.Ticks; try { project = new Project(fullPath, globalProperties, toolsVersion, projectCollection, DefaultProjectLoadSettings); ProjectLoader.LogProjectStartedEvent(_logger, project.CreateProjectInstance(ProjectInstanceSettings.ImmutableWithFastItemLookup, ProjectLoader.SharedEvaluationContext)); } catch (InvalidProjectFileException e) { _logger.LogError(e.Message, e.ErrorCode, e.ProjectFile, e.LineNumber, e.ColumnNumber); return(false); } catch (Exception e) { _logger.LogError(message: e.ToString()); return(false); } if (CollectStats) { Statistics.TryAddProjectLoadTime(path, TimeSpan.FromTicks(DateTime.Now.Ticks - now)); } return(true); }
/// <summary> /// Gets specified projects or all projects in the current working directory. /// </summary> /// <returns>An <see cref="IEnumerable{String}" /> containing the full paths to projects to generate a solution for.</returns> private IEnumerable <string> GetEntryProjectPaths(ISlnGenLogger logger) { if (_arguments.Projects == null || !_arguments.Projects.Any()) { logger.LogMessageNormal("Searching \"{0}\" for projects", Environment.CurrentDirectory); bool projectFound = false; foreach (string projectPath in Directory.EnumerateFiles(Environment.CurrentDirectory, "*.*proj")) { projectFound = true; logger.LogMessageNormal("Generating solution for project \"{0}\"", projectPath); yield return(projectPath); } if (!projectFound) { logger.LogError("No projects found in the current directory. Please specify the path to the project you want to generate a solution for."); } yield break; } foreach (string projectPath in _arguments.Projects.Select(Path.GetFullPath)) { if (!File.Exists(projectPath)) { logger.LogError(string.Format("Project file \"{0}\" does not exist", projectPath)); continue; } logger.LogMessageNormal("Generating solution for project \"{0}\"", projectPath); yield return(projectPath); } }
private (TimeSpan projectEvaluation, int projectCount) LoadProjects(ProjectCollection projectCollection, ISlnGenLogger logger) { List <string> entryProjects = GetEntryProjectPaths(logger).ToList(); if (logger.HasLoggedErrors) { return(TimeSpan.Zero, 0); } logger.LogMessageHigh("Loading project references..."); Stopwatch sw = Stopwatch.StartNew(); IProjectLoader projectLoader = ProjectLoaderFactory.Create(_msbuildExePath, logger); IDictionary <string, string> globalProperties = GetGlobalProperties(); using (new MSBuildFeatureFlags { CacheFileEnumerations = true, LoadAllFilesAsReadOnly = true, MSBuildSkipEagerWildCardEvaluationRegexes = true, UseSimpleProjectRootElementCacheConcurrency = true, MSBuildExePath = _msbuildExePath.FullName, }) { try { projectLoader.LoadProjects(entryProjects, projectCollection, globalProperties); } catch (InvalidProjectFileException) { return(TimeSpan.Zero, 0); } catch (Exception e) { logger.LogError(e.ToString()); return(TimeSpan.Zero, 0); } } sw.Stop(); logger.LogMessageNormal($"Loaded {projectCollection.LoadedProjects.Count:N0} project(s) in {sw.ElapsedMilliseconds:N0}ms"); return(sw.Elapsed, projectCollection.LoadedProjects.Count); }
/// <summary> /// Launches Visual Studio. /// </summary> /// <param name="solutionFileFullPath">The full path to the solution file.</param> /// <param name="loadProjects">A value indicating whether to load projects in Visual Studio.</param> /// <param name="devEnvFullPath">An optional full path to devenv.exe.</param> /// <param name="logger">A <see cref="ISlnGenLogger" /> to use for logging.</param> public static void Launch(string solutionFileFullPath, bool loadProjects, string devEnvFullPath, ISlnGenLogger logger) { if (solutionFileFullPath.IsNullOrWhiteSpace()) { throw new ArgumentNullException(nameof(solutionFileFullPath)); } CommandLineBuilder commandLineBuilder = new CommandLineBuilder(); ProcessStartInfo processStartInfo; if (!devEnvFullPath.IsNullOrWhiteSpace()) { if (!File.Exists(devEnvFullPath)) { logger.LogError($"The specified path to Visual Studio ({devEnvFullPath}) does not exist or is inaccessible."); return; } processStartInfo = new ProcessStartInfo { FileName = devEnvFullPath, UseShellExecute = false, }; commandLineBuilder.AppendFileNameIfNotNull(solutionFileFullPath); if (!loadProjects) { commandLineBuilder.AppendSwitch(DoNotLoadProjectsCommandLineArgument); } } else { processStartInfo = new ProcessStartInfo { FileName = solutionFileFullPath, UseShellExecute = true, }; } try { processStartInfo.Arguments = commandLineBuilder.ToString(); Process process = new Process { StartInfo = processStartInfo, }; logger.LogMessageHigh("Launching Visual Studio..."); logger.LogMessageLow(" FileName = {0}", processStartInfo.FileName); logger.LogMessageLow(" Arguments = {0}", processStartInfo.Arguments); logger.LogMessageLow(" UseShellExecute = {0}", processStartInfo.UseShellExecute); logger.LogMessageLow(" WindowStyle = {0}", processStartInfo.WindowStyle); if (!process.Start()) { logger.LogError("Failed to launch Visual Studio."); } } catch (Exception e) { logger.LogError($"Failed to launch Visual Studio. {e.Message}"); } }
/// <summary> /// Generates a solution file. /// </summary> /// <param name="arguments">The current <see cref="ProgramArguments" />.</param> /// <param name="projects">A <see cref="IEnumerable{String}" /> containing the entry projects.</param> /// <param name="logger">A <see cref="ISlnGenLogger" /> to use for logging.</param> /// <returns>A <see cref="Tuple{String, Int32, Int32, Guid}" /> with the full path to the solution file, the count of custom project type GUIDs used, the count of solution items, and the solution GUID.</returns> public static (string solutionFileFullPath, int customProjectTypeGuidCount, int solutionItemCount, Guid solutionGuid) GenerateSolutionFile(ProgramArguments arguments, IEnumerable <Project> projects, ISlnGenLogger logger) { List <Project> projectList = projects.ToList(); Project firstProject = projectList.First(); IReadOnlyDictionary <string, Guid> customProjectTypeGuids = SlnProject.GetCustomProjectTypeGuids(firstProject); IReadOnlyCollection <string> solutionItems = SlnProject.GetSolutionItems(firstProject, logger).ToList(); string solutionFileFullPath = arguments.SolutionFileFullPath?.LastOrDefault(); if (solutionFileFullPath.IsNullOrWhiteSpace()) { string solutionDirectoryFullPath = arguments.SolutionDirectoryFullPath?.LastOrDefault(); if (solutionDirectoryFullPath.IsNullOrWhiteSpace()) { solutionDirectoryFullPath = firstProject.DirectoryPath; } string solutionFileName = Path.ChangeExtension(Path.GetFileName(firstProject.FullPath), "sln"); solutionFileFullPath = Path.Combine(solutionDirectoryFullPath !, solutionFileName); } logger.LogMessageHigh($"Generating Visual Studio solution \"{solutionFileFullPath}\" ..."); if (customProjectTypeGuids.Count > 0) { logger.LogMessageLow("Custom Project Type GUIDs:"); foreach (KeyValuePair <string, Guid> item in customProjectTypeGuids) { logger.LogMessageLow(" {0} = {1}", item.Key, item.Value); } } SlnFile solution = new SlnFile { Platforms = arguments.GetPlatforms(), Configurations = arguments.GetConfigurations(), }; if (SlnFile.TryParseExistingSolution(solutionFileFullPath, out Guid solutionGuid, out IReadOnlyDictionary <string, Guid> projectGuidsByPath)) { logger.LogMessageNormal("Updating existing solution file and reusing Visual Studio cache"); solution.SolutionGuid = solutionGuid; solution.ExistingProjectGuids = projectGuidsByPath; arguments.LoadProjectsInVisualStudio = new[] { bool.TrueString }; } solution.AddProjects(projectList, customProjectTypeGuids, arguments.IgnoreMainProject ? null : firstProject.FullPath); solution.AddSolutionItems(solutionItems); if (!arguments.EnableFolders()) { foreach (SlnProject project in solution._projects.Distinct(new EqualityComparer <SlnProject>((x, y) => string.Equals(x.SolutionFolder, y.SolutionFolder))) .Where(i => !string.IsNullOrWhiteSpace(i.SolutionFolder) && i.SolutionFolder.Contains(Path.DirectorySeparatorChar))) { logger.LogError($"The value of SlnGenSolutionFolder \"{project.SolutionFolder}\" cannot contain directory separators."); } } if (!logger.HasLoggedErrors) { solution.Save(solutionFileFullPath, arguments.EnableFolders(), arguments.EnableCollapseFolders()); } return(solutionFileFullPath, customProjectTypeGuids.Count, solutionItems.Count, solution.SolutionGuid); }
/// <summary> /// Launches Visual Studio. /// </summary> /// <param name="arguments">The current <see cref="ProgramArguments" />.</param> /// <param name="visualStudioInstance">A <see cref="VisualStudioInstance" /> object representing which instance of Visual Studio to launch.</param> /// <param name="solutionFileFullPath">The full path to the solution file.</param> /// <param name="logger">A <see cref="ISlnGenLogger" /> to use for logging.</param> /// <returns>true if Visual Studio was launched, otherwise false.</returns> public static bool TryLaunch(ProgramArguments arguments, VisualStudioInstance visualStudioInstance, string solutionFileFullPath, ISlnGenLogger logger) { if (!arguments.ShouldLaunchVisualStudio()) { return(true); } if (!Utility.RunningOnWindows) { logger.LogWarning("Launching Visual Studio is not currently supported on your operating system."); return(true); } bool loadProjectsInVisualStudio = arguments.ShouldLoadProjectsInVisualStudio(); bool enableShellExecute = arguments.EnableShellExecute(); string devEnvFullPath = arguments.DevEnvFullPath?.LastOrDefault(); if (!enableShellExecute || !loadProjectsInVisualStudio || Program.CurrentDevelopmentEnvironment.IsCorext) { if (devEnvFullPath.IsNullOrWhiteSpace()) { if (visualStudioInstance == null) { logger.LogError( Program.CurrentDevelopmentEnvironment.IsCorext ? $"Could not find a Visual Studio {Environment.GetEnvironmentVariable("VisualStudioVersion")} installation. Please do one of the following:\n a) Specify a full path to devenv.exe via the -vs command-line argument\n b) Update your corext.config to specify a version of MSBuild.Corext that matches a Visual Studio version you have installed\n c) Install a version of Visual Studio that matches the version of MSBuild.Corext in your corext.config" : "Could not find a Visual Studio installation. Please specify the full path to devenv.exe via the -vs command-line argument"); return(false); } if (visualStudioInstance.IsBuildTools) { logger.LogError("Cannot use a BuildTools instance of Visual Studio."); return(false); } devEnvFullPath = Path.Combine(visualStudioInstance.InstallationPath, "Common7", "IDE", "devenv.exe"); } } if (solutionFileFullPath.IsNullOrWhiteSpace()) { throw new ArgumentNullException(nameof(solutionFileFullPath)); } CommandLineBuilder commandLineBuilder = new CommandLineBuilder(); ProcessStartInfo processStartInfo; if (!devEnvFullPath.IsNullOrWhiteSpace()) { if (!File.Exists(devEnvFullPath)) { logger.LogError($"The specified path to Visual Studio ({devEnvFullPath}) does not exist or is inaccessible."); return(false); } processStartInfo = new ProcessStartInfo { FileName = devEnvFullPath !, UseShellExecute = false, }; commandLineBuilder.AppendFileNameIfNotNull(solutionFileFullPath); if (!arguments.ShouldLoadProjectsInVisualStudio()) { commandLineBuilder.AppendSwitch(DoNotLoadProjectsCommandLineArgument); } } else { processStartInfo = new ProcessStartInfo { FileName = solutionFileFullPath, UseShellExecute = true, }; } try { processStartInfo.Arguments = commandLineBuilder.ToString(); Process process = new Process { StartInfo = processStartInfo, }; logger.LogMessageHigh("Launching Visual Studio..."); logger.LogMessageLow(" FileName = {0}", processStartInfo.FileName); logger.LogMessageLow(" Arguments = {0}", processStartInfo.Arguments); logger.LogMessageLow(" UseShellExecute = {0}", processStartInfo.UseShellExecute); logger.LogMessageLow(" WindowStyle = {0}", processStartInfo.WindowStyle); if (!process.Start()) { logger.LogError("Failed to launch Visual Studio."); } } catch (Exception e) { logger.LogError($"Failed to launch Visual Studio. {e.Message}"); } return(true); }
/// <summary> /// Launches Visual Studio. /// </summary> /// <param name="arguments">The current <see cref="ProgramArguments" />.</param> /// <param name="visualStudioInstance">A <see cref="VisualStudioInstance" /> object representing which instance of Visual Studio to launch.</param> /// <param name="solutionFileFullPath">The full path to the solution file.</param> /// <param name="logger">A <see cref="ISlnGenLogger" /> to use for logging.</param> /// <returns>true if Visual Studio was launched, otherwise false.</returns> public static bool TryLaunch(ProgramArguments arguments, VisualStudioInstance visualStudioInstance, string solutionFileFullPath, ISlnGenLogger logger) { if (!arguments.ShouldLaunchVisualStudio()) { return(true); } if (!Utility.RunningOnWindows) { logger.LogWarning("Launching Visual Studio is not currently supported on your operating system."); return(true); } string devEnvFullPath = arguments.GetDevEnvFullPath(visualStudioInstance); if (!devEnvFullPath.IsNullOrWhiteSpace()) { visualStudioInstance = VisualStudioConfiguration.GetInstanceForPath(devEnvFullPath); } if (visualStudioInstance == null) { logger.LogError( Program.CurrentDevelopmentEnvironment.IsCorext ? $"Could not find a Visual Studio {Environment.GetEnvironmentVariable("VisualStudioVersion")} installation. Please do one of the following:\n a) Specify a full path to devenv.exe via the -vs command-line argument\n b) Update your corext.config to specify a version of MSBuild.Corext that matches a Visual Studio version you have installed\n c) Install a version of Visual Studio that matches the version of MSBuild.Corext in your corext.config" : "Could not find a Visual Studio installation. Please run from a command window that has MSBuild.exe on the PATH or specify the full path to devenv.exe via the -vs command-line argument"); return(false); } if (visualStudioInstance.IsBuildTools) { logger.LogError("Cannot use a BuildTools instance of Visual Studio."); return(false); } if (!File.Exists(devEnvFullPath)) { logger.LogError($"The specified path to Visual Studio ({devEnvFullPath}) does not exist or is inaccessible."); return(false); } CommandLineBuilder commandLineBuilder = new CommandLineBuilder(); commandLineBuilder.AppendFileNameIfNotNull(solutionFileFullPath); if (!arguments.ShouldLoadProjectsInVisualStudio()) { commandLineBuilder.AppendSwitch(DoNotLoadProjectsCommandLineArgument); } try { Process process = new Process { StartInfo = new ProcessStartInfo { FileName = devEnvFullPath, Arguments = commandLineBuilder.ToString(), UseShellExecute = false, }, }; logger.LogMessageHigh("Launching Visual Studio..."); logger.LogMessageLow(" FileName = {0}", process.StartInfo.FileName); logger.LogMessageLow(" Arguments = {0}", process.StartInfo.Arguments); if (!process.Start()) { logger.LogError("Failed to launch Visual Studio."); } } catch (Exception e) { logger.LogError($"Failed to launch Visual Studio. {e.Message}"); } return(true); }