diff --git a/src/MSBuildProjectCreator.UnitTests/BuildOutputTests.cs b/src/MSBuildProjectCreator.UnitTests/BuildOutputTests.cs index 445c346..a001d60 100644 --- a/src/MSBuildProjectCreator.UnitTests/BuildOutputTests.cs +++ b/src/MSBuildProjectCreator.UnitTests/BuildOutputTests.cs @@ -21,7 +21,7 @@ public void ConsoleLog() eventSource.OnErrorRaised("FDC8FB4F8E084055974580DF7CD7531E", "6496288436BE4E7CAE014F163914063C", "7B07B020E38343A89B3FA844A40895E4", 1, 2, 0, 0); eventSource.OnWarningRaised("E00BBDAEEFAB45949AFEE1BF792B1691", "56206897E63F44159603D22BB7C08145", "C455F26F4D4543E78F109BCB00F02BE2", 1, 2, 0, 0); eventSource.OnMessageRaised("55B991507D52403295E92E4FFA8704F3", MessageImportance.High); - eventSource.OnMessageRaised("FA7FCCBE43B741998BAB399E74F2997D", MessageImportance.Normal); + eventSource.OnMessageRaised("FA7FCCBE43B741998BAB399E74F2997D"); eventSource.OnMessageRaised("67C0E0E52F2A45A981F3143BAF00A4A3", MessageImportance.Low); }); diff --git a/src/MSBuildProjectCreator.UnitTests/ChooseTests.cs b/src/MSBuildProjectCreator.UnitTests/ChooseTests.cs new file mode 100644 index 0000000..b0ff538 --- /dev/null +++ b/src/MSBuildProjectCreator.UnitTests/ChooseTests.cs @@ -0,0 +1,283 @@ +// Copyright (c) Jeff Kluge. All rights reserved. +// +// Licensed under the MIT license. + +using Microsoft.Build.Evaluation; +using Shouldly; +using Xunit; + +namespace Microsoft.Build.Utilities.ProjectCreation.UnitTests +{ + public class ChooseTests : MSBuildTestBase + { + [Fact] + public void ChooseComplex() + { + ProjectCreator.Create(projectFileOptions: NewProjectFileOptions.None) + .Choose() + .When("86578122B49842F891B9E4364611C4B5") + .WhenProperty("B7AF35A6A56D42EFACF4DFE1D7B0BC22", "0CA39A3C5E5348C4B5F1F50A33D8E6D8") + .WhenItemInclude("B5A8719D78D94961AFFA5134869C0DA5", "451053866BBB40B4A384EF5621994A65") + .WhenPropertyGroup("F192F2869950424BBE8FF979B12D98F8") + .WhenProperty("EB4919AA53C5457BB43F2E655469CF87", "1169C171C8F545D98691AB1DA5022763") + .When("ED64B6236EB94B639CF64DA27F4DAA21") + .WhenProperty("EA807B50DDD64BFAB67D74F34B2D091F", "8DB5478CDBC74BD1A222FE8FFA3F02A7") + .WhenItemInclude("F5624A5EE6B54AF6BBF6A03A163290A6", "4B45B6646D704CD8B06ED8401853C6F2") + .Otherwise() + .OtherwisePropertyGroup() + .OtherwiseProperty("EEE980CF390149DFB7B54B944B1F81C3", "7A886840832A4AACB0190A13E97D3502") + .OtherwiseItemGroup("84B3B0E939CE4F0CAABE555F3DD5940F") + .OtherwiseItemInclude("BDD653F0C8504129845B4CBBAD0732B1", "0887CC7FB1B842BD9B69AACD0ACBC6D3") + .OtherwiseItemGroup() + .OtherwiseItemInclude("C93C964CC1D6475BA622D37155E27398", "C005C57892AD48F8994C1003D91F8A01") + .Property("F5EA50D160D84FEA9D19049E41BD75EB", "2B50B6EF9CB148D583726948D7565E01") + .Choose() + .When("BFB1AB9E0D224C42AEB2A19A8247FD37") + .WhenProperty("DF4F395C964342C98430D7F9DD047D0F", "DCF0536D69BA4904BE25A69B1C834E92") + .WhenItemInclude("A9CA9BB3DFDE41808AA7646F44D628F3", "16405519E583482D8BAF5D7F23F13C2F") + .OtherwiseProperty("EF325D1CDDA249D396CEA0F6B6C52BFB", "8732DB2BD30E4334A9B0913DA6DA9E0F") + .OtherwiseItemInclude("A604962F0D14471C961BA98592A78ADC", "3224EC5ED0A743E2B4C32BC4E6AC4BAC") + .Xml + .ShouldBe( + @" + + + + 0CA39A3C5E5348C4B5F1F50A33D8E6D8 + + + + + + 1169C171C8F545D98691AB1DA5022763 + + + + + 8DB5478CDBC74BD1A222FE8FFA3F02A7 + + + + + + + + 7A886840832A4AACB0190A13E97D3502 + + + + + + + + + + + 2B50B6EF9CB148D583726948D7565E01 + + + + + DCF0536D69BA4904BE25A69B1C834E92 + + + + + + + + 8732DB2BD30E4334A9B0913DA6DA9E0F + + + + + + +", + StringCompareShould.IgnoreLineEndings); + } + + [Fact] + public void ChooseDuplicateOtherwiseThrows() + { + Assert.Throws(() => + ProjectCreator.Create(projectFileOptions: NewProjectFileOptions.None) + .When("775FF5802F144DEDAACE58B9490C33BA") + .Otherwise() + .Otherwise()) + .Message + .ShouldBe("You can only add one Otherwise to a Choose."); + } + + [Fact] + public void ChooseSimple() + { + ProjectCreator.Create(projectFileOptions: NewProjectFileOptions.None) + .When("9E523BB0891C418C8F65DFF9AC8DAE4D") + .WhenProperty("A3E6CF45FEC246018B4EF3DBA9194874", "782A17D1FABD4109A7AF3FBF81C2BEE4") + .OtherwiseProperty("CED127BDCF6E4CD68320A7F74DFB753E", "636471FE65DE453489594A635A64C958") + .Xml + .ShouldBe( + @" + + + + 782A17D1FABD4109A7AF3FBF81C2BEE4 + + + + + 636471FE65DE453489594A635A64C958 + + + +", + StringCompareShould.IgnoreLineEndings); + } + + [Fact] + public void OtherwiseItemGroupIncludeSimple() + { + ProjectCreator.Create(projectFileOptions: NewProjectFileOptions.None) + .When("CD76E65E1E824325A4925A194AD4F483") + .OtherwiseItemInclude("CDC2199A17E246ABA6F265152F787089", "92FE0C66858E4D7DAA74D8E0EA2DB048") + .OtherwiseItemGroup("B7C857786BF3465AB66FBFFAC1685282") + .OtherwiseItemInclude("B8F32E43B164439E9BC571B44DAAC1C6", "C49C78223E25441FBC7564E29C7DDC4F") + .Xml + .ShouldBe( + @" + + + + + + + + + + + +", + StringCompareShould.IgnoreLineEndings); + } + + [Fact] + public void OtherwiseItemGroupThowsIfNoWhen() + { + Assert.Throws(() => + ProjectCreator.Create(projectFileOptions: NewProjectFileOptions.None) + .OtherwiseItemGroup()) + .Message + .ShouldBe("You must add a When before adding an Otherwise."); + } + + [Fact] + public void OtherwiseItemIncludeSimple() + { + ProjectCreator.Create(projectFileOptions: NewProjectFileOptions.None) + .When("FC8B0F3FC5AF46A0ACC25560933CF785") + .OtherwiseItemInclude("B799C10460D94BE5984EC263CFE3D137", "35E7FB0DAF0545A0B536C356D767FA16") + .OtherwiseItemInclude("C0BC9946435E4DDDB2EA6A2D2B646887", "9903791A0A484F628894BD9CDEFFCECC") + .Xml + .ShouldBe( + @" + + + + + + + + + +", + StringCompareShould.IgnoreLineEndings); + } + + [Fact] + public void OtherwiseItemThowsIfNoWhen() + { + Assert.Throws(() => + ProjectCreator.Create(projectFileOptions: NewProjectFileOptions.None) + .OtherwiseItemInclude("DF56AA18486E493E843BF397B1524E32", "6936461B06F8470EAB28C980E50F86E2")) + .Message + .ShouldBe("You must add a When before adding an Otherwise."); + } + + [Fact] + public void OtherwisePropertyGroupThowsIfNoWhen() + { + Assert.Throws(() => + ProjectCreator.Create(projectFileOptions: NewProjectFileOptions.None) + .OtherwisePropertyGroup()) + .Message + .ShouldBe("You must add a When before adding an Otherwise."); + } + + [Fact] + public void OtherwisePropertyThowsIfNoWhen() + { + Assert.Throws(() => + ProjectCreator.Create(projectFileOptions: NewProjectFileOptions.None) + .OtherwiseProperty("C59717A7F40F40AE984A2B0DFDA165FC", "F070DA5E5F8A46619653DDE31818A23A")) + .Message + .ShouldBe("You must add a When before adding an Otherwise."); + } + + [Fact] + public void OtherwiseThowsIfNoWhen() + { + Assert.Throws(() => + ProjectCreator.Create(projectFileOptions: NewProjectFileOptions.None) + .Otherwise()) + .Message + .ShouldBe("You must add a When before adding an Otherwise."); + } + + [Fact] + public void WhenOtherwiseSimple() + { + ProjectCreator.Create(projectFileOptions: NewProjectFileOptions.None) + .When("7466B77C62134D72A9B37A7B4377EA1A") + .WhenProperty("BAEA343538474A71A3E9D9C01DABA1EE", "798AFBC1D5894E95BC7CD61D27B41CB6") + .OtherwiseProperty("BAEA343538474A71A3E9D9C01DABA1EE", "F08CB25B5C7C40AE88ADD98CC052DBD6") + .Xml + .ShouldBe( + @" + + + + 798AFBC1D5894E95BC7CD61D27B41CB6 + + + + + F08CB25B5C7C40AE88ADD98CC052DBD6 + + + +", + StringCompareShould.IgnoreLineEndings); + } + + [Fact] + public void WhenPropertyGroupThowsIfNoWhen() + { + Assert.Throws(() => + ProjectCreator.Create(projectFileOptions: NewProjectFileOptions.None) + .WhenPropertyGroup()) + .Message + .ShouldBe("You must add a When before adding a When PropertyGroup."); + } + + [Fact] + public void WhenPropertyThowsIfNoWhen() + { + Assert.Throws(() => + ProjectCreator.Create(projectFileOptions: NewProjectFileOptions.None) + .WhenProperty("A32ADB0D9F5D4BFAA927A39E1749A2A8", "A00D15F65FD34166BE03764D642ADD2A")) + .Message + .ShouldBe("You must add a When before adding a When PropertyGroup."); + } + } +} \ No newline at end of file diff --git a/src/MSBuildProjectCreator.UnitTests/CreationTests.cs b/src/MSBuildProjectCreator.UnitTests/CreationTests.cs deleted file mode 100644 index e2b844c..0000000 --- a/src/MSBuildProjectCreator.UnitTests/CreationTests.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) Jeff Kluge. All rights reserved. -// -// Licensed under the MIT license. - -using Microsoft.Build.Evaluation; -using Shouldly; -using System.Collections.Generic; -using Xunit; - -namespace Microsoft.Build.Utilities.ProjectCreation.UnitTests -{ - public class CreationTests : MSBuildTestBase - { - [Fact] - public void GlobalPropertiesArePassedThrough() - { - ProjectCollection projectCollection = new ProjectCollection(new Dictionary - { - ["Property1"] = "5DFF776EBCFF4173B0E14160C2191402" - }); - - ProjectCreator creator = ProjectCreator.Create(projectCollection: projectCollection); - - creator.ProjectCollection.ShouldBe(projectCollection); - - creator.Project.ProjectCollection.ShouldBe(projectCollection); - - creator.Project.GlobalProperties.ShouldBe(projectCollection.GlobalProperties); - - creator.Project.GetPropertyValue("Property1").ShouldBe("5DFF776EBCFF4173B0E14160C2191402"); - } - } -} diff --git a/src/MSBuildProjectCreator.UnitTests/ProjectTests.cs b/src/MSBuildProjectCreator.UnitTests/ProjectTests.cs new file mode 100644 index 0000000..900baa6 --- /dev/null +++ b/src/MSBuildProjectCreator.UnitTests/ProjectTests.cs @@ -0,0 +1,100 @@ +// Copyright (c) Jeff Kluge. All rights reserved. +// +// Licensed under the MIT license. + +using Microsoft.Build.Evaluation; +using Microsoft.Build.Exceptions; +using Shouldly; +using System; +using System.Collections.Generic; +using Xunit; + +namespace Microsoft.Build.Utilities.ProjectCreation.UnitTests +{ + public class ProjectTests : MSBuildTestBase + { + [Fact] + public void ProjectIsReEvaluated() + { + ProjectCollection projectCollection = new ProjectCollection(); + + ProjectCreator creator = ProjectCreator.Create(projectCollection: projectCollection); + + creator.Project.GetPropertyValue("Property1").ShouldBe(String.Empty); + + creator.Project.SetGlobalProperty("Property1", "8AD6F0530E774E468DBBD5B4143A1B1D"); + + creator.Project.GetPropertyValue("Property1").ShouldBe("8AD6F0530E774E468DBBD5B4143A1B1D"); + } + + [Fact] + public void ProjectWithGlobalProperties() + { + ProjectCollection projectCollection = new ProjectCollection(new Dictionary + { + ["Property1"] = "5DFF776EBCFF4173B0E14160C2191402" + }); + + ProjectCreator creator = ProjectCreator.Create(projectCollection: projectCollection); + + creator.ProjectCollection.ShouldBeSameAs(projectCollection); + + creator.Project.ProjectCollection.ShouldBeSameAs(projectCollection); + + creator.Project.GlobalProperties.ShouldBe(projectCollection.GlobalProperties); + + creator.Project.GetPropertyValue("Property1").ShouldBe("5DFF776EBCFF4173B0E14160C2191402"); + } + + [Fact] + public void TryGetProjectBuildOutput() + { + ProjectCreator.Create() + .Property("ImportDirectoryBuildTargets", "false") + .Import(@"$(MSBuildBinPath)\Microsoft.Common.targets") + .Import(@"$(MSBuildBinPath)\Microsoft.Common.targets") + .TryGetProject(out Project _, out BuildOutput buildOutput); + + buildOutput.WarningEvents.ShouldHaveSingleItem().Code.ShouldBe("MSB4011"); + } + + [Fact] + public void TryGetProjectWithGlobalProperties() + { + ProjectCreator.Create() + .Property("Foo", "E82055CD4BBE40E58DF224A8734E75AC", setIfEmpty: true) + .TryGetProject(out Project project, new Dictionary + { + ["Foo"] = "CF8CBA9CEA034D2AB1704B11287579C8" + }); + + project.GetPropertyValue("Foo").ShouldBe("CF8CBA9CEA034D2AB1704B11287579C8"); + } + + [Fact] + public void TryGetProjectWithProjectCollection() + { + ProjectCollection expectedProjectCollection = new ProjectCollection(new Dictionary + { + ["Foo"] = "CF3478738DC04B3C9358FE0D23456BCD" + }); + + ProjectCreator.Create() + .Property("Foo", "4458994367D741719B24DE003EE4F541", setIfEmpty: true) + .TryGetProject(out Project project, projectCollection: expectedProjectCollection); + + project.ProjectCollection.ShouldBeSameAs(expectedProjectCollection); + + project.GetPropertyValue("Foo").ShouldBe("CF3478738DC04B3C9358FE0D23456BCD"); + } + + [Fact] + public void TryGetProjectWithToolsVersion() + { + Should.Throw( + () => ProjectCreator.Create().TryGetProject(out _, toolsVersion: "624491E368E24CE38F51D9D620685809")) + .Message + .ShouldStartWith("The tools version \"624491E368E24CE38F51D9D620685809\" is unrecognized. Available tools versions are"); + } + } +} \ No newline at end of file diff --git a/src/MSBuildProjectCreator.UnitTests/SdkTests.cs b/src/MSBuildProjectCreator.UnitTests/SdkTests.cs new file mode 100644 index 0000000..d3e8afb --- /dev/null +++ b/src/MSBuildProjectCreator.UnitTests/SdkTests.cs @@ -0,0 +1,60 @@ +// Copyright (c) Jeff Kluge. All rights reserved. +// +// Licensed under the MIT license. + +using Microsoft.Build.Evaluation; +using Shouldly; +using Xunit; + +namespace Microsoft.Build.Utilities.ProjectCreation.UnitTests +{ + public class SdkTests : MSBuildTestBase + { + [Fact] + public void SdkComplex() + { + ProjectCreator.Create(projectFileOptions: NewProjectFileOptions.None) + .Sdk("3F86C1A37FFF45D698B5E496B0FF2096") + .Property("D1290ECCFEF44C8A98EF6CBCC4CB2D72", "E4BB6358D96B4C0B926C76D0F1C7BE89") + .Import("1A3971D93CF74C5EA7C952184159FD8C") + .Sdk("FC46B7002BC443D8836D3566D76D362D") + .Xml + .ShouldBe( + @" + + + E4BB6358D96B4C0B926C76D0F1C7BE89 + + + +", + StringCompareShould.IgnoreLineEndings); + } + + [Fact] + public void SdkSimple() + { + ProjectCreator.Create(projectFileOptions: NewProjectFileOptions.None) + .Sdk("8CFA5E8611DB4F12963FF495C43E015D") + .Xml + .ShouldBe( + @" + +", + StringCompareShould.IgnoreLineEndings); + } + + [Fact] + public void SdkWithVersion() + { + ProjectCreator.Create(projectFileOptions: NewProjectFileOptions.None) + .Sdk("9104FA3137FB4581B337262DCBDBAD6A", "AA99A15EABF84EB2A6F23A9495A1D160") + .Xml + .ShouldBe( + @" + +", + StringCompareShould.IgnoreLineEndings); + } + } +} \ No newline at end of file diff --git a/src/MSBuildProjectCreator.UnitTests/TargetTests.cs b/src/MSBuildProjectCreator.UnitTests/TargetTests.cs index b307822..8a97f88 100644 --- a/src/MSBuildProjectCreator.UnitTests/TargetTests.cs +++ b/src/MSBuildProjectCreator.UnitTests/TargetTests.cs @@ -119,6 +119,22 @@ public void TargetSimple() .ShouldBe( @" +", + StringCompareShould.IgnoreLineEndings); + } + + [Fact] + public void TargetWithOnError() + { + ProjectCreator.Create(projectFileOptions: NewProjectFileOptions.None) + .Target("80A389DEE56C4DFE9D81CCC9B176C09E") + .TargetOnError("CA51741A444D4B718A83E2364FE7DC98", "2F8BF0791F8F45AFB2F8D9338ECFDBA1") + .Xml + .ShouldBe( + @" + + + ", StringCompareShould.IgnoreLineEndings); } diff --git a/src/MSBuildProjectCreator/MSBuildAssemblyResolver.cs b/src/MSBuildProjectCreator/MSBuildAssemblyResolver.cs index dd74bff..92ea993 100644 --- a/src/MSBuildProjectCreator/MSBuildAssemblyResolver.cs +++ b/src/MSBuildProjectCreator/MSBuildAssemblyResolver.cs @@ -4,7 +4,6 @@ using System; using System.IO; -using System.Linq; using System.Reflection; namespace Microsoft.Build.Utilities.ProjectCreation @@ -14,11 +13,6 @@ namespace Microsoft.Build.Utilities.ProjectCreation /// public static class MSBuildAssemblyResolver { - /// - /// The MSBuild public key token b03f5f7f11d50a3a. - /// - private static readonly byte[] MicrosoftPublicKeyToken = { 0xB0, 0x3F, 0x5F, 0x7F, 0x11, 0xD5, 0x0A, 0x3A }; - private static readonly Lazy MSBuildDirectoryLazy = new Lazy( () => { @@ -63,7 +57,7 @@ public static Assembly AssemblyResolve(object sender, ResolveEventArgs args) { AssemblyName assemblyName = new AssemblyName(args.Name); - FileInfo fileInfo = new FileInfo(Path.Combine(MSBuildDirectoryLazy.Value, $"{assemblyName.Name}.dll")); + FileInfo fileInfo = new FileInfo(Path.Combine(MSBuildPath, $"{assemblyName.Name}.dll")); if (!fileInfo.Exists) { diff --git a/src/MSBuildProjectCreator/ProjectCreator.Choose.cs b/src/MSBuildProjectCreator/ProjectCreator.Choose.cs new file mode 100644 index 0000000..b827901 --- /dev/null +++ b/src/MSBuildProjectCreator/ProjectCreator.Choose.cs @@ -0,0 +1,288 @@ +// Copyright (c) Jeff Kluge. All rights reserved. +// +// Licensed under the MIT license. + +using Microsoft.Build.Construction; +using Microsoft.Build.Utilities.ProjectCreation.Resources; +using System.Collections.Generic; + +namespace Microsoft.Build.Utilities.ProjectCreation +{ + public partial class ProjectCreator + { + private ProjectChooseElement _lastChoose; + private ProjectItemGroupElement _lastOtherwiseItemGroup; + private ProjectPropertyGroupElement _lastOtherwisePropertyGroup; + private ProjectWhenElement _lastWhen; + private ProjectItemGroupElement _lastWhenItemGroup; + private ProjectPropertyGroupElement _lastWhenPropertyGroup; + + /// + /// Gets the last <Choose /> element that was added. + /// + protected ProjectChooseElement LastChoose + { + get + { + if (_lastChoose == null) + { + Choose(); + } + + return _lastChoose; + } + } + + /// + /// Adds a <Choose /> element to the current project. + /// + /// The current . + public ProjectCreator Choose() + { + _lastChoose = AddTopLevelElement(RootElement.CreateChooseElement()); + + _lastOtherwiseItemGroup = null; + _lastOtherwisePropertyGroup = null; + _lastWhen = null; + _lastWhenItemGroup = null; + _lastWhenPropertyGroup = null; + + return this; + } + + /// + /// Adds an <Otherwise /> element to the current project. + /// + /// The current . + /// A <When /> element has not been added + /// -or- + /// An <Otherwise /> has already been added to the current <Choose /> element. + public ProjectCreator Otherwise() + { + if (_lastWhen == null) + { + throw new ProjectCreatorException(Strings.ErrorOtherwiseRequresWhen); + } + + if (_lastChoose.OtherwiseElement != null) + { + throw new ProjectCreatorException(Strings.ErrorOtherwiseCanOnlyBeSetOnce); + } + + LastChoose.AppendChild(RootElement.CreateOtherwiseElement()); + + return this; + } + + /// + /// Adds an <ItemGroup /> element to the current <Otherwise /> element. + /// + /// An optional condition to add to the item group. + /// The current . + /// A <When /> element has not been added. + public ProjectCreator OtherwiseItemGroup(string condition = null) + { + if (_lastWhen == null) + { + throw new ProjectCreatorException(Strings.ErrorOtherwiseRequresWhen); + } + + if (_lastChoose.OtherwiseElement == null) + { + Otherwise(); + } + + _lastOtherwiseItemGroup = ItemGroup(_lastChoose.OtherwiseElement, condition); + + return this; + } + + /// + /// Adds an item to the current item group within the current <Otherwise /> element. + /// + /// The type of the item to add. + /// The file or wildcard to include in the list of items. + /// An optional file or wildcard to exclude from the list of items. + /// An optional containing metadata for the item. + /// An optional condition to add to the item. + /// The current . + /// A <When /> element has not been added. + public ProjectCreator OtherwiseItemInclude( + string itemType, + string include, + string exclude = null, + IDictionary metadata = null, + string condition = null) + { + if (_lastWhen == null) + { + throw new ProjectCreatorException(Strings.ErrorOtherwiseRequresWhen); + } + + if (_lastOtherwiseItemGroup == null) + { + OtherwiseItemGroup(); + } + + return Item( + itemGroup: _lastOtherwiseItemGroup, + itemType: itemType, + include: include, + exclude: exclude, + metadata: metadata, + remove: null, + update: null, + condition: condition); + } + + /// + /// Adds a property to the current <PropertyGroup /> element within the current <Otherwise /> element. + /// + /// The name of the property. + /// The unevaluated value of the property. + /// An optional condition to add to the property. + /// An optional value indicating whether or not a condition should be added that checks if the property has already been set. + /// The current . + /// + /// The parameter will add a condition such as " '$(Property)' == '' " which will only set the property if it has not already been set. + /// + public ProjectCreator OtherwiseProperty(string name, string unevaluatedValue, string condition = null, bool setIfEmpty = false) + { + if (_lastOtherwisePropertyGroup == null) + { + OtherwisePropertyGroup(); + } + + Property(_lastOtherwisePropertyGroup, name, unevaluatedValue, condition, setIfEmpty); + + return this; + } + + /// + /// Adds a <PropertyGroup /> element to the current <Otherwise /> element. + /// + /// An optional condition to add to the property group. + /// The current . + public ProjectCreator OtherwisePropertyGroup(string condition = null) + { + if (LastChoose.OtherwiseElement == null) + { + Otherwise(); + } + + _lastOtherwisePropertyGroup = PropertyGroup(LastChoose.OtherwiseElement, condition); + + return this; + } + + /// + /// Adds a <When /> element to the current <Choose /> element. + /// + /// An optional condition to add to the <When /> element. + /// The current . + public ProjectCreator When(string condition) + { + ProjectChooseElement lastChoose = LastChoose; + + _lastWhen = RootElement.CreateWhenElement(condition); + + lastChoose.AppendChild(_lastWhen); + + _lastWhenPropertyGroup = null; + _lastWhenItemGroup = null; + + return this; + } + + /// + /// Adds an <ItemGroup /> element to the current <When /> element. + /// + /// An optional condition to add to the item group. + /// The current . + /// A <When /> element has not been added. + public ProjectCreator WhenItemGroup(string condition = null) + { + if (_lastWhen == null) + { + throw new ProjectCreatorException(Strings.ErrorWhenPropertyGroupRequiresWhen); + } + + _lastWhenItemGroup = ItemGroup(_lastWhen, condition); + + return this; + } + + /// + /// Adds an item to the current item group within the current <When /> element. + /// + /// The type of the item to add. + /// The file or wildcard to include in the list of items. + /// An optional file or wildcard to exclude from the list of items. + /// An optional containing metadata for the item. + /// An optional condition to add to the item. + /// The current . + public ProjectCreator WhenItemInclude( + string itemType, + string include, + string exclude = null, + IDictionary metadata = null, + string condition = null) + { + if (_lastWhenItemGroup == null) + { + WhenItemGroup(); + } + + return Item( + itemGroup: _lastWhenItemGroup, + itemType: itemType, + include: include, + exclude: exclude, + metadata: metadata, + remove: null, + update: null, + condition: condition); + } + + /// + /// Adds a property to the current <PropertyGroup /> element within the current <When /> element. + /// + /// The name of the property. + /// The unevaluated value of the property. + /// An optional condition to add to the property. + /// An optional value indicating whether or not a condition should be added that checks if the property has already been set. + /// The current . + /// + /// The parameter will add a condition such as " '$(Property)' == '' " which will only set the property if it has not already been set. + /// + public ProjectCreator WhenProperty(string name, string unevaluatedValue, string condition = null, bool setIfEmpty = false) + { + if (_lastWhenPropertyGroup == null) + { + WhenPropertyGroup(); + } + + Property(_lastWhenPropertyGroup, name, unevaluatedValue, condition, setIfEmpty); + + return this; + } + + /// + /// Adds a <PropertyGroup /> element to the current <When /> element. + /// + /// An optional condition to add to the property group. + /// The current . + /// A <When /> element has not been added. + public ProjectCreator WhenPropertyGroup(string condition = null) + { + if (_lastWhen == null) + { + throw new ProjectCreatorException(Strings.ErrorWhenPropertyGroupRequiresWhen); + } + + _lastWhenPropertyGroup = PropertyGroup(_lastWhen, condition); + + return this; + } + } +} \ No newline at end of file diff --git a/src/MSBuildProjectCreator/ProjectCreator.Conversions.cs b/src/MSBuildProjectCreator/ProjectCreator.Conversions.cs index 9a0e476..2dd81f2 100644 --- a/src/MSBuildProjectCreator/ProjectCreator.Conversions.cs +++ b/src/MSBuildProjectCreator/ProjectCreator.Conversions.cs @@ -4,6 +4,8 @@ using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; +using System; +using System.Xml.Linq; namespace Microsoft.Build.Utilities.ProjectCreation { @@ -18,6 +20,15 @@ public static implicit operator Project(ProjectCreator creator) return creator?.Project; } + /// + /// Defines an implicit conversion of a to a . + /// + /// A to convert. + public static implicit operator ProjectCollection(ProjectCreator creator) + { + return creator.ProjectCollection; + } + /// /// Defines an implicit conversion of a to a . /// @@ -26,5 +37,23 @@ public static implicit operator ProjectRootElement(ProjectCreator creator) { return creator.RootElement; } + + /// + /// Defines an implicit conversion of a to a . + /// + /// A to convert. + public static implicit operator String(ProjectCreator creator) + { + return creator.FullPath; + } + + /// + /// Defines an implicit conversion of a to an . + /// + /// A to convert. + public static implicit operator XDocument(ProjectCreator creator) + { + return XDocument.Parse(creator.Xml); + } } } \ No newline at end of file diff --git a/src/MSBuildProjectCreator/ProjectCreator.Imports.cs b/src/MSBuildProjectCreator/ProjectCreator.Imports.cs index 501210d..a018a73 100644 --- a/src/MSBuildProjectCreator/ProjectCreator.Imports.cs +++ b/src/MSBuildProjectCreator/ProjectCreator.Imports.cs @@ -88,7 +88,7 @@ public ProjectCreator Import(ProjectRootElement projectRootElement, string condi /// The current . public ProjectCreator ImportSdk(string project, string name, string version = null, string condition = null) { - return Import(project, condition, name, version, false); + return Import(project, condition, name, version); } } } \ No newline at end of file diff --git a/src/MSBuildProjectCreator/ProjectCreator.ItemGroups.cs b/src/MSBuildProjectCreator/ProjectCreator.ItemGroups.cs index 3736fc6..e499e11 100644 --- a/src/MSBuildProjectCreator/ProjectCreator.ItemGroups.cs +++ b/src/MSBuildProjectCreator/ProjectCreator.ItemGroups.cs @@ -42,5 +42,22 @@ public ProjectCreator ItemGroup(string condition = null) return this; } + + /// + /// Adds an <ItemGroup /> element to the specifed parent. + /// + /// A parent to add the item group to. + /// An optional condition to add to the item group. + /// The current . + protected ProjectItemGroupElement ItemGroup(ProjectElementContainer parent, string condition = null) + { + ProjectItemGroupElement itemGroup = RootElement.CreateItemGroupElement(); + + parent.AppendChild(itemGroup); + + itemGroup.Condition = condition; + + return itemGroup; + } } } \ No newline at end of file diff --git a/src/MSBuildProjectCreator/ProjectCreator.Linq.cs b/src/MSBuildProjectCreator/ProjectCreator.Linq.cs index fb70c56..63d5ff2 100644 --- a/src/MSBuildProjectCreator/ProjectCreator.Linq.cs +++ b/src/MSBuildProjectCreator/ProjectCreator.Linq.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Text; namespace Microsoft.Build.Utilities.ProjectCreation { diff --git a/src/MSBuildProjectCreator/ProjectCreator.Project.cs b/src/MSBuildProjectCreator/ProjectCreator.Project.cs new file mode 100644 index 0000000..6cd8523 --- /dev/null +++ b/src/MSBuildProjectCreator/ProjectCreator.Project.cs @@ -0,0 +1,96 @@ +// Copyright (c) Jeff Kluge. All rights reserved. +// +// Licensed under the MIT license. + +using Microsoft.Build.Evaluation; +using System; +using System.Collections.Generic; + +namespace Microsoft.Build.Utilities.ProjectCreation +{ + public partial class ProjectCreator + { + /// + /// Stores the instance used to create a object lazily. + /// + private Project _project; + + /// + /// Gets the instance for the current project. The project is re-evaluated if necessary every time this property is accessed. + /// + public Project Project + { + get + { + if (_project == null) + { + TryGetProject(out _project, ProjectCollection.GlobalProperties, String.IsNullOrEmpty(RootElement.ToolsVersion) ? null : RootElement.ToolsVersion, ProjectCollection); + } + + _project.ReevaluateIfNecessary(); + + return _project; + } + } + + /// + /// Gets a instance from the current project. + /// + /// Receives the instance. + /// Optional containing global properties. + /// Optional tools version. + /// Optional to use. Defaults to ProjectCollection.GlobalProjectCollection. + /// Optional to use. Defaults to . + /// The current . + public ProjectCreator TryGetProject( + out Project project, + IDictionary globalProperties = null, + string toolsVersion = null, + ProjectCollection projectCollection = null, + ProjectLoadSettings projectLoadSettings = ProjectLoadSettings.Default) + { + project = new Project( + RootElement, + globalProperties ?? projectCollection?.GlobalProperties, + toolsVersion, + projectCollection ?? ProjectCollection.GlobalProjectCollection, + projectLoadSettings); + + return this; + } + + /// + /// Gets a instance from the current project. + /// + /// Receives the instance. + /// Receives instance. + /// Optional containing global properties. + /// Optional tools version. + /// Optional to use. Defaults to ProjectCollection.GlobalProjectCollection. + /// Optional to use. Defaults to . + /// The current . + public ProjectCreator TryGetProject( + out Project project, + out BuildOutput buildOutput, + IDictionary globalProperties = null, + string toolsVersion = null, + ProjectCollection projectCollection = null, + ProjectLoadSettings projectLoadSettings = ProjectLoadSettings.Default) + { + buildOutput = BuildOutput.Create(); + + projectCollection = projectCollection ?? ProjectCollection ?? new ProjectCollection(); + + projectCollection.RegisterLogger(buildOutput); + + project = new Project( + RootElement, + globalProperties ?? projectCollection.GlobalProperties, + toolsVersion, + projectCollection, + projectLoadSettings); + + return this; + } + } +} \ No newline at end of file diff --git a/src/MSBuildProjectCreator/ProjectCreator.PropertyGroups.cs b/src/MSBuildProjectCreator/ProjectCreator.PropertyGroups.cs index 55c0929..910a240 100644 --- a/src/MSBuildProjectCreator/ProjectCreator.PropertyGroups.cs +++ b/src/MSBuildProjectCreator/ProjectCreator.PropertyGroups.cs @@ -42,5 +42,22 @@ public ProjectCreator PropertyGroup(string condition = null) return this; } + + /// + /// Adds a <PropertyGroup /> element to the specified . + /// + /// The parent to add the property group to. + /// An optional condition to add to the property group. + /// The that was added. + protected ProjectPropertyGroupElement PropertyGroup(ProjectElementContainer parent, string condition = null) + { + ProjectPropertyGroupElement propertyGroup = RootElement.CreatePropertyGroupElement(); + + parent.AppendChild(propertyGroup); + + propertyGroup.Condition = condition; + + return propertyGroup; + } } } \ No newline at end of file diff --git a/src/MSBuildProjectCreator/ProjectCreator.Sdk.cs b/src/MSBuildProjectCreator/ProjectCreator.Sdk.cs new file mode 100644 index 0000000..ab481f0 --- /dev/null +++ b/src/MSBuildProjectCreator/ProjectCreator.Sdk.cs @@ -0,0 +1,22 @@ +// Copyright (c) Jeff Kluge. All rights reserved. +// +// Licensed under the MIT license. + +namespace Microsoft.Build.Utilities.ProjectCreation +{ + public partial class ProjectCreator + { + /// + /// Adds an <Sdk /> element to the current project. + /// + /// The name of the SDK. + /// An optional version of the SDK. + /// The current . + public ProjectCreator Sdk(string name, string version = null) + { + AddTopLevelElement(RootElement.CreateProjectSdkElement(name, version)); + + return this; + } + } +} \ No newline at end of file diff --git a/src/MSBuildProjectCreator/ProjectCreator.Targets.cs b/src/MSBuildProjectCreator/ProjectCreator.Targets.cs index e9a0243..7ae2237 100644 --- a/src/MSBuildProjectCreator/ProjectCreator.Targets.cs +++ b/src/MSBuildProjectCreator/ProjectCreator.Targets.cs @@ -90,9 +90,7 @@ public ProjectCreator Target( /// The current . public ProjectCreator TargetItemGroup(string condition = null) { - _lastTargetItemGroup = LastTarget.AddItemGroup(); - - _lastTargetItemGroup.Condition = condition; + _lastTargetItemGroup = ItemGroup(LastTarget, condition); return this; } @@ -129,6 +127,23 @@ public ProjectCreator TargetItemInclude( condition: condition); } + /// + /// Adds an <OnError /> element to the current target. + /// + /// The targets to execute if a task fails. Separate multiple targets with semicolons. Multiple targets are executed in the order specified. + /// Condition to be evaluated. + /// The current . + public ProjectCreator TargetOnError(string executeTargets, string condition = null) + { + ProjectOnErrorElement onErrorElement = RootElement.CreateOnErrorElement(executeTargets); + + LastTarget.AppendChild(onErrorElement); + + onErrorElement.Condition = condition; + + return this; + } + /// /// Adds a property element to the current <PropertyGroup /> of the current target. A property group is automatically added if necessary. /// @@ -157,11 +172,7 @@ public ProjectCreator TargetProperty(string name, string unevaluatedValue, strin /// The current . public ProjectCreator TargetPropertyGroup(string condition = null) { - _lastTargetPropertyGroup = RootElement.CreatePropertyGroupElement(); - - LastTarget.AppendChild(_lastTargetPropertyGroup); - - _lastTargetPropertyGroup.Condition = condition; + _lastTargetPropertyGroup = PropertyGroup(LastTarget, condition); return this; } diff --git a/src/MSBuildProjectCreator/ProjectCreator.cs b/src/MSBuildProjectCreator/ProjectCreator.cs index 86b397c..ae7de14 100644 --- a/src/MSBuildProjectCreator/ProjectCreator.cs +++ b/src/MSBuildProjectCreator/ProjectCreator.cs @@ -17,11 +17,6 @@ namespace Microsoft.Build.Utilities.ProjectCreation /// public partial class ProjectCreator { - /// - /// Stores the instance used to create a object lazily. - /// - private readonly Lazy _projectLazy; - /// /// Stores the last top-level element added to the project XML. /// @@ -34,14 +29,6 @@ public partial class ProjectCreator private ProjectCreator(ProjectRootElement rootElement) { RootElement = rootElement; - - _projectLazy = new Lazy( - () => new Project( - RootElement, - ProjectCollection.GlobalProperties, - String.IsNullOrEmpty(RootElement.ToolsVersion) ? null : RootElement.ToolsVersion, - ProjectCollection), - isThreadSafe: true); } /// @@ -49,19 +36,6 @@ private ProjectCreator(ProjectRootElement rootElement) /// public string FullPath => RootElement.FullPath; - /// - /// Gets the instance for the current project. The project is re-evaluated if necessary every time this property is accessed. - /// - public Project Project - { - get - { - _projectLazy.Value.ReevaluateIfNecessary(); - - return _projectLazy.Value; - } - } - /// /// Gets the for the current project. /// diff --git a/src/MSBuildProjectCreator/Resources/Strings.Designer.cs b/src/MSBuildProjectCreator/Resources/Strings.Designer.cs index 392692c..ef824ef 100644 --- a/src/MSBuildProjectCreator/Resources/Strings.Designer.cs +++ b/src/MSBuildProjectCreator/Resources/Strings.Designer.cs @@ -10,7 +10,6 @@ namespace Microsoft.Build.Utilities.ProjectCreation.Resources { using System; - using System.Reflection; /// @@ -40,7 +39,7 @@ internal Strings() { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Build.Utilities.ProjectCreation.Resources.Strings", typeof(Strings).GetTypeInfo().Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Build.Utilities.ProjectCreation.Resources.Strings", typeof(Strings).Assembly); resourceMan = temp; } return resourceMan; @@ -61,6 +60,24 @@ internal Strings() { } } + /// + /// Looks up a localized string similar to You can only add one Otherwise to a Choose.. + /// + internal static string ErrorOtherwiseCanOnlyBeSetOnce { + get { + return ResourceManager.GetString("ErrorOtherwiseCanOnlyBeSetOnce", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You must add a When before adding an Otherwise.. + /// + internal static string ErrorOtherwiseRequresWhen { + get { + return ResourceManager.GetString("ErrorOtherwiseRequresWhen", resourceCulture); + } + } + /// /// Looks up a localized string similar to You must add a Task by before you can add an output item.. /// @@ -105,5 +122,23 @@ internal static string ErrorUsingTaskParameterRequiresUsingTask { return ResourceManager.GetString("ErrorUsingTaskParameterRequiresUsingTask", resourceCulture); } } + + /// + /// Looks up a localized string similar to You must add a When before adding a When ItemGroup.. + /// + internal static string ErrorWhenItemGroupRequiresWhen { + get { + return ResourceManager.GetString("ErrorWhenItemGroupRequiresWhen", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You must add a When before adding a When PropertyGroup.. + /// + internal static string ErrorWhenPropertyGroupRequiresWhen { + get { + return ResourceManager.GetString("ErrorWhenPropertyGroupRequiresWhen", resourceCulture); + } + } } } diff --git a/src/MSBuildProjectCreator/Resources/Strings.resx b/src/MSBuildProjectCreator/Resources/Strings.resx index c85ec7d..fe9b42e 100644 --- a/src/MSBuildProjectCreator/Resources/Strings.resx +++ b/src/MSBuildProjectCreator/Resources/Strings.resx @@ -117,7 +117,12 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + You can only add one Otherwise to a Choose. + + + You must add a When before adding an Otherwise. + You must add a Task by before you can add an output item. @@ -133,4 +138,10 @@ You must add a UsingTask before adding a UsingTask parameter. + + You must add a When before adding a When ItemGroup. + + + You must add a When before adding a When PropertyGroup. + \ No newline at end of file diff --git a/version.json b/version.json index b36402d..3bed904 100644 --- a/version.json +++ b/version.json @@ -10,7 +10,7 @@ "cloudBuild": { "setVersionVariables": true, "buildNumber": { - "enabled": false, + "enabled": true, "includeCommitId": { "when": "nonPublicReleaseOnly", "where": "buildMetadata"