From 17d063fa218fa2aa5d0e71815c52aaacede4de3a Mon Sep 17 00:00:00 2001 From: Louis DeJardin Date: Mon, 8 Feb 2010 01:09:38 -0800 Subject: [PATCH] Post-processing the compiled web project Custom msbuild task performs heavy lifting over csproj xml Project references are changed to file references, additional content files are added, and code files are copied over --HG-- branch : dev --- Orchard.proj | 21 +- src/Orchard.sln | 16 + .../MSBuild.Orchard.Tasks.Tests.csproj | 87 +++++ .../Properties/AssemblyInfo.cs | 36 +++ .../StageProjectAlterationTests.cs | 104 ++++++ .../TestData/ExtraFiles.xml | 20 ++ .../TestData/ProjectReferences.xml | 23 ++ .../TestData/SimpleWebProject.xml | 305 ++++++++++++++++++ .../TestData/TestDataFiles.cs | 49 +++ .../MSBuild.Orchard.Tasks.csproj | 63 ++++ .../Properties/AssemblyInfo.cs | 36 +++ .../StageProjectAlteration.cs | 164 ++++++++++ 12 files changed, 921 insertions(+), 3 deletions(-) create mode 100644 src/Tools/MSBuild.Orchard.Tasks.Tests/MSBuild.Orchard.Tasks.Tests.csproj create mode 100644 src/Tools/MSBuild.Orchard.Tasks.Tests/Properties/AssemblyInfo.cs create mode 100644 src/Tools/MSBuild.Orchard.Tasks.Tests/StageProjectAlterationTests.cs create mode 100644 src/Tools/MSBuild.Orchard.Tasks.Tests/TestData/ExtraFiles.xml create mode 100644 src/Tools/MSBuild.Orchard.Tasks.Tests/TestData/ProjectReferences.xml create mode 100644 src/Tools/MSBuild.Orchard.Tasks.Tests/TestData/SimpleWebProject.xml create mode 100644 src/Tools/MSBuild.Orchard.Tasks.Tests/TestData/TestDataFiles.cs create mode 100644 src/Tools/MSBuild.Orchard.Tasks/MSBuild.Orchard.Tasks.csproj create mode 100644 src/Tools/MSBuild.Orchard.Tasks/Properties/AssemblyInfo.cs create mode 100644 src/Tools/MSBuild.Orchard.Tasks/StageProjectAlteration.cs diff --git a/Orchard.proj b/Orchard.proj index e6fd5758b..f249e9a33 100644 --- a/Orchard.proj +++ b/Orchard.proj @@ -48,17 +48,19 @@ - + - + + + - + @@ -67,6 +69,19 @@ + + + + + + + + + + diff --git a/src/Orchard.sln b/src/Orchard.sln index 903d86f2c..f0297539d 100644 --- a/src/Orchard.sln +++ b/src/Orchard.sln @@ -41,6 +41,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Pages", "Orchard.We EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Setup", "Orchard.Web\Modules\Orchard.Setup\Orchard.Setup.csproj", "{8C7FCBC2-E6E1-405E-BFB5-D8D9E67A09C4}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{383DBA32-4A3E-48D1-AAC3-75377A694452}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSBuild.Orchard.Tasks", "Tools\MSBuild.Orchard.Tasks\MSBuild.Orchard.Tasks.csproj", "{5E5E7A21-C7B2-44D8-8593-2F9541AE041D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSBuild.Orchard.Tasks.Tests", "Tools\MSBuild.Orchard.Tasks.Tests\MSBuild.Orchard.Tasks.Tests.csproj", "{4AB4B5B6-277E-4FF6-B69B-7AE9E16D2A56}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -119,6 +125,14 @@ Global {8C7FCBC2-E6E1-405E-BFB5-D8D9E67A09C4}.Debug|Any CPU.Build.0 = Debug|Any CPU {8C7FCBC2-E6E1-405E-BFB5-D8D9E67A09C4}.Release|Any CPU.ActiveCfg = Release|Any CPU {8C7FCBC2-E6E1-405E-BFB5-D8D9E67A09C4}.Release|Any CPU.Build.0 = Release|Any CPU + {5E5E7A21-C7B2-44D8-8593-2F9541AE041D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5E5E7A21-C7B2-44D8-8593-2F9541AE041D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5E5E7A21-C7B2-44D8-8593-2F9541AE041D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5E5E7A21-C7B2-44D8-8593-2F9541AE041D}.Release|Any CPU.Build.0 = Release|Any CPU + {4AB4B5B6-277E-4FF6-B69B-7AE9E16D2A56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4AB4B5B6-277E-4FF6-B69B-7AE9E16D2A56}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4AB4B5B6-277E-4FF6-B69B-7AE9E16D2A56}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4AB4B5B6-277E-4FF6-B69B-7AE9E16D2A56}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -139,5 +153,7 @@ Global {5D0F00F0-26C9-4785-AD61-B85710C60EB0} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} {4A9C04A6-0986-4A92-A610-5F59FF273FB9} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} {8C7FCBC2-E6E1-405E-BFB5-D8D9E67A09C4} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} + {5E5E7A21-C7B2-44D8-8593-2F9541AE041D} = {383DBA32-4A3E-48D1-AAC3-75377A694452} + {4AB4B5B6-277E-4FF6-B69B-7AE9E16D2A56} = {383DBA32-4A3E-48D1-AAC3-75377A694452} EndGlobalSection EndGlobal diff --git a/src/Tools/MSBuild.Orchard.Tasks.Tests/MSBuild.Orchard.Tasks.Tests.csproj b/src/Tools/MSBuild.Orchard.Tasks.Tests/MSBuild.Orchard.Tasks.Tests.csproj new file mode 100644 index 000000000..e0944c5b2 --- /dev/null +++ b/src/Tools/MSBuild.Orchard.Tasks.Tests/MSBuild.Orchard.Tasks.Tests.csproj @@ -0,0 +1,87 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {4AB4B5B6-277E-4FF6-B69B-7AE9E16D2A56} + Library + Properties + MSBuild.Orchard.Tasks.Tests + MSBuild.Orchard.Tasks.Tests + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + 3.5 + + + False + ..\..\..\lib\moq\Moq.dll + + + False + ..\..\..\lib\nunit\nunit.framework.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + + {5E5E7A21-C7B2-44D8-8593-2F9541AE041D} + MSBuild.Orchard.Tasks + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Tools/MSBuild.Orchard.Tasks.Tests/Properties/AssemblyInfo.cs b/src/Tools/MSBuild.Orchard.Tasks.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..1e90812f5 --- /dev/null +++ b/src/Tools/MSBuild.Orchard.Tasks.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MSBuild.Orchard.Tasks.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft IT")] +[assembly: AssemblyProduct("MSBuild.Orchard.Tasks.Tests")] +[assembly: AssemblyCopyright("Copyright © Microsoft IT 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("db4cb512-5c00-44c9-9173-7ede47af1967")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Tools/MSBuild.Orchard.Tasks.Tests/StageProjectAlterationTests.cs b/src/Tools/MSBuild.Orchard.Tasks.Tests/StageProjectAlterationTests.cs new file mode 100644 index 000000000..2f194a630 --- /dev/null +++ b/src/Tools/MSBuild.Orchard.Tasks.Tests/StageProjectAlterationTests.cs @@ -0,0 +1,104 @@ +using System.Linq; +using System.Xml.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Moq; +using MSBuild.Orchard.Tasks.Tests.TestData; +using NUnit.Framework; + +namespace MSBuild.Orchard.Tasks.Tests { + [TestFixture] + public class StageProjectAlterationTests { + private TestDataFiles _testDataFiles; + private StageProjectAlteration _task; + private string _xmlns = "http://schemas.microsoft.com/developer/msbuild/2003"; + + [SetUp] + public void Init() { + _testDataFiles = new TestDataFiles(); + _task = new StageProjectAlteration(); + + var engine = new Mock(); + _task.BuildEngine = engine.Object; + } + + [TearDown] + public void Term() { + _testDataFiles.Dispose(); + _testDataFiles = null; + } + + [Test] + public void ClassShouldBeCallable() { + var result = _task.Execute(); + Assert.That(result, Is.False); + } + + [Test] + public void ProjectFileNameMustExist() { + _task.ProjectFileName = "no-such-file.csproj"; + var result = _task.Execute(); + Assert.That(result, Is.False); + + _task.ProjectFileName = _testDataFiles.Get("SimpleWebProject.xml"); + result = _task.Execute(); + Assert.That(result, Is.True); + } + + [Test] + public void ProjectReferencesMustChange() { + _task.ProjectFileName = _testDataFiles.Get("ProjectReferences.xml"); + var before = XDocument.Load(_task.ProjectFileName); + var result = _task.Execute(); + var after = XDocument.Load(_task.ProjectFileName); + + Assert.That(result, Is.True); + + var beforeProjectReferences = before + .Elements(XName.Get("Project", _xmlns)) + .Elements(XName.Get("ItemGroup", _xmlns)) + .Elements(XName.Get("ProjectReference", _xmlns)); + + Assert.That(beforeProjectReferences.Count(), Is.Not.EqualTo(0)); + + var afterProjectReferences = after + .Elements(XName.Get("Project", _xmlns)) + .Elements(XName.Get("ItemGroup", _xmlns)) + .Elements(XName.Get("ProjectReference", _xmlns)); + + Assert.That(afterProjectReferences.Count(), Is.EqualTo(0)); + } + + [Test] + public void ExtraFilesAreDetected() { + _task.ProjectFileName = _testDataFiles.Get("ExtraFiles.xml"); + var result = _task.Execute(); + Assert.That(result, Is.True); + + Assert.That(_task.ExtraFiles.Count(), Is.EqualTo(5)); + } + + [Test] + public void ContentFilesAreDetected() { + _task.ProjectFileName = _testDataFiles.Get("ExtraFiles.xml"); + _task.AddContentFiles = new[] { + new TaskItem("newfile.xml"), + new TaskItem("another\\newfile.txt") + }; + var result = _task.Execute(); + Assert.That(result, Is.True); + + var after = XDocument.Load(_task.ProjectFileName); + var afterIncludes = after + .Elements(XName.Get("Project", _xmlns)) + .Elements(XName.Get("ItemGroup", _xmlns)) + .Elements(XName.Get("Content", _xmlns)) + .Attributes("Include") + .Select(attr => (string)attr) + .ToArray(); + + Assert.That(afterIncludes, Has.Some.EqualTo("newfile.xml")); + Assert.That(afterIncludes, Has.Some.EqualTo("another\\newfile.txt")); + } + } +} diff --git a/src/Tools/MSBuild.Orchard.Tasks.Tests/TestData/ExtraFiles.xml b/src/Tools/MSBuild.Orchard.Tasks.Tests/TestData/ExtraFiles.xml new file mode 100644 index 000000000..015aa66af --- /dev/null +++ b/src/Tools/MSBuild.Orchard.Tasks.Tests/TestData/ExtraFiles.xml @@ -0,0 +1,20 @@ + + + + Default.aspx + ASPXCodeBehind + + + Global.asax + + + + + + + + + + + + diff --git a/src/Tools/MSBuild.Orchard.Tasks.Tests/TestData/ProjectReferences.xml b/src/Tools/MSBuild.Orchard.Tasks.Tests/TestData/ProjectReferences.xml new file mode 100644 index 000000000..9736a10de --- /dev/null +++ b/src/Tools/MSBuild.Orchard.Tasks.Tests/TestData/ProjectReferences.xml @@ -0,0 +1,23 @@ + + + + False + ..\..\lib\autofac\Autofac.dll + + + False + ..\..\lib\autofac\Autofac.Integration.Web.dll + + + + + + {2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6} + Orchard + + + {9916839C-39FC-4CEB-A5AF-89CA7E87119F} + Orchard.Core + + + diff --git a/src/Tools/MSBuild.Orchard.Tasks.Tests/TestData/SimpleWebProject.xml b/src/Tools/MSBuild.Orchard.Tasks.Tests/TestData/SimpleWebProject.xml new file mode 100644 index 000000000..025df4ecb --- /dev/null +++ b/src/Tools/MSBuild.Orchard.Tasks.Tests/TestData/SimpleWebProject.xml @@ -0,0 +1,305 @@ + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {50B779EA-EC00-4699-84C0-03B395C365D2} + {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + Orchard.Web + Orchard.Web + v3.5 + false + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\ + TRACE + prompt + 4 + + + + False + ..\..\lib\autofac\Autofac.dll + + + False + ..\..\lib\autofac\Autofac.Integration.Web.dll + + + False + ..\..\lib\fluentnhibernate\NHibernate.ByteCode.Castle.dll + + + + + 3.5 + + + 3.5 + + + 3.5 + + + False + ..\..\lib\sqlite\System.Data.SQLite.DLL + True + + + False + ..\..\lib\aspnetmvc\System.Web.Mvc.dll + True + + + 3.5 + + + + + + + + + + + + + + + Default.aspx + ASPXCodeBehind + + + Global.asax + + + + + + + + + + + + + {2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6} + Orchard + + + {9916839C-39FC-4CEB-A5AF-89CA7E87119F} + Orchard.Core + + + {63FBD4D9-E1DA-4A7B-AA6A-D6074FE50867} + Orchard.Blogs + + + {14C049FD-B35B-415A-A824-87F26B26E7FD} + Orchard.Comments + + + {67C1D3AF-A0EC-46B2-BAE1-DF1DA8E0B890} + Orchard.DevTools + + + {D9A7B330-CD22-4DA1-A95A-8DE1982AD8EB} + Orchard.Media + + + {4A9C04A6-0986-4A92-A610-5F59FF273FB9} + Orchard.Pages + + + {D10AD48F-407D-4DB5-A328-173EC7CB010F} + Orchard.Roles + + + {8C7FCBC2-E6E1-405E-BFB5-D8D9E67A09C4} + Orchard.Setup + + + {5D0F00F0-26C9-4785-AD61-B85710C60EB0} + Orchard.Tags + + + {79AED36E-ABD0-4747-93D3-8722B042454B} + Orchard.Users + + + {17C44253-65A2-4597-98C7-16EE576824B6} + Orchard.Sandbox + + + {954CA994-D204-468B-9D69-51F6AD3E1C29} + TinyMce + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(ProjectDir)\..\Manifests + + + + + + + + + + + + False + False + 30320 + / + + + False + False + + + False + + + + + \ No newline at end of file diff --git a/src/Tools/MSBuild.Orchard.Tasks.Tests/TestData/TestDataFiles.cs b/src/Tools/MSBuild.Orchard.Tasks.Tests/TestData/TestDataFiles.cs new file mode 100644 index 000000000..6f192a7b7 --- /dev/null +++ b/src/Tools/MSBuild.Orchard.Tasks.Tests/TestData/TestDataFiles.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace MSBuild.Orchard.Tasks.Tests.TestData { + public class TestDataFiles : IDisposable { + List _entries = new List(); + private readonly string _tempPath; + + public TestDataFiles() { + _tempPath = Path.GetTempFileName(); + File.Delete(_tempPath); + Directory.CreateDirectory(_tempPath); + } + + public void Dispose() { + Directory.Delete(_tempPath, true); + } + + public string Get(string name) { + if (!_entries.Any(entry => entry.Name == name)) { + var type = GetType(); + var fullPath = Path.Combine(_tempPath, name); + using (var inputStream = type.Assembly.GetManifestResourceStream(type, name)) { + if (inputStream == null) + throw new ApplicationException("Tests data not found"); + + using (var outputStream = new FileStream(fullPath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) { + var buffer = new byte[8192]; + for (; ; ) { + var size = inputStream.Read(buffer, 0, buffer.Length); + if (size < 1) + break; + outputStream.Write(buffer, 0, size); + } + } + } + _entries.Add(new Entry { Name = name, FullPath = fullPath }); + } + return _entries.Single(entry => entry.Name == name).FullPath; + } + + class Entry { + public string Name { get; set; } + public string FullPath { get; set; } + } + } +} diff --git a/src/Tools/MSBuild.Orchard.Tasks/MSBuild.Orchard.Tasks.csproj b/src/Tools/MSBuild.Orchard.Tasks/MSBuild.Orchard.Tasks.csproj new file mode 100644 index 000000000..06cb2ebfb --- /dev/null +++ b/src/Tools/MSBuild.Orchard.Tasks/MSBuild.Orchard.Tasks.csproj @@ -0,0 +1,63 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {5E5E7A21-C7B2-44D8-8593-2F9541AE041D} + Library + Properties + MSBuild.Orchard.Tasks + MSBuild.Orchard.Tasks + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + 3.5 + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + \ No newline at end of file diff --git a/src/Tools/MSBuild.Orchard.Tasks/Properties/AssemblyInfo.cs b/src/Tools/MSBuild.Orchard.Tasks/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..2b0b5cc83 --- /dev/null +++ b/src/Tools/MSBuild.Orchard.Tasks/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MSBuild.Orchard.Tasks")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft IT")] +[assembly: AssemblyProduct("MSBuild.Orchard.Tasks")] +[assembly: AssemblyCopyright("Copyright © Microsoft IT 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("e0a78419-51a9-4612-932c-4b0143bfff5d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Tools/MSBuild.Orchard.Tasks/StageProjectAlteration.cs b/src/Tools/MSBuild.Orchard.Tasks/StageProjectAlteration.cs new file mode 100644 index 000000000..f0e141d73 --- /dev/null +++ b/src/Tools/MSBuild.Orchard.Tasks/StageProjectAlteration.cs @@ -0,0 +1,164 @@ +using System; +using System.IO; +using System.Linq; +using System.Xml.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace MSBuild.Orchard.Tasks { + public class StageProjectAlteration : Task { + public string ProjectFileName { get; set; } + + public ITaskItem[] AddContentFiles { get; set; } + + [Output] + public ITaskItem[] ExtraFiles { get; set; } + + + + public override bool Execute() { + Log.LogMessage("Altering \"{0}\"", ProjectFileName); + + var context = new Context(this); + if (context.LoadProject() && + context.ChangeProjectReferencesToFileReferences() && + context.FindExtraFiles() && + context.AddContentFiles() && + context.SaveProject()) { + + Log.LogMessage("Stage project altered successfully"); + return true; + } + + Log.LogWarning("Stage project alteration failed"); + return false; + } + + class Context { + private readonly StageProjectAlteration _task; + XDocument _document; + + private const string Xmlns = "http://schemas.microsoft.com/developer/msbuild/2003"; + private static readonly XName Project = XName.Get("Project", Xmlns); + private static readonly XName ItemGroup = XName.Get("ItemGroup", Xmlns); + private static readonly XName ProjectReference = XName.Get("ProjectReference", Xmlns); + private static readonly XName Reference = XName.Get("Reference", Xmlns); + private static readonly XName Name = XName.Get("Name", Xmlns); + private static readonly XName Include = XName.Get("Include"); + private static readonly XName HintPath = XName.Get("HintPath", Xmlns); + private static readonly XName SpecificVersion = XName.Get("SpecificVersion", Xmlns); + private static readonly XName Content = XName.Get("Content", Xmlns); + private static readonly XName Compile = XName.Get("Compile", Xmlns); + private static readonly XName None = XName.Get("None", Xmlns); + + public Context(StageProjectAlteration task) { + _task = task; + } + + public bool LoadProject() { + try { + _document = XDocument.Load(_task.ProjectFileName); + return true; + } + catch (Exception) { + _task.Log.LogError("Unable to load project file"); + return false; + } + } + + public bool SaveProject() { + _document.Save(_task.ProjectFileName); + return true; + } + + public bool ChangeProjectReferencesToFileReferences() { + var projectReferences = _document + .Elements(Project) + .Elements(ItemGroup) + .Elements(ProjectReference); + + var referenceItemGroup = _document + .Elements(Project) + .Elements(ItemGroup) + .FirstOrDefault(elt => elt.Elements(Reference).Any()); + + if (referenceItemGroup == null) { + referenceItemGroup = new XElement(ItemGroup); + _document.Root.Add(referenceItemGroup); + } + + foreach (var projectReferenceName in projectReferences.Elements(Name)) { + var reference = new XElement( + Reference, + new XAttribute(Include, (string)projectReferenceName), + new XElement(SpecificVersion, "False"), + new XElement(HintPath, string.Format("bin\\{0}.dll", (string)projectReferenceName))); + referenceItemGroup.Add(reference); + } + + foreach (var projectReference in projectReferences.ToArray()) { + projectReference.Remove(); + } + + return true; + } + + public bool FindExtraFiles() { + var extraFiles = _document + .Elements(Project) + .Elements(ItemGroup) + .Elements().Where(elt => elt.Name == Compile || elt.Name == None) + .Attributes(Include) + .Select(attr => (string)attr); + + _task.ExtraFiles = extraFiles + .Select(file => { + _task.Log.LogMessage("Detected extra file \"{0}\"", file); + var item = new TaskItem(file); + item.SetMetadata("RecursiveDir", Path.GetDirectoryName(file)); + return item; + }) + .ToArray(); + + + return true; + } + + public bool AddContentFiles() { + var existingContent = _document + .Elements(Project) + .Elements(ItemGroup) + .Elements(Content) + .Attributes(Include) + .Select(attr => (string)attr); + + var contentItemGroup = _document + .Elements(Project) + .Elements(ItemGroup) + .FirstOrDefault(elt => elt.Elements(Content).Any()); + + if (contentItemGroup == null) { + contentItemGroup = new XElement(ItemGroup); + _document.Root.Add(contentItemGroup); + } + + if (_task.AddContentFiles != null) { + foreach (var addContent in _task.AddContentFiles) { + if (existingContent.Contains(addContent.ItemSpec)) { + // don't add more than once + continue; + } + _task.Log.LogMessage("Adding Content file \"{0}\"", addContent.ItemSpec); + + var content = new XElement( + Content, + new XAttribute(Include, addContent.ItemSpec)); + contentItemGroup.Add(content); + } + } + + return true; + } + } + } +}