提交 e1f5100f 编写于 作者: K Kevin Pilch-Bisson

Start using project reference information

Also, use the right solution load event so we don't have to delay.

Finally, use the IVsSolution APIs to get project file names and guids.
上级 c669eb52
......@@ -5,12 +5,13 @@
using System.Text;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{
internal interface IDeferredProjectWorkspaceService : IWorkspaceService
{
bool IsDeferredProjectLoadEnabled { get; }
Task<ImmutableArray<string>> GetCommandLineArgumentsForProjectAsync(string projectFilePath);
Task<ValueTuple<ImmutableArray<string>, ImmutableArray<string>>> GetCommandLineArgumentsAndProjectReferencesForProjectAsync(string projectFilePath);
}
}
......@@ -21,6 +21,12 @@ int IVsSolutionEvents.OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded)
int IVsSolutionEvents.OnAfterOpenSolution(object pUnkReserved, int fNewSolution)
{
var deferredProjectWorkspaceService = _workspace.Services.GetService<IDeferredProjectWorkspaceService>();
if (deferredProjectWorkspaceService?.IsDeferredProjectLoadEnabled ?? false)
{
LoadSolutionFromMSBuild(deferredProjectWorkspaceService, _solutionParsingCancellationTokenSource.Token).FireAndForget();
}
return VSConstants.S_OK;
}
......
......@@ -31,19 +31,11 @@ internal partial class VisualStudioProjectTracker : IVsSolutionLoadEvents
int IVsSolutionLoadEvents.OnBeforeOpenSolution(string pszSolutionFilename)
{
var deferredProjectWorkspaceService = _workspace.Services.GetService<IDeferredProjectWorkspaceService>();
if (deferredProjectWorkspaceService?.IsDeferredProjectLoadEnabled ?? false)
{
LoadSolutionFromMSBuild(deferredProjectWorkspaceService, pszSolutionFilename, _solutionParsingCancellationTokenSource.Token).FireAndForget();
}
return VSConstants.S_OK;
}
private async Task LoadSolutionFromMSBuild(
IDeferredProjectWorkspaceService deferredProjectWorkspaceService,
string solutionFileName,
CancellationToken cancellationToken)
{
AssertIsForeground();
......@@ -53,12 +45,10 @@ int IVsSolutionLoadEvents.OnBeforeOpenSolution(string pszSolutionFilename)
ErrorHandler.Succeeded(outputWindow.GetPane(ref paneGuid, out _pane)) && _pane != null)
{
_pane.Activate();
OutputToOutputWindow("OnBeforeOpenSolution - waiting 3 seconds to load solution in background");
}
// Continue on the UI thread for these operations, since we are touching the VisualStudioWorkspace, etc.
await Task.Delay(TimeSpan.FromSeconds(3), cancellationToken).ConfigureAwait(true);
await LoadSolutionInBackground(deferredProjectWorkspaceService, solutionFileName, cancellationToken).ConfigureAwait(true);
await LoadSolutionInBackground(deferredProjectWorkspaceService, cancellationToken).ConfigureAwait(true);
}
int IVsSolutionLoadEvents.OnBeforeBackgroundSolutionLoadBegins()
......@@ -114,7 +104,6 @@ int IVsSolutionLoadEvents.OnAfterBackgroundSolutionLoadComplete()
private async Task LoadSolutionInBackground(
IDeferredProjectWorkspaceService deferredProjectWorkspaceService,
string solutionFilename,
CancellationToken cancellationToken)
{
AssertIsForeground();
......@@ -124,8 +113,16 @@ int IVsSolutionLoadEvents.OnAfterBackgroundSolutionLoadComplete()
OutputToOutputWindow("Parsing solution - start");
var start = DateTimeOffset.UtcNow;
var loader = new MSBuildProjectLoader(_workspace);
var projectFilenames = loader.GetProjectPathsInSolution(solutionFilename);
var solution = _serviceProvider.GetService(typeof(SVsSolution)) as IVsSolution;
// Get the count of projects in the solution.
uint projectCount;
ErrorHandler.ThrowOnFailure(solution.GetProjectFilesInSolution(grfGetOpts: 0, cProjects: 0, rgbstrProjectNames: null, pcProjectsFetched: out projectCount));
// Now get the actual filenames.
var projectFilenames = new string[projectCount];
ErrorHandler.ThrowOnFailure(solution.GetProjectFilesInSolution(grfGetOpts: 0, cProjects: projectCount, rgbstrProjectNames: projectFilenames, pcProjectsFetched: out projectCount));
OutputToOutputWindow($"Parsing solution - done (took {DateTimeOffset.UtcNow - start})");
OutputToOutputWindow($"Creating projects - start ({projectFilenames.Length} to create)");
start = DateTimeOffset.UtcNow;
......@@ -133,13 +130,12 @@ int IVsSolutionLoadEvents.OnAfterBackgroundSolutionLoadComplete()
foreach (var projectFilename in projectFilenames)
{
// Capture the context so that we come back on the UI thread, and do the actual project creation there.
var commandLineStrings = await deferredProjectWorkspaceService.GetCommandLineArgumentsForProjectAsync(projectFilename).ConfigureAwait(true);
await CreateProjectFromArgumentsAndReferencesAsync(
workspaceProjectContextFactory,
solution,
deferredProjectWorkspaceService,
projectFilename).ConfigureAwait(true);
AssertIsForeground();
if (commandLineStrings != null)
{
CreateProjectFromProjectInfo(workspaceProjectContextFactory, projectFilename, commandLineStrings);
}
}
OutputToOutputWindow($"Creating projects - done (took {DateTimeOffset.UtcNow - start})");
......@@ -157,16 +153,30 @@ private void OutputToOutputWindow(string message)
}
}
private IWorkspaceProjectContext CreateProjectFromProjectInfo(
private async Task<IWorkspaceProjectContext> CreateProjectFromArgumentsAndReferencesAsync(
IWorkspaceProjectContextFactory workspaceProjectContextFactory,
string projectFilename,
ImmutableArray<string> commandLineStrings)
IVsSolution solution,
IDeferredProjectWorkspaceService deferredProjectWorkspaceService,
string projectFilename)
{
var languageName = GetLanguageOfProject(projectFilename);
if (languageName == null)
{
return null;
}
// Capture the context so that we come back on the UI thread, and do the actual project creation there.
var commandLineArgumentsAndProjectReferences = await deferredProjectWorkspaceService
.GetCommandLineArgumentsAndProjectReferencesForProjectAsync(projectFilename).ConfigureAwait(true);
if (commandLineArgumentsAndProjectReferences.Item1.IsDefaultOrEmpty)
{
var languageName = Path.GetExtension(projectFilename) == ".csproj" ? LanguageNames.CSharp : LanguageNames.VisualBasic;
return null;
}
var commandLineParser = _workspace.Services.GetLanguageServices(languageName).GetService<ICommandLineParserService>();
var projectDirectory = Path.GetDirectoryName(projectFilename);
var commandLineArguments = commandLineParser.Parse(
commandLineStrings,
commandLineArgumentsAndProjectReferences.Item1,
projectDirectory,
isInteractive: false,
sdkDirectory: RuntimeEnvironment.GetRuntimeDirectory());
......@@ -183,15 +193,16 @@ private void OutputToOutputWindow(string message)
// TODO: We need to actually get an output path somehow.
OutputToOutputWindow($"\tCreating '{projectName}':\t{commandLineArguments.SourceFiles.Length} source files,\t{commandLineArguments.MetadataReferences.Length} references.");
var projectGuid = ((IVsSolution5)solution).GetGuidOfProjectFile(projectFilename);
var projectContext = workspaceProjectContextFactory.CreateProjectContext(
languageName,
projectName,
projectFilename,
Guid.Empty,
projectGuid: projectGuid,
hierarchy: null,
binOutputPath: null);
projectContext.SetOptions(commandLineStrings.Join(" "));
projectContext.SetOptions(commandLineArgumentsAndProjectReferences.Item1.Join(" "));
foreach (var sourceFile in commandLineArguments.SourceFiles)
{
......@@ -208,42 +219,44 @@ private void OutputToOutputWindow(string message)
projectContext.AddMetadataReference(reference.Reference, reference.Properties);
}
#if false
foreach (var reference in projectInfo.ProjectReferences)
foreach (var projectReference in commandLineArgumentsAndProjectReferences.Item2)
{
var referencedProject = (IWorkspaceProjectContext)projectToProjectInfo.SingleOrDefault(kvp => kvp.Value.Id == reference.ProjectId).Key;
var projectReferencePath = projectReference.Substring("/ProjectReference:".Length);
var referencedProject = Projects.SingleOrDefault(p => StringComparer.OrdinalIgnoreCase.Equals(p.ProjectFilePath, projectReferencePath));
if (referencedProject == null)
{
var referencedProjectInfo = solutionInfo.Projects.Single(pi => pi.Id == reference.ProjectId);
referencedProject = CreateProjectFromProjectInfo(workspaceProjectContextFactory, projectToProjectInfo, referencedProjectInfo, solutionInfo);
// Capture the context so that we come back on the UI thread, and do the actual project creation there.
referencedProject = (AbstractProject)(await CreateProjectFromArgumentsAndReferencesAsync(
workspaceProjectContextFactory,
solution,
deferredProjectWorkspaceService,
projectReferencePath).ConfigureAwait(true));
}
if (referencedProject != null)
var referencedProjectContext = referencedProject as IWorkspaceProjectContext;
if (referencedProjectContext != null)
{
projectContext.AddProjectReference(
referencedProject,
new MetadataReferenceProperties(aliases: reference.Aliases, embedInteropTypes: reference.EmbedInteropTypes));
referencedProjectContext,
new MetadataReferenceProperties());
}
else
else if (referencedProject != null)
{
// If referencedProject is still null, it means that this project was already created by the regular project system.
// See if we can find the matching project somehow.
var referenceInfo = solutionInfo.Projects.SingleOrDefault(p => p.Id == reference.ProjectId);
if (referenceInfo != null)
{
var existingReference = Projects.SingleOrDefault(p => StringComparer.OrdinalIgnoreCase.Equals(p.ProjectFilePath, referenceInfo.FilePath));
var existingReferenceOutputPath = existingReference?.BinOutputPath;
// 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)
{
projectContext.AddMetadataReference(
existingReferenceOutputPath,
new MetadataReferenceProperties(aliases: reference.Aliases, embedInteropTypes: reference.EmbedInteropTypes));
new MetadataReferenceProperties());
}
}
else
{
// We don't know how to create this project. Another language or something?
}
}
#endif
foreach (var reference in commandLineArguments.AnalyzerReferences)
{
......@@ -253,6 +266,19 @@ private void OutputToOutputWindow(string message)
return projectContext;
}
private static string GetLanguageOfProject(string projectFilename)
{
switch (Path.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.
......
......@@ -11,6 +11,7 @@
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Workspace.Extensions;
using Microsoft.VisualStudio.Workspace.VSIntegration;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.ProjectSystem
{
......@@ -29,17 +30,18 @@ public DeferredProjectWorkspaceService(SVsServiceProvider serviceProvider)
public bool IsDeferredProjectLoadEnabled { get; }
public async Task<ImmutableArray<string>> GetCommandLineArgumentsForProjectAsync(string projectFilePath)
public async Task<ValueTuple<ImmutableArray<string>, ImmutableArray<string>>> GetCommandLineArgumentsAndProjectReferencesForProjectAsync(string projectFilePath)
{
var commandLineInfos = await _solutionWorkspaceService.GetManagedCommandLineInfoAsync(projectFilePath).ConfigureAwait(false);
//return commandLineInfos.Any() ? commandLineInfos.First().CommandLineArgs : ImmutableArray<string>.Empty;
if (commandLineInfos.Any())
{
return commandLineInfos.First().CommandLineArgs;
var commandLineInfo = commandLineInfos.First();
return ValueTuple.Create(commandLineInfo.CommandLineArgs, commandLineInfo.ProjectReferences);
}
else
{
return ImmutableArray<string>.Empty;
return ValueTuple.Create(ImmutableArray<string>.Empty, ImmutableArray<string>.Empty);
}
}
}
......
Microsoft.CodeAnalysis.MSBuild.MSBuildProjectLoader.GetProjectPathsInSolution(string solutionFilePath) -> System.Collections.Immutable.ImmutableArray<string>
\ No newline at end of file
......@@ -167,38 +167,6 @@ private void SetSolutionProperties(string solutionFilePath)
return SolutionInfo.Create(SolutionId.CreateNewId(debugName: absoluteSolutionPath), version, absoluteSolutionPath, loadedProjects.Projects);
}
public ImmutableArray<string> GetProjectPathsInSolution(string solutionFilePath)
{
if (solutionFilePath == null)
{
throw new ArgumentNullException(nameof(solutionFilePath));
}
var absoluteSolutionPath = this.GetAbsoluteSolutionPath(solutionFilePath, Directory.GetCurrentDirectory());
using (_dataGuard.DisposableWait(CancellationToken.None))
{
this.SetSolutionProperties(absoluteSolutionPath);
}
Microsoft.Build.Construction.SolutionFile solutionFile = Microsoft.Build.Construction.SolutionFile.Parse(absoluteSolutionPath);
var reportMode = this.SkipUnrecognizedProjects ? ReportMode.Log : ReportMode.Throw;
var builder = ImmutableArray.CreateBuilder<string>();
foreach (var project in solutionFile.ProjectsInOrder)
{
if (project.ProjectType != SolutionProjectType.SolutionFolder)
{
var projectAbsolutePath = TryGetAbsolutePath(project.AbsolutePath, reportMode);
if (projectAbsolutePath != null)
{
builder.Add(projectAbsolutePath);
}
}
}
return builder.ToImmutable();
}
internal string GetAbsoluteSolutionPath(string path, string baseDirectory)
{
string absolutePath;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册