private SourceItemHandler CreateInstance(UnconfiguredProject project = null, IWorkspaceProjectContext context = null) { project = project ?? UnconfiguredProjectFactory.Create(); context = context ?? IWorkspaceProjectContextFactory.Create(); return(new SourceItemHandler(project, context)); }
public void UniqueSourceFilesPushedToWorkspace() { var sourceFilesPushedToWorkspace = new HashSet <string>(StringComparers.Paths); Action <string> onSourceFileAdded = s => Assert.True(sourceFilesPushedToWorkspace.Add(s)); Action <string> onSourceFileRemoved = s => sourceFilesPushedToWorkspace.Remove(s); var project = UnconfiguredProjectFactory.Create(filePath: @"C:\Myproject.csproj"); var context = IWorkspaceProjectContextFactory.CreateForSourceFiles(project, onSourceFileAdded, onSourceFileRemoved); var logger = Mock.Of <IProjectLogger>(); var handler = new SourceItemHandler(project, context); var projectDir = Path.GetDirectoryName(project.FullPath); var added = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: new[] { @"C:\file1.cs", @"C:\file2.cs", @"C:\file1.cs" }, baseDirectory: projectDir, sdkDirectory: null)); var empty = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: new string[] { }, baseDirectory: projectDir, sdkDirectory: null)); handler.Handle(10, added: added, removed: empty, isActiveContext: true, logger: logger); Assert.Equal(2, sourceFilesPushedToWorkspace.Count); Assert.Contains(@"C:\file1.cs", sourceFilesPushedToWorkspace); Assert.Contains(@"C:\file2.cs", sourceFilesPushedToWorkspace); var removed = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: new[] { @"C:\file1.cs", @"C:\file1.cs" }, baseDirectory: projectDir, sdkDirectory: null)); handler.Handle(10, added: empty, removed: removed, isActiveContext: true, logger: logger); Assert.Equal(1, sourceFilesPushedToWorkspace.Count); Assert.Contains(@"C:\file2.cs", sourceFilesPushedToWorkspace); }
public void DuplicateMetadataReferencesPushedToWorkspace() { var referencesPushedToWorkspace = new HashSet <string>(StringComparers.Paths); Action <string> onReferenceAdded = s => referencesPushedToWorkspace.Add(s); Action <string> onReferenceRemoved = s => referencesPushedToWorkspace.Remove(s); var project = UnconfiguredProjectFactory.Create(filePath: @"C:\Myproject.csproj"); var context = IWorkspaceProjectContextFactory.CreateForMetadataReferences(project, onReferenceAdded, onReferenceRemoved); var handler = new MetadataReferenceItemHandler(project); var projectDir = Path.GetDirectoryName(project.FullPath); var added = CSharpCommandLineParser.Default.Parse(args: new[] { @"/reference:C:\Assembly1.dll", @"/reference:C:\Assembly2.dll", @"/reference:C:\Assembly1.dll" }, baseDirectory: projectDir, sdkDirectory: null); var empty = CSharpCommandLineParser.Default.Parse(args: new string[] { }, baseDirectory: projectDir, sdkDirectory: null); handler.Handle(added: added, removed: empty, context: context, isActiveContext: true); Assert.Equal(2, referencesPushedToWorkspace.Count); Assert.Contains(@"C:\Assembly1.dll", referencesPushedToWorkspace); Assert.Contains(@"C:\Assembly2.dll", referencesPushedToWorkspace); var removed = CSharpCommandLineParser.Default.Parse(args: new[] { @"/reference:C:\Assembly1.dll", @"/reference:C:\Assembly1.dll" }, baseDirectory: projectDir, sdkDirectory: null); handler.Handle(added: empty, removed: removed, context: context, isActiveContext: true); Assert.Equal(1, referencesPushedToWorkspace.Count); Assert.Contains(@"C:\Assembly2.dll", referencesPushedToWorkspace); }
public void Handle_WhenMSBuildProjectFullPathPropertyChanged_SetsProjectFilePath() { var context = IWorkspaceProjectContextFactory.Create(); context.ProjectFilePath = @"ProjectFilePath"; var handler = CreateInstance(context: context); var projectChange = IProjectChangeDescriptionFactory.FromJson( @"{ ""Difference"": { ""AnyChanges"": true, ""ChangedProperties"": [ ""MSBuildProjectFullPath"" ] }, ""After"": { ""Properties"": { ""MSBuildProjectFullPath"": ""NewProjectFilePath"" } } }"); Handle(handler, projectChange); Assert.Equal(@"NewProjectFilePath", context.ProjectFilePath); }
public void DuplicateMetadataReferencesPushedToWorkspace() { var referencesPushedToWorkspace = new HashSet <string>(StringComparers.Paths); Action <string> onReferenceAdded = s => referencesPushedToWorkspace.Add(s); Action <string> onReferenceRemoved = s => referencesPushedToWorkspace.Remove(s); var project = UnconfiguredProjectFactory.Create(filePath: @"C:\Myproject.csproj"); var context = IWorkspaceProjectContextFactory.CreateForMetadataReferences(project, onReferenceAdded, onReferenceRemoved); var logger = Mock.Of <IProjectLogger>(); var handler = new MetadataReferenceItemHandler(project, context); var projectDir = Path.GetDirectoryName(project.FullPath); var added = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: new[] { @"/reference:C:\Assembly1.dll", @"/reference:C:\Assembly2.dll", @"/reference:C:\Assembly1.dll" }, baseDirectory: projectDir, sdkDirectory: null)); var empty = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: new string[] { }, baseDirectory: projectDir, sdkDirectory: null)); handler.Handle(10, added: added, removed: empty, isActiveContext: true, logger: logger); AssertEx.CollectionLength(referencesPushedToWorkspace, 2); Assert.Contains(@"C:\Assembly1.dll", referencesPushedToWorkspace); Assert.Contains(@"C:\Assembly2.dll", referencesPushedToWorkspace); var removed = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: new[] { @"/reference:C:\Assembly1.dll", @"/reference:C:\Assembly1.dll" }, baseDirectory: projectDir, sdkDirectory: null)); handler.Handle(10, added: empty, removed: removed, isActiveContext: true, logger: logger); Assert.Single(referencesPushedToWorkspace); Assert.Contains(@"C:\Assembly2.dll", referencesPushedToWorkspace); }
public void Constructor_NullAsProject_ThrowsArgumentNull() { var context = IWorkspaceProjectContextFactory.Create(); Assert.Throws <ArgumentNullException>("project", () => { new MetadataReferenceItemHandler((UnconfiguredProject)null, context); }); }
public void Handle_NullAsProjectChange_ThrowsArgumentNull() { var handler = CreateInstance(); var context = IWorkspaceProjectContextFactory.Create(); Assert.Throws <ArgumentNullException>("projectChange", () => { handler.Handle((IProjectChangeDescription)null, context, true); }); }
public void Constructor_NullAsProject_ThrowsArgumentNull() { var context = IWorkspaceProjectContextFactory.Create(); Assert.Throws <ArgumentNullException>("project", () => { new AdditionalFilesItemHandler((UnconfiguredProject)null); }); }
public void Initialize_WhenAlreadyInitialized_ThrowsInvalidOperation() { var handler = CreateInstance(); var workspaceContext = IWorkspaceProjectContextFactory.Create(); handler.Initialize(workspaceContext); Assert.Throws <InvalidOperationException>(() => { handler.Initialize(workspaceContext); }); }
public void RootedSourceFilesPushedToWorkspace() { var sourceFilesPushedToWorkspace = new HashSet <string>(StringComparers.Paths); Action <string> onSourceFileAdded = s => Assert.True(sourceFilesPushedToWorkspace.Add(s)); Action <string> onSourceFileRemoved = s => sourceFilesPushedToWorkspace.Remove(s); var project = UnconfiguredProjectFactory.Create(filePath: @"C:\ProjectFolder\Myproject.csproj"); var context = IWorkspaceProjectContextFactory.CreateForSourceFiles(project, onSourceFileAdded, onSourceFileRemoved); var handler = new SourceItemHandler(project, IPhysicalProjectTreeFactory.Create()); var projectDir = Path.GetDirectoryName(project.FullPath); var added = CSharpCommandLineParser.Default.Parse(args: new[] { @"file1.cs", @"..\ProjectFolder\file1.cs" }, baseDirectory: projectDir, sdkDirectory: null); var removed = CSharpCommandLineParser.Default.Parse(args: new string[] { }, baseDirectory: projectDir, sdkDirectory: null); handler.Handle(added: added, removed: removed, context: context, isActiveContext: true); Assert.Equal(1, sourceFilesPushedToWorkspace.Count); Assert.Contains(@"C:\ProjectFolder\file1.cs", sourceFilesPushedToWorkspace); }
public void RootedReferencesPushedToWorkspace() { var referencesPushedToWorkspace = new HashSet <string>(StringComparers.Paths); Action <string> onReferenceAdded = s => referencesPushedToWorkspace.Add(s); Action <string> onReferenceRemoved = s => referencesPushedToWorkspace.Remove(s); var project = UnconfiguredProjectFactory.Create(filePath: @"C:\ProjectFolder\Myproject.csproj"); var context = IWorkspaceProjectContextFactory.CreateForMetadataReferences(project, onReferenceAdded, onReferenceRemoved); var handler = new MetadataReferenceItemHandler(project, context); var projectDir = Path.GetDirectoryName(project.FullPath); var added = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: new[] { @"/reference:Assembly1.dll", @"/reference:C:\ProjectFolder\Assembly2.dll", @"/reference:..\ProjectFolder\Assembly3.dll" }, baseDirectory: projectDir, sdkDirectory: null)); var removed = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: new string[] { }, baseDirectory: projectDir, sdkDirectory: null)); handler.Handle(10, added: added, removed: removed, isActiveContext: true); Assert.Equal(3, referencesPushedToWorkspace.Count); Assert.Contains(@"C:\ProjectFolder\Assembly1.dll", referencesPushedToWorkspace); Assert.Contains(@"C:\ProjectFolder\Assembly2.dll", referencesPushedToWorkspace); Assert.Contains(@"C:\ProjectFolder\Assembly3.dll", referencesPushedToWorkspace); }
public void Handle_WhenMSBuildProjectFullPathPropertyNotChanged_DoesNothing() { var context = IWorkspaceProjectContextFactory.Create(); context.ProjectFilePath = @"ProjectFilePath"; context.DisplayName = "DisplayName"; var handler = CreateInstance(context: context); var projectChange = IProjectChangeDescriptionFactory.FromJson( @"{ ""Difference"": { ""AnyChanges"": true } }"); Handle(handler, projectChange); Assert.Equal(@"ProjectFilePath", context.ProjectFilePath); Assert.Equal(@"DisplayName", context.DisplayName); }
public void Handle_WhenMSBuildProjectFullPathPropertyChanged_SetsDisplayNameToFileNameWithoutExtension() { var context = IWorkspaceProjectContextFactory.Create(); var handler = CreateInstance(context: context); var projectChange = IProjectChangeDescriptionFactory.FromJson( @"{ ""Difference"": { ""AnyChanges"": true, ""ChangedProperties"": [ ""MSBuildProjectFullPath"" ] }, ""After"": { ""Properties"": { ""MSBuildProjectFullPath"": ""C:\\Project\\Project.csproj"" } } }"); Handle(handler, projectChange); Assert.Equal(@"Project", context.DisplayName); }
public void Handle_WhenMSBuildProjectFullPathPropertyChangedAndMultitargeting_IncludeDimensionValuesInDisplayName(string dimensionNames, string dimensionValues, string implicitDimensionNames, string expected) { var context = IWorkspaceProjectContextFactory.Create(); var implicitlyActiveDimensionProvider = IImplicitlyActiveDimensionProviderFactory.ImplementGetImplicitlyActiveDimensions(n => implicitDimensionNames.SplitReturningEmptyIfEmpty('|')); var configuration = ProjectConfigurationFactory.Create(dimensionNames, dimensionValues); var handler = CreateInstance(configuration, implicitlyActiveDimensionProvider, context); var projectChange = IProjectChangeDescriptionFactory.FromJson( @"{ ""Difference"": { ""AnyChanges"": true, ""ChangedProperties"": [ ""MSBuildProjectFullPath"" ] }, ""After"": { ""Properties"": { ""MSBuildProjectFullPath"": ""C:\\Project\\Project.csproj"" } } }"); Handle(handler, projectChange); Assert.Equal(expected, context.DisplayName); }
private AbstractProject GetOrCreateProjectFromArgumentsAndReferences( IWorkspaceProjectContextFactory workspaceProjectContextFactory, string projectFilename, IReadOnlyDictionary<string, DeferredProjectInformation> allProjectInfos, IReadOnlyDictionary<string, string> targetPathsToProjectPaths) { var languageName = GetLanguageOfProject(projectFilename); if (languageName == null) { return null; } DeferredProjectInformation projectInfo; if (!allProjectInfos.TryGetValue(projectFilename, out projectInfo)) { // This could happen if we were called recursively about a dangling P2P reference // that isn't actually in the solution. return null; } var commandLineParser = _workspaceServices.GetLanguageServices(languageName).GetService<ICommandLineParserService>(); var projectDirectory = PathUtilities.GetDirectoryName(projectFilename); var commandLineArguments = commandLineParser.Parse( projectInfo.CommandLineArguments, projectDirectory, isInteractive: false, sdkDirectory: RuntimeEnvironment.GetRuntimeDirectory()); // TODO: Should come from sln file? var projectName = PathUtilities.GetFileName(projectFilename, includeExtension: false); // `AbstractProject` only sets the filename if it actually exists. Since we want // our ids to match, mimic that behavior here. var projectId = File.Exists(projectFilename) ? GetOrCreateProjectIdForPath(projectFilename, projectName) : GetOrCreateProjectIdForPath(projectName, projectName); // See if we've already created this project and we're now in a recursive call to // hook up a P2P ref. AbstractProject project; if (_projectMap.TryGetValue(projectId, out project)) { return project; } OutputToOutputWindow($"\tCreating '{projectName}':\t{commandLineArguments.SourceFiles.Length} source files,\t{commandLineArguments.MetadataReferences.Length} references."); var solution5 = _serviceProvider.GetService(typeof(SVsSolution)) as IVsSolution5; // If the index is stale, it might give us a path that doesn't exist anymore that the // solution doesn't know about - be resilient to that case. Guid projectGuid; try { projectGuid = solution5.GetGuidOfProjectFile(projectFilename); } catch (ArgumentException) { var message = $"Failed to get the project guid for '{projectFilename}' from the solution, using random guid instead."; Debug.Fail(message); OutputToOutputWindow(message); projectGuid = Guid.NewGuid(); } // NOTE: If the indexing service fails for a project, it will give us an *empty* // target path, which we aren't prepared to handle. Instead, convert it to a *null* // value, which we do handle. var outputPath = projectInfo.TargetPath; if (outputPath == string.Empty) { outputPath = null; } var projectContext = workspaceProjectContextFactory.CreateProjectContext( languageName, projectName, projectFilename, projectGuid: projectGuid, hierarchy: null, binOutputPath: outputPath); projectContext.SetOptions(projectInfo.CommandLineArguments.Join(" ")); foreach (var sourceFile in commandLineArguments.SourceFiles) { projectContext.AddSourceFile(sourceFile.Path); } foreach (var sourceFile in commandLineArguments.AdditionalFiles) { projectContext.AddAdditionalFile(sourceFile.Path); } var addedProjectReferences = new HashSet<string>(); foreach (var projectReferencePath in projectInfo.ReferencedProjectFilePaths) { // NOTE: ImmutableProjects might contain projects for other languages like // Xaml, or Typescript where the project file ends up being identical. var referencedProject = ImmutableProjects.SingleOrDefault( p => (p.Language == LanguageNames.CSharp || p.Language == LanguageNames.VisualBasic) && StringComparer.OrdinalIgnoreCase.Equals(p.ProjectFilePath, projectReferencePath)); if (referencedProject == null) { referencedProject = GetOrCreateProjectFromArgumentsAndReferences( workspaceProjectContextFactory, projectReferencePath, allProjectInfos, targetPathsToProjectPaths); } var referencedProjectContext = referencedProject as IWorkspaceProjectContext; if (referencedProjectContext != null) { // TODO: Can we get the properties from corresponding metadata reference in // commandLineArguments? addedProjectReferences.Add(projectReferencePath); projectContext.AddProjectReference( referencedProjectContext, new MetadataReferenceProperties()); } else if (referencedProject != null) { // This project was already created by the regular project system. See if we // can find the matching project somehow. var existingReferenceOutputPath = referencedProject?.BinOutputPath; if (existingReferenceOutputPath != null) { addedProjectReferences.Add(projectReferencePath); projectContext.AddMetadataReference( existingReferenceOutputPath, new MetadataReferenceProperties()); } } else { // We don't know how to create this project. Another language or something? OutputToOutputWindow($"Failed to create a project for '{projectReferencePath}'."); } } foreach (var reference in commandLineArguments.MetadataReferences) { string possibleProjectReference; if (targetPathsToProjectPaths.TryGetValue(reference.Reference, out possibleProjectReference) && addedProjectReferences.Contains(possibleProjectReference)) { // We already added a P2P reference for this, we don't need to add the file reference too. continue; } projectContext.AddMetadataReference(reference.Reference, reference.Properties); } foreach (var reference in commandLineArguments.AnalyzerReferences) { var path = reference.FilePath; if (!PathUtilities.IsAbsolute(path)) { path = PathUtilities.CombineAbsoluteAndRelativePaths( projectDirectory, path); } projectContext.AddAnalyzerReference(path); } return (AbstractProject)projectContext; }
private AbstractProject GetOrCreateProjectFromArgumentsAndReferences( IWorkspaceProjectContextFactory workspaceProjectContextFactory, IAnalyzerAssemblyLoader analyzerAssemblyLoader, string projectFilename, IReadOnlyDictionary <string, DeferredProjectInformation> allProjectInfos, IReadOnlyDictionary <string, string> targetPathsToProjectPaths) { var languageName = GetLanguageOfProject(projectFilename); if (languageName == null) { return(null); } if (!allProjectInfos.TryGetValue(projectFilename, out var projectInfo)) { // This could happen if we were called recursively about a dangling P2P reference // that isn't actually in the solution. return(null); } var commandLineParser = _workspaceServices.GetLanguageServices(languageName).GetService <ICommandLineParserService>(); var projectDirectory = PathUtilities.GetDirectoryName(projectFilename); var commandLineArguments = commandLineParser.Parse( projectInfo.CommandLineArguments, projectDirectory, isInteractive: false, sdkDirectory: RuntimeEnvironment.GetRuntimeDirectory()); // TODO: Should come from sln file? var projectName = PathUtilities.GetFileName(projectFilename, includeExtension: false); // `AbstractProject` only sets the filename if it actually exists. Since we want // our ids to match, mimic that behavior here. var projectId = File.Exists(projectFilename) ? GetOrCreateProjectIdForPath(projectFilename, projectName) : GetOrCreateProjectIdForPath(projectName, projectName); // See if we've already created this project and we're now in a recursive call to // hook up a P2P ref. if (_projectMap.TryGetValue(projectId, out var project)) { return(project); } OutputToOutputWindow($"\tCreating '{projectName}':\t{commandLineArguments.SourceFiles.Length} source files,\t{commandLineArguments.MetadataReferences.Length} references."); var solution5 = _serviceProvider.GetService(typeof(SVsSolution)) as IVsSolution5; // If the index is stale, it might give us a path that doesn't exist anymore that the // solution doesn't know about - be resilient to that case. Guid projectGuid; try { projectGuid = solution5.GetGuidOfProjectFile(projectFilename); } catch (ArgumentException) { var message = $"Failed to get the project guid for '{projectFilename}' from the solution, using random guid instead."; Debug.Fail(message); OutputToOutputWindow(message); projectGuid = Guid.NewGuid(); } // NOTE: If the indexing service fails for a project, it will give us an *empty* // target path, which we aren't prepared to handle. Instead, convert it to a *null* // value, which we do handle. var outputPath = projectInfo.TargetPath; if (outputPath == string.Empty) { outputPath = null; } var projectContext = workspaceProjectContextFactory.CreateProjectContext( languageName, projectName, projectFilename, projectGuid: projectGuid, hierarchy: null, binOutputPath: outputPath); project = (AbstractProject)projectContext; projectContext.SetOptions(projectInfo.CommandLineArguments.Join(" ")); foreach (var sourceFile in commandLineArguments.SourceFiles) { projectContext.AddSourceFile(sourceFile.Path); } foreach (var sourceFile in commandLineArguments.AdditionalFiles) { projectContext.AddAdditionalFile(sourceFile.Path); } var addedProjectReferences = new HashSet <string>(); foreach (var projectReferencePath in projectInfo.ReferencedProjectFilePaths) { // NOTE: ImmutableProjects might contain projects for other languages like // Xaml, or Typescript where the project file ends up being identical. var referencedProject = ImmutableProjects.SingleOrDefault( p => (p.Language == LanguageNames.CSharp || p.Language == LanguageNames.VisualBasic) && StringComparer.OrdinalIgnoreCase.Equals(p.ProjectFilePath, projectReferencePath)); if (referencedProject == null) { referencedProject = GetOrCreateProjectFromArgumentsAndReferences( workspaceProjectContextFactory, analyzerAssemblyLoader, projectReferencePath, allProjectInfos, targetPathsToProjectPaths); } var referencedProjectContext = referencedProject as IWorkspaceProjectContext; if (referencedProjectContext != null) { // TODO: Can we get the properties from corresponding metadata reference in // commandLineArguments? addedProjectReferences.Add(projectReferencePath); projectContext.AddProjectReference( referencedProjectContext, new MetadataReferenceProperties()); } else if (referencedProject != null) { // This project was already created by the regular project system. See if we // can find the matching project somehow. var existingReferenceOutputPath = referencedProject?.BinOutputPath; if (existingReferenceOutputPath != null) { addedProjectReferences.Add(projectReferencePath); projectContext.AddMetadataReference( existingReferenceOutputPath, new MetadataReferenceProperties()); } } else { // We don't know how to create this project. Another language or something? OutputToOutputWindow($"Failed to create a project for '{projectReferencePath}'."); } } foreach (var reference in commandLineArguments.ResolveMetadataReferences(project.CurrentCompilationOptions.MetadataReferenceResolver)) { // Some references may fail to be resolved - if they are, we'll still pass them // through, in case they come into existence later (they may be built by other // parts of the build system). var unresolvedReference = reference as UnresolvedMetadataReference; var path = unresolvedReference == null ? ((PortableExecutableReference)reference).FilePath : unresolvedReference.Reference; if (targetPathsToProjectPaths.TryGetValue(path, out var possibleProjectReference) && addedProjectReferences.Contains(possibleProjectReference)) { // We already added a P2P reference for this, we don't need to add the file reference too. continue; } projectContext.AddMetadataReference(path, reference.Properties); } foreach (var reference in commandLineArguments.ResolveAnalyzerReferences(analyzerAssemblyLoader)) { var path = reference.FullPath; if (!PathUtilities.IsAbsolute(path)) { path = PathUtilities.CombineAbsoluteAndRelativePaths( projectDirectory, path); } projectContext.AddAnalyzerReference(path); } return((AbstractProject)projectContext); }
private AbstractProject GetOrCreateProjectFromArgumentsAndReferences( IWorkspaceProjectContextFactory workspaceProjectContextFactory, IAnalyzerAssemblyLoader analyzerAssemblyLoader, string projectFilename, IReadOnlyDictionary <string, DeferredProjectInformation> allProjectInfos, IReadOnlyDictionary <string, string> targetPathsToProjectPaths) { var languageName = GetLanguageOfProject(projectFilename); if (languageName == null) { return(null); } if (!allProjectInfos.TryGetValue(projectFilename, out var projectInfo)) { // This could happen if we were called recursively about a dangling P2P reference // that isn't actually in the solution. return(null); } // TODO: Should come from .sln file? var projectName = PathUtilities.GetFileName(projectFilename, includeExtension: false); // `AbstractProject` only sets the filename if it actually exists. Since we want // our ids to match, mimic that behavior here. var projectId = File.Exists(projectFilename) ? GetOrCreateProjectIdForPath(projectFilename, projectName) : GetOrCreateProjectIdForPath(projectName, projectName); // See if something has already created this project - it's not deferred, the AnyCode design time build // failed so we force loaded it, or we already created a deferred project and we're in a recursive call // to find a ProjectReference if (_projectMap.TryGetValue(projectId, out var project)) { return(project); } // If the project system has opted this project out of deferred loading, or AnyCode // was unable to get command line info for it, we can't create a project for it. // NOTE: We need to check this even though it happened in CreateDeferredProjects // because we could be in a recursive call from a project reference below. var solution7 = (IVsSolution7)_vsSolution; if (DesignTimeBuildFailed(projectInfo) || !solution7.IsDeferredProjectLoadAllowed(projectFilename)) { return(null); } var commandLineParser = _workspaceServices.GetLanguageServices(languageName).GetService <ICommandLineParserService>(); var projectDirectory = PathUtilities.GetDirectoryName(projectFilename); var commandLineArguments = commandLineParser.Parse( projectInfo.CommandLineArguments, projectDirectory, isInteractive: false, sdkDirectory: RuntimeEnvironment.GetRuntimeDirectory()); OutputToOutputWindow($"\tCreating '{projectName}':\t{commandLineArguments.SourceFiles.Length} source files,\t{commandLineArguments.MetadataReferences.Length} references."); var projectGuid = GetProjectGuid(projectFilename); var projectContext = workspaceProjectContextFactory.CreateProjectContext( languageName, projectName, projectFilename, projectGuid: projectGuid, hierarchy: null, binOutputPath: projectInfo.TargetPath); project = (AbstractProject)projectContext; projectContext.SetOptions(projectInfo.CommandLineArguments.Join(" ")); var addedSourceFilePaths = new HashSet <string>(StringComparer.OrdinalIgnoreCase); foreach (var sourceFile in commandLineArguments.SourceFiles) { if (addedSourceFilePaths.Add(sourceFile.Path)) { projectContext.AddSourceFile(sourceFile.Path); } } var addedAdditionalFilePaths = new HashSet <string>(StringComparer.OrdinalIgnoreCase); foreach (var additionalFile in commandLineArguments.AdditionalFiles) { if (addedAdditionalFilePaths.Add(additionalFile.Path)) { projectContext.AddAdditionalFile(additionalFile.Path); } } var metadataReferences = commandLineArguments.ResolveMetadataReferences(project.CurrentCompilationOptions.MetadataReferenceResolver).AsImmutable(); var addedProjectReferences = new HashSet <string>(); foreach (var projectReferencePath in projectInfo.ReferencedProjectFilePaths) { var referencedProject = TryFindExistingProjectForProjectReference(projectReferencePath, metadataReferences); if (referencedProject == null) { referencedProject = GetOrCreateProjectFromArgumentsAndReferences( workspaceProjectContextFactory, analyzerAssemblyLoader, projectReferencePath, allProjectInfos, targetPathsToProjectPaths); } var referencedProjectContext = referencedProject as IWorkspaceProjectContext; if (referencedProjectContext != null) { // TODO: Can we get the properties from corresponding metadata reference in // commandLineArguments? addedProjectReferences.Add(projectReferencePath); projectContext.AddProjectReference( referencedProjectContext, new MetadataReferenceProperties()); } else if (referencedProject != null) { // This project was already created by the regular project system. See if we // can find the matching project somehow. var existingReferenceOutputPath = referencedProject?.BinOutputPath; if (existingReferenceOutputPath != null) { addedProjectReferences.Add(projectReferencePath); projectContext.AddMetadataReference( existingReferenceOutputPath, new MetadataReferenceProperties()); } } else { // We don't know how to create this project. Another language or something? OutputToOutputWindow($"\t\tFailed to create a project for '{projectReferencePath}'."); } } var addedReferencePaths = new HashSet <string>(StringComparer.OrdinalIgnoreCase); foreach (var reference in metadataReferences) { var path = GetReferencePath(reference); if (targetPathsToProjectPaths.TryGetValue(path, out var possibleProjectReference) && addedProjectReferences.Contains(possibleProjectReference)) { // We already added a P2P reference for this, we don't need to add the file reference too. continue; } if (addedReferencePaths.Add(path)) { projectContext.AddMetadataReference(path, reference.Properties); } } var addedAnalyzerPaths = new HashSet <string>(StringComparer.OrdinalIgnoreCase); foreach (var reference in commandLineArguments.ResolveAnalyzerReferences(analyzerAssemblyLoader)) { var path = reference.FullPath; if (!PathUtilities.IsAbsolute(path)) { path = PathUtilities.CombineAbsoluteAndRelativePaths( projectDirectory, path); } if (addedAnalyzerPaths.Add(path)) { projectContext.AddAnalyzerReference(path); } } return((AbstractProject)projectContext); }
private AbstractProject GetOrCreateProjectFromArgumentsAndReferences( IWorkspaceProjectContextFactory workspaceProjectContextFactory, string projectFilename, IReadOnlyDictionary<string, DeferredProjectInformation> allProjectInfos, IReadOnlyDictionary<string, string> targetPathsToProjectPaths) { var languageName = GetLanguageOfProject(projectFilename); if (languageName == null) { return null; } DeferredProjectInformation projectInfo; if (!allProjectInfos.TryGetValue(projectFilename, out projectInfo)) { // This could happen if we were called recursively about a dangling P2P reference // that isn't actually in the solution. return null; } var commandLineParser = _workspaceServices.GetLanguageServices(languageName).GetService<ICommandLineParserService>(); var projectDirectory = Path.GetDirectoryName(projectFilename); var commandLineArguments = commandLineParser.Parse( projectInfo.CommandLineArguments, projectDirectory, isInteractive: false, sdkDirectory: RuntimeEnvironment.GetRuntimeDirectory()); // TODO: Should come from sln file? var projectName = Path.GetFileNameWithoutExtension(projectFilename); var projectId = GetOrCreateProjectIdForPath(projectFilename, projectName); // See if we've already created this project and we're now in a recursive call to // hook up a P2P ref. AbstractProject project; if (_projectMap.TryGetValue(projectId, out project)) { return project; } OutputToOutputWindow($"\tCreating '{projectName}':\t{commandLineArguments.SourceFiles.Length} source files,\t{commandLineArguments.MetadataReferences.Length} references."); var solution5 = _serviceProvider.GetService(typeof(SVsSolution)) as IVsSolution5; var projectGuid = solution5.GetGuidOfProjectFile(projectFilename); var projectContext = workspaceProjectContextFactory.CreateProjectContext( languageName, projectName, projectFilename, projectGuid: projectGuid, hierarchy: null, binOutputPath: projectInfo.TargetPath); projectContext.SetOptions(projectInfo.CommandLineArguments.Join(" ")); foreach (var sourceFile in commandLineArguments.SourceFiles) { projectContext.AddSourceFile(sourceFile.Path); } foreach (var sourceFile in commandLineArguments.AdditionalFiles) { projectContext.AddAdditionalFile(sourceFile.Path); } var addedProjectReferences = new HashSet<string>(); foreach (var projectReferencePath in projectInfo.ReferencedProjectFilePaths) { var referencedProject = ImmutableProjects.SingleOrDefault(p => StringComparer.OrdinalIgnoreCase.Equals(p.ProjectFilePath, projectReferencePath)); if (referencedProject == null) { referencedProject = GetOrCreateProjectFromArgumentsAndReferences( workspaceProjectContextFactory, projectReferencePath, allProjectInfos, targetPathsToProjectPaths); } var referencedProjectContext = referencedProject as IWorkspaceProjectContext; if (referencedProjectContext != null) { // TODO: Can we get the properties from corresponding metadata reference in // commandLineArguments? addedProjectReferences.Add(projectReferencePath); projectContext.AddProjectReference( referencedProjectContext, new MetadataReferenceProperties()); } else if (referencedProject != null) { // This project was already created by the regular project system. See if we // can find the matching project somehow. var existingReferenceOutputPath = referencedProject?.BinOutputPath; if (existingReferenceOutputPath != null) { addedProjectReferences.Add(projectReferencePath); projectContext.AddMetadataReference( existingReferenceOutputPath, new MetadataReferenceProperties()); } } else { // We don't know how to create this project. Another language or something? OutputToOutputWindow($"Failed to create a project for '{projectReferencePath}'."); } } foreach (var reference in commandLineArguments.MetadataReferences) { string possibleProjectReference; if (targetPathsToProjectPaths.TryGetValue(reference.Reference, out possibleProjectReference) && addedProjectReferences.Contains(possibleProjectReference)) { // We already added a P2P reference for this, we don't need to add the file reference too. continue; } projectContext.AddMetadataReference(reference.Reference, reference.Properties); } foreach (var reference in commandLineArguments.AnalyzerReferences) { projectContext.AddAnalyzerReference(reference.FilePath); } return (AbstractProject)projectContext; }
public FSharpWorkspaceProjectContextFactory(IWorkspaceProjectContextFactory factory, IThreadingContext threadingContext) { _factory = factory; _threadingContext = threadingContext; }
private AbstractProject GetOrCreateProjectFromArgumentsAndReferences( IWorkspaceProjectContextFactory workspaceProjectContextFactory, string projectFilename, IReadOnlyDictionary <string, DeferredProjectInformation> allProjectInfos, IReadOnlyDictionary <string, string> targetPathsToProjectPaths) { var languageName = GetLanguageOfProject(projectFilename); if (languageName == null) { return(null); } DeferredProjectInformation projectInfo; if (!allProjectInfos.TryGetValue(projectFilename, out projectInfo)) { // This could happen if we were called recursively about a dangling P2P reference // that isn't actually in the solution. return(null); } var commandLineParser = _workspaceServices.GetLanguageServices(languageName).GetService <ICommandLineParserService>(); var projectDirectory = Path.GetDirectoryName(projectFilename); var commandLineArguments = commandLineParser.Parse( projectInfo.CommandLineArguments, projectDirectory, isInteractive: false, sdkDirectory: RuntimeEnvironment.GetRuntimeDirectory()); // TODO: Should come from sln file? var projectName = Path.GetFileNameWithoutExtension(projectFilename); var projectId = GetOrCreateProjectIdForPath(projectFilename, projectName); // See if we've already created this project and we're now in a recursive call to // hook up a P2P ref. AbstractProject project; if (_projectMap.TryGetValue(projectId, out project)) { return(project); } OutputToOutputWindow($"\tCreating '{projectName}':\t{commandLineArguments.SourceFiles.Length} source files,\t{commandLineArguments.MetadataReferences.Length} references."); var solution5 = _serviceProvider.GetService(typeof(SVsSolution)) as IVsSolution5; var projectGuid = solution5.GetGuidOfProjectFile(projectFilename); var projectContext = workspaceProjectContextFactory.CreateProjectContext( languageName, projectName, projectFilename, projectGuid: projectGuid, hierarchy: null, binOutputPath: projectInfo.TargetPath); projectContext.SetOptions(projectInfo.CommandLineArguments.Join(" ")); foreach (var sourceFile in commandLineArguments.SourceFiles) { projectContext.AddSourceFile(sourceFile.Path); } foreach (var sourceFile in commandLineArguments.AdditionalFiles) { projectContext.AddAdditionalFile(sourceFile.Path); } var addedProjectReferences = new HashSet <string>(); foreach (var projectReferencePath in projectInfo.ReferencedProjectFilePaths) { var referencedProject = ImmutableProjects.SingleOrDefault(p => StringComparer.OrdinalIgnoreCase.Equals(p.ProjectFilePath, projectReferencePath)); if (referencedProject == null) { referencedProject = GetOrCreateProjectFromArgumentsAndReferences( workspaceProjectContextFactory, projectReferencePath, allProjectInfos, targetPathsToProjectPaths); } var referencedProjectContext = referencedProject as IWorkspaceProjectContext; if (referencedProjectContext != null) { // TODO: Can we get the properties from corresponding metadata reference in // commandLineArguments? addedProjectReferences.Add(projectReferencePath); projectContext.AddProjectReference( referencedProjectContext, new MetadataReferenceProperties()); } else if (referencedProject != null) { // This project was already created by the regular project system. See if we // can find the matching project somehow. var existingReferenceOutputPath = referencedProject?.BinOutputPath; if (existingReferenceOutputPath != null) { addedProjectReferences.Add(projectReferencePath); projectContext.AddMetadataReference( existingReferenceOutputPath, new MetadataReferenceProperties()); } } else { // We don't know how to create this project. Another language or something? OutputToOutputWindow($"Failed to create a project for '{projectReferencePath}'."); } } foreach (var reference in commandLineArguments.MetadataReferences) { string possibleProjectReference; if (targetPathsToProjectPaths.TryGetValue(reference.Reference, out possibleProjectReference) && addedProjectReferences.Contains(possibleProjectReference)) { // We already added a P2P reference for this, we don't need to add the file reference too. continue; } projectContext.AddMetadataReference(reference.Reference, reference.Properties); } foreach (var reference in commandLineArguments.AnalyzerReferences) { projectContext.AddAnalyzerReference(reference.FilePath); } return((AbstractProject)projectContext); }