/// <summary> /// Gets a metadata reference to the metadata-only-image corresponding to the compilation. /// </summary> private async Task <MetadataReference> GetMetadataOnlyImageReferenceAsync( SolutionState solution, ProjectReference projectReference, CancellationToken cancellationToken) { try { using (Logger.LogBlock(FunctionId.Workspace_SkeletonAssembly_GetMetadataOnlyImage, cancellationToken)) { var version = await this.GetDependentSemanticVersionAsync(solution, cancellationToken).ConfigureAwait(false); // get or build compilation up to declaration state. this compilation will be used to provide live xml doc comment var declarationCompilation = await this.GetOrBuildDeclarationCompilationAsync(solution, cancellationToken : cancellationToken).ConfigureAwait(false); solution.Workspace.LogTestMessage($"Looking for a cached skeleton assembly for {projectReference.ProjectId} before taking the lock..."); if (!MetadataOnlyReference.TryGetReference(solution, projectReference, declarationCompilation, version, out var reference)) { // using async build lock so we don't get multiple consumers attempting to build metadata-only images for the same compilation. using (await _buildLock.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { solution.Workspace.LogTestMessage($"Build lock taken for {ProjectState.Id}..."); // okay, we still don't have one. bring the compilation to final state since we are going to use it to create skeleton assembly var compilationInfo = await this.GetOrBuildCompilationInfoAsync(solution, lockGate : false, cancellationToken : cancellationToken).ConfigureAwait(false); reference = MetadataOnlyReference.GetOrBuildReference(solution, projectReference, compilationInfo.Compilation, version, cancellationToken); } } else { solution.Workspace.LogTestMessage($"Reusing the already cached skeleton assembly for {projectReference.ProjectId}"); } return(reference); } } catch (Exception e) when(FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
private async Task <VersionStamp> ComputeDependentVersionAsync(SolutionState solution, CancellationToken cancellationToken) { var projectState = this.ProjectState; var projVersion = projectState.Version; var docVersion = await projectState.GetLatestDocumentVersionAsync(cancellationToken).ConfigureAwait(false); var version = docVersion.GetNewerVersion(projVersion); foreach (var dependentProjectReference in projectState.ProjectReferences) { cancellationToken.ThrowIfCancellationRequested(); if (solution.ContainsProject(dependentProjectReference.ProjectId)) { var dependentProjectVersion = await solution.GetDependentVersionAsync(dependentProjectReference.ProjectId, cancellationToken).ConfigureAwait(false); version = dependentProjectVersion.GetNewerVersion(version); } } return(version); }
private Solution(SolutionState state) { _projectIdToProjectMap = ImmutableHashMap <ProjectId, Project> .Empty; _state = state; }
internal static MetadataReference GetOrBuildReference( SolutionState solution, ProjectReference projectReference, Compilation finalCompilation, VersionStamp version, CancellationToken cancellationToken) { solution.Workspace.LogTestMessage($"Looking to see if we already have a skeleton assembly for {projectReference.ProjectId} before we build one..."); if (TryGetReference(solution, projectReference, finalCompilation, version, out var reference)) { solution.Workspace.LogTestMessage($"A reference was found {projectReference.ProjectId} so we're skipping the build."); return(reference); } // okay, we don't have one. so create one now. // first, prepare image // * NOTE * image is cancellable, do not create it inside of conditional weak table. var service = solution.Workspace.Services.GetService <ITemporaryStorageService>(); var image = MetadataOnlyImage.Create(solution.Workspace, service, finalCompilation, cancellationToken); if (image.IsEmpty) { // unfortunately, we couldn't create one. do best effort if (TryGetReference(solution, projectReference, finalCompilation, VersionStamp.Default, out reference)) { solution.Workspace.LogTestMessage($"We failed to create metadata so we're using the one we just found from an earlier version."); // we have one from previous compilation!!, it might be out-of-date big time, but better than nothing. // re-use it return(reference); } } // okay, proceed with whatever image we have // now, remove existing set var mapFromBranch = s_cache.GetValue(solution.BranchId, s_createReferenceSetMap); mapFromBranch.Remove(projectReference.ProjectId); // create new one var newReferenceSet = new MetadataOnlyReferenceSet(version, image); var referenceSet = s_snapshotCache.GetValue(finalCompilation, _ => newReferenceSet); if (newReferenceSet != referenceSet) { // someone else has beaten us. // let image go eagerly. otherwise, finalizer in temporary storage will take care of it image.Cleanup(); // return new reference return(referenceSet.GetMetadataReference(finalCompilation, projectReference.Aliases, projectReference.EmbedInteropTypes)); } else { solution.Workspace.LogTestMessage($"Successfully stored the metadata generated for {projectReference.ProjectId}"); } // record it to version based cache as well. snapshot cache always has a higher priority. we don't need to check returned set here // since snapshot based cache will take care of same compilation for us. mapFromBranch.GetValue(projectReference.ProjectId, _ => referenceSet); // return new reference return(referenceSet.GetMetadataReference(finalCompilation, projectReference.Aliases, projectReference.EmbedInteropTypes)); }
private async Task <bool> HasSuccessfullyLoadedSlowAsync(SolutionState solution, CancellationToken cancellationToken) { var compilationInfo = await GetOrBuildCompilationInfoAsync(solution, lockGate : true, cancellationToken : cancellationToken).ConfigureAwait(false); return(compilationInfo.HasSuccessfullyLoaded); }
/// <summary> /// Add all appropriate references to the compilation and set it as our final compilation /// state. /// </summary> private async Task <CompilationInfo> FinalizeCompilationAsync( SolutionState solution, Compilation compilation, CancellationToken cancellationToken) { try { // if HasAllInformation is false, then this project is always not completed. bool hasSuccessfullyLoaded = this.ProjectState.HasAllInformation; var newReferences = new List <MetadataReference>(); var metadataReferenceToProjectId = new Dictionary <MetadataReference, ProjectId>(); newReferences.AddRange(this.ProjectState.MetadataReferences); foreach (var projectReference in this.ProjectState.ProjectReferences) { var referencedProject = solution.GetProjectState(projectReference.ProjectId); // Even though we're creating a final compilation (vs. an in progress compilation), // it's possible that the target project has been removed. if (referencedProject != null) { // If both projects are submissions, we'll count this as a previous submission link // instead of a regular metadata reference if (referencedProject.IsSubmission) { // if the referenced project is a submission project must be a submission as well: Debug.Assert(this.ProjectState.IsSubmission); var previousSubmissionCompilation = await solution.GetCompilationAsync(projectReference.ProjectId, cancellationToken).ConfigureAwait(false); compilation = compilation.WithScriptCompilationInfo( compilation.ScriptCompilationInfo.WithPreviousScriptCompilation(previousSubmissionCompilation)); } else { var metadataReference = await solution.GetMetadataReferenceAsync( projectReference, this.ProjectState, cancellationToken).ConfigureAwait(false); // A reference can fail to be created if a skeleton assembly could not be constructed. if (metadataReference != null) { newReferences.Add(metadataReference); metadataReferenceToProjectId.Add(metadataReference, projectReference.ProjectId); } else { hasSuccessfullyLoaded = false; } } } } compilation = UpdateCompilationWithNewReferencesAndRecordAssemblySymbols(compilation, newReferences, metadataReferenceToProjectId); this.WriteState(new FinalState(State.CreateValueSource(compilation, solution.Services), hasSuccessfullyLoaded), solution); return(new CompilationInfo(compilation, hasSuccessfullyLoaded)); } catch (Exception e) when(FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
private async Task <Compilation> GetCompilationSlowAsync(SolutionState solution, CancellationToken cancellationToken) { var compilationInfo = await GetOrBuildCompilationInfoAsync(solution, lockGate : true, cancellationToken : cancellationToken).ConfigureAwait(false); return(compilationInfo.Compilation); }
/// <summary> /// Tries to get the latest snapshot of the compilation without waiting for it to be /// fully built. This method takes advantage of the progress side-effect produced during /// <see cref="BuildCompilationInfoAsync(SolutionState, CancellationToken)"/>. It will either return the already built compilation, any /// in-progress compilation or any known old compilation in that order of preference. /// The compilation state that is returned will have a compilation that is retained so /// that it cannot disappear. /// </summary> private void GetPartialCompilationState( SolutionState solution, DocumentId id, out ProjectState inProgressProject, out Compilation inProgressCompilation, CancellationToken cancellationToken) { var state = this.ReadState(); inProgressCompilation = state.Compilation.GetValue(cancellationToken); // check whether we can bail out quickly for typing case var inProgressState = state as InProgressState; // all changes left for this document is modifying the given document. // we can use current state as it is since we will replace the document with latest document anyway. if (inProgressState != null && inProgressCompilation != null && inProgressState.IntermediateProjects.All(t => IsTouchDocumentActionForDocument(t, id))) { inProgressProject = this.ProjectState; SolutionLogger.UseExistingPartialProjectState(); return; } inProgressProject = inProgressState != null?inProgressState.IntermediateProjects.First().Item1 : this.ProjectState; // if we already have a final compilation we are done. if (inProgressCompilation != null && state is FinalState) { SolutionLogger.UseExistingFullProjectState(); return; } // 1) if we have an in-progress compilation use it. // 2) If we don't, then create a simple empty compilation/project. // 3) then, make sure that all it's p2p refs and whatnot are correct. if (inProgressCompilation == null) { inProgressProject = inProgressProject.RemoveAllDocuments(); inProgressCompilation = this.CreateEmptyCompilation(); } // first remove all project from the project and compilation. inProgressProject = inProgressProject.WithProjectReferences(ImmutableArray.Create <ProjectReference>()); // Now add in back a consistent set of project references. For project references // try to get either a CompilationReference or a SkeletonReference. This ensures // that the in-progress project only reports a reference to another project if it // could actually get a reference to that project's metadata. var metadataReferences = new List <MetadataReference>(); var newProjectReferences = new List <ProjectReference>(); metadataReferences.AddRange(this.ProjectState.MetadataReferences); var metadataReferenceToProjectId = new Dictionary <MetadataReference, ProjectId>(); foreach (var projectReference in this.ProjectState.ProjectReferences) { var referencedProject = solution.GetProjectState(projectReference.ProjectId); if (referencedProject != null) { if (referencedProject.IsSubmission) { var compilation = solution.GetCompilationAsync(projectReference.ProjectId, cancellationToken).WaitAndGetResult(cancellationToken); inProgressCompilation = inProgressCompilation.WithScriptCompilationInfo(inProgressCompilation.ScriptCompilationInfo.WithPreviousScriptCompilation(compilation)); } else { // get the latest metadata for the partial compilation of the referenced project. var metadata = solution.GetPartialMetadataReference(projectReference, this.ProjectState, cancellationToken); if (metadata == null) { // if we failed to get the metadata, check to see if we previously had existing metadata and reuse it instead. var inProgressCompilationNotRef = inProgressCompilation; metadata = inProgressCompilationNotRef.ExternalReferences.FirstOrDefault( r => solution.GetProjectState(inProgressCompilationNotRef.GetAssemblyOrModuleSymbol(r) as IAssemblySymbol, cancellationToken)?.Id == projectReference.ProjectId); } if (metadata != null) { newProjectReferences.Add(projectReference); metadataReferences.Add(metadata); metadataReferenceToProjectId.Add(metadata, projectReference.ProjectId); } } } } inProgressProject = inProgressProject.AddProjectReferences(newProjectReferences); inProgressCompilation = UpdateCompilationWithNewReferencesAndRecordAssemblySymbols(inProgressCompilation, metadataReferences, metadataReferenceToProjectId); SolutionLogger.CreatePartialProjectState(); }