diff options
author | Lluis Sanchez <lluis@xamarin.com> | 2019-08-08 12:34:38 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-08-08 12:34:38 +0300 |
commit | 8aedb546df9f942e972d22b93d692fc3f87f8c7d (patch) | |
tree | 47e0a5388236e655a488153650d65f61c8f3723a /main | |
parent | dad818d539c194683e5d93c93d8b35077dc868fd (diff) | |
parent | b5ca7dd297d3551c67ac5b4823549cee8cadf8e3 (diff) |
Merge pull request #8358 from mono/backport-pr-8353-to-release-8.2
[release-8.2] [Core] Cache ProjectFile.Include to improve project save performance
Diffstat (limited to 'main')
3 files changed, 96 insertions, 0 deletions
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectFile.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectFile.cs index b3f0717fdb..8c53b0504d 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectFile.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectFile.cs @@ -74,14 +74,22 @@ namespace MonoDevelop.Projects BuildAction = buildAction; } + string cachedInclude; + public override string Include { get { if (Project != null) { + if (cachedInclude != null) + return cachedInclude; + string path = MSBuildProjectService.ToMSBuildPath (Project.ItemDirectory, FilePath); if (path.Length > 0) { //directory paths must end with '/' if ((Subtype == Subtype.Directory) && path [path.Length - 1] != '\\') path = path + "\\"; + // Cache the include path to avoid recalculating MSBuildProjectService.ToMSBuildPath + // which can slow down saving SDK style projects that contain thousands of files. + cachedInclude = path; return path; } } @@ -186,6 +194,8 @@ namespace MonoDevelop.Projects if (IsLink && Link.FileName == oldPath.FileName) link = Path.Combine (Path.GetDirectoryName (link), filename.FileName); + cachedInclude = null; + // If a file that belongs to a project is being renamed, update the value of UnevaluatedInclude // since that is used when saving if (Project != null) @@ -474,15 +484,33 @@ namespace MonoDevelop.Projects } } + Project project; + protected override void OnProjectSet () { base.OnProjectSet (); + if (project != null) { + project.Modified -= OnProjectModified; + project = null; + } if (Project != null) { base.Include = Include; + project = Project; + project.Modified += OnProjectModified; VirtualPathChanged?.Invoke (this, new ProjectFileVirtualPathChangedEventArgs (this, FilePath.Null, ProjectVirtualPath)); } } + void OnProjectModified (object sender, SolutionItemModifiedEventArgs e) + { + foreach (var eventInfo in e) { + if (eventInfo.Hint == "FileName") { + cachedInclude = null; + return; + } + } + } + public override string ToString () { return "[ProjectFile: FileName=" + filename + "]"; @@ -503,6 +531,10 @@ namespace MonoDevelop.Projects public virtual void Dispose () { + if (project != null) { + project.Modified -= OnProjectModified; + project = null; + } } internal event EventHandler<ProjectFileVirtualPathChangedEventArgs> VirtualPathChanged; diff --git a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Projects/DotNetCoreProjectTests.cs b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Projects/DotNetCoreProjectTests.cs index 86a3aadfc2..0ba1113eb9 100644 --- a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Projects/DotNetCoreProjectTests.cs +++ b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Projects/DotNetCoreProjectTests.cs @@ -696,6 +696,44 @@ namespace MonoDevelop.Projects } } + [Test] + public async Task AddNewFileToProjectAndSave_ProjectHas1500CSharpFiles_SavingIsFast () + { + FilePath solFile = Util.GetSampleProject ("netstandard-sdk", "netstandard-sdk.sln"); + + // Create 1500 C# files. + var sourceDirectory = solFile.ParentDirectory.Combine ("Files"); + Directory.CreateDirectory (sourceDirectory); + + for (int i = 0; i < 1500; ++i) { + string fileName = sourceDirectory.Combine ($"Test{i}.cs"); + string code = "class Test" + i + "{}"; + File.WriteAllText (fileName, code); + } + + using (var solution = (Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), solFile)) { + var p = solution.GetAllProjects ().Single () as DotNetProject; + // Sanity check - ensure project and solution in same directory otherwise the generated .cs files + // will not be used. + Assert.AreEqual (solution.BaseDirectory, p.BaseDirectory); + + string fileName = p.BaseDirectory.Combine ("NewClass.cs"); + string code = "class NewClass {}"; + File.WriteAllText (fileName, code); + + p.AddFile (fileName, BuildAction.Compile); + + var timer = Stopwatch.StartNew (); + await p.SaveAsync (Util.GetMonitor ()); + timer.Stop (); + + // This was taking 20 seconds before. + // Takes about 300ms with ProjectFile.Include caching. Here we use 2 seconds + // in case the build server is slow. + Assert.That (timer.ElapsedMilliseconds, Is.LessThan (2000)); + } + } + static void RunMSBuildRestore (FilePath fileName) {
CreateNuGetConfigFile (fileName.ParentDirectory); diff --git a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Projects/ProjectTests.cs b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Projects/ProjectTests.cs index e44b98f5c6..0913f550de 100644 --- a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Projects/ProjectTests.cs +++ b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Projects/ProjectTests.cs @@ -1226,6 +1226,32 @@ namespace MonoDevelop.Projects } } + /// <summary> + /// Ensure the cached value is updated when the Project or FileName changes. + /// </summary> + [Test] + public void ProjectFileIncludeCachingTests () + { + using (var project = Services.ProjectService.CreateDotNetProject ("C#")) { + FilePath directory = Util.CreateTmpDir ("ProjectFileIncludeCachingTests"); + project.FileName = directory.Combine ("Project.csproj"); + + var fileName = project.BaseDirectory.Combine ("Source", "Test.cs"); + var projectFile = new ProjectFile (fileName); + projectFile.Project = project; + + Assert.AreEqual (@"Source\Test.cs", projectFile.Include); + + projectFile.Name = projectFile.FilePath.ChangeName ("Changed"); + Assert.AreEqual (@"Source\Changed.cs", projectFile.Include); + Assert.AreEqual (@"Source\Changed.cs", projectFile.UnevaluatedInclude); + + // Change project's ItemDirectory. This will change the ProjectFile's Include. + project.FileName = project.BaseDirectory.Combine ("Source", "Project.csproj"); + Assert.AreEqual ("Changed.cs", projectFile.Include); + } + } + class TestGetReferencesProjectExtension : DotNetProjectExtension { protected internal override Task<List<AssemblyReference>> OnGetReferences (ConfigurationSelector configuration, CancellationToken token) |