Skip to content

Commit

Permalink
Merge pull request #635 from DustinCampbell/dotnet-msbuild
Browse files Browse the repository at this point in the history
Expand MSBuildProjectSystem to load .csproj projects from a root path
  • Loading branch information
DustinCampbell authored Sep 18, 2016
2 parents e42ba33 + 834ba72 commit cdd99ae
Show file tree
Hide file tree
Showing 10 changed files with 213 additions and 91 deletions.
16 changes: 0 additions & 16 deletions src/OmniSharp.MSBuild/MSBuildContext.cs

This file was deleted.

149 changes: 101 additions & 48 deletions src/OmniSharp.MSBuild/MSBuildProjectSystem.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.IO;
using System.Linq;
Expand Down Expand Up @@ -28,12 +29,16 @@ public class MSBuildProjectSystem : IProjectSystem
private readonly IEventEmitter _eventEmitter;
private readonly IMetadataFileReferenceCache _metadataReferenceCache;
private readonly IFileSystemWatcher _fileSystemWatcher;
private readonly MSBuildContext _context;
private readonly ILogger _logger;

private readonly object _gate = new object();
private readonly ProjectFileInfoCollection _projects;

private MSBuildOptions _options;
private string _solutionFileOrRootPath;

private static readonly Guid[] _supportsProjectTypes = new[] {
private static readonly Guid[] _supportedProjectTypes = new[]
{
new Guid("fae04ec0-301f-11d3-bf4b-00c04f79efbc") // CSharp
};

Expand All @@ -48,17 +53,16 @@ public MSBuildProjectSystem(
ILoggerFactory loggerFactory,
IEventEmitter eventEmitter,
IMetadataFileReferenceCache metadataReferenceCache,
IFileSystemWatcher fileSystemWatcher,
MSBuildContext context)
IFileSystemWatcher fileSystemWatcher)
{
_workspace = workspace;
_environment = environment;
_loggerFactory = loggerFactory;
_eventEmitter = eventEmitter;
_fileSystemWatcher = fileSystemWatcher;
_metadataReferenceCache = metadataReferenceCache;
_context = context;

_projects = new ProjectFileInfoCollection();
_logger = loggerFactory.CreateLogger("OmniSharp#MSBuild");
}

Expand All @@ -76,75 +80,124 @@ public void Initalize(IConfiguration configuration)
}
}

var solutionFilePath = _environment.SolutionFilePath;
if (string.IsNullOrEmpty(solutionFilePath))
AddProjects();

foreach (var projectFileInfo in _projects)
{
solutionFilePath = FindSolutionFilePath(_environment.Path, _logger);
UpdateProject(projectFileInfo);

if (string.IsNullOrEmpty(solutionFilePath))
{
return;
}
// TODO: This needs some improvement. Currently, it tracks both deletions and changes
// as "updates". We should properly remove projects that are deleted.
_fileSystemWatcher.Watch(projectFileInfo.ProjectFilePath, OnProjectChanged);
}
}

_context.SolutionPath = solutionFilePath;
private void AddProjects()
{
if (!string.IsNullOrEmpty(_environment.SolutionFilePath))
{
// If a solution file path was provided, process that solution
_solutionFileOrRootPath = _environment.SolutionFilePath;
AddProjectsFromSolution(_environment.SolutionFilePath);
return;
}

// Otherwise, assume that the path provided is a directory and
// look for a solution there.
var solutionFilePath = FindSolutionFilePath(_environment.Path, _logger);
if (!string.IsNullOrEmpty(solutionFilePath))
{
_solutionFileOrRootPath = solutionFilePath;
AddProjectsFromSolution(solutionFilePath);
return;
}

// Finally, if there isn't a single solution immediately available,
// Just process all of the projects beneath the root path.
_solutionFileOrRootPath = _environment.Path;
AddProjectsFromRootPath(_environment.Path);
}

private void AddProjectsFromSolution(string solutionFilePath)
{
_logger.LogInformation($"Detecting projects in '{solutionFilePath}'.");

var solutionFile = ReadSolutionFile(solutionFilePath);
var processedProjects = new HashSet<Guid>();

foreach (var projectBlock in solutionFile.ProjectBlocks)
{
if (!_supportsProjectTypes.Contains(projectBlock.ProjectTypeGuid) &&
!UnityHelper.IsUnityProject(projectBlock))
if (!_supportedProjectTypes.Contains(projectBlock.ProjectTypeGuid) &&
!UnityHelper.IsUnityProject(projectBlock.ProjectName, projectBlock.ProjectTypeGuid))
{
_logger.LogWarning("Skipped unsupported project type '{0}'", projectBlock.ProjectPath);
continue;
}

// Have we seen this project GUID? If so, move on.
if (_context.ProjectGuidToProjectIdMap.ContainsKey(projectBlock.ProjectGuid))
if (processedProjects.Contains(projectBlock.ProjectGuid))
{
continue;
}

// Solution files are assumed to contain relative paths to project files
// with Windows-style slashes.
var projectFilePath = projectBlock.ProjectPath.Replace('\\', Path.DirectorySeparatorChar);
projectFilePath = Path.Combine(_environment.Path, projectFilePath);
projectFilePath = Path.GetFullPath(projectFilePath);

_logger.LogInformation($"Loading project from '{projectFilePath}'.");

var projectFileInfo = CreateProjectFileInfo(projectFilePath);
var projectFileInfo = AddProject(projectFilePath);
if (projectFileInfo == null)
{
continue;
}

var compilationOptions = CreateCompilationOptions(projectFileInfo);

var projectInfo = ProjectInfo.Create(
id: ProjectId.CreateNewId(projectFileInfo.Name),
version: VersionStamp.Create(),
name: projectFileInfo.Name,
assemblyName: projectFileInfo.AssemblyName,
language: LanguageNames.CSharp,
filePath: projectFileInfo.ProjectFilePath,
compilationOptions: compilationOptions);

_workspace.AddProject(projectInfo);

projectFileInfo.SetProjectId(projectInfo.Id);
processedProjects.Add(projectBlock.ProjectGuid);
}
}

_context.Projects[projectFileInfo.ProjectFilePath] = projectFileInfo;
_context.ProjectGuidToProjectIdMap[projectBlock.ProjectGuid] = projectInfo.Id;
private void AddProjectsFromRootPath(string rootPath)
{
foreach (var projectFilePath in Directory.GetFiles(rootPath, "*.csproj", SearchOption.AllDirectories))
{
AddProject(projectFilePath);
}
}

_fileSystemWatcher.Watch(projectFilePath, OnProjectChanged);
private ProjectFileInfo AddProject(string filePath)
{
if (_projects.ContainsKey(filePath))
{
_logger.LogWarning($"Can't add project, it already exists: {filePath}");
return null;
}

foreach (var projectFileInfo in _context.Projects.Values)
var fileInfo = CreateProjectFileInfo(filePath);
if (fileInfo == null)
{
UpdateProject(projectFileInfo);
return null;
}

_projects.Add(fileInfo);

var compilationOptions = CreateCompilationOptions(fileInfo);

var projectInfo = ProjectInfo.Create(
id: ProjectId.CreateNewId(fileInfo.Name),
version: VersionStamp.Create(),
name: fileInfo.Name,
assemblyName: fileInfo.AssemblyName,
language: LanguageNames.CSharp,
filePath: fileInfo.ProjectFilePath,
compilationOptions: compilationOptions);

_workspace.AddProject(projectInfo);

fileInfo.SetProjectId(projectInfo.Id);

return fileInfo;
}

private static CSharpCompilationOptions CreateCompilationOptions(ProjectFileInfo projectFileInfo)
Expand Down Expand Up @@ -192,7 +245,7 @@ private static string FindSolutionFilePath(string rootPath, ILogger logger)
logger.LogInformation(result.Message);
}

return result.Solution;
return result.FilePath;
}

private ProjectFileInfo CreateProjectFileInfo(string projectFilePath)
Expand Down Expand Up @@ -231,19 +284,19 @@ private ProjectFileInfo CreateProjectFileInfo(string projectFilePath)

private void OnProjectChanged(string projectFilePath)
{
var newProjectInfo = CreateProjectFileInfo(projectFilePath);
var newProjectFileInfo = CreateProjectFileInfo(projectFilePath);

// Should we remove the entry if the project is malformed?
if (newProjectInfo != null)
// TODO: Should we remove the entry if the project is malformed?
if (newProjectFileInfo != null)
{
lock (_context)
lock (_gate)
{
ProjectFileInfo oldProjectFileInfo;
if (_context.Projects.TryGetValue(projectFilePath, out oldProjectFileInfo))
if (_projects.TryGetValue(projectFilePath, out oldProjectFileInfo))
{
_context.Projects[projectFilePath] = newProjectInfo;
newProjectInfo.SetProjectId(oldProjectFileInfo.ProjectId);
UpdateProject(newProjectInfo);
_projects[projectFilePath] = newProjectFileInfo;
newProjectFileInfo.SetProjectId(oldProjectFileInfo.ProjectId);
UpdateProject(newProjectFileInfo);
}
}
}
Expand Down Expand Up @@ -331,7 +384,7 @@ private void UpdateProjectReferences(Project project, IList<string> projectRefer
foreach (var projectReference in projectReferences)
{
ProjectFileInfo projectReferenceInfo;
if (_context.Projects.TryGetValue(projectReference, out projectReferenceInfo))
if (_projects.TryGetValue(projectReference, out projectReferenceInfo))
{
var reference = new ProjectReference(projectReferenceInfo.ProjectId);

Expand Down Expand Up @@ -388,7 +441,7 @@ private void UpdateReferences(Project project, IList<string> references)
private ProjectFileInfo GetProjectFileInfo(string path)
{
ProjectFileInfo projectFileInfo;
if (!_context.Projects.TryGetValue(path, out projectFileInfo))
if (!_projects.TryGetValue(path, out projectFileInfo))
{
return null;
}
Expand All @@ -399,7 +452,7 @@ private ProjectFileInfo GetProjectFileInfo(string path)
Task<object> IProjectSystem.GetWorkspaceModelAsync(WorkspaceInformationRequest request)
{
return Task.FromResult<object>(
new MsBuildWorkspaceInformation(_context,
new MsBuildWorkspaceInformation(_solutionFileOrRootPath, _projects,
excludeSourceFiles: request?.ExcludeSourceFiles ?? false));
}

Expand Down
10 changes: 4 additions & 6 deletions src/OmniSharp.MSBuild/Models/MsBuildWorkspaceInformation.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
using System.Collections.Generic;
using System.Linq;
using OmniSharp.MSBuild;
using OmniSharp.MSBuild.ProjectFile;

namespace OmniSharp.Models
{
public class MsBuildWorkspaceInformation
{
public MsBuildWorkspaceInformation(MSBuildContext context, bool excludeSourceFiles)
public MsBuildWorkspaceInformation(string solutionFilePath, IEnumerable<ProjectFileInfo> projects, bool excludeSourceFiles)
{
SolutionPath = context.SolutionPath;
SolutionPath = solutionFilePath;

Projects = context
.Projects
.Values
Projects = projects
.OrderBy(x => x.AssemblyName)
.Select(p => {
var project = new MSBuildProject(p);
Expand Down
8 changes: 7 additions & 1 deletion src/OmniSharp.MSBuild/ProjectFile/ProjectFileInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ public partial class ProjectFileInfo
public IList<string> ProjectReferences { get; }
public IList<string> Analyzers { get; }

public ProjectFileInfo()
public ProjectFileInfo(string projectFilePath)
{
this.ProjectFilePath = projectFilePath;
}

private ProjectFileInfo(
Expand Down Expand Up @@ -95,6 +96,11 @@ public static ProjectFileInfo Create(
MSBuildOptions options,
ICollection<MSBuildDiagnosticsMessage> diagnostics)
{
if (!File.Exists(projectFilePath))
{
return null;
}

#if NET451
if (PlatformHelper.IsMono)
{
Expand Down
Loading

0 comments on commit cdd99ae

Please sign in to comment.