private async Task <ResolvedReferences> ResolveReferencesAsync(ProjectId id, ProjectFileInfo projectFileInfo, CommandLineArguments commandLineArgs, CancellationToken cancellationToken) { // First, gather all of the metadata references from the command-line arguments. var resolvedMetadataReferences = commandLineArgs.ResolveMetadataReferences( new WorkspaceMetadataFileReferenceResolver( metadataService: GetWorkspaceService <IMetadataService>(), pathResolver: new RelativePathResolver(commandLineArgs.ReferencePaths, commandLineArgs.BaseDirectory))); var builder = new ResolvedReferencesBuilder(resolvedMetadataReferences); var projectDirectory = Path.GetDirectoryName(projectFileInfo.FilePath); // Next, iterate through all project references in the file and create project references. foreach (var projectFileReference in projectFileInfo.ProjectReferences) { var aliases = projectFileReference.Aliases; if (_pathResolver.TryGetAbsoluteProjectPath(projectFileReference.Path, baseDirectory: projectDirectory, _discoveredProjectOptions.OnPathFailure, out var projectReferencePath)) { // The easiest case is to add a reference to a project we already know about. if (TryAddReferenceToKnownProject(id, projectReferencePath, aliases, builder)) { continue; } // If we don't know how to load a project (that is, it's not a language we support), we can still // attempt to verify that its output exists on disk and is included in our set of metadata references. // If it is, we'll just leave it in place. if (!IsProjectLoadable(projectReferencePath) && await VerifyUnloadableProjectOutputExistsAsync(projectReferencePath, builder, cancellationToken).ConfigureAwait(false)) { continue; } // If metadata is preferred, see if the project reference's output exists on disk and is included // in our metadata references. If it is, don't create a project reference; we'll just use the metadata. if (_preferMetadataForReferencesOfDiscoveredProjects && await VerifyProjectOutputExistsAsync(projectReferencePath, builder, cancellationToken).ConfigureAwait(false)) { continue; } // Finally, we'll try to load and reference the project. if (await TryLoadAndAddReferenceAsync(id, projectReferencePath, aliases, builder, cancellationToken).ConfigureAwait(false)) { continue; } } // We weren't able to handle this project reference, so add it without further processing. var unknownProjectId = _projectMap.GetOrCreateProjectId(projectFileReference.Path); var newProjectReference = CreateProjectReference(from: id, to: unknownProjectId, aliases); builder.AddProjectReference(newProjectReference); } // Are there still any unresolved metadata references? If so, remove them and report diagnostics. foreach (var unresolvedMetadataReference in builder.GetUnresolvedMetadataReferences()) { var filePath = unresolvedMetadataReference.Reference; builder.Remove(filePath); _diagnosticReporter.Report(new ProjectDiagnostic( WorkspaceDiagnosticKind.Warning, string.Format(WorkspaceMSBuildResources.Unresolved_metadata_reference_removed_from_project_0, filePath), id)); } return(builder.ToResolvedReferences()); }
private async Task <bool> VerifyProjectOutputExistsAsync(string projectPath, ResolvedReferencesBuilder builder, CancellationToken cancellationToken) { // Note: Load the project, but don't report failures. var projectFileInfos = await LoadProjectFileInfosAsync(projectPath, DiagnosticReportingOptions.IgnoreAll, cancellationToken).ConfigureAwait(false); foreach (var projectFileInfo in projectFileInfos) { var outputFilePath = projectFileInfo.OutputFilePath; var outputRefFilePath = projectFileInfo.OutputRefFilePath; if ((builder.Contains(outputFilePath) && File.Exists(outputFilePath)) || (builder.Contains(outputRefFilePath) && File.Exists(outputRefFilePath))) { return(true); } } return(false); }
private async Task <bool> TryLoadAndAddReferenceAsync(ProjectId id, string projectReferencePath, ImmutableArray <string> aliases, ResolvedReferencesBuilder builder, CancellationToken cancellationToken) { var projectReferenceInfos = await LoadProjectInfosFromPathAsync(projectReferencePath, _discoveredProjectOptions, cancellationToken).ConfigureAwait(false); if (projectReferenceInfos.IsEmpty) { return(false); } // Find the project reference info whose output we have a metadata reference for. ProjectInfo projectReferenceInfo = null; foreach (var info in projectReferenceInfos) { if (builder.Contains(info.OutputFilePath) || builder.Contains(info.OutputRefFilePath)) { projectReferenceInfo = info; break; } } if (projectReferenceInfo == null) { // We didn't find the project reference info that matches any of our metadata references. // In this case, we'll go ahead and use the first project reference info that was found, // but report a warning because this likely means that either a metadata reference path // or a project output path is incorrect. projectReferenceInfo = projectReferenceInfos[0]; _diagnosticReporter.Report(new ProjectDiagnostic( WorkspaceDiagnosticKind.Warning, string.Format(WorkspaceMSBuildResources.Found_project_reference_without_a_matching_metadata_reference_0, projectReferencePath), id)); } if (!ProjectReferenceExists(to: id, from: projectReferenceInfo)) { var newProjectReference = CreateProjectReference(from: id, to: projectReferenceInfo.Id, aliases); builder.SwapMetadataReferenceForProjectReference(newProjectReference, projectReferenceInfo.OutputRefFilePath, projectReferenceInfo.OutputFilePath); } else { // This project already has a reference on us. Don't introduce a circularity by referencing it. // However, if the project's output doesn't exist on disk, we need to remove from our list of // metadata references to avoid failures later. Essentially, the concern here is that the metadata // reference is an UnresolvedMetadataReference, which will throw when we try to create a // Compilation with it. if (!File.Exists(projectReferenceInfo.OutputRefFilePath)) { builder.Remove(projectReferenceInfo.OutputRefFilePath); } if (!File.Exists(projectReferenceInfo.OutputFilePath)) { builder.Remove(projectReferenceInfo.OutputFilePath); } } // Note that we return true even if we don't actually add a reference due to a circularity because, // in that case, we've still handled everything. return(true); }
private async Task <bool> VerifyUnloadableProjectOutputExistsAsync(string projectPath, ResolvedReferencesBuilder builder, CancellationToken cancellationToken) { var outputFilePath = await _buildManager.TryGetOutputFilePathAsync(projectPath, cancellationToken).ConfigureAwait(false); return(builder.Contains(outputFilePath) && File.Exists(outputFilePath)); }
private async Task <bool> TryLoadAndAddReferenceAsync(ProjectId id, string projectReferencePath, ImmutableArray <string> aliases, ResolvedReferencesBuilder builder, CancellationToken cancellationToken) { var projectReferenceInfos = await LoadProjectInfosFromPathAsync(projectReferencePath, _discoveredProjectOptions, cancellationToken).ConfigureAwait(false); // Find the project reference info whose output we have a metadata reference for. ProjectInfo projectReferenceInfo = null; foreach (var info in projectReferenceInfos) { if (builder.Contains(info.OutputFilePath) || builder.Contains(info.OutputRefFilePath)) { projectReferenceInfo = info; break; } } if (projectReferenceInfo == null) { return(false); } if (!ProjectReferenceExists(to: id, from: projectReferenceInfo)) { var newProjectReference = CreateProjectReference(from: id, to: projectReferenceInfo.Id, aliases); builder.SwapMetadataReferenceForProjectReference(newProjectReference, projectReferenceInfo.OutputRefFilePath, projectReferenceInfo.OutputFilePath); } else { // This project already has a reference on us. Don't introduce a circularity by referencing it. // However, if the project's output doesn't exist on disk, we need to remove from our list of // metadata references to avoid failures later. Essentially, the concern here is that the metadata // reference is an UnresolvedMetadataReference, which will throw when we try to create a // Compilation with it. if (!File.Exists(projectReferenceInfo.OutputRefFilePath)) { builder.Remove(projectReferenceInfo.OutputRefFilePath); } if (!File.Exists(projectReferenceInfo.OutputFilePath)) { builder.Remove(projectReferenceInfo.OutputFilePath); } } // Note that we return true even if we don't actually add a reference due to a circularity because, // in that case, we've still handled everything. return(true); }