public Worker( Workspace workspace, 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) { _workspace = workspace; _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); }
/// <summary> /// Loads the <see cref="ProjectInfo"/> from the specified project file and all referenced projects. /// The first <see cref="ProjectInfo"/> in the result corresponds to the specified project file. /// </summary> /// <param name="projectFilePath">The path to the project file to be loaded. This may be an absolute path or a path relative to the /// current working directory.</param> /// <param name="projectMap">An optional <see cref="ProjectMap"/> that will be used to resolve project references to existing projects. /// This is useful when populating a custom <see cref="Workspace"/>.</param> /// <param name="progress">An optional <see cref="IProgress{T}"/> that will receive updates as the project is loaded.</param> /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> to allow cancellation of this operation.</param> public async Task <ImmutableArray <ProjectInfo> > LoadProjectInfoAsync( string projectFilePath, ProjectMap projectMap = null, IProgress <ProjectLoadProgress> progress = null, CancellationToken cancellationToken = default) { if (projectFilePath == null) { throw new ArgumentNullException(nameof(projectFilePath)); } var requestedProjectOptions = DiagnosticReportingOptions.ThrowForAll; var reportingMode = GetReportingModeForUnrecognizedProjects(); var discoveredProjectOptions = new DiagnosticReportingOptions( onPathFailure: reportingMode, onLoaderFailure: reportingMode); var buildManager = new ProjectBuildManager(_properties); var worker = new Worker( _workspace, _diagnosticReporter, _pathResolver, _projectFileLoaderRegistry, buildManager, requestedProjectPaths: ImmutableArray.Create(projectFilePath), baseDirectory: Directory.GetCurrentDirectory(), globalProperties: _properties, projectMap, progress, requestedProjectOptions, discoveredProjectOptions, this.LoadMetadataForReferencedProjects); return(await worker.LoadAsync(cancellationToken).ConfigureAwait(false)); }
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()); }