提交 2ba6d5db 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #17563 from dotnet/merges/dev15.0.x-to-dev15.1.x-20170306-000001

Merge dev15.0.x to dev15.1.x
......@@ -42,6 +42,7 @@ try
{
"dev15.0.x" { }
"master" { }
"dev_pilchie_Fix389698" { }
default
{
if (-not $test)
......
......@@ -4,8 +4,10 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
......@@ -13,6 +15,7 @@
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Host;
using Microsoft.Internal.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.Interop;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.Legacy;
using Microsoft.VisualStudio.LanguageServices.ProjectSystem;
......@@ -74,6 +77,14 @@ internal sealed partial class VisualStudioProjectTracker : ForegroundThreadAffin
private readonly Dictionary<string, ProjectId> _projectPathToIdMap;
#endregion
// Temporary for prototyping purposes
private IVsOutputWindowPane _pane;
/// <summary>
/// Used to cancel our background solution parse if we get a solution close event from VS.
/// </summary>
private CancellationTokenSource _solutionParsingCancellationTokenSource = new CancellationTokenSource();
/// <summary>
/// Provided to not break CodeLens which has a dependency on this API until there is a
/// public release which calls <see cref="ImmutableProjects"/>. Once there is, we should
......@@ -556,5 +567,344 @@ public void OnAfterCloseSolution()
_solutionIsClosing = false;
}
public async Task LoadSolutionFromMSBuildAsync()
{
AssertIsForeground();
InitializeOutputPane();
// Continue on the UI thread for these operations, since we are touching the VisualStudioWorkspace, etc.
await PopulateWorkspaceFromDeferredProjectInfoAsync(_solutionParsingCancellationTokenSource.Token).ConfigureAwait(true);
}
[Conditional("DEBUG")]
private void InitializeOutputPane()
{
var outputWindow = (IVsOutputWindow)_serviceProvider.GetService(typeof(SVsOutputWindow));
var paneGuid = new Guid("07aaa8e9-d776-47d6-a1be-5ce00332d74d");
if (ErrorHandler.Succeeded(outputWindow.CreatePane(ref paneGuid, "Roslyn DPL Status", fInitVisible: 1, fClearWithSolution: 1)) &&
ErrorHandler.Succeeded(outputWindow.GetPane(ref paneGuid, out _pane)) && _pane != null)
{
_pane.Activate();
}
}
private async Task PopulateWorkspaceFromDeferredProjectInfoAsync(
CancellationToken cancellationToken)
{
// NOTE: We need to check cancellationToken after each await, in case the user has
// already closed the solution.
AssertIsForeground();
var componentModel = _serviceProvider.GetService(typeof(SComponentModel)) as IComponentModel;
var workspaceProjectContextFactory = componentModel.GetService<IWorkspaceProjectContextFactory>();
var dte = _serviceProvider.GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE;
var solutionConfig = (EnvDTE80.SolutionConfiguration2)dte.Solution.SolutionBuild.ActiveConfiguration;
OutputToOutputWindow($"Getting project information - start");
var start = DateTimeOffset.UtcNow;
var projectInfos = SpecializedCollections.EmptyReadOnlyDictionary<string, DeferredProjectInformation>();
// Note that `solutionConfig` may be null. For example: if the solution doesn't actually
// contain any projects.
if (solutionConfig != null)
{
// Capture the context so that we come back on the UI thread, and do the actual project creation there.
var deferredProjectWorkspaceService = _workspaceServices.GetService<IDeferredProjectWorkspaceService>();
projectInfos = await deferredProjectWorkspaceService.GetDeferredProjectInfoForConfigurationAsync(
$"{solutionConfig.Name}|{solutionConfig.PlatformName}",
cancellationToken).ConfigureAwait(true);
}
AssertIsForeground();
cancellationToken.ThrowIfCancellationRequested();
OutputToOutputWindow($"Getting project information - done (took {DateTimeOffset.UtcNow - start})");
OutputToOutputWindow($"Creating projects - start");
start = DateTimeOffset.UtcNow;
var targetPathsToProjectPaths = BuildTargetPathMap(projectInfos);
var analyzerAssemblyLoader = _workspaceServices.GetRequiredService<IAnalyzerService>().GetLoader();
foreach (var projectFilename in projectInfos.Keys)
{
cancellationToken.ThrowIfCancellationRequested();
GetOrCreateProjectFromArgumentsAndReferences(
workspaceProjectContextFactory,
analyzerAssemblyLoader,
projectFilename,
projectInfos,
targetPathsToProjectPaths);
}
OutputToOutputWindow($"Creating projects - done (took {DateTimeOffset.UtcNow - start})");
OutputToOutputWindow($"Pushing to workspace - start");
start = DateTimeOffset.UtcNow;
FinishLoad();
OutputToOutputWindow($"Pushing to workspace - done (took {DateTimeOffset.UtcNow - start})");
}
private static ImmutableDictionary<string, string> BuildTargetPathMap(IReadOnlyDictionary<string, DeferredProjectInformation> projectInfos)
{
var builder = ImmutableDictionary.CreateBuilder<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var item in projectInfos)
{
var targetPath = item.Value.TargetPath;
if (!string.IsNullOrEmpty(targetPath))
{
if (!builder.ContainsKey(targetPath))
{
builder[targetPath] = item.Key;
}
else
{
Debug.Fail($"Already have a target path of '{item.Value.TargetPath}', with value '{builder[item.Value.TargetPath]}'.");
}
}
}
return builder.ToImmutable();
}
[Conditional("DEBUG")]
private void OutputToOutputWindow(string message)
{
_pane?.OutputString(message + Environment.NewLine);
}
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 static string GetLanguageOfProject(string projectFilename)
{
switch (PathUtilities.GetExtension(projectFilename))
{
case ".csproj":
return LanguageNames.CSharp;
case ".vbproj":
return LanguageNames.VisualBasic;
default:
return null;
};
}
private void FinishLoad()
{
// We are now completely done, so let's simply ensure all projects are added.
StartPushingToWorkspaceAndNotifyOfOpenDocuments(this.ImmutableProjects);
// Also, all remaining project adds need to immediately pushed as well, since we're now "interactive"
_solutionLoadComplete = true;
// Check that the set of analyzers is complete and consistent.
GetAnalyzerDependencyCheckingService()?.CheckForConflictsAsync();
}
private AnalyzerDependencyCheckingService GetAnalyzerDependencyCheckingService()
{
var componentModel = (IComponentModel)_serviceProvider.GetService(typeof(SComponentModel));
return componentModel.GetService<AnalyzerDependencyCheckingService>();
}
internal void OnBeforeLoadProjectBatch(bool fIsBackgroundIdleBatch)
{
AssertIsForeground();
_projectsLoadedThisBatch.Clear();
}
internal void OnAfterLoadProjectBatch(bool fIsBackgroundIdleBatch)
{
AssertIsForeground();
if (!fIsBackgroundIdleBatch)
{
// This batch was loaded eagerly. This might be because the user is force expanding the projects in the
// Solution Explorer, or they had some files open in an .suo we need to push.
StartPushingToWorkspaceAndNotifyOfOpenDocuments(_projectsLoadedThisBatch);
}
_projectsLoadedThisBatch.Clear();
}
internal void OnAfterBackgroundSolutionLoadComplete()
{
AssertIsForeground();
// In Non-DPL scenarios, this indicates that ASL is complete, and we should push any
// remaining information we have to the Workspace. If DPL is enabled, this is never
// called.
FinishLoad();
}
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host;
using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.VisualStudio.LanguageServices.ProjectSystem;
using Microsoft.VisualStudio.Shell.Interop;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{
internal partial class VisualStudioProjectTracker : IVsSolutionLoadEvents
{
// Temporary for prototyping purposes
private IVsOutputWindowPane _pane;
/// <summary>
/// Used to cancel our background solution parse if we get a solution close event from VS.
/// </summary>
private CancellationTokenSource _solutionParsingCancellationTokenSource = new CancellationTokenSource();
int IVsSolutionLoadEvents.OnBeforeOpenSolution(string pszSolutionFilename)
{
return VSConstants.S_OK;
}
public async Task LoadSolutionFromMSBuildAsync()
{
AssertIsForeground();
InitializeOutputPane();
// Continue on the UI thread for these operations, since we are touching the VisualStudioWorkspace, etc.
await PopulateWorkspaceFromDeferredProjectInfoAsync(_solutionParsingCancellationTokenSource.Token).ConfigureAwait(true);
}
[Conditional("DEBUG")]
private void InitializeOutputPane()
{
var outputWindow = (IVsOutputWindow)_serviceProvider.GetService(typeof(SVsOutputWindow));
var paneGuid = new Guid("07aaa8e9-d776-47d6-a1be-5ce00332d74d");
if (ErrorHandler.Succeeded(outputWindow.CreatePane(ref paneGuid, "Roslyn DPL Status", fInitVisible: 1, fClearWithSolution: 1)) &&
ErrorHandler.Succeeded(outputWindow.GetPane(ref paneGuid, out _pane)) && _pane != null)
{
_pane.Activate();
}
}
int IVsSolutionLoadEvents.OnBeforeBackgroundSolutionLoadBegins()
{
return VSConstants.S_OK;
}
int IVsSolutionLoadEvents.OnQueryBackgroundLoadProjectBatch(out bool pfShouldDelayLoadToNextIdle)
{
pfShouldDelayLoadToNextIdle = false;
return VSConstants.S_OK;
}
int IVsSolutionLoadEvents.OnBeforeLoadProjectBatch(bool fIsBackgroundIdleBatch)
{
AssertIsForeground();
_projectsLoadedThisBatch.Clear();
return VSConstants.S_OK;
}
int IVsSolutionLoadEvents.OnAfterLoadProjectBatch(bool fIsBackgroundIdleBatch)
{
AssertIsForeground();
if (!fIsBackgroundIdleBatch)
{
// This batch was loaded eagerly. This might be because the user is force expanding the projects in the
// Solution Explorer, or they had some files open in an .suo we need to push.
StartPushingToWorkspaceAndNotifyOfOpenDocuments(_projectsLoadedThisBatch);
}
_projectsLoadedThisBatch.Clear();
return VSConstants.S_OK;
}
int IVsSolutionLoadEvents.OnAfterBackgroundSolutionLoadComplete()
{
AssertIsForeground();
// In Non-DPL scenarios, this indicates that ASL is complete, and we should push any
// remaining information we have to the Workspace. If DPL is enabled, this is never
// called.
FinishLoad();
return VSConstants.S_OK;
}
private async Task PopulateWorkspaceFromDeferredProjectInfoAsync(
CancellationToken cancellationToken)
{
// NOTE: We need to check cancellationToken after each await, in case the user has
// already closed the solution.
AssertIsForeground();
var componentModel = _serviceProvider.GetService(typeof(SComponentModel)) as IComponentModel;
var workspaceProjectContextFactory = componentModel.GetService<IWorkspaceProjectContextFactory>();
var dte = _serviceProvider.GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE;
var solutionConfig = (EnvDTE80.SolutionConfiguration2)dte.Solution.SolutionBuild.ActiveConfiguration;
OutputToOutputWindow($"Getting project information - start");
var start = DateTimeOffset.UtcNow;
var projectInfos = SpecializedCollections.EmptyReadOnlyDictionary<string, DeferredProjectInformation>();
// Note that `solutionConfig` may be null. For example: if the solution doesn't actually
// contain any projects.
if (solutionConfig != null)
{
// Capture the context so that we come back on the UI thread, and do the actual project creation there.
var deferredProjectWorkspaceService = _workspaceServices.GetService<IDeferredProjectWorkspaceService>();
projectInfos = await deferredProjectWorkspaceService.GetDeferredProjectInfoForConfigurationAsync(
$"{solutionConfig.Name}|{solutionConfig.PlatformName}",
cancellationToken).ConfigureAwait(true);
}
AssertIsForeground();
cancellationToken.ThrowIfCancellationRequested();
OutputToOutputWindow($"Getting project information - done (took {DateTimeOffset.UtcNow - start})");
OutputToOutputWindow($"Creating projects - start");
start = DateTimeOffset.UtcNow;
var targetPathsToProjectPaths = BuildTargetPathMap(projectInfos);
var analyzerAssemblyLoader = _workspaceServices.GetRequiredService<IAnalyzerService>().GetLoader();
foreach (var projectFilename in projectInfos.Keys)
{
cancellationToken.ThrowIfCancellationRequested();
GetOrCreateProjectFromArgumentsAndReferences(
workspaceProjectContextFactory,
analyzerAssemblyLoader,
projectFilename,
projectInfos,
targetPathsToProjectPaths);
}
OutputToOutputWindow($"Creating projects - done (took {DateTimeOffset.UtcNow - start})");
OutputToOutputWindow($"Pushing to workspace - start");
start = DateTimeOffset.UtcNow;
FinishLoad();
OutputToOutputWindow($"Pushing to workspace - done (took {DateTimeOffset.UtcNow - start})");
}
private static ImmutableDictionary<string, string> BuildTargetPathMap(IReadOnlyDictionary<string, DeferredProjectInformation> projectInfos)
{
var builder = ImmutableDictionary.CreateBuilder<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var item in projectInfos)
{
var targetPath = item.Value.TargetPath;
if (!string.IsNullOrEmpty(targetPath))
{
if (!builder.ContainsKey(targetPath))
{
builder[targetPath] = item.Key;
}
else
{
Debug.Fail($"Already have a target path of '{item.Value.TargetPath}', with value '{builder[item.Value.TargetPath]}'.");
}
}
}
return builder.ToImmutable();
}
[Conditional("DEBUG")]
private void OutputToOutputWindow(string message)
{
_pane?.OutputString(message + Environment.NewLine);
}
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 static string GetLanguageOfProject(string projectFilename)
{
switch (PathUtilities.GetExtension(projectFilename))
{
case ".csproj":
return LanguageNames.CSharp;
case ".vbproj":
return LanguageNames.VisualBasic;
default:
return null;
};
}
private void FinishLoad()
{
// We are now completely done, so let's simply ensure all projects are added.
StartPushingToWorkspaceAndNotifyOfOpenDocuments(this.ImmutableProjects);
// Also, all remaining project adds need to immediately pushed as well, since we're now "interactive"
_solutionLoadComplete = true;
// Check that the set of analyzers is complete and consistent.
GetAnalyzerDependencyCheckingService()?.CheckForConflictsAsync();
}
private AnalyzerDependencyCheckingService GetAnalyzerDependencyCheckingService()
{
var componentModel = (IComponentModel)_serviceProvider.GetService(typeof(SComponentModel));
return componentModel.GetService<AnalyzerDependencyCheckingService>();
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host;
using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.VisualStudio.LanguageServices.ProjectSystem;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{
internal partial class VisualStudioWorkspaceImpl : IVsSolutionLoadEvents
{
int IVsSolutionLoadEvents.OnBeforeOpenSolution(string pszSolutionFilename)
{
return VSConstants.S_OK;
}
int IVsSolutionLoadEvents.OnBeforeBackgroundSolutionLoadBegins()
{
return VSConstants.S_OK;
}
int IVsSolutionLoadEvents.OnQueryBackgroundLoadProjectBatch(out bool pfShouldDelayLoadToNextIdle)
{
pfShouldDelayLoadToNextIdle = false;
return VSConstants.S_OK;
}
int IVsSolutionLoadEvents.OnBeforeLoadProjectBatch(bool fIsBackgroundIdleBatch)
{
_foregroundObject.Value.AssertIsForeground();
GetProjectTrackerAndInitializeIfNecessary(ServiceProvider.GlobalProvider).OnBeforeLoadProjectBatch(fIsBackgroundIdleBatch);
return VSConstants.S_OK;
}
int IVsSolutionLoadEvents.OnAfterLoadProjectBatch(bool fIsBackgroundIdleBatch)
{
_foregroundObject.Value.AssertIsForeground();
GetProjectTrackerAndInitializeIfNecessary(ServiceProvider.GlobalProvider).OnAfterLoadProjectBatch(fIsBackgroundIdleBatch);
return VSConstants.S_OK;
}
int IVsSolutionLoadEvents.OnAfterBackgroundSolutionLoadComplete()
{
_foregroundObject.Value.AssertIsForeground();
GetProjectTrackerAndInitializeIfNecessary(ServiceProvider.GlobalProvider).OnAfterBackgroundSolutionLoadComplete();
return VSConstants.S_OK;
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Internal.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{
internal partial class VisualStudioProjectTracker : IVsSolutionWorkingFoldersEvents
internal partial class VisualStudioWorkspaceImpl : IVsSolutionWorkingFoldersEvents
{
void IVsSolutionWorkingFoldersEvents.OnAfterLocationChange(uint location, bool contentMoved)
{
......@@ -14,7 +15,8 @@ void IVsSolutionWorkingFoldersEvents.OnAfterLocationChange(uint location, bool c
}
// notify the working folder change
NotifyWorkspaceHosts(host => (host as IVisualStudioWorkingFolder)?.OnAfterWorkingFolderChange());
GetProjectTrackerAndInitializeIfNecessary(ServiceProvider.GlobalProvider).NotifyWorkspaceHosts(
host => (host as IVisualStudioWorkingFolder)?.OnAfterWorkingFolderChange());
}
void IVsSolutionWorkingFoldersEvents.OnQueryLocationChange(uint location, out bool pfCanMoveContent)
......@@ -27,7 +29,8 @@ void IVsSolutionWorkingFoldersEvents.OnQueryLocationChange(uint location, out bo
// notify the working folder change
pfCanMoveContent = true;
NotifyWorkspaceHosts(host => (host as IVisualStudioWorkingFolder)?.OnBeforeWorkingFolderChange());
GetProjectTrackerAndInitializeIfNecessary(ServiceProvider.GlobalProvider).NotifyWorkspaceHosts(
host => (host as IVisualStudioWorkingFolder)?.OnBeforeWorkingFolderChange());
}
}
}
}
\ No newline at end of file
......@@ -115,7 +115,7 @@
<Compile Include="Implementation\ProjectSystem\MetadataReferences\VisualStudioFrameworkAssemblyPathResolverFactory.cs" />
<Compile Include="Implementation\ProjectSystem\MetadataReferences\VisualStudioMetadataReferenceManager.Factory.cs" />
<Compile Include="Implementation\ProjectSystem\RuleSets\RuleSetEventHandler.cs" />
<Compile Include="Implementation\ProjectSystem\VisualStudioProjectTracker_IVsSolutionWorkingFoldersEvents.cs" />
<Compile Include="Implementation\ProjectSystem\VisualStudioWorkspaceImpl_IVsSolutionWorkingFoldersEvents.cs" />
<Compile Include="Implementation\Extensions\VisualStudioWorkspaceImplExtensions.cs" />
<Compile Include="Implementation\Options\LanguageSettingsPersister.cs" />
<Compile Include="Implementation\ProjectSystem\Interop\IVsUndoState.cs" />
......@@ -550,7 +550,7 @@
<Compile Include="Implementation\ProjectSystem\VisualStudioProjectTracker.cs" />
<Compile Include="Implementation\ProjectSystem\VisualStudioProjectTracker.WorkspaceHostState.cs" />
<Compile Include="Implementation\ProjectSystem\VisualStudioWorkspaceImpl_IVsSolutionEvents.cs" />
<Compile Include="Implementation\ProjectSystem\VisualStudioProjectTracker_IVsSolutionLoadEvents.cs" />
<Compile Include="Implementation\ProjectSystem\VisualStudioWorkspaceImpl_IVsSolutionLoadEvents.cs" />
<Compile Include="Implementation\ProjectSystem\VisualStudioWorkspace.cs" />
<Compile Include="Implementation\ProjectSystem\VisualStudioWorkspaceImpl.cs" />
<Compile Include="Implementation\ProjectSystem\WorkspaceBackgroundWork.cs" />
......
......@@ -31,9 +31,9 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim
<Trait(Traits.Feature, Traits.Features.ProjectSystemShims)>
Public Sub DoNotDeferLoadIfInNonBackgroundBatch()
Using testEnvironment = New TestEnvironment(solutionIsFullyLoaded:=False)
testEnvironment.GetSolutionLoadEvents().OnBeforeLoadProjectBatch(fIsBackgroundIdleBatch:=False)
testEnvironment.ProjectTracker.OnBeforeLoadProjectBatch(fIsBackgroundIdleBatch:=False)
CreateVisualBasicProject(testEnvironment, "TestProject")
testEnvironment.GetSolutionLoadEvents().OnAfterLoadProjectBatch(fIsBackgroundIdleBatch:=False)
testEnvironment.ProjectTracker.OnAfterLoadProjectBatch(fIsBackgroundIdleBatch:=False)
' We should have pushed this project to the workspace
Assert.Single(testEnvironment.Workspace.CurrentSolution.Projects)
......@@ -51,9 +51,9 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim
Using testEnvironment = New TestEnvironment(solutionIsFullyLoaded:=False)
CreateVisualBasicProject(testEnvironment, "TestProject1")
testEnvironment.GetSolutionLoadEvents().OnBeforeLoadProjectBatch(fIsBackgroundIdleBatch:=False)
testEnvironment.ProjectTracker.OnBeforeLoadProjectBatch(fIsBackgroundIdleBatch:=False)
CreateVisualBasicProject(testEnvironment, "TestProject2")
testEnvironment.GetSolutionLoadEvents().OnAfterLoadProjectBatch(fIsBackgroundIdleBatch:=False)
testEnvironment.ProjectTracker.OnAfterLoadProjectBatch(fIsBackgroundIdleBatch:=False)
' We should have pushed the second project only
Assert.Equal("TestProject2", testEnvironment.Workspace.CurrentSolution.Projects.Single().Name)
......@@ -72,10 +72,10 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim
Dim project1 = CreateVisualBasicProject(testEnvironment, "TestProject1")
' Include a project reference in this batch. This means that project1 must also be pushed
testEnvironment.GetSolutionLoadEvents().OnBeforeLoadProjectBatch(fIsBackgroundIdleBatch:=False)
testEnvironment.ProjectTracker.OnBeforeLoadProjectBatch(fIsBackgroundIdleBatch:=False)
Dim project2 = CreateVisualBasicProject(testEnvironment, "TestProject2")
project2.AddProjectReference(project1)
testEnvironment.GetSolutionLoadEvents().OnAfterLoadProjectBatch(fIsBackgroundIdleBatch:=False)
testEnvironment.ProjectTracker.OnAfterLoadProjectBatch(fIsBackgroundIdleBatch:=False)
' We should have pushed both projects
Assert.Equal(2, testEnvironment.Workspace.CurrentSolution.Projects.Count())
......@@ -89,9 +89,9 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim
Dim project1 = CreateVisualBasicProject(testEnvironment, "TestProject1")
' Include a project reference in this batch. This means that project1 must also be pushed
testEnvironment.GetSolutionLoadEvents().OnBeforeLoadProjectBatch(fIsBackgroundIdleBatch:=False)
testEnvironment.ProjectTracker.OnBeforeLoadProjectBatch(fIsBackgroundIdleBatch:=False)
Dim project2 = CreateVisualBasicProject(testEnvironment, "TestProject2")
testEnvironment.GetSolutionLoadEvents().OnAfterLoadProjectBatch(fIsBackgroundIdleBatch:=False)
testEnvironment.ProjectTracker.OnAfterLoadProjectBatch(fIsBackgroundIdleBatch:=False)
' We should have pushed the second project only
Assert.Equal("TestProject2", testEnvironment.Workspace.CurrentSolution.Projects.Single().Name)
......
......@@ -59,13 +59,9 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Fr
Public Sub NotifySolutionAsFullyLoaded()
_monitorSelectionMock.SolutionIsFullyLoaded = True
GetSolutionLoadEvents().OnAfterBackgroundSolutionLoadComplete()
_projectTracker.OnAfterBackgroundSolutionLoadComplete()
End Sub
Public Function GetSolutionLoadEvents() As IVsSolutionLoadEvents
Return DirectCast(_projectTracker, IVsSolutionLoadEvents)
End Function
Public ReadOnly Property ProjectTracker As VisualStudioProjectTracker
Get
Return _projectTracker
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册