diff options
author | Matt Ward <ward.matt@gmail.com> | 2017-07-24 12:21:09 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-07-24 12:21:09 +0300 |
commit | a05fa40edec864b2c6c064cdf7cb21538199a4ab (patch) | |
tree | 5de66a42e8a31b0ec5499c2e922d8030adac6db6 /main/src | |
parent | 927ea1c108062763a242c7b492034fb98237c778 (diff) | |
parent | d5b73a82d59474835b7808e1b082ea7c84d6b3a2 (diff) |
Merge pull request #2786 from mono/nuget-transitive-assembly-references-not-available
[NuGet] Transitive assembly references not available until solution reloaded
Diffstat (limited to 'main/src')
19 files changed, 961 insertions, 36 deletions
diff --git a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectReloadMonitor.cs b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectReloadMonitor.cs index 1e875529c0..637d0a6f84 100644 --- a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectReloadMonitor.cs +++ b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectReloadMonitor.cs @@ -76,7 +76,7 @@ namespace MonoDevelop.DotNetCore void OnDotNetCoreProjectReloaded (ProjectReloadedEventArgs e) { DotNetCoreProjectBuilderMaintainer.OnProjectReload (e); - RestorePackagesInProjectHandler.Run (e.NewProject.DotNetProject); + RestorePackagesInProjectHandler.Run (e.NewProject.DotNetProject, restoreTransitiveProjectReferences: true); } async void FileChanged (object sender, FileEventArgs e) diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/RestorePackagesInProjectHandler.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/RestorePackagesInProjectHandler.cs index aca0efb247..fbeb69dffa 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/RestorePackagesInProjectHandler.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/RestorePackagesInProjectHandler.cs @@ -43,25 +43,34 @@ namespace MonoDevelop.PackageManagement.Commands info.Enabled = SelectedDotNetProjectHasPackages (); } - public static void Run (DotNetProject project) + public static void Run (DotNetProject project)
+ {
+ Run (project, false);
+ }
+
+ public static void Run (DotNetProject project, bool restoreTransitiveProjectReferences) { try {
ProgressMonitorStatusMessage message = ProgressMonitorStatusMessageFactory.CreateRestoringPackagesInProjectMessage ();
- IPackageAction action = CreateRestorePackagesAction (project); + IPackageAction action = CreateRestorePackagesAction (project, restoreTransitiveProjectReferences); PackageManagementServices.BackgroundPackageActionRunner.Run (message, action);
} catch (Exception ex) {
ShowStatusBarError (ex);
}
}
- static IPackageAction CreateRestorePackagesAction (DotNetProject project) + static IPackageAction CreateRestorePackagesAction (DotNetProject project, bool restoreTransitiveProjectReferences) {
var solutionManager = PackageManagementServices.Workspace.GetSolutionManager (project.ParentSolution);
var nugetProject = solutionManager.GetNuGetProject (new DotNetProjectProxy (project));
var buildIntegratedProject = nugetProject as BuildIntegratedNuGetProject;
if (buildIntegratedProject != null) {
- return new RestoreNuGetPackagesInNuGetIntegratedProject (project, buildIntegratedProject, solutionManager);
+ return new RestoreNuGetPackagesInNuGetIntegratedProject (
+ project,
+ buildIntegratedProject,
+ solutionManager,
+ restoreTransitiveProjectReferences);
}
var nugetAwareProject = project as INuGetAwareProject;
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeMonoDevelopBuildIntegratedRestorer.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeMonoDevelopBuildIntegratedRestorer.cs new file mode 100644 index 0000000000..c60ab67a26 --- /dev/null +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeMonoDevelopBuildIntegratedRestorer.cs @@ -0,0 +1,58 @@ +// +// FakeMonoDevelopBuildIntegratedRestorer.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2017 Xamarin Inc. (http://xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using NuGet.ProjectManagement.Projects; + +namespace MonoDevelop.PackageManagement.Tests.Helpers +{ + class FakeMonoDevelopBuildIntegratedRestorer : IMonoDevelopBuildIntegratedRestorer + { + public bool LockFileChanged { get; set; } + + public BuildIntegratedNuGetProject ProjectRestored; + + public Task RestorePackages ( + BuildIntegratedNuGetProject project, + CancellationToken cancellationToken) + { + ProjectRestored = project; + return Task.FromResult (0); + } + + public List<BuildIntegratedNuGetProject> ProjectsRestored = new List<BuildIntegratedNuGetProject> (); + + public Task RestorePackages ( + IEnumerable<BuildIntegratedNuGetProject> projects, + CancellationToken cancellationToken) + { + ProjectsRestored.AddRange (projects); + return Task.FromResult (0); + } + } +} diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeNuGetProject.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeNuGetProject.cs index b2a0de2557..4b9eaae8f3 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeNuGetProject.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeNuGetProject.cs @@ -107,7 +107,7 @@ namespace MonoDevelop.PackageManagement.Tests.Helpers return Task.FromResult (0); } - public void NotifyProjectReferencesChanged () + public void NotifyProjectReferencesChanged (bool includeTransitiveProjectReferences) { } } diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeSolutionManager.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeSolutionManager.cs index 0cf7e37214..b88ace6581 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeSolutionManager.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeSolutionManager.cs @@ -27,6 +27,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using MonoDevelop.Projects; using NuGet.Configuration; using NuGet.PackageManagement; using NuGet.ProjectManagement; @@ -113,14 +114,20 @@ namespace MonoDevelop.PackageManagement.Tests.Helpers throw new NotImplementedException (); } - public Dictionary<IDotNetProject, FakeNuGetProject> NuGetProjects = new Dictionary<IDotNetProject, FakeNuGetProject> (); + public Dictionary<IDotNetProject, NuGetProject> NuGetProjects = new Dictionary<IDotNetProject, NuGetProject> (); + public Dictionary<DotNetProject, NuGetProject> NuGetProjectsUsingDotNetProjects = new Dictionary<DotNetProject, NuGetProject> (); public NuGetProject GetNuGetProject (IDotNetProject project) { - FakeNuGetProject nugetProject = null; + NuGetProject nugetProject = null; if (NuGetProjects.TryGetValue (project, out nugetProject)) return nugetProject; + if (project.DotNetProject != null) { + if (NuGetProjectsUsingDotNetProjects.TryGetValue (project.DotNetProject, out nugetProject)) + return nugetProject; + } + return new FakeNuGetProject (project); } diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestableDotNetCoreNuGetProject.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestableDotNetCoreNuGetProject.cs index 6380f38036..3ccbecf181 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestableDotNetCoreNuGetProject.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestableDotNetCoreNuGetProject.cs @@ -34,6 +34,7 @@ namespace MonoDevelop.PackageManagement.Tests.Helpers public TestableDotNetCoreNuGetProject (DotNetProject project) : base (project, new [] { "netcoreapp1.0" }) { + BuildIntegratedRestorer = new FakeMonoDevelopBuildIntegratedRestorer (); } public bool IsSaved { get; set; } @@ -43,5 +44,14 @@ namespace MonoDevelop.PackageManagement.Tests.Helpers IsSaved = true; return Task.FromResult (0); } + + public FakeMonoDevelopBuildIntegratedRestorer BuildIntegratedRestorer; + public Solution SolutionUsedToCreateBuildIntegratedRestorer; + + protected override IMonoDevelopBuildIntegratedRestorer CreateBuildIntegratedRestorer (Solution solution) + { + SolutionUsedToCreateBuildIntegratedRestorer = solution; + return BuildIntegratedRestorer; + } } } diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.csproj b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.csproj index 6d12c04bbb..f26b667dc4 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.csproj +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.csproj @@ -210,6 +210,8 @@ <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\TestableUninstallNuGetPackagesAction.cs" /> <Compile Include="MonoDevelop.PackageManagement.Tests\PackageReferenceNuGetProjectTests.cs" /> <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\TestablePackageReferenceNuGetProject.cs" /> + <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\FakeMonoDevelopBuildIntegratedRestorer.cs" /> + <Compile Include="MonoDevelop.PackageManagement.Tests\RestoreNuGetPackagesInNuGetIntegratedProjectTests.cs" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\..\..\core\MonoDevelop.Core\MonoDevelop.Core.csproj"> diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetCoreNuGetProjectTests.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetCoreNuGetProjectTests.cs index e9148330f5..69135e9943 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetCoreNuGetProjectTests.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetCoreNuGetProjectTests.cs @@ -30,6 +30,7 @@ using System.Threading.Tasks; using MonoDevelop.PackageManagement.Tests.Helpers; using MonoDevelop.Projects; using NUnit.Framework; +using NuGet.PackageManagement; using NuGet.Packaging.Core; using NuGet.ProjectManagement; using NuGet.ProjectModel; @@ -44,14 +45,24 @@ namespace MonoDevelop.PackageManagement.Tests TestableDotNetCoreNuGetProject project; FakeNuGetProjectContext context; DependencyGraphCacheContext dependencyGraphCacheContext; + FakeMonoDevelopBuildIntegratedRestorer buildIntegratedRestorer; void CreateNuGetProject (string projectName = "MyProject", string fileName = @"d:\projects\MyProject\MyProject.csproj") { context = new FakeNuGetProjectContext (); - dotNetProject = new DummyDotNetProject (); - dotNetProject.Name = projectName; - dotNetProject.FileName = fileName.ToNativePath (); + dotNetProject = CreateDotNetCoreProject (projectName, fileName); + var solution = new Solution (); + solution.RootFolder.AddItem (dotNetProject); project = new TestableDotNetCoreNuGetProject (dotNetProject); + buildIntegratedRestorer = project.BuildIntegratedRestorer; + } + + static DummyDotNetProject CreateDotNetCoreProject (string projectName = "MyProject", string fileName = @"d:\projects\MyProject\MyProject.csproj") + { + var project = new DummyDotNetProject (); + project.Name = projectName; + project.FileName = fileName.ToNativePath (); + return project; } void AddDotNetProjectPackageReference (string packageId, string version) @@ -86,6 +97,12 @@ namespace MonoDevelop.PackageManagement.Tests return specs.Single (); } + void OnAfterExecuteActions (string packageId, string version, NuGetProjectActionType actionType) + { + var action = new FakeNuGetProjectAction (packageId, version, actionType); + project.OnAfterExecuteActions (new [] { action }); + } + [Test] public async Task GetInstalledPackagesAsync_OnePackageReference_ReturnsOnePackageReference () { @@ -255,5 +272,233 @@ namespace MonoDevelop.PackageManagement.Tests Assert.IsTrue (packageReference.IsFloating ()); Assert.AreEqual ("2.6.0-*", packageReference.AllowedVersions.Float.ToString ()); } + + [Test] + public async Task PostProcessAsync_ProjectAssetsFile_NotifyChangeInAssetsFile () + { + CreateNuGetProject (); + AddDotNetProjectPackageReference ("NUnit", "2.6.0"); + dotNetProject.BaseIntermediateOutputPath = @"d:\projects\MyProject\obj".ToNativePath (); + string fileNameChanged = null; + PackageManagementServices.PackageManagementEvents.FileChanged += (sender, e) => { + fileNameChanged = e.Single ().FileName; + }; + + await project.PostProcessAsync (context, CancellationToken.None); + + string expectedFileNameChanged = @"d:\projects\MyProject\obj\project.assets.json".ToNativePath (); + Assert.AreEqual (expectedFileNameChanged, fileNameChanged); + } + + [Test] + public async Task PostProcessAsync_References_NotifyReferencesChangedEventFired () + { + CreateNuGetProject (); + AddDotNetProjectPackageReference ("NUnit", "2.6.0"); + string modifiedHint = null; + dotNetProject.Modified += (sender, e) => { + modifiedHint = e.Single ().Hint; + }; + + await project.PostProcessAsync (context, CancellationToken.None); + + Assert.AreEqual ("References", modifiedHint); + } + + [Test] + public async Task PostProcessAsync_RestoreRunLockFileNotChanged_NotifyReferencesChangedEventFired () + { + CreateNuGetProject (); + AddDotNetProjectPackageReference ("NUnit", "2.6.0"); + string modifiedHint = null; + dotNetProject.Modified += (sender, e) => { + modifiedHint = e.Single ().Hint; + }; + OnAfterExecuteActions ("NUnit", "2.6.3", NuGetProjectActionType.Install); + + await project.PostProcessAsync (context, CancellationToken.None); + + Assert.IsNotNull (dotNetProject.ParentSolution); + Assert.AreEqual (dotNetProject.ParentSolution, project.SolutionUsedToCreateBuildIntegratedRestorer); + Assert.AreEqual (project, buildIntegratedRestorer.ProjectRestored); + Assert.AreEqual ("References", modifiedHint); + } + + [Test] + public async Task PostProcessAsync_RestoreRunLockFileNotChanged_NotifyChangeInAssetsFile () + { + CreateNuGetProject (); + AddDotNetProjectPackageReference ("NUnit", "2.6.0"); + dotNetProject.BaseIntermediateOutputPath = @"d:\projects\MyProject\obj".ToNativePath (); + string fileNameChanged = null; + PackageManagementServices.PackageManagementEvents.FileChanged += (sender, e) => { + fileNameChanged = e.Single ().FileName; + }; + OnAfterExecuteActions ("NUnit", "2.6.3", NuGetProjectActionType.Install); + + await project.PostProcessAsync (context, CancellationToken.None); + + string expectedFileNameChanged = @"d:\projects\MyProject\obj\project.assets.json".ToNativePath (); + Assert.AreEqual (expectedFileNameChanged, fileNameChanged); + Assert.AreEqual (project, buildIntegratedRestorer.ProjectRestored); + } + + /// <summary> + /// Build restorer would trigger the notification itself. + /// </summary> + [Test] + public async Task PostProcessAsync_RestoreRunLockFileChanged_NotifyReferencesChangedEventNotFired () + { + CreateNuGetProject (); + AddDotNetProjectPackageReference ("NUnit", "2.6.0"); + string modifiedHint = null; + dotNetProject.Modified += (sender, e) => { + modifiedHint = e.Single ().Hint; + }; + buildIntegratedRestorer.LockFileChanged = true; + OnAfterExecuteActions ("NUnit", "2.6.3", NuGetProjectActionType.Install); + + await project.PostProcessAsync (context, CancellationToken.None); + + Assert.AreEqual (project, buildIntegratedRestorer.ProjectRestored); + Assert.IsNull (modifiedHint); + } + + /// <summary> + /// Build restorer would trigger the notification itself. + /// </summary> + [Test] + public async Task PostProcessAsync_RestoreRunLockFileChanged_NotifyChangeInAssetsFileIsNotMade () + { + CreateNuGetProject (); + AddDotNetProjectPackageReference ("NUnit", "2.6.0"); + dotNetProject.BaseIntermediateOutputPath = @"d:\projects\MyProject\obj".ToNativePath (); + string fileNameChanged = null; + PackageManagementServices.PackageManagementEvents.FileChanged += (sender, e) => { + fileNameChanged = e.Single ().FileName; + }; + buildIntegratedRestorer.LockFileChanged = true; + OnAfterExecuteActions ("NUnit", "2.6.3", NuGetProjectActionType.Install); + + await project.PostProcessAsync (context, CancellationToken.None); + + Assert.IsNull (fileNameChanged); + Assert.AreEqual (project, buildIntegratedRestorer.ProjectRestored); + } + + [Test] + public void NotifyProjectReferencesChanged_References_NotifyReferencesChangedEventFired () + { + CreateNuGetProject (); + string modifiedHint = null; + dotNetProject.Modified += (sender, e) => { + modifiedHint = e.Single ().Hint; + }; + + project.NotifyProjectReferencesChanged (false); + + Assert.AreEqual ("References", modifiedHint); + } + + /// <summary> + /// Assembly references are transitive for .NET Core projects so any .NET Core project + /// that references the project having a NuGet package being installed needs to refresh + /// references for the other projects not just itself. + /// </summary> + [Test] + public async Task PostProcessAsync_DotNetCoreProjectReferencesThisProject_NotifyReferencesChangedEventFiredForBothProjects () + { + CreateNuGetProject (); + AddDotNetProjectPackageReference ("NUnit", "2.6.0"); + var dotNetProjectWithProjectReference = CreateDotNetCoreProject ("MyProject2", @"d:\projects\MyProject2\MyProject2.csproj"); + dotNetProject.ParentSolution.RootFolder.AddItem (dotNetProjectWithProjectReference); + var projectReference = ProjectReference.CreateProjectReference (dotNetProject); + dotNetProjectWithProjectReference.References.Add (projectReference); + string modifiedHintMainProject = null; + dotNetProject.Modified += (sender, e) => { + modifiedHintMainProject = e.Single ().Hint; + }; + string modifiedHintProjectWithReference = null; + dotNetProjectWithProjectReference.Modified += (sender, e) => { + modifiedHintProjectWithReference = e.Single ().Hint; + }; + + await project.PostProcessAsync (context, CancellationToken.None); + + Assert.AreEqual ("References", modifiedHintMainProject); + Assert.AreEqual ("References", modifiedHintProjectWithReference); + } + + [Test] + public async Task PostProcessAsync_DotNetCoreProjectReferencesThisProjectLockFileNotChanged_NotifyReferencesChangedEventFiredForBothProjects () + { + CreateNuGetProject (); + AddDotNetProjectPackageReference ("NUnit", "2.6.0"); + var dotNetProjectWithProjectReference = CreateDotNetCoreProject ("MyProject2", @"d:\projects\MyProject2\MyProject2.csproj"); + dotNetProject.ParentSolution.RootFolder.AddItem (dotNetProjectWithProjectReference); + var projectReference = ProjectReference.CreateProjectReference (dotNetProject); + dotNetProjectWithProjectReference.References.Add (projectReference); + string modifiedHintMainProject = null; + dotNetProject.Modified += (sender, e) => { + modifiedHintMainProject = e.Single ().Hint; + }; + string modifiedHintProjectWithReference = null; + dotNetProjectWithProjectReference.Modified += (sender, e) => { + modifiedHintProjectWithReference = e.Single ().Hint; + }; + OnAfterExecuteActions ("NUnit", "2.6.3", NuGetProjectActionType.Install); + + await project.PostProcessAsync (context, CancellationToken.None); + + Assert.AreEqual (project, buildIntegratedRestorer.ProjectRestored); + Assert.AreEqual ("References", modifiedHintMainProject); + Assert.AreEqual ("References", modifiedHintProjectWithReference); + } + + [Test] + public void NotifyProjectReferencesChanged_IncludeTransitiveReferences_NotifyReferencesChangedEventFiredForAllProjects () + { + CreateNuGetProject (); + var dotNetProjectWithProjectReference = CreateDotNetCoreProject ("MyProject2", @"d:\projects\MyProject2\MyProject2.csproj"); + dotNetProject.ParentSolution.RootFolder.AddItem (dotNetProjectWithProjectReference); + var projectReference = ProjectReference.CreateProjectReference (dotNetProject); + dotNetProjectWithProjectReference.References.Add (projectReference); + string modifiedHintMainProject = null; + dotNetProject.Modified += (sender, e) => { + modifiedHintMainProject = e.Single ().Hint; + }; + string modifiedHintProjectWithReference = null; + dotNetProjectWithProjectReference.Modified += (sender, e) => { + modifiedHintProjectWithReference = e.Single ().Hint; + }; + + project.NotifyProjectReferencesChanged (true); + + Assert.AreEqual ("References", modifiedHintMainProject); + Assert.AreEqual ("References", modifiedHintProjectWithReference); + } + + [Test] + public void NotifyProjectReferencesChanged_DoNotIncludeTransitiveReferences_NotifyReferencesChangedEventFiredForMainProjectOnly () + { + CreateNuGetProject (); + var dotNetProjectWithProjectReference = CreateDotNetCoreProject ("MyProject2", @"d:\projects\MyProject2\MyProject2.csproj"); + dotNetProject.ParentSolution.RootFolder.AddItem (dotNetProjectWithProjectReference); + var projectReference = ProjectReference.CreateProjectReference (dotNetProject); + dotNetProjectWithProjectReference.References.Add (projectReference); + string modifiedHintMainProject = null; + dotNetProject.Modified += (sender, e) => { + modifiedHintMainProject = e.Single ().Hint; + }; + string modifiedHintProjectWithReference = null; + dotNetProjectWithProjectReference.Modified += (sender, e) => { + modifiedHintProjectWithReference = e.Single ().Hint; + }; + + project.NotifyProjectReferencesChanged (false); + + Assert.AreEqual ("References", modifiedHintMainProject); + Assert.IsNull (modifiedHintProjectWithReference); + } } } diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetProjectExtensionsTests.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetProjectExtensionsTests.cs index c92c7b099b..303c4f39d5 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetProjectExtensionsTests.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetProjectExtensionsTests.cs @@ -25,8 +25,9 @@ // THE SOFTWARE.
using System.Collections.Generic;
-using MonoDevelop.PackageManagement;
+using System.Linq;
using MonoDevelop.PackageManagement.Tests.Helpers;
+using MonoDevelop.Projects;
using NUnit.Framework;
namespace MonoDevelop.PackageManagement.Tests
@@ -56,6 +57,20 @@ namespace MonoDevelop.PackageManagement.Tests existingFiles.Add (fileName.ToNativePath ());
}
+ static DummyDotNetProject CreateDotNetCoreProject (string projectName = "MyProject", string fileName = @"d:\projects\MyProject\MyProject.csproj")
+ {
+ var project = new DummyDotNetProject ();
+ project.Name = projectName;
+ project.FileName = fileName.ToNativePath ();
+ return project;
+ } + + void AddParentSolution (DotNetProject dotNetProject) + { + var solution = new Solution (); + solution.RootFolder.AddItem (dotNetProject); + }
+
[Test]
public void GetPackagesConfigFilePath_ProjectPackagesConfigFileDoesNotExist_ReturnsDefaultPackagesConfigFile ()
{
@@ -162,6 +177,213 @@ namespace MonoDevelop.PackageManagement.Tests Assert.IsTrue (result);
}
+
+ [Test]
+ public void DotNetCoreNotifyReferencesChanged_NoProjectReferencesAllProjects_NotifyReferencesChangedForProject ()
+ {
+ var dotNetProject = CreateDotNetCoreProject ();
+ AddParentSolution (dotNetProject); + string modifiedHint = null;
+ dotNetProject.Modified += (sender, e) => {
+ modifiedHint = e.First ().Hint;
+ };
+
+ dotNetProject.DotNetCoreNotifyReferencesChanged (transitiveOnly: false);
+
+ Assert.AreEqual ("References", modifiedHint);
+ }
+
+ [Test]
+ public void DotNetCoreNotifyReferencesChanged_NoProjectReferencesTransitiveProjectReferencesOnly_NotifyReferencesChangedNotFiredForProject ()
+ {
+ var dotNetProject = CreateDotNetCoreProject ();
+ AddParentSolution (dotNetProject); + string modifiedHint = null;
+ dotNetProject.Modified += (sender, e) => {
+ modifiedHint = e.First ().Hint;
+ };
+
+ dotNetProject.DotNetCoreNotifyReferencesChanged (transitiveOnly: true);
+
+ Assert.IsNull (modifiedHint);
+ } + + [Test] + public void DotNetCoreNotifyReferencesChanged_OneProjectReferencesProject_NotifyReferencesChangedForAllProjects () + { + var dotNetProject = CreateDotNetCoreProject (); + AddParentSolution (dotNetProject); + var referencingProject = CreateDotNetCoreProject (); + dotNetProject.ParentSolution.RootFolder.AddItem (referencingProject); + referencingProject.References.Add (ProjectReference.CreateProjectReference (dotNetProject)); + string modifiedHint = null; + dotNetProject.Modified += (sender, e) => { + modifiedHint = e.First ().Hint; + }; + string modifiedHintForReferencingProject = null; + referencingProject.Modified += (sender, e) => { + modifiedHintForReferencingProject = e.First ().Hint; + }; + + dotNetProject.DotNetCoreNotifyReferencesChanged (); + + Assert.AreEqual ("References", modifiedHint); + Assert.AreEqual ("References", modifiedHintForReferencingProject); + } + + [Test] + public void DotNetCoreNotifyReferencesChanged_OneProjectReferencesProjectWithReferencedOutputAssemblyFalse_NotifyReferencesChangedNotFiredForReferencingProject () + { + var dotNetProject = CreateDotNetCoreProject (); + AddParentSolution (dotNetProject); + var referencingProject = CreateDotNetCoreProject (); + dotNetProject.ParentSolution.RootFolder.AddItem (referencingProject); + var projectReference = ProjectReference.CreateProjectReference (dotNetProject); + projectReference.ReferenceOutputAssembly = false; + referencingProject.References.Add (projectReference); + string modifiedHintForReferencingProject = null; + referencingProject.Modified += (sender, e) => { + modifiedHintForReferencingProject = e.First ().Hint; + }; + + dotNetProject.DotNetCoreNotifyReferencesChanged (true); + + Assert.IsNull (modifiedHintForReferencingProject); + } + + [Test] + public void DotNetCoreNotifyReferencesChanged_TwoOneProjectReferencesModifiedProject_NotifyReferencesChangedForAllProjects () + { + var dotNetProject = CreateDotNetCoreProject (); + AddParentSolution (dotNetProject); + var referencingProject1 = CreateDotNetCoreProject (); + dotNetProject.ParentSolution.RootFolder.AddItem (referencingProject1); + referencingProject1.References.Add (ProjectReference.CreateProjectReference (dotNetProject)); + var referencingProject2 = CreateDotNetCoreProject (); + dotNetProject.ParentSolution.RootFolder.AddItem (referencingProject2); + referencingProject2.References.Add (ProjectReference.CreateProjectReference (dotNetProject)); + string modifiedHint = null; + dotNetProject.Modified += (sender, e) => { + modifiedHint = e.First ().Hint; + }; + string modifiedHintForReferencingProject1 = null; + referencingProject1.Modified += (sender, e) => { + modifiedHintForReferencingProject1 = e.First ().Hint; + }; + string modifiedHintForReferencingProject2 = null; + referencingProject2.Modified += (sender, e) => { + modifiedHintForReferencingProject2 = e.First ().Hint; + }; + + dotNetProject.DotNetCoreNotifyReferencesChanged (); + + Assert.AreEqual ("References", modifiedHint); + Assert.AreEqual ("References", modifiedHintForReferencingProject1); + Assert.AreEqual ("References", modifiedHintForReferencingProject2); + } + + [Test] + public void DotNetCoreNotifyReferencesChanged_TwoProjectReferencesChainToModifiedProject_NotifyReferencesChangedForAllProjects () + { + var dotNetProject = CreateDotNetCoreProject (); + AddParentSolution (dotNetProject); + var referencingProject1 = CreateDotNetCoreProject (); + dotNetProject.ParentSolution.RootFolder.AddItem (referencingProject1); + referencingProject1.References.Add (ProjectReference.CreateProjectReference (dotNetProject)); + var referencingProject2 = CreateDotNetCoreProject (); + dotNetProject.ParentSolution.RootFolder.AddItem (referencingProject2); + referencingProject2.References.Add (ProjectReference.CreateProjectReference (referencingProject1)); + string modifiedHint = null; + dotNetProject.Modified += (sender, e) => { + modifiedHint = e.First ().Hint; + }; + string modifiedHintForReferencingProject1 = null; + referencingProject1.Modified += (sender, e) => { + modifiedHintForReferencingProject1 = e.First ().Hint; + }; + string modifiedHintForReferencingProject2 = null; + referencingProject2.Modified += (sender, e) => { + modifiedHintForReferencingProject2 = e.First ().Hint; + }; + + dotNetProject.DotNetCoreNotifyReferencesChanged (); + + Assert.AreEqual ("References", modifiedHint); + Assert.AreEqual ("References", modifiedHintForReferencingProject1); + Assert.AreEqual ("References", modifiedHintForReferencingProject2); + } + + /// <summary> + /// Same as above but the projects are added to the solution in a different order. + /// </summary> + [Test] + public void DotNetCoreNotifyReferencesChanged_TwoProjectReferencesChainToModifiedProject_NotifyReferencesChangedForAllProjects2 () + { + var dotNetProject = CreateDotNetCoreProject (); + AddParentSolution (dotNetProject); + var referencingProject1 = CreateDotNetCoreProject (); + var referencingProject2 = CreateDotNetCoreProject (); + dotNetProject.ParentSolution.RootFolder.AddItem (referencingProject2); + dotNetProject.ParentSolution.RootFolder.AddItem (referencingProject1); + referencingProject1.References.Add (ProjectReference.CreateProjectReference (dotNetProject)); + referencingProject2.References.Add (ProjectReference.CreateProjectReference (referencingProject1)); + string modifiedHint = null; + dotNetProject.Modified += (sender, e) => { + modifiedHint = e.First ().Hint; + }; + string modifiedHintForReferencingProject1 = null; + referencingProject1.Modified += (sender, e) => { + modifiedHintForReferencingProject1 = e.First ().Hint; + }; + string modifiedHintForReferencingProject2 = null; + referencingProject2.Modified += (sender, e) => { + modifiedHintForReferencingProject2 = e.First ().Hint; + }; + + dotNetProject.DotNetCoreNotifyReferencesChanged (); + + Assert.AreEqual ("References", modifiedHint); + Assert.AreEqual ("References", modifiedHintForReferencingProject1); + Assert.AreEqual ("References", modifiedHintForReferencingProject2); + }
+
+ [Test]
+ public void GetReferencingProjects_ThreeProjectsOneProjectReferencesModifiedProject_OneProjectReturned ()
+ {
+ var dotNetProject = CreateDotNetCoreProject ();
+ AddParentSolution (dotNetProject);
+ var referencedProject = CreateDotNetCoreProject ();
+ dotNetProject.ParentSolution.RootFolder.AddItem (referencedProject);
+ referencedProject.References.Add (ProjectReference.CreateProjectReference (dotNetProject));
+ var otherProject = CreateDotNetCoreProject ();
+ dotNetProject.ParentSolution.RootFolder.AddItem (otherProject);
+
+ var projects = dotNetProject.GetReferencingProjects ().ToList ();
+
+ Assert.AreEqual (1, projects.Count);
+ Assert.AreEqual (projects[0], referencedProject);
+ }
+
+ [Test]
+ public void GetReferencingProjects_TwoProjectReferencesChainToModifiedProject_NotifyReferencesChangedForAllProjects ()
+ {
+ var dotNetProject = CreateDotNetCoreProject ();
+ AddParentSolution (dotNetProject);
+ var referencingProject1 = CreateDotNetCoreProject ();
+ dotNetProject.ParentSolution.RootFolder.AddItem (referencingProject1);
+ referencingProject1.References.Add (ProjectReference.CreateProjectReference (dotNetProject));
+ var referencingProject2 = CreateDotNetCoreProject ();
+ dotNetProject.ParentSolution.RootFolder.AddItem (referencingProject2);
+ referencingProject2.References.Add (ProjectReference.CreateProjectReference (referencingProject1));
+ var otherProject = CreateDotNetCoreProject ();
+ dotNetProject.ParentSolution.RootFolder.AddItem (otherProject);
+
+ var projects = dotNetProject.GetReferencingProjects ().ToList ();
+
+ Assert.AreEqual (2, projects.Count);
+ Assert.That (projects, Contains.Item (referencingProject1));
+ Assert.That (projects, Contains.Item (referencingProject2));
+ }
}
}
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/RestoreNuGetPackagesInNuGetIntegratedProjectTests.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/RestoreNuGetPackagesInNuGetIntegratedProjectTests.cs new file mode 100644 index 0000000000..bf48dc9bdd --- /dev/null +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/RestoreNuGetPackagesInNuGetIntegratedProjectTests.cs @@ -0,0 +1,151 @@ +// +// RestoreNuGetPackagesInNuGetIntegratedProjectTests.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2017 Xamarin Inc. (http://xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using MonoDevelop.PackageManagement.Tests.Helpers; +using MonoDevelop.Projects; +using NUnit.Framework; +namespace MonoDevelop.PackageManagement.Tests +{ + [TestFixture] + class RestoreNuGetPackagesInNuGetIntegratedProjectTests + { + RestoreNuGetPackagesInNuGetIntegratedProject action; + FakeSolutionManager solutionManager; + FakeSolution fakeSolution; + DotNetProject dotNetProject; + FakeDotNetProject fakeDotNetProject; + TestableDotNetCoreNuGetProject nugetProject; + FakeMonoDevelopBuildIntegratedRestorer buildIntegratedRestorer; + + void CreateProject () + { + solutionManager = new FakeSolutionManager (); + fakeSolution = new FakeSolution (); + CreateNuGetProject (); + fakeDotNetProject = new FakeDotNetProject (dotNetProject.FileName); + fakeDotNetProject.ParentSolution = fakeSolution; + fakeDotNetProject.DotNetProject = dotNetProject; + fakeSolution.Projects.Add (fakeDotNetProject); + } + + void CreateAction (bool restoreTransitiveProjectReferences = false) + { + action = new RestoreNuGetPackagesInNuGetIntegratedProject ( + fakeDotNetProject, + nugetProject, + solutionManager, + buildIntegratedRestorer, + restoreTransitiveProjectReferences); + } + + void CreateNuGetProject (string projectName = "MyProject", string fileName = @"d:\projects\MyProject\MyProject.csproj") + { + dotNetProject = CreateDotNetCoreProject (projectName, fileName); + var solution = new Solution (); + solution.RootFolder.AddItem (dotNetProject); + nugetProject = new TestableDotNetCoreNuGetProject (dotNetProject); + buildIntegratedRestorer = nugetProject.BuildIntegratedRestorer; + } + + TestableDotNetCoreNuGetProject CreateNuGetProject (DotNetProject project) + { + var dotNetProjectProxy = new FakeDotNetProject (); + dotNetProjectProxy.DotNetProject = project; + fakeSolution.Projects.Add (dotNetProjectProxy); + + var dotNetCoreNuGetProject = new TestableDotNetCoreNuGetProject (project); + dotNetCoreNuGetProject.BuildIntegratedRestorer = null; + + solutionManager.NuGetProjectsUsingDotNetProjects.Add (project, dotNetCoreNuGetProject); + + return dotNetCoreNuGetProject; + } + + static DummyDotNetProject CreateDotNetCoreProject (string projectName = "MyProject", string fileName = @"d:\projects\MyProject\MyProject.csproj") + { + var project = new DummyDotNetProject (); + project.Name = projectName; + project.FileName = fileName.ToNativePath (); + return project; + } + + [Test] + public void Execute_BuildIntegratedRestorer_PackagesRestoredForProject () + { + CreateProject (); + CreateAction (); + + action.Execute (); + + Assert.AreEqual (nugetProject, buildIntegratedRestorer.ProjectRestored); + } + + [Test] + public void Execute_Events_PackagesRestoredEventFired () + { + CreateProject (); + CreateAction (); + bool packagesRestoredEventFired = false; + PackageManagementServices.PackageManagementEvents.PackagesRestored += (sender, e) => { + packagesRestoredEventFired = true; + }; + + action.Execute (); + + Assert.IsTrue (packagesRestoredEventFired); + } + + [Test] + public void Execute_ReferenceStatus_IsRefreshed () + { + CreateProject (); + CreateAction (); + + action.Execute (); + + Assert.IsTrue (fakeDotNetProject.IsReferenceStatusRefreshed); + } + + [Test] + public void IncludeTransitiveProjectReferences_ThreeProjectsOneReferencedAnother_TwoProjectsRestored () + { + CreateProject (); + var referencingProject = CreateDotNetCoreProject (); + dotNetProject.ParentSolution.RootFolder.AddItem (referencingProject); + referencingProject.References.Add (ProjectReference.CreateProjectReference (dotNetProject)); + var otherProject = CreateDotNetCoreProject (); + dotNetProject.ParentSolution.RootFolder.AddItem (otherProject); + var referencingNuGetProject = CreateNuGetProject (referencingProject); + CreateAction (true); + + action.Execute (); + + Assert.AreEqual (2, buildIntegratedRestorer.ProjectsRestored.Count); + Assert.AreEqual (buildIntegratedRestorer.ProjectsRestored[0], nugetProject); + Assert.AreEqual (buildIntegratedRestorer.ProjectsRestored[1], referencingNuGetProject); + } + } +} diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.csproj b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.csproj index c14285774d..20f387686f 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.csproj +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.csproj @@ -425,6 +425,7 @@ <Compile Include="MonoDevelop.PackageManagement\PendingPackageActionsInformation.cs" /> <Compile Include="MonoDevelop.PackageManagement\PackageActionType.cs" /> <Compile Include="MonoDevelop.PackageManagement\PendingPackageActionsHandler.cs" /> + <Compile Include="MonoDevelop.PackageManagement\IMonoDevelopBuildIntegratedRestorer.cs" /> </ItemGroup> <ItemGroup> <EmbeddedResource Include="MonoDevelop.PackageManagement.addin.xml" /> diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/DotNetCoreNuGetProject.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/DotNetCoreNuGetProject.cs index 701ea37597..c861d731d8 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/DotNetCoreNuGetProject.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/DotNetCoreNuGetProject.cs @@ -281,7 +281,7 @@ namespace MonoDevelop.PackageManagement } Runtime.RunInMainThread (() => { - DotNetProject.NotifyModified ("References"); + DotNetProject.DotNetCoreNotifyReferencesChanged (); }); packageManagementEvents.OnFileChanged (project.GetNuGetAssetsFilePath ()); @@ -292,8 +292,7 @@ namespace MonoDevelop.PackageManagement async Task RestorePackages (INuGetProjectContext nuGetProjectContext, CancellationToken token) { var packageRestorer = await Runtime.RunInMainThread (() => { - var solutionManager = PackageManagementServices.Workspace.GetSolutionManager (project.ParentSolution); - return new MonoDevelopBuildIntegratedRestorer (solutionManager); + return CreateBuildIntegratedRestorer (project.ParentSolution); }); var restoreTask = packageRestorer.RestorePackages (this, token); @@ -304,7 +303,7 @@ namespace MonoDevelop.PackageManagement if (!packageRestorer.LockFileChanged) { // Need to refresh the references since the restore did not. await Runtime.RunInMainThread (() => { - DotNetProject.NotifyModified ("References"); + DotNetProject.DotNetCoreNotifyReferencesChanged (); packageManagementEvents.OnFileChanged (project.GetNuGetAssetsFilePath ()); }); } @@ -312,6 +311,12 @@ namespace MonoDevelop.PackageManagement await base.PostProcessAsync (nuGetProjectContext, token); } + protected virtual IMonoDevelopBuildIntegratedRestorer CreateBuildIntegratedRestorer (Solution solution) + { + var solutionManager = PackageManagementServices.Workspace.GetSolutionManager (project.ParentSolution); + return new MonoDevelopBuildIntegratedRestorer (solutionManager); + } + public void OnBeforeUninstall (IEnumerable<NuGetProjectAction> actions) { } @@ -321,12 +326,16 @@ namespace MonoDevelop.PackageManagement restoreRequired = actions.Any (action => action.NuGetProjectActionType == NuGetProjectActionType.Install); } - public void NotifyProjectReferencesChanged () + public void NotifyProjectReferencesChanged (bool includeTransitiveProjectReferences) { Runtime.AssertMainThread (); DotNetProject.RefreshProjectBuilder (); - DotNetProject.NotifyModified ("References"); + + if (includeTransitiveProjectReferences) + DotNetProject.DotNetCoreNotifyReferencesChanged (); + else + DotNetProject.NotifyModified ("References"); } /// <summary> diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/DotNetProjectExtensions.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/DotNetProjectExtensions.cs index fb79aee86b..4d230bb4e3 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/DotNetProjectExtensions.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/DotNetProjectExtensions.cs @@ -216,5 +216,97 @@ namespace MonoDevelop.PackageManagement return File.Exists (propsFileName) &&
File.Exists (targetsFileName);
}
+
+ /// <summary>
+ /// If a NuGet package is installed into a .NET Core project then all .NET Core projects that
+ /// reference this project need to have their reference information updated. This allows the
+ /// assemblies from the NuGet package to be made available to the other projects since .NET
+ /// Core projects support transitive references. This method calls NotifyModified for each
+ /// project that references it, as well as for the project itself, passing the hint 'References'
+ /// which will cause the type system to refresh its reference information, which will be taken
+ /// from MSBuild.
+ ///
+ /// All projects that reference .NET Core projects will have their references refreshed. If a
+ /// .NET Framework project (non SDK), has PackageReferences and references a .NET Standard project
+ /// (SDK project) then NuGet dependencies from the .NET Standard project are available to the
+ /// .NET Framework project transitively without needing the NuGet package to be installed into
+ /// the .NET Framework project. So refreshing the references of any project that references a
+ /// .NET Core project will ensure assemblies from NuGet packages are available after installing
+ /// a new NuGet package into the referenced project.
+ /// </summary>
+ /// <param name="project">.NET Core project</param>
+ /// <param name="transitiveOnly">If false then the project passed will also have its
+ /// references refreshed. Otherwise only the projects that reference the project will
+ /// have their references refreshed.</param>
+ public static void DotNetCoreNotifyReferencesChanged (this DotNetProject project, bool transitiveOnly = false)
+ {
+ if (!transitiveOnly)
+ project.NotifyModified ("References");
+
+ foreach (var referencingProject in project.GetReferencingProjects ()) {
+ referencingProject.NotifyModified ("References");
+ }
+ }
+
+ /// <summary>
+ /// Returns all projects that directly or indirectly referencing the specified project.
+ /// </summary>
+ public static IEnumerable<DotNetProject> GetReferencingProjects (this DotNetProject project)
+ {
+ var projects = new List<DotNetProject> ();
+ var traversedProjects = new Dictionary<string, bool> (StringComparer.OrdinalIgnoreCase);
+ traversedProjects.Add (project.ItemId, true);
+
+ foreach (var currentProject in project.ParentSolution.GetAllDotNetProjects ()) {
+ if (!traversedProjects.ContainsKey (currentProject.ItemId))
+ GetReferencingProjects (project, currentProject, traversedProjects, projects);
+ }
+
+ return projects;
+ }
+
+ static bool GetReferencingProjects (
+ DotNetProject mainProject,
+ DotNetProject project,
+ Dictionary<string, bool> traversedProjects,
+ List<DotNetProject> referencingProjects)
+ {
+ foreach (var projectReference in project.References.Where (IncludeProjectReference)) {
+ var resolvedProject = projectReference.ResolveProject (mainProject.ParentSolution) as DotNetProject;
+ if (resolvedProject == null)
+ continue;
+
+ if (resolvedProject == mainProject) {
+ traversedProjects [project.ItemId] = true;
+ referencingProjects.Add (project);
+ return true;
+ }
+
+ if (traversedProjects.TryGetValue (resolvedProject.ItemId, out bool referencesProject)) {
+ if (referencesProject) {
+ traversedProjects [project.ItemId] = referencesProject;
+ referencingProjects.Add (project);
+ return true;
+ }
+ continue;
+ }
+
+ if (GetReferencingProjects (mainProject, resolvedProject, traversedProjects, referencingProjects)) {
+ traversedProjects [project.ItemId] = true;
+ referencingProjects.Add (project);
+ return true;
+ }
+ }
+
+ traversedProjects [project.ItemId] = false;
+
+ return false;
+ }
+
+ static bool IncludeProjectReference (ProjectReference projectReference)
+ {
+ return projectReference.ReferenceType == ReferenceType.Project &&
+ projectReference.ReferenceOutputAssembly;
+ }
}
}
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/IBuildIntegratedNuGetProject.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/IBuildIntegratedNuGetProject.cs index b14d19a7ba..666e49130f 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/IBuildIntegratedNuGetProject.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/IBuildIntegratedNuGetProject.cs @@ -37,7 +37,13 @@ namespace MonoDevelop.PackageManagement void OnBeforeUninstall (IEnumerable<NuGetProjectAction> actions); void OnAfterExecuteActions (IEnumerable<NuGetProjectAction> actions); Task PostProcessAsync (INuGetProjectContext nuGetProjectContext, CancellationToken token); - void NotifyProjectReferencesChanged (); + + /// <summary> + /// Notifies the project references changed. + /// </summary> + /// <param name="includeTransitiveProjectReferences">If set to <c>true</c> also notify references + /// have changed in all projects that transitively reference this project.</param> + void NotifyProjectReferencesChanged (bool includeTransitiveProjectReferences); } } diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/IMonoDevelopBuildIntegratedRestorer.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/IMonoDevelopBuildIntegratedRestorer.cs new file mode 100644 index 0000000000..8c693b321e --- /dev/null +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/IMonoDevelopBuildIntegratedRestorer.cs @@ -0,0 +1,46 @@ +// +// IMonoDevelopBuildIntegratedRestorer.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2017 Xamarin Inc. (http://xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System.Collections.Generic; +using System.Threading;
+using System.Threading.Tasks; +using NuGet.ProjectManagement.Projects; + +namespace MonoDevelop.PackageManagement +{ + interface IMonoDevelopBuildIntegratedRestorer + { + Task RestorePackages ( + BuildIntegratedNuGetProject project, + CancellationToken cancellationToken); + + Task RestorePackages ( + IEnumerable<BuildIntegratedNuGetProject> projects, + CancellationToken cancellationToken); + + bool LockFileChanged { get; } + } +} diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/MonoDevelopBuildIntegratedRestorer.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/MonoDevelopBuildIntegratedRestorer.cs index f11a4b1082..21cf4da934 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/MonoDevelopBuildIntegratedRestorer.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/MonoDevelopBuildIntegratedRestorer.cs @@ -44,7 +44,7 @@ using NuGet.Protocol.Core.Types; namespace MonoDevelop.PackageManagement
{
- internal class MonoDevelopBuildIntegratedRestorer
+ internal class MonoDevelopBuildIntegratedRestorer : IMonoDevelopBuildIntegratedRestorer
{
IPackageManagementEvents packageManagementEvents;
List<SourceRepository> sourceRepositories;
@@ -98,8 +98,10 @@ namespace MonoDevelop.PackageManagement LockFileChanged = true; await Runtime.RunInMainThread (() => { FileService.NotifyFilesChanged (changedLocks); - foreach (var project in affectedProjects) { - NotifyProjectReferencesChanged (project); + foreach (var project in affectedProjects) {
+ // Restoring the entire solution so do not refresh references for
+ // transitive project references since they should be refreshed anyway. + NotifyProjectReferencesChanged (project, includeTransitiveProjectReferences: false); } }); }
@@ -114,12 +116,17 @@ namespace MonoDevelop.PackageManagement var changedLock = await RestorePackagesInternal (project, cancellationToken); if (projectToReload != null) {
- await ReloadProject (projectToReload, changedLock);
+ // Need to ensure transitive project references are refreshed if only the single
+ // project is reloaded since they will still be out of date.
+ await ReloadProject (projectToReload, changedLock, refreshTransitiveReferences: true);
} else if (changedLock != null) { LockFileChanged = true;
await Runtime.RunInMainThread (() => { - FileService.NotifyFileChanged (changedLock); - NotifyProjectReferencesChanged (project); + FileService.NotifyFileChanged (changedLock);
+
+ // Restoring a single project so ensure references are refreshed for
+ // transitive project references. + NotifyProjectReferencesChanged (project, includeTransitiveProjectReferences: true); }); } } @@ -153,11 +160,13 @@ namespace MonoDevelop.PackageManagement return null; } - static void NotifyProjectReferencesChanged (BuildIntegratedNuGetProject project) + static void NotifyProjectReferencesChanged (
+ BuildIntegratedNuGetProject project,
+ bool includeTransitiveProjectReferences) { var buildIntegratedProject = project as IBuildIntegratedNuGetProject; if (buildIntegratedProject != null) {
- buildIntegratedProject.NotifyProjectReferencesChanged (); + buildIntegratedProject.NotifyProjectReferencesChanged (includeTransitiveProjectReferences); } }
@@ -219,7 +228,7 @@ namespace MonoDevelop.PackageManagement return null;
}
- Task ReloadProject (DotNetProject projectToReload, string changedLock)
+ Task ReloadProject (DotNetProject projectToReload, string changedLock, bool refreshTransitiveReferences = false)
{
return Runtime.RunInMainThread (async () => {
if (changedLock != null) {
@@ -227,6 +236,9 @@ namespace MonoDevelop.PackageManagement FileService.NotifyFileChanged (changedLock);
}
await projectToReload.ReevaluateProject (new ProgressMonitor ());
+
+ if (refreshTransitiveReferences)
+ projectToReload.DotNetCoreNotifyReferencesChanged (transitiveOnly: true);
});
}
}
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/PackageReferenceNuGetProject.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/PackageReferenceNuGetProject.cs index 5caaec2673..a85f52f17f 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/PackageReferenceNuGetProject.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/PackageReferenceNuGetProject.cs @@ -284,7 +284,7 @@ namespace MonoDevelop.PackageManagement { } - public void NotifyProjectReferencesChanged () + public void NotifyProjectReferencesChanged (bool includeTransitiveProjectReferences) { Runtime.AssertMainThread (); diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ProjectJsonBuildIntegratedProjectSystem.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ProjectJsonBuildIntegratedProjectSystem.cs index 36f92f4490..1ea10e4766 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ProjectJsonBuildIntegratedProjectSystem.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ProjectJsonBuildIntegratedProjectSystem.cs @@ -247,7 +247,7 @@ namespace MonoDevelop.PackageManagement return excludedReferences;
}
- public void NotifyProjectReferencesChanged ()
+ public void NotifyProjectReferencesChanged (bool includeTransitiveProjectReferences)
{
Runtime.AssertMainThread ();
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/RestoreNuGetPackagesInNuGetIntegratedProject.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/RestoreNuGetPackagesInNuGetIntegratedProject.cs index 7b796c7b1c..e5cef02523 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/RestoreNuGetPackagesInNuGetIntegratedProject.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/RestoreNuGetPackagesInNuGetIntegratedProject.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+using System.Collections.Generic;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MonoDevelop.Core;
@@ -34,21 +36,42 @@ namespace MonoDevelop.PackageManagement {
internal class RestoreNuGetPackagesInNuGetIntegratedProject : IPackageAction
{
- DotNetProject project;
+ IDotNetProject project;
BuildIntegratedNuGetProject nugetProject;
- MonoDevelopBuildIntegratedRestorer packageRestorer;
+ IMonoDevelopSolutionManager solutionManager;
+ IMonoDevelopBuildIntegratedRestorer packageRestorer;
IPackageManagementEvents packageManagementEvents;
+ List<BuildIntegratedNuGetProject> referencingProjects;
public RestoreNuGetPackagesInNuGetIntegratedProject (
DotNetProject project,
BuildIntegratedNuGetProject nugetProject,
- IMonoDevelopSolutionManager solutionManager)
+ IMonoDevelopSolutionManager solutionManager,
+ bool restoreTransitiveProjectReferences = false)
+ : this (
+ new DotNetProjectProxy (project),
+ nugetProject,
+ solutionManager,
+ new MonoDevelopBuildIntegratedRestorer (solutionManager),
+ restoreTransitiveProjectReferences)
+ {
+ }
+
+ public RestoreNuGetPackagesInNuGetIntegratedProject (
+ IDotNetProject project,
+ BuildIntegratedNuGetProject nugetProject,
+ IMonoDevelopSolutionManager solutionManager,
+ IMonoDevelopBuildIntegratedRestorer packageRestorer,
+ bool restoreTransitiveProjectReferences)
{
this.project = project;
this.nugetProject = nugetProject;
+ this.solutionManager = solutionManager;
+ this.packageRestorer = packageRestorer;
packageManagementEvents = PackageManagementServices.PackageManagementEvents;
- - packageRestorer = new MonoDevelopBuildIntegratedRestorer (solutionManager); +
+ if (restoreTransitiveProjectReferences)
+ IncludeTransitiveProjectReferences (); }
public PackageActionType ActionType {
@@ -75,12 +98,44 @@ namespace MonoDevelop.PackageManagement async Task ExecuteAsync (CancellationToken cancellationToken)
{
- await packageRestorer.RestorePackages (nugetProject, cancellationToken);
+ if (referencingProjects == null)
+ await packageRestorer.RestorePackages (nugetProject, cancellationToken);
+ else
+ await RestoreMultiplePackages (cancellationToken);
await Runtime.RunInMainThread (() => project.RefreshReferenceStatus ());
packageManagementEvents.OnPackagesRestored ();
}
+
+ /// <summary>
+ /// Execute will restore packages for all projects that transitively reference the project
+ /// passed to the constructor of this restore action if this method is passed.
+ /// </summary>
+ void IncludeTransitiveProjectReferences ()
+ {
+ var projects = project.DotNetProject.GetReferencingProjects ();
+ if (!projects.Any ())
+ return;
+
+ referencingProjects = new List<BuildIntegratedNuGetProject> ();
+ foreach (var referencingProject in projects) {
+ var projectProxy = new DotNetProjectProxy (referencingProject);
+ var currentNuGetProject = solutionManager.GetNuGetProject (projectProxy) as BuildIntegratedNuGetProject;
+ if (currentNuGetProject != null) {
+ referencingProjects.Add (currentNuGetProject);
+ }
+ }
+ }
+
+ Task RestoreMultiplePackages (CancellationToken cancellationToken)
+ {
+ var projects = new List<BuildIntegratedNuGetProject> ();
+ projects.Add (nugetProject);
+ projects.AddRange (referencingProjects);
+
+ return packageRestorer.RestorePackages (projects, cancellationToken);
+ }
}
}
|