async Task <ProjectCacheInfo> WaitForProjectInfoCacheToChange( Solution sol, Project p, ProjectCacheInfo oldCacheInfo) { const int timeout = 10000; // ms int howLong = 0; const int interval = 200; // ms while (true) { var cacheInfo = GetProjectCacheInfo(sol, p); if (cacheInfo != null) { if (!oldCacheInfo.Equals(cacheInfo)) { return(cacheInfo); } } if (howLong >= timeout) { Assert.Fail("Timed out waiting for project info cache file to be updated"); } await Task.Delay(interval); howLong += interval; } }
public async Task TestWorkspaceFilesCache_CacheOutOfDate_CacheUpdatedFromMSBuold() { // First we create the cache by loading the solution. string solFile = Util.GetSampleProject("console-project", "ConsoleProject.sln"); FilePath projectFileName = null; ProjectCacheInfo cacheInfo = null; await IdeServices.Workspace.OpenWorkspaceItem(solFile); await IdeServices.TypeSystemService.ProcessPendingLoadOperations(); var sol = IdeServices.Workspace.GetAllItems <Solution> ().First(); IdeServices.Workspace.ActiveConfigurationId = sol.DefaultConfigurationId; var p = sol.FindProjectByName("ConsoleProject") as DotNetProject; projectFileName = p.FileName; cacheInfo = GetProjectCacheInfo(sol, p); Assert.IsNotNull(cacheInfo); Assert.IsFalse(cacheInfo.References.Any(r => StringComparer.OrdinalIgnoreCase.Equals(r.FilePath.FileNameWithoutExtension, "System.Net"))); await IdeServices.Workspace.Close(); // Project file changed so cache is now invalid. var updatedProjectFile = projectFileName + ".reference-added"; File.Copy(updatedProjectFile, projectFileName, overwrite: true); // Reload project and check cache is updated. await IdeServices.Workspace.OpenWorkspaceItem(solFile); await IdeServices.TypeSystemService.ProcessPendingLoadOperations(); sol = IdeServices.Workspace.GetAllItems <Solution> ().First(); IdeServices.Workspace.ActiveConfigurationId = sol.DefaultConfigurationId; p = sol.FindProjectByName("ConsoleProject") as DotNetProject; var updatedCacheInfo = await WaitForProjectInfoCacheToChange(sol, p, cacheInfo); Assert.IsTrue(updatedCacheInfo.References.Any(r => r.FilePath.FileNameWithoutExtension == "System.Net")); Assert.IsTrue(updatedCacheInfo.References.Any(r => r.FilePath.FileNameWithoutExtension == "System.Xml.Linq")); Assert.IsTrue(p.References.Any(r => r.Include == "System.Net")); Assert.IsTrue(p.References.Any(r => r.Include == "System.Xml.Linq")); var ws = IdeServices.TypeSystemService.GetWorkspace(sol); var projectId = ws.GetProjectId(p); foreach (var reference in p.References) { var analysisProject = ws.CurrentSolution.GetProject(projectId); var matchedReference = analysisProject .MetadataReferences .OfType <MonoDevelopMetadataReference.Snapshot> () .FirstOrDefault(r => ((FilePath)r.FilePath).FileNameWithoutExtension == reference.Include); Assert.IsNotNull(matchedReference, "Reference not found: " + reference.Include); } }
public void Equals_EditorConfigFiles() { var cacheInfo1 = new ProjectCacheInfo { AdditionalFiles = ImmutableArray <FilePath> .Empty, AnalyzerFiles = ImmutableArray <FilePath> .Empty, EditorConfigFiles = ImmutableArray <FilePath> .Empty, ProjectReferences = ImmutableArray <Microsoft.CodeAnalysis.ProjectReference> .Empty, References = ImmutableArray <MonoDevelopMetadataReference> .Empty, SourceFiles = ImmutableArray <ProjectFile> .Empty, }; var cacheInfo2 = new ProjectCacheInfo { AdditionalFiles = ImmutableArray <FilePath> .Empty, AnalyzerFiles = ImmutableArray <FilePath> .Empty, EditorConfigFiles = ImmutableArray <FilePath> .Empty, ProjectReferences = ImmutableArray <Microsoft.CodeAnalysis.ProjectReference> .Empty, References = ImmutableArray <MonoDevelopMetadataReference> .Empty, SourceFiles = ImmutableArray <ProjectFile> .Empty, }; Assert.IsTrue(cacheInfo1.Equals(cacheInfo2)); var files1 = new FilePath [] { "a/.editorconfig", "b/.editorconfig" }; cacheInfo1.AdditionalFiles = files1.ToImmutableArray(); cacheInfo2.AdditionalFiles = files1.ToImmutableArray(); Assert.IsTrue(cacheInfo1.Equals(cacheInfo2)); var files2 = new FilePath [] { "a/.editorconfig", "c/.editorconfig" }; cacheInfo2.AdditionalFiles = files2.ToImmutableArray(); Assert.IsFalse(cacheInfo1.Equals(cacheInfo2)); }
public bool TryGetCachedItems(Project p, MonoDevelopMetadataReferenceManager provider, MonoDevelopWorkspace.ProjectDataMap projectMap, out ProjectCacheInfo info) { info = new ProjectCacheInfo(); return(TryGetCachedItems(p, provider, projectMap, out info.SourceFiles, out info.AnalyzerFiles, out info.References, out info.ProjectReferences)); }
public void Update(ProjectConfiguration projConfig, Project proj, MonoDevelopWorkspace.ProjectDataMap projectMap, ProjectCacheInfo info) { Update(projConfig, proj, projectMap, info.SourceFiles, info.AnalyzerFiles, info.References, info.ProjectReferences); }
async Task <ProjectDocuments> CreateDocuments(ProjectData projectData, MonoDevelop.Projects.Project p, CancellationToken token, ProjectCacheInfo cacheInfo, ProjectData oldProjectData) { var projectDocuments = new ProjectDocuments(); var duplicates = new HashSet <DocumentId> (); // use given source files instead of project.Files because there may be additional files added by msbuild targets foreach (var f in cacheInfo.SourceFiles) { if (token.IsCancellationRequested) { return(null); } if (f.Subtype == MonoDevelop.Projects.Subtype.Directory) { continue; } if (CanCompile(p, f) || CanGenerateAnalysisContextForNonCompileable(p, f)) { var filePath = (FilePath)f.Name; var id = projectData.DocumentData.GetOrCreate(filePath.ResolveLinks(), oldProjectData?.DocumentData); if (!duplicates.Add(id)) { continue; } projectDocuments.Documents.Add(CreateDocumentInfo(solutionData, p.Name, projectData, f)); } else { foreach (var projectedDocument in await GenerateProjections(f, projectData.DocumentData, p, token, oldProjectData, null)) { var projectedId = projectData.DocumentData.GetOrCreate(projectedDocument.FilePath, oldProjectData?.DocumentData); if (!duplicates.Add(projectedId)) { continue; } projectDocuments.Documents.Add(projectedDocument); } } } foreach (var f in cacheInfo.AdditionalFiles) { var filePath = f.ResolveLinks(); var id = projectData.DocumentData.GetOrCreate(filePath, oldProjectData?.DocumentData); if (duplicates.Add(id)) { projectDocuments.AdditionalDocuments.Add(CreateDocumentInfo(solutionData, p.Name, projectData, filePath, filePath)); } } foreach (var f in cacheInfo.EditorConfigFiles) { var filePath = f.ResolveLinks(); var id = projectData.DocumentData.GetOrCreate(filePath, oldProjectData?.DocumentData); if (duplicates.Add(id)) { projectDocuments.EditorConfigDocuments.Add(CreateDocumentInfo(solutionData, p.Name, projectData, filePath, filePath)); } } return(projectDocuments); }
internal async Task <ProjectInfo> LoadProject( MonoDevelop.Projects.Project p, CancellationToken token, MonoDevelop.Projects.Project oldProject, ProjectCacheInfo cacheInfo, string framework) { var projectId = projectMap.GetOrCreateId(p, oldProject, framework); var config = await GetDotNetProjectConfiguration(p, framework).ConfigureAwait(false); MonoDevelop.Projects.DotNetCompilerParameters cp = config?.CompilationParameters; FilePath fileName = IdeApp.IsInitialized ? p.GetOutputFileName(IdeApp.Workspace.ActiveConfiguration) : (FilePath)""; if (fileName.IsNullOrEmpty) { fileName = new FilePath(p.Name + ".dll"); } if (cacheInfo == null) { cacheInfo = await LoadProjectCacheInfo(p, config, framework, token).ConfigureAwait(false); if (token.IsCancellationRequested) { return(null); } if (config != null) { workspaceCache.Update(config, framework, p, projectMap, cacheInfo); } } if (token.IsCancellationRequested) { return(null); } var loader = workspace.Services.GetService <IAnalyzerService> ().GetLoader(); ProjectData projectData, oldProjectData; ProjectDocuments projectDocuments; try { await workspace.LoadLock.WaitAsync().ConfigureAwait(false); //when reloading e.g. after a save, preserve document IDs projectData = projectMap.ReplaceData(projectId, cacheInfo.References, out oldProjectData); projectDocuments = await CreateDocuments(projectData, p, token, cacheInfo, oldProjectData).ConfigureAwait(false); if (projectDocuments == null) { // Restore old document data if cancellation happens here. projectMap.ReplaceData(projectId, oldProjectData, out _); return(null); } } finally { workspace.LoadLock.Release(); } if (token.IsCancellationRequested) { return(null); } IEnumerable <DocumentInfo> documents = projectDocuments.Documents; var virtualDocuments = workspace.GetVirtualDocuments(projectId); if (virtualDocuments.Any()) { documents = documents.Concat(virtualDocuments); } // TODO: Pass in the WorkspaceMetadataFileReferenceResolver var info = ProjectInfo.Create( projectId, GetVersionStamp(p), GetProjectInfoName(p.Name, framework), fileName.FileNameWithoutExtension, (p as MonoDevelop.Projects.DotNetProject)?.RoslynLanguageName ?? LanguageNames.CSharp, p.FileName, fileName, null, // outputRefPath null, // defaultNamespace cp?.CreateCompilationOptions(), cp?.CreateParseOptions(config), documents, cacheInfo.ProjectReferences, cacheInfo.References.Select(x => x.CurrentSnapshot), analyzerReferences: cacheInfo.AnalyzerFiles.SelectAsArray(x => { var analyzer = new MonoDevelopAnalyzer(x, hostDiagnosticUpdateSource.Value, projectId, workspace, loader, LanguageNames.CSharp); analyzersToDispose.Add(analyzer); return(analyzer.GetReference()); }), analyzerConfigDocuments: projectDocuments.EditorConfigDocuments, additionalDocuments: projectDocuments.AdditionalDocuments, isSubmission: false, hostObjectType: null, hasAllInformation: true ); info = workspace.WithDynamicDocuments(p, info); return(info); }
internal async Task <ProjectInfo> LoadProject(MonoDevelop.Projects.Project p, CancellationToken token, MonoDevelop.Projects.Project oldProject, ProjectCacheInfo cacheInfo) { var projectId = projectMap.GetOrCreateId(p, oldProject); var config = GetDotNetProjectConfiguration(p); MonoDevelop.Projects.DotNetCompilerParameters cp = config?.CompilationParameters; FilePath fileName = IdeApp.IsInitialized ? p.GetOutputFileName(IdeApp.Workspace.ActiveConfiguration) : (FilePath)""; if (fileName.IsNullOrEmpty) { fileName = new FilePath(p.Name + ".dll"); } if (cacheInfo == null) { cacheInfo = await LoadProjectCacheInfo(p, config, token).ConfigureAwait(false); if (token.IsCancellationRequested) { return(null); } if (config != null) { workspaceCache.Update(config, p, projectMap, cacheInfo); } } if (token.IsCancellationRequested) { return(null); } var loader = workspace.Services.GetService <IAnalyzerService> ().GetLoader(); ProjectData projectData, oldProjectData; List <DocumentInfo> mainDocuments, additionalDocuments; try { await workspace.LoadLock.WaitAsync().ConfigureAwait(false); //when reloading e.g. after a save, preserve document IDs oldProjectData = projectMap.RemoveData(projectId); projectData = projectMap.CreateData(projectId, cacheInfo.References); var documents = await CreateDocuments(projectData, p, token, cacheInfo.SourceFiles, oldProjectData).ConfigureAwait(false); if (documents == null) { return(null); } mainDocuments = documents.Item1; additionalDocuments = documents.Item2; } finally { workspace.LoadLock.Release(); } // TODO: Pass in the WorkspaceMetadataFileReferenceResolver var info = ProjectInfo.Create( projectId, GetVersionStamp(p), p.Name, fileName.FileNameWithoutExtension, (p as MonoDevelop.Projects.DotNetProject)?.RoslynLanguageName ?? LanguageNames.CSharp, p.FileName, fileName, cp?.CreateCompilationOptions(), cp?.CreateParseOptions(config), mainDocuments, cacheInfo.ProjectReferences, cacheInfo.References.Select(x => x.CurrentSnapshot), analyzerReferences: cacheInfo.AnalyzerFiles.SelectAsArray(x => { var analyzer = new MonoDevelopAnalyzer(x, hostDiagnosticUpdateSource.Value, projectId, workspace, loader, LanguageNames.CSharp); analyzersToDispose.Add(analyzer); return(analyzer.GetReference()); }), additionalDocuments: additionalDocuments ); return(info); }
public bool TryGetCachedItems(Project p, MonoDevelopMetadataReferenceManager provider, MonoDevelopWorkspace.ProjectDataMap projectMap, string framework, out ProjectCacheInfo info) { info = new ProjectCacheInfo(); List <ProjectCache> cachedDataList; lock (cachedItems) { if (!cachedItems.TryGetValue(p.FileName, out cachedDataList)) { return(false); } } ProjectCache cachedData = cachedDataList.FirstOrDefault(cache => cache.Framework == framework); if (cachedData == null) { return(false); } var filesBuilder = ImmutableArray.CreateBuilder <ProjectFile> (cachedData.Files.Length); for (int i = 0; i < cachedData.Files.Length; ++i) { filesBuilder.Add(new ProjectFile(cachedData.Files [i], cachedData.BuildActions [i]) { Project = p, }); } info.SourceFiles = filesBuilder.MoveToImmutable(); info.AdditionalFiles = ToImmutableFilePathArray(cachedData.AdditionalFiles); info.AnalyzerFiles = ToImmutableFilePathArray(cachedData.Analyzers); info.EditorConfigFiles = ToImmutableFilePathArray(cachedData.EditorConfigFiles); var mrBuilder = ImmutableArray.CreateBuilder <MonoDevelopMetadataReference> (cachedData.MetadataReferences.Length); foreach (var item in cachedData.MetadataReferences) { var aliases = item.Aliases != null?item.Aliases.ToImmutableArray() : default; var reference = provider.GetOrCreateMetadataReference(item.FilePath, new Microsoft.CodeAnalysis.MetadataReferenceProperties(aliases: aliases)); mrBuilder.Add(reference); } info.References = mrBuilder.MoveToImmutable(); var sol = p.ParentSolution; var solConfig = sol.GetConfiguration(IdeServices.Workspace.ActiveConfiguration); var allProjects = sol.GetAllProjects().ToDictionary(x => x.FileName, x => x); var prBuilder = ImmutableArray.CreateBuilder <Microsoft.CodeAnalysis.ProjectReference> (cachedData.ProjectReferences.Length); foreach (var item in cachedData.ProjectReferences) { if (!allProjects.TryGetValue(item.FilePath, out var mdProject)) { return(false); } var aliases = item.Aliases != null?item.Aliases.ToImmutableArray() : default; var pr = new Microsoft.CodeAnalysis.ProjectReference(projectMap.GetOrCreateId(mdProject, null, item.Framework), aliases.ToImmutableArray()); prBuilder.Add(pr); } info.ProjectReferences = prBuilder.MoveToImmutable(); return(true); }
public void Update(ProjectConfiguration projConfig, string framework, Project proj, MonoDevelopWorkspace.ProjectDataMap projectMap, ProjectCacheInfo info) { if (!loaded) { return; } var paths = new string [info.SourceFiles.Length]; var actions = new string [info.SourceFiles.Length]; for (int i = 0; i < info.SourceFiles.Length; ++i) { paths [i] = info.SourceFiles [i].FilePath; actions [i] = info.SourceFiles [i].BuildAction; } var projectRefs = new ReferenceItem [info.ProjectReferences.Length]; for (int i = 0; i < info.ProjectReferences.Length; ++i) { var pr = info.ProjectReferences [i]; (Project mdProject, string projectReferenceFramework) = projectMap.GetMonoProjectAndFramework(pr.ProjectId); projectRefs [i] = new ReferenceItem { FilePath = mdProject.FileName, Aliases = pr.Aliases.ToArray(), Framework = projectReferenceFramework }; } var item = new ProjectCache { Format = format, AdditionalFiles = info.AdditionalFiles.Select(x => (string)x).ToArray(), Analyzers = info.AnalyzerFiles.Select(x => (string)x).ToArray(), EditorConfigFiles = info.EditorConfigFiles.Select(x => (string)x).ToArray(), Files = paths, BuildActions = actions, MetadataReferences = info.References.Select(x => { var ri = new ReferenceItem { FilePath = x.FilePath, Aliases = x.Properties.Aliases.ToArray(), }; return(ri); }).ToArray(), ProjectReferences = projectRefs, }; string configId = GetConfigId(projConfig); var cacheFile = GetProjectCacheFile(proj, configId, framework); WriteCacheFile(item, cacheFile); }