Browse Source

Support NuGet packages containing MSBuild targets and props files.

MSBuild props files are added to the top of the project whilst
targets files are added to the end.
pull/44/head
Matt Ward 12 years ago
parent
commit
164a546985
  1. 65
      src/AddIns/Misc/PackageManagement/Project/Src/MSBuildBasedProjectExtensions.cs
  2. 20
      src/AddIns/Misc/PackageManagement/Project/Src/SharpDevelopProjectSystem.cs
  3. 1
      src/AddIns/Misc/PackageManagement/Test/PackageManagement.Tests.csproj
  4. 19
      src/AddIns/Misc/PackageManagement/Test/Src/Helpers/TestableProject.cs
  5. 6
      src/AddIns/Misc/PackageManagement/Test/Src/Helpers/TestableSharpDevelopProjectSystem.cs
  6. 202
      src/AddIns/Misc/PackageManagement/Test/Src/SharpDevelopProjectSystemTests.cs
  7. 2
      src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs

65
src/AddIns/Misc/PackageManagement/Project/Src/MSBuildBasedProjectExtensions.cs

@ -2,7 +2,10 @@ @@ -2,7 +2,10 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Linq;
using ICSharpCode.SharpDevelop.Project;
using Microsoft.Build.Construction;
using NuGet;
namespace ICSharpCode.PackageManagement
{
@ -15,5 +18,67 @@ namespace ICSharpCode.PackageManagement @@ -15,5 +18,67 @@ namespace ICSharpCode.PackageManagement
{
return project.HasProjectType(WebApplication) || project.HasProjectType(WebSite);
}
public static void AddImportIfMissing(
this MSBuildBasedProject project,
string importedProjectFile,
ProjectImportLocation importLocation)
{
lock (project.SyncRoot) {
if (project.ImportExists(importedProjectFile))
return;
ProjectImportElement import = AddImport(project.MSBuildProjectFile, importedProjectFile, importLocation);
import.Condition = GetCondition(importedProjectFile);
}
}
static ProjectImportElement AddImport(
ProjectRootElement projectRoot,
string importedProjectFile,
ProjectImportLocation importLocation)
{
if (importLocation == ProjectImportLocation.Top) {
return AddImportAtTop(projectRoot, importedProjectFile);
}
return projectRoot.AddImport(importedProjectFile);
}
static ProjectImportElement AddImportAtTop(ProjectRootElement projectRoot, string importedProjectFile)
{
ProjectImportElement import = projectRoot.CreateImportElement(importedProjectFile);
projectRoot.InsertBeforeChild(import, projectRoot.FirstChild);
return import;
}
static string GetCondition(string importedProjectFile)
{
return String.Format("Exists('{0}')", importedProjectFile);
}
public static bool ImportExists(this MSBuildBasedProject project, string importedProjectFile)
{
lock (project.SyncRoot) {
return project.FindImport(importedProjectFile) != null;
}
}
public static void RemoveImport(this MSBuildBasedProject project, string importedProjectFile)
{
lock (project.SyncRoot) {
ProjectImportElement import = project.FindImport(importedProjectFile);
if (import != null) {
import.Parent.RemoveChild(import);
}
}
}
static ProjectImportElement FindImport(this MSBuildBasedProject project, string importedProjectFile)
{
return project
.MSBuildProjectFile
.Imports
.FirstOrDefault(import => String.Equals(import.Project, importedProjectFile, StringComparison.OrdinalIgnoreCase));
}
}
}

20
src/AddIns/Misc/PackageManagement/Project/Src/SharpDevelopProjectSystem.cs

@ -303,12 +303,28 @@ namespace ICSharpCode.PackageManagement @@ -303,12 +303,28 @@ namespace ICSharpCode.PackageManagement
public void AddImport(string targetPath, ProjectImportLocation location)
{
throw new NotImplementedException();
string relativeTargetPath = GetRelativePath(targetPath);
project.AddImportIfMissing(relativeTargetPath, location);
ReevaluateProjectIfNecessary();
projectService.Save(project);
}
string GetRelativePath(string path)
{
return FileUtility.GetRelativePath(project.Directory, path);
}
public void RemoveImport(string targetPath)
{
throw new NotImplementedException();
string relativeTargetPath = GetRelativePath(targetPath);
project.RemoveImport(relativeTargetPath);
ReevaluateProjectIfNecessary();
projectService.Save(project);
}
protected virtual void ReevaluateProjectIfNecessary()
{
project.ReevaluateIfNecessary();
}
}
}

1
src/AddIns/Misc/PackageManagement/Test/PackageManagement.Tests.csproj

@ -37,6 +37,7 @@ @@ -37,6 +37,7 @@
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Build" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="NuGet.Console.Types">
<HintPath>..\RequiredLibraries\NuGet.Console.Types.dll</HintPath>

19
src/AddIns/Misc/PackageManagement/Test/Src/Helpers/TestableProject.cs

@ -2,9 +2,13 @@ @@ -2,9 +2,13 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using ICSharpCode.SharpDevelop.Internal.Templates;
using ICSharpCode.SharpDevelop.Project;
using Microsoft.Build.Construction;
namespace PackageManagement.Tests.Helpers
{
@ -92,5 +96,20 @@ namespace PackageManagement.Tests.Helpers @@ -92,5 +96,20 @@ namespace PackageManagement.Tests.Helpers
dependentFile.DependentUpon = dependentUpon;
return dependentFile;
}
public ProjectImportElement GetLastMSBuildChildElement()
{
return MSBuildProjectFile.LastChild as ProjectImportElement;
}
public ProjectImportElement GetFirstMSBuildChildElement()
{
return MSBuildProjectFile.FirstChild as ProjectImportElement;
}
public ICollection<ProjectImportElement> GetImports()
{
return MSBuildProjectFile.Imports;
}
}
}

6
src/AddIns/Misc/PackageManagement/Test/Src/Helpers/TestableSharpDevelopProjectSystem.cs

@ -22,6 +22,7 @@ namespace PackageManagement.Tests.Helpers @@ -22,6 +22,7 @@ namespace PackageManagement.Tests.Helpers
public ReferenceAndProjectName ReferenceAndProjectNamePassedToLogAddedReferenceToProject;
public ReferenceAndProjectName ReferenceAndProjectNamePassedToLogRemovedReferenceFromProject;
public FileNameAndProjectName FileNameAndProjectNamePassedToLogAddedFileToProject;
public bool IsReevaluateProjectIfNecessaryCalled;
public TestableSharpDevelopProjectSystem(MSBuildBasedProject project)
: this(
@ -82,5 +83,10 @@ namespace PackageManagement.Tests.Helpers @@ -82,5 +83,10 @@ namespace PackageManagement.Tests.Helpers
FileNameAndProjectNamePassedToLogAddedFileToProject =
new FileNameAndProjectName(fileName, projectName);
}
protected override void ReevaluateProjectIfNecessary()
{
IsReevaluateProjectIfNecessaryCalled = true;
}
}
}

202
src/AddIns/Misc/PackageManagement/Test/Src/SharpDevelopProjectSystemTests.cs

@ -3,10 +3,13 @@ @@ -3,10 +3,13 @@
using System;
using System.IO;
using System.Linq;
using System.Runtime.Versioning;
using ICSharpCode.PackageManagement;
using ICSharpCode.SharpDevelop.Project;
using Microsoft.Build.Construction;
using NuGet;
using NUnit.Framework;
using PackageManagement.Tests.Helpers;
@ -59,6 +62,30 @@ namespace PackageManagement.Tests @@ -59,6 +62,30 @@ namespace PackageManagement.Tests
projectSystem.AddFile(fileName, (Stream)null);
}
void AssertLastMSBuildChildElementHasProjectAttributeValue(string expectedAttributeValue)
{
ProjectImportElement import = project.GetLastMSBuildChildElement();
Assert.AreEqual(expectedAttributeValue, import.Project);
}
void AssertLastMSBuildChildHasCondition(string expectedCondition)
{
ProjectImportElement import = project.GetLastMSBuildChildElement();
Assert.AreEqual(expectedCondition, import.Condition);
}
void AssertFirstMSBuildChildElementHasProjectAttributeValue(string expectedAttributeValue)
{
ProjectImportElement import = project.GetFirstMSBuildChildElement();
Assert.AreEqual(expectedAttributeValue, import.Project);
}
void AssertFirstMSBuildChildHasCondition(string expectedCondition)
{
ProjectImportElement import = project.GetFirstMSBuildChildElement();
Assert.AreEqual(expectedCondition, import.Condition);
}
[Test]
public void Root_NewInstanceCreated_ReturnsProjectDirectory()
{
@ -833,5 +860,180 @@ namespace PackageManagement.Tests @@ -833,5 +860,180 @@ namespace PackageManagement.Tests
string customTool = fileItem.CustomTool;
Assert.AreEqual(String.Empty, customTool);
}
[Test]
public void AddImport_FullImportFilePathAndBottomOfProject_PathRelativeToProjectAddedAsLastImportInProject()
{
CreateTestProject(@"d:\projects\MyProject\MyProject\MyProject.csproj");
CreateProjectSystem(project);
string targetPath = @"d:\projects\MyProject\packages\Foo.0.1\build\Foo.targets";
projectSystem.AddImport(targetPath, ProjectImportLocation.Bottom);
AssertLastMSBuildChildElementHasProjectAttributeValue(@"..\packages\Foo.0.1\build\Foo.targets");
}
[Test]
public void AddImport_AddImportToBottomOfProject_ImportAddedWithConditionThatChecksForExistenceOfTargetsFile()
{
CreateTestProject(@"d:\projects\MyProject\MyProject\MyProject.csproj");
CreateProjectSystem(project);
string targetPath = @"d:\projects\MyProject\packages\Foo.0.1\build\Foo.targets";
projectSystem.AddImport(targetPath, ProjectImportLocation.Bottom);
AssertLastMSBuildChildHasCondition("Exists('..\\packages\\Foo.0.1\\build\\Foo.targets')");
}
[Test]
public void AddImport_AddSameImportTwice_ImportOnlyAddedOnceToProject()
{
CreateTestProject(@"d:\projects\MyProject\MyProject\MyProject.csproj");
CreateProjectSystem(project);
string targetPath = @"d:\projects\MyProject\packages\Foo.0.1\build\Foo.targets";
projectSystem.AddImport(targetPath, ProjectImportLocation.Bottom);
projectSystem.AddImport(targetPath, ProjectImportLocation.Bottom);
Assert.AreEqual(1, project.GetImports().Count);
}
[Test]
public void AddImport_AddSameImportTwiceButWithDifferentCase_ImportOnlyAddedOnceToProject()
{
CreateTestProject(@"d:\projects\MyProject\MyProject\MyProject.csproj");
CreateProjectSystem(project);
string targetPath1 = @"d:\projects\MyProject\packages\Foo.0.1\build\Foo.targets";
string targetPath2 = @"d:\projects\MyProject\packages\Foo.0.1\BUILD\FOO.TARGETS";
projectSystem.AddImport(targetPath1, ProjectImportLocation.Bottom);
projectSystem.AddImport(targetPath2, ProjectImportLocation.Bottom);
Assert.AreEqual(1, project.GetImports().Count);
}
[Test]
public void AddImport_FullImportFilePathAndBottomOfProject_ProjectIsSaved()
{
CreateTestProject(@"d:\projects\MyProject\MyProject\MyProject.csproj");
CreateProjectSystem(project);
string targetPath = @"d:\projects\MyProject\packages\Foo.0.1\build\Foo.targets";
projectSystem.AddImport(targetPath, ProjectImportLocation.Bottom);
Assert.IsTrue(project.IsSaved);
}
[Test]
public void AddImport_FullImportFilePathAndBottomOfProject_ProjectIsReevaluated()
{
CreateTestProject(@"d:\projects\MyProject\MyProject\MyProject.csproj");
CreateProjectSystem(project);
string targetPath = @"d:\projects\MyProject\packages\Foo.0.1\build\Foo.targets";
projectSystem.AddImport(targetPath, ProjectImportLocation.Bottom);
Assert.IsTrue(projectSystem.IsReevaluateProjectIfNecessaryCalled);
}
[Test]
public void RemoveImport_ImportAlreadyAddedToBottomOfProject_ImportRemoved()
{
CreateTestProject(@"d:\projects\MyProject\MyProject\MyProject.csproj");
CreateProjectSystem(project);
string targetPath = @"d:\projects\MyProject\packages\Foo.0.1\build\Foo.targets";
projectSystem.AddImport(targetPath, ProjectImportLocation.Bottom);
projectSystem.RemoveImport(targetPath);
Assert.AreEqual(0, project.GetImports().Count);
}
[Test]
public void RemoveImport_ImportAlreadyWithDifferentCaseAddedToBottomOfProject_ImportRemoved()
{
CreateTestProject(@"d:\projects\MyProject\MyProject\MyProject.csproj");
CreateProjectSystem(project);
string targetPath1 = @"d:\projects\MyProject\packages\Foo.0.1\build\Foo.targets";
projectSystem.AddImport(targetPath1, ProjectImportLocation.Bottom);
string targetPath2 = @"d:\projects\MyProject\packages\Foo.0.1\BUILD\FOO.TARGETS";
projectSystem.RemoveImport(targetPath2);
Assert.AreEqual(0, project.GetImports().Count);
}
[Test]
public void RemoveImport_DifferentImportAdded_ExceptionNotThrown()
{
CreateTestProject(@"d:\projects\MyProject\MyProject\MyProject.csproj");
CreateProjectSystem(project);
string targetPath1 = @"d:\projects\MyProject\packages\Foo.0.1\build\Foo.targets";
projectSystem.AddImport(targetPath1, ProjectImportLocation.Bottom);
string targetPath2 = @"d:\projects\MyProject\packages\Bar.0.1\build\Bar.targets";
Assert.DoesNotThrow(() => projectSystem.RemoveImport(targetPath2));
Assert.AreEqual(1, project.GetImports().Count);
}
[Test]
public void RemoveImport_NoImportsAdded_ProjectIsSaved()
{
CreateTestProject(@"d:\projects\MyProject\MyProject\MyProject.csproj");
CreateProjectSystem(project);
projectSystem.RemoveImport("Unknown.targets");
Assert.IsTrue(project.IsSaved);
}
[Test]
public void RemoveImport_NoImportsAdded_ProjectIsReevaluated()
{
CreateTestProject(@"d:\projects\MyProject\MyProject\MyProject.csproj");
CreateProjectSystem(project);
projectSystem.RemoveImport("Unknown.targets");
Assert.IsTrue(projectSystem.IsReevaluateProjectIfNecessaryCalled);
}
[Test]
public void AddImport_AddToTopOfProject_ImportAddedAsFirstChildElement()
{
CreateTestProject(@"d:\projects\MyProject\MyProject\MyProject.csproj");
CreateProjectSystem(project);
string targetPath = @"d:\projects\MyProject\packages\Foo.0.1\build\Foo.targets";
projectSystem.AddImport(targetPath, ProjectImportLocation.Top);
AssertFirstMSBuildChildElementHasProjectAttributeValue(@"..\packages\Foo.0.1\build\Foo.targets");
}
[Test]
public void AddImport_AddImportToTopOfProject_ImportAddedWithConditionThatChecksForExistenceOfTargetsFile()
{
CreateTestProject(@"d:\projects\MyProject\MyProject\MyProject.csproj");
CreateProjectSystem(project);
string targetPath = @"d:\projects\MyProject\packages\Foo.0.1\build\Foo.targets";
projectSystem.AddImport(targetPath, ProjectImportLocation.Top);
AssertFirstMSBuildChildHasCondition("Exists('..\\packages\\Foo.0.1\\build\\Foo.targets')");
}
[Test]
public void AddImport_AddToTopOfProjectTwice_ImportAddedOnlyOnce()
{
CreateTestProject(@"d:\projects\MyProject\MyProject\MyProject.csproj");
CreateProjectSystem(project);
string targetPath = @"d:\projects\MyProject\packages\Foo.0.1\build\Foo.targets";
projectSystem.AddImport(targetPath, ProjectImportLocation.Top);
projectSystem.AddImport(targetPath, ProjectImportLocation.Top);
Assert.AreEqual(1, project.GetImports().Count);
}
}
}

2
src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs

@ -335,7 +335,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -335,7 +335,7 @@ namespace ICSharpCode.SharpDevelop.Project
}
}
protected void ReevaluateIfNecessary()
public void ReevaluateIfNecessary()
{
using (var c = OpenCurrentConfiguration()) {
c.Project.ReevaluateIfNecessary();

Loading…
Cancel
Save