Skip to content

Commit

Permalink
Merge pull request #760 from dotnet/improve-folder-performance
Browse files Browse the repository at this point in the history
Improve folder performance
  • Loading branch information
JoeRobich committed Aug 15, 2020
2 parents 2f08a90 + 93136a2 commit 7164b28
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 147 deletions.
12 changes: 6 additions & 6 deletions eng/Versions.props
Expand Up @@ -10,23 +10,23 @@
<MicrosoftBuildVersion>15.3.409</MicrosoftBuildVersion>
<!-- Dependencies from https://github.com/dotnet/roslyn -->
<MicrosoftNETCoreCompilersPackageVersion>3.8.0-2.20414.4</MicrosoftNETCoreCompilersPackageVersion>
<MicrosoftExtensionsVersion>3.1.1</MicrosoftExtensionsVersion>
<MicrosoftExtensionsVersion>5.0.0-preview.7.20364.11</MicrosoftExtensionsVersion>
</PropertyGroup>
<!--
Other Dependency versions
-->
<PropertyGroup>
<SystemCommandLineVersion>2.0.0-beta1.20303.1</SystemCommandLineVersion>
<SystemCommandLineRenderingVersion>0.3.0-alpha.20303.1</SystemCommandLineRenderingVersion>
<SystemCommandLineVersion>2.0.0-beta1.20371.2</SystemCommandLineVersion>
<SystemCommandLineRenderingVersion>0.3.0-alpha.20371.2</SystemCommandLineRenderingVersion>
<MicrosoftBuildFrameworkVersion>$(MicrosoftBuildVersion)</MicrosoftBuildFrameworkVersion>
<MicrosoftBuildLocatorVersion>1.2.6</MicrosoftBuildLocatorVersion>
<MicrosoftCodeAnalysisAnalyzerTestingVersion>1.0.1-beta1.20114.4</MicrosoftCodeAnalysisAnalyzerTestingVersion>
<MicrosoftCodeAnalysisAnalyzerTestingVersion>1.0.1-beta1.20413.3</MicrosoftCodeAnalysisAnalyzerTestingVersion>
<MicrosoftExtensionsDependencyInjectionVersion>$(MicrosoftExtensionsVersion)</MicrosoftExtensionsDependencyInjectionVersion>
<MicrosoftExtensionsFileSystemGlobbingVersion>$(MicrosoftExtensionsVersion)</MicrosoftExtensionsFileSystemGlobbingVersion>
<MicrosoftExtensionsLoggingVersion>$(MicrosoftExtensionsVersion)</MicrosoftExtensionsLoggingVersion>
<MicrosoftCodeAnalysisAnalyzersVersion>3.0.0</MicrosoftCodeAnalysisAnalyzersVersion>
<MicrosoftCodeAnalysisAnalyzersVersion>3.3.0</MicrosoftCodeAnalysisAnalyzersVersion>
<MicrosoftCodeAnalysisVersion>$(MicrosoftNETCoreCompilersPackageVersion)</MicrosoftCodeAnalysisVersion>
<SystemTextJsonVersion>4.7.0</SystemTextJsonVersion>
<SystemTextJsonVersion>$(MicrosoftExtensionsVersion)</SystemTextJsonVersion>
<!--
Workaround for the inability to load the NugetSdkResolver from the .NET 5 SDK
https://github.com/microsoft/MSBuildLocator/issues/88
Expand Down
82 changes: 9 additions & 73 deletions src/CodeFormatter.cs
Expand Up @@ -9,8 +9,6 @@
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Build.Logging;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis.Tools.Analyzers;
using Microsoft.CodeAnalysis.Tools.Formatters;
using Microsoft.CodeAnalysis.Tools.Utilities;
Expand Down Expand Up @@ -150,87 +148,25 @@ private static string GetReportFilePath(string reportPath)
}
}

private static async Task<Workspace> OpenFolderWorkspaceAsync(string workspacePath, Matcher fileMatcher, CancellationToken cancellationToken)
private static Task<Workspace> OpenFolderWorkspaceAsync(string workspacePath, Matcher fileMatcher, CancellationToken cancellationToken)
{
var folderWorkspace = FolderWorkspace.Create();
await folderWorkspace.OpenFolder(workspacePath, fileMatcher, cancellationToken).ConfigureAwait(false);
return folderWorkspace;
return Task.Run<Workspace>(() =>
{
var folderWorkspace = FolderWorkspace.Create();
folderWorkspace.OpenFolder(workspacePath, fileMatcher);
return folderWorkspace;
}, cancellationToken);
}

private static async Task<Workspace?> OpenMSBuildWorkspaceAsync(
private static Task<Workspace?> OpenMSBuildWorkspaceAsync(
string solutionOrProjectPath,
WorkspaceType workspaceType,
bool createBinaryLog,
bool logWorkspaceWarnings,
ILogger logger,
CancellationToken cancellationToken)
{
var properties = new Dictionary<string, string>(StringComparer.Ordinal)
{
// This property ensures that XAML files will be compiled in the current AppDomain
// rather than a separate one. Any tasks isolated in AppDomains or tasks that create
// AppDomains will likely not work due to https://github.com/Microsoft/MSBuildLocator/issues/16.
{ "AlwaysCompileMarkupFilesInSeparateDomain", bool.FalseString },
};

var workspace = MSBuildWorkspace.Create(properties);

Build.Framework.ILogger? binlog = null;
if (createBinaryLog)
{
binlog = new BinaryLogger()
{
Parameters = Path.Combine(Environment.CurrentDirectory, "formatDiagnosticLog.binlog"),
Verbosity = Build.Framework.LoggerVerbosity.Diagnostic,
};
}

if (workspaceType == WorkspaceType.Solution)
{
await workspace.OpenSolutionAsync(solutionOrProjectPath, msbuildLogger: binlog, cancellationToken: cancellationToken).ConfigureAwait(false);
}
else
{
try
{
await workspace.OpenProjectAsync(solutionOrProjectPath, msbuildLogger: binlog, cancellationToken: cancellationToken).ConfigureAwait(false);
}
catch (InvalidOperationException)
{
logger.LogError(Resources.Could_not_format_0_Format_currently_supports_only_CSharp_and_Visual_Basic_projects, solutionOrProjectPath);
workspace.Dispose();
return null;
}
}

LogWorkspaceDiagnostics(logger, logWorkspaceWarnings, workspace.Diagnostics);

return workspace;

static void LogWorkspaceDiagnostics(ILogger logger, bool logWorkspaceWarnings, ImmutableList<WorkspaceDiagnostic> diagnostics)
{
if (!logWorkspaceWarnings)
{
if (diagnostics.Count > 0)
{
logger.LogWarning(Resources.Warnings_were_encountered_while_loading_the_workspace_Set_the_verbosity_option_to_the_diagnostic_level_to_log_warnings);
}

return;
}

foreach (var diagnostic in diagnostics)
{
if (diagnostic.Kind == WorkspaceDiagnosticKind.Failure)
{
logger.LogError(diagnostic.Message);
}
else
{
logger.LogWarning(diagnostic.Message);
}
}
}
return MSBuildWorkspaceLoader.LoadAsync(solutionOrProjectPath, workspaceType, createBinaryLog, logWorkspaceWarnings, logger, cancellationToken);
}

private static async Task<Solution> RunCodeFormattersAsync(
Expand Down
37 changes: 20 additions & 17 deletions src/Program.cs
Expand Up @@ -106,28 +106,31 @@ private static async Task<int> Main(string[] args)
}
}

var runtimeVersion = GetRuntimeVersion();
if (workspaceType != WorkspaceType.Folder)
{
var runtimeVersion = GetRuntimeVersion();

logger.LogDebug(Resources.The_dotnet_runtime_version_is_0, runtimeVersion);
logger.LogDebug(Resources.The_dotnet_runtime_version_is_0, runtimeVersion);

// Load MSBuild
Environment.CurrentDirectory = workspaceDirectory;
// Load MSBuild
Environment.CurrentDirectory = workspaceDirectory;

if (!TryGetDotNetCliVersion(out var dotnetVersion))
{
logger.LogError(Resources.Unable_to_locate_dotnet_CLI_Ensure_that_it_is_on_the_PATH);
return UnableToLocateDotNetCliExitCode;
}
if (!TryGetDotNetCliVersion(out var dotnetVersion))
{
logger.LogError(Resources.Unable_to_locate_dotnet_CLI_Ensure_that_it_is_on_the_PATH);
return UnableToLocateDotNetCliExitCode;
}

logger.LogTrace(Resources.The_dotnet_CLI_version_is_0, dotnetVersion);
logger.LogTrace(Resources.The_dotnet_CLI_version_is_0, dotnetVersion);

if (!TryLoadMSBuild(out var msBuildPath))
{
logger.LogError(Resources.Unable_to_locate_MSBuild_Ensure_the_NET_SDK_was_installed_with_the_official_installer);
return UnableToLocateMSBuildExitCode;
}
if (!TryLoadMSBuild(out var msBuildPath))
{
logger.LogError(Resources.Unable_to_locate_MSBuild_Ensure_the_NET_SDK_was_installed_with_the_official_installer);
return UnableToLocateMSBuildExitCode;
}

logger.LogTrace(Resources.Using_msbuildexe_located_in_0, msBuildPath);
logger.LogTrace(Resources.Using_msbuildexe_located_in_0, msBuildPath);
}

var fileMatcher = SourceFileMatcher.CreateMatcher(include, exclude);

Expand Down Expand Up @@ -225,7 +228,7 @@ private static ILogger<Program> SetupLogging(IConsole console, LogLevel logLevel
var serviceProvider = serviceCollection.BuildServiceProvider();
var logger = serviceProvider.GetService<ILogger<Program>>();

return logger;
return logger!;
}

private static bool TryGetDotNetCliVersion([NotNullWhen(returnValue: true)] out string? dotnetVersion)
Expand Down
8 changes: 3 additions & 5 deletions src/Workspaces/FolderWorkspace.cs
Expand Up @@ -3,8 +3,6 @@
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Text;
Expand All @@ -23,15 +21,15 @@ private FolderWorkspace(HostServices hostServices)

public static FolderWorkspace Create()
{
return Create(MSBuildMefHostServices.DefaultServices);
return Create(MefHostServices.DefaultHost);
}

public static FolderWorkspace Create(HostServices hostServices)
{
return new FolderWorkspace(hostServices);
}

public async Task<Solution> OpenFolder(string folderPath, Matcher fileMatcher, CancellationToken cancellationToken)
public Solution OpenFolder(string folderPath, Matcher fileMatcher)
{
if (string.IsNullOrEmpty(folderPath) || !Directory.Exists(folderPath))
{
Expand All @@ -40,7 +38,7 @@ public async Task<Solution> OpenFolder(string folderPath, Matcher fileMatcher, C

ClearSolution();

var solutionInfo = await FolderSolutionLoader.LoadSolutionInfoAsync(folderPath, fileMatcher, cancellationToken).ConfigureAwait(false);
var solutionInfo = FolderSolutionLoader.LoadSolutionInfo(folderPath, fileMatcher);

OnSolutionAdded(solutionInfo);

Expand Down
10 changes: 6 additions & 4 deletions src/Workspaces/FolderWorkspace_FolderSolutionLoader.cs
Expand Up @@ -2,8 +2,7 @@

using System.Collections.Immutable;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Tools.Utilities;
using Microsoft.Extensions.FileSystemGlobbing;

namespace Microsoft.CodeAnalysis.Tools.Workspaces
Expand All @@ -15,18 +14,21 @@ private static class FolderSolutionLoader
private static ImmutableArray<ProjectLoader> ProjectLoaders
=> ImmutableArray.Create<ProjectLoader>(new CSharpProjectLoader(), new VisualBasicProjectLoader());

public static async Task<SolutionInfo> LoadSolutionInfoAsync(string folderPath, Matcher fileMatcher, CancellationToken cancellationToken)
public static SolutionInfo LoadSolutionInfo(string folderPath, Matcher fileMatcher)
{
var absoluteFolderPath = Path.IsPathFullyQualified(folderPath)
? folderPath
: Path.GetFullPath(folderPath, Directory.GetCurrentDirectory());

var filePaths = fileMatcher.GetResultsInFullPath(absoluteFolderPath).ToImmutableArray();
var editorConfigPaths = EditorConfigFinder.GetEditorConfigPaths(folderPath);

var projectInfos = ImmutableArray.CreateBuilder<ProjectInfo>(ProjectLoaders.Length);

// Create projects for each of the supported languages.
foreach (var loader in ProjectLoaders)
{
var projectInfo = await loader.LoadProjectInfoAsync(folderPath, fileMatcher, cancellationToken).ConfigureAwait(false);
var projectInfo = loader.LoadProjectInfo(folderPath, filePaths, editorConfigPaths);
if (projectInfo is null)
{
continue;
Expand Down
59 changes: 17 additions & 42 deletions src/Workspaces/FolderWorkspace_ProjectLoader.cs
@@ -1,13 +1,10 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

using System;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Tools.Utilities;
using Microsoft.Extensions.FileSystemGlobbing;

namespace Microsoft.CodeAnalysis.Tools.Workspaces
{
Expand All @@ -19,23 +16,30 @@ private abstract class ProjectLoader
public abstract string FileExtension { get; }
public virtual string ProjectName => $"{Language}{FileExtension}proj";

public virtual async Task<ProjectInfo?> LoadProjectInfoAsync(string folderPath, Matcher fileMatcher, CancellationToken cancellationToken)
public virtual ProjectInfo? LoadProjectInfo(string folderPath, ImmutableArray<string> filePaths, ImmutableArray<string> editorConfigPaths)
{
var projectId = ProjectId.CreateNewId(debugName: folderPath);
var projectFilePaths = filePaths.Where(path => path.EndsWith(FileExtension, StringComparison.InvariantCulture));

var documents = projectFilePaths.Select(
path => DocumentInfo.Create(
DocumentId.CreateNewId(projectId, debugName: path),
name: Path.GetFileName(path),
loader: new FileTextLoader(path, DefaultEncoding),
filePath: path))
.ToImmutableArray();

var documents = await LoadDocumentInfosAsync(projectId, folderPath, FileExtension, fileMatcher).ConfigureAwait(false);
if (documents.IsDefaultOrEmpty)
{
return null;
}

var editorConfigDocuments = EditorConfigFinder.GetEditorConfigPaths(folderPath)
.Select(path =>
DocumentInfo.Create(
DocumentId.CreateNewId(projectId),
name: path,
loader: new FileTextLoader(path, Encoding.UTF8),
filePath: path))
var editorConfigDocuments = editorConfigPaths.Select(
path => DocumentInfo.Create(
DocumentId.CreateNewId(projectId),
name: path,
loader: new FileTextLoader(path, Encoding.UTF8),
filePath: path))
.ToImmutableArray();

return ProjectInfo.Create(
Expand All @@ -48,35 +52,6 @@ private abstract class ProjectLoader
documents: documents)
.WithAnalyzerConfigDocuments(editorConfigDocuments);
}

private static Task<ImmutableArray<DocumentInfo>> LoadDocumentInfosAsync(ProjectId projectId, string folderPath, string fileExtension, Matcher fileMatcher)
{
return Task.Run(() =>
{
var filePaths = Directory.GetFiles(folderPath, $"*{fileExtension}", SearchOption.AllDirectories)
.Where(filePath => fileMatcher.Match(filePath).HasMatches).ToArray();
if (filePaths.Length == 0)
{
return ImmutableArray<DocumentInfo>.Empty;
}
var documentInfos = ImmutableArray.CreateBuilder<DocumentInfo>(filePaths.Length);
foreach (var filePath in filePaths)
{
var documentInfo = DocumentInfo.Create(
DocumentId.CreateNewId(projectId, debugName: filePath),
name: Path.GetFileName(filePath),
loader: new FileTextLoader(filePath, DefaultEncoding),
filePath: filePath);
documentInfos.Add(documentInfo);
}
return documentInfos.ToImmutableArray();
});
}
}
}
}

0 comments on commit 7164b28

Please sign in to comment.