public Worker( HostWorkspaceServices services, DiagnosticReporter diagnosticReporter, PathResolver pathResolver, ProjectFileLoaderRegistry projectFileLoaderRegistry, ProjectBuildManager buildManager, ImmutableArray <string> requestedProjectPaths, string baseDirectory, ImmutableDictionary <string, string> globalProperties, ProjectMap projectMap, IProgress <ProjectLoadProgress> progress, DiagnosticReportingOptions requestedProjectOptions, DiagnosticReportingOptions discoveredProjectOptions, bool preferMetadataForReferencesOfDiscoveredProjects) { _workspaceServices = services; _diagnosticReporter = diagnosticReporter; _pathResolver = pathResolver; _projectFileLoaderRegistry = projectFileLoaderRegistry; _buildManager = buildManager; _baseDirectory = baseDirectory; _requestedProjectPaths = requestedProjectPaths; _globalProperties = globalProperties; _projectMap = projectMap ?? ProjectMap.Create(); _progress = progress; _requestedProjectOptions = requestedProjectOptions; _discoveredProjectOptions = discoveredProjectOptions; _preferMetadataForReferencesOfDiscoveredProjects = preferMetadataForReferencesOfDiscoveredProjects; _projectIdToFileInfoMap = new Dictionary <ProjectId, ProjectFileInfo>(); _pathToDiscoveredProjectInfosMap = new Dictionary <string, ImmutableArray <ProjectInfo> >(PathUtilities.Comparer); _projectIdToProjectReferencesMap = new Dictionary <ProjectId, List <ProjectReference> >(); }
private async Task <ImmutableArray <ProjectInfo> > LoadProjectInfosFromPathAsync( string projectPath, DiagnosticReportingOptions reportingOptions, CancellationToken cancellationToken) { if (_projectMap.TryGetProjectInfosByProjectPath(projectPath, out var results) || _pathToDiscoveredProjectInfosMap.TryGetValue(projectPath, out results)) { return(results); } var builder = ImmutableArray.CreateBuilder <ProjectInfo>(); var projectFileInfos = await LoadProjectFileInfosAsync(projectPath, reportingOptions, cancellationToken).ConfigureAwait(false); var idsAndFileInfos = new List <(ProjectId id, ProjectFileInfo fileInfo)>(); foreach (var projectFileInfo in projectFileInfos) { var projectId = _projectMap.GetOrCreateProjectId(projectFileInfo); if (_projectIdToFileInfoMap.ContainsKey(projectId)) { // There are multiple projects with the same project path and output path. This can happen // if a multi-TFM project does not have unique output file paths for each TFM. In that case, // we'll create a new ProjectId to ensure that the project is added to the workspace. _diagnosticReporter.Report( DiagnosticReportingMode.Log, string.Format(WorkspaceMSBuildResources.Found_project_with_the_same_file_path_and_output_path_as_another_project_0, projectFileInfo.FilePath)); projectId = ProjectId.CreateNewId(debugName: projectFileInfo.FilePath); } idsAndFileInfos.Add((projectId, projectFileInfo)); _projectIdToFileInfoMap.Add(projectId, projectFileInfo); } // If this project resulted in more than a single project, a discrimator (e.g. TFM) should be // added to the project name. var addDiscriminator = idsAndFileInfos.Count > 1; foreach (var(id, fileInfo) in idsAndFileInfos) { var projectInfo = await CreateProjectInfoAsync(fileInfo, id, addDiscriminator, cancellationToken).ConfigureAwait(false); builder.Add(projectInfo); _projectMap.AddProjectInfo(projectInfo); } results = builder.ToImmutable(); _pathToDiscoveredProjectInfosMap.Add(projectPath, results); return(results); }
private async Task <ImmutableArray <ProjectFileInfo> > LoadProjectFileInfosAsync(string projectPath, DiagnosticReportingOptions reportingOptions, CancellationToken cancellationToken) { if (!_projectFileLoaderRegistry.TryGetLoaderFromProjectPath(projectPath, reportingOptions.OnLoaderFailure, out var loader)) { return(ImmutableArray <ProjectFileInfo> .Empty); // Failure should already be reported. } var projectFile = await DoOperationAndReportProgressAsync( ProjectLoadOperation.Evaluate, projectPath, targetFramework : null, () => loader.LoadProjectFileAsync(projectPath, _buildManager, cancellationToken) ).ConfigureAwait(false); // If there were any failures during load, we won't be able to build the project. So, bail early with an empty project. if (projectFile.Log.HasFailure) { _diagnosticReporter.Report(projectFile.Log); return(ImmutableArray.Create( ProjectFileInfo.CreateEmpty(loader.Language, projectPath, projectFile.Log))); } var projectFileInfos = await DoOperationAndReportProgressAsync( ProjectLoadOperation.Build, projectPath, targetFramework : null, () => projectFile.GetProjectFileInfosAsync(cancellationToken) ).ConfigureAwait(false); var results = ImmutableArray.CreateBuilder <ProjectFileInfo>(projectFileInfos.Length); foreach (var projectFileInfo in projectFileInfos) { // If any diagnostics were logged during build, we'll carry on and try to produce a meaningful project. if (!projectFileInfo.Log.IsEmpty) { _diagnosticReporter.Report(projectFileInfo.Log); } results.Add(projectFileInfo); } return(results.MoveToImmutable()); }
/// <summary> /// Loads the <see cref="SolutionInfo"/> for the specified solution file, including all projects referenced by the solution file and /// all the projects referenced by the project files. /// </summary> /// <param name="solutionFilePath">The path to the solution file to be loaded. This may be an absolute path or a path relative to the /// current working directory.</param> /// <param name="progress">An optional <see cref="IProgress{T}"/> that will receive updates as the solution is loaded.</param> /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> to allow cancellation of this operation.</param> public async Task <SolutionInfo> LoadSolutionInfoAsync( string solutionFilePath, IProgress <ProjectLoadProgress> progress = null, CancellationToken cancellationToken = default) { if (solutionFilePath == null) { throw new ArgumentNullException(nameof(solutionFilePath)); } if (!_pathResolver.TryGetAbsoluteSolutionPath(solutionFilePath, baseDirectory: Directory.GetCurrentDirectory(), DiagnosticReportingMode.Throw, out var absoluteSolutionPath)) { // TryGetAbsoluteSolutionPath should throw before we get here. return(null); } using (_dataGuard.DisposableWait(cancellationToken)) { this.SetSolutionProperties(absoluteSolutionPath); } var solutionFile = MSB.Construction.SolutionFile.Parse(absoluteSolutionPath); var reportingMode = GetReportingModeForUnrecognizedProjects(); var reportingOptions = new DiagnosticReportingOptions( onPathFailure: reportingMode, onLoaderFailure: reportingMode); var projectPaths = ImmutableArray.CreateBuilder <string>(); // load all the projects foreach (var project in solutionFile.ProjectsInOrder) { cancellationToken.ThrowIfCancellationRequested(); if (project.ProjectType != MSB.Construction.SolutionProjectType.SolutionFolder) { projectPaths.Add(project.RelativePath); } } var buildManager = new ProjectBuildManager(_properties); var worker = new Worker( _workspaceServices, _diagnosticReporter, _pathResolver, _projectFileLoaderRegistry, buildManager, projectPaths.ToImmutable(), baseDirectory: Path.GetDirectoryName(absoluteSolutionPath), _properties, projectMap: null, progress, requestedProjectOptions: reportingOptions, discoveredProjectOptions: reportingOptions, preferMetadataForReferencesOfDiscoveredProjects: false); var projects = await worker.LoadAsync(cancellationToken).ConfigureAwait(false); // construct workspace from loaded project infos return(SolutionInfo.Create( SolutionId.CreateNewId(debugName: absoluteSolutionPath), version: default,