Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve folder performance #760

Merged
merged 6 commits into from Aug 15, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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.20413.3</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();
});
}
}
}
}