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}"); } }
public static ProjectCollection LoadProjectsAndReferences( IDictionary <string, string> globalProperties, string toolsVersion, IBuildEngine buildEngine, bool collectStats, string projectFullPath, IEnumerable <ITaskItem> projectReferences, ISlnGenLogger logger) { // Create an MSBuildProject loader with the same global properties of the project that requested a solution file MSBuildProjectLoader projectLoader = new MSBuildProjectLoader(globalProperties, toolsVersion, buildEngine, ProjectLoadSettings.IgnoreMissingImports) { CollectStats = collectStats, }; logger.LogMessageHigh("Loading project references..."); ProjectCollection projectCollection = projectLoader.LoadProjectsAndReferences(projectReferences.Select(i => i.GetMetadata("FullPath")).Concat(new[] { projectFullPath })); logger.LogMessageNormal($"Loaded {projectCollection.LoadedProjects.Count} project(s)"); if (collectStats) { LogStatistics(projectLoader, logger); } return(projectCollection); }
public static void GenerateSolutionFile( ProjectCollection projectCollection, string solutionFileFullPath, string projectFileFullPath, IReadOnlyDictionary <string, Guid> customProjectTypeGuids, bool folders, IEnumerable <string> solutionItems, ISlnGenLogger logger) { if (string.IsNullOrWhiteSpace(solutionFileFullPath)) { solutionFileFullPath = Path.ChangeExtension(projectFileFullPath, ".sln"); } 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(); solution.AddProjects(projectCollection, customProjectTypeGuids, projectFileFullPath); solution.AddSolutionItems(solutionItems); solution.Save(solutionFileFullPath, folders); }
/// <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); }
private (string solutionFileFullPath, int customProjectTypeGuidCount, int solutionItemCount) GenerateSolutionFile(IEnumerable <Project> projects, ISlnGenLogger logger) { Project project = projects.First(); IReadOnlyDictionary <string, Guid> customProjectTypeGuids = SlnProject.GetCustomProjectTypeGuids(project); IReadOnlyCollection <string> solutionItems = SlnProject.GetSolutionItems(project, logger).ToList(); string solutionFileFullPath = _arguments.SolutionFileFullPath?.LastOrDefault(); if (solutionFileFullPath.IsNullOrWhiteSpace()) { string solutionDirectoryFullPath = _arguments.SolutionDirectoryFullPath?.LastOrDefault(); if (solutionDirectoryFullPath.IsNullOrWhiteSpace()) { solutionDirectoryFullPath = project.DirectoryPath; } string solutionFileName = Path.ChangeExtension(Path.GetFileName(project.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(projects, customProjectTypeGuids, project.FullPath); solution.AddSolutionItems(solutionItems); solution.Save(solutionFileFullPath, _arguments.EnableFolders(), _arguments.EnableCollapseFolders()); return(solutionFileFullPath, customProjectTypeGuids.Count, solutionItems.Count); }
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); }
private int Generate() { using (SlnGenTelemetryData telemetryData = new SlnGenTelemetryData { DevEnvFullPathSpecified = !_programArguments.DevEnvFullPath.IsNullOrWhitespace(), EntryProjectCount = _programArguments.Projects.Length, Folders = _programArguments.Folders, LaunchVisualStudio = _programArguments.LaunchVisualStudio, SolutionFileFullPathSpecified = !_programArguments.SolutionFileFullPath.IsNullOrWhitespace(), UseBinaryLogger = _programArguments.BinaryLogger.HasValue, UseFileLogger = _programArguments.FileLoggerParameters.HasValue, UseShellExecute = _programArguments.UseShellExecute, }) { _logger.LogMessageHigh("Loading project references..."); Stopwatch sw = Stopwatch.StartNew(); IDictionary <string, string> globalProperties = new Dictionary <string, string> { ["BuildingProject"] = "false", ["DesignTimeBuild"] = "true", ["ExcludeRestorePackageImports"] = "true", }; ICollection <ProjectGraphEntryPoint> entryProjects = _programArguments.GetProjects(_logger).Select(i => new ProjectGraphEntryPoint(i, globalProperties)).ToList(); _ = new ProjectGraph(entryProjects, _projectCollection, CreateProjectInstance); sw.Stop(); _logger.LogMessageNormal($"Loaded {_projectCollection.LoadedProjects.Count} project(s) in {sw.ElapsedMilliseconds:N2}ms"); telemetryData.ProjectEvaluationMilliseconds = sw.ElapsedMilliseconds; telemetryData.ProjectEvaluationCount = _projectCollection.LoadedProjects.Count; Project project = _projectCollection.LoadedProjects.First(); Dictionary <string, Guid> customProjectTypeGuids = SlnProject.GetCustomProjectTypeGuids(project.GetItems("SlnGenCustomProjectTypeGuid").Select(i => new MSBuildProjectItem(i))); IReadOnlyCollection <string> solutionItems = SlnFile.GetSolutionItems(project.GetItems("SlnGenSolutionItem").Select(i => new MSBuildProjectItem(i)), _logger).ToList(); telemetryData.CustomProjectTypeGuidCount = customProjectTypeGuids.Count; telemetryData.SolutionItemCount = solutionItems.Count; string solutionFileFullPath = SlnGenUtility.GenerateSolutionFile( _projectCollection, solutionFileFullPath: null, projectFileFullPath: entryProjects.First().ProjectFile, customProjectTypeGuids: customProjectTypeGuids, folders: _programArguments.Folders, solutionItems: solutionItems, logger: _logger); if (_programArguments.LaunchVisualStudio) { SlnGenUtility.LaunchVisualStudio(_programArguments.DevEnvFullPath, _programArguments.UseShellExecute, solutionFileFullPath, _logger); } return(_logger.HasLoggedErrors ? 1 : 0); } }
/// <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); }