diff options
Diffstat (limited to 'src')
21 files changed, 1903 insertions, 69 deletions
diff --git a/src/WixToolset.BuildTasks/ConvertReferences.cs b/src/WixToolset.BuildTasks/ConvertReferences.cs index fe137633..ef50c918 100644 --- a/src/WixToolset.BuildTasks/ConvertReferences.cs +++ b/src/WixToolset.BuildTasks/ConvertReferences.cs | |||
| @@ -3,13 +3,10 @@ | |||
| 3 | namespace WixToolset.BuildTasks | 3 | namespace WixToolset.BuildTasks |
| 4 | { | 4 | { |
| 5 | using System; | 5 | using System; |
| 6 | using System.Collections; | ||
| 7 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
| 8 | using System.Globalization; | ||
| 9 | using System.IO; | ||
| 10 | using System.Xml; | ||
| 11 | using Microsoft.Build.Framework; | 7 | using Microsoft.Build.Framework; |
| 12 | using Microsoft.Build.Utilities; | 8 | using Microsoft.Build.Utilities; |
| 9 | using WixToolset.Tools.Core; | ||
| 13 | 10 | ||
| 14 | /// <summary> | 11 | /// <summary> |
| 15 | /// This task assigns Culture metadata to files based on the value of the Culture attribute on the | 12 | /// This task assigns Culture metadata to files based on the value of the Culture attribute on the |
| @@ -62,7 +59,7 @@ namespace WixToolset.BuildTasks | |||
| 62 | { | 59 | { |
| 63 | Dictionary<string, string> newItemMetadeta = new Dictionary<string, string>(); | 60 | Dictionary<string, string> newItemMetadeta = new Dictionary<string, string>(); |
| 64 | 61 | ||
| 65 | if (!String.IsNullOrEmpty(item.GetMetadata(Common.DoNotHarvest))) | 62 | if (!String.IsNullOrEmpty(item.GetMetadata(ToolsCommon.DoNotHarvest))) |
| 66 | { | 63 | { |
| 67 | continue; | 64 | continue; |
| 68 | } | 65 | } |
diff --git a/src/WixToolset.BuildTasks/RefreshBundleGeneratedFile.cs b/src/WixToolset.BuildTasks/RefreshBundleGeneratedFile.cs index 5445e0cd..80305f59 100644 --- a/src/WixToolset.BuildTasks/RefreshBundleGeneratedFile.cs +++ b/src/WixToolset.BuildTasks/RefreshBundleGeneratedFile.cs | |||
| @@ -6,19 +6,16 @@ namespace WixToolset.BuildTasks | |||
| 6 | using System.Collections; | 6 | using System.Collections; |
| 7 | using System.Globalization; | 7 | using System.Globalization; |
| 8 | using System.IO; | 8 | using System.IO; |
| 9 | using System.Text.RegularExpressions; | ||
| 10 | using System.Xml; | 9 | using System.Xml; |
| 11 | using Microsoft.Build.Framework; | 10 | using Microsoft.Build.Framework; |
| 12 | using Microsoft.Build.Utilities; | 11 | using Microsoft.Build.Utilities; |
| 12 | using WixToolset.Tools.Core; | ||
| 13 | 13 | ||
| 14 | /// <summary> | 14 | /// <summary> |
| 15 | /// This task refreshes the generated file for bundle projects. | 15 | /// This task refreshes the generated file for bundle projects. |
| 16 | /// </summary> | 16 | /// </summary> |
| 17 | public class RefreshBundleGeneratedFile : Task | 17 | public class RefreshBundleGeneratedFile : Task |
| 18 | { | 18 | { |
| 19 | private static readonly Regex AddPrefix = new Regex(@"^[^a-zA-Z_]", RegexOptions.Compiled); | ||
| 20 | private static readonly Regex IllegalIdentifierCharacters = new Regex(@"[^A-Za-z0-9_\.]|\.{2,}", RegexOptions.Compiled); // non 'words' and assorted valid characters | ||
| 21 | |||
| 22 | private ITaskItem[] generatedFiles; | 19 | private ITaskItem[] generatedFiles; |
| 23 | private ITaskItem[] projectReferencePaths; | 20 | private ITaskItem[] projectReferencePaths; |
| 24 | 21 | ||
| @@ -54,14 +51,14 @@ namespace WixToolset.BuildTasks | |||
| 54 | { | 51 | { |
| 55 | ITaskItem item = this.ProjectReferencePaths[i]; | 52 | ITaskItem item = this.ProjectReferencePaths[i]; |
| 56 | 53 | ||
| 57 | if (!String.IsNullOrEmpty(item.GetMetadata(Common.DoNotHarvest))) | 54 | if (!String.IsNullOrEmpty(item.GetMetadata(ToolsCommon.DoNotHarvest))) |
| 58 | { | 55 | { |
| 59 | continue; | 56 | continue; |
| 60 | } | 57 | } |
| 61 | 58 | ||
| 62 | string projectPath = CreateProjectReferenceDefineConstants.GetProjectPath(this.ProjectReferencePaths, i); | 59 | string projectPath = CreateProjectReferenceDefineConstants.GetProjectPath(this.ProjectReferencePaths, i); |
| 63 | string projectName = Path.GetFileNameWithoutExtension(projectPath); | 60 | string projectName = Path.GetFileNameWithoutExtension(projectPath); |
| 64 | string referenceName = Common.GetIdentifierFromName(CreateProjectReferenceDefineConstants.GetReferenceName(item, projectName)); | 61 | string referenceName = ToolsCommon.GetIdentifierFromName(CreateProjectReferenceDefineConstants.GetReferenceName(item, projectName)); |
| 65 | 62 | ||
| 66 | string[] pogs = item.GetMetadata("RefProjectOutputGroups").Split(';'); | 63 | string[] pogs = item.GetMetadata("RefProjectOutputGroups").Split(';'); |
| 67 | foreach (string pog in pogs) | 64 | foreach (string pog in pogs) |
diff --git a/src/WixToolset.BuildTasks/RefreshGeneratedFile.cs b/src/WixToolset.BuildTasks/RefreshGeneratedFile.cs index fdfc4774..101b5363 100644 --- a/src/WixToolset.BuildTasks/RefreshGeneratedFile.cs +++ b/src/WixToolset.BuildTasks/RefreshGeneratedFile.cs | |||
| @@ -6,10 +6,10 @@ namespace WixToolset.BuildTasks | |||
| 6 | using System.Collections; | 6 | using System.Collections; |
| 7 | using System.Globalization; | 7 | using System.Globalization; |
| 8 | using System.IO; | 8 | using System.IO; |
| 9 | using System.Text.RegularExpressions; | ||
| 10 | using System.Xml; | 9 | using System.Xml; |
| 11 | using Microsoft.Build.Framework; | 10 | using Microsoft.Build.Framework; |
| 12 | using Microsoft.Build.Utilities; | 11 | using Microsoft.Build.Utilities; |
| 12 | using WixToolset.Tools.Core; | ||
| 13 | 13 | ||
| 14 | /// <summary> | 14 | /// <summary> |
| 15 | /// This task refreshes the generated file that contains ComponentGroupRefs | 15 | /// This task refreshes the generated file that contains ComponentGroupRefs |
| @@ -17,9 +17,6 @@ namespace WixToolset.BuildTasks | |||
| 17 | /// </summary> | 17 | /// </summary> |
| 18 | public class RefreshGeneratedFile : Task | 18 | public class RefreshGeneratedFile : Task |
| 19 | { | 19 | { |
| 20 | private static readonly Regex AddPrefix = new Regex(@"^[^a-zA-Z_]", RegexOptions.Compiled); | ||
| 21 | private static readonly Regex IllegalIdentifierCharacters = new Regex(@"[^A-Za-z0-9_\.]|\.{2,}", RegexOptions.Compiled); // non 'words' and assorted valid characters | ||
| 22 | |||
| 23 | private ITaskItem[] generatedFiles; | 20 | private ITaskItem[] generatedFiles; |
| 24 | private ITaskItem[] projectReferencePaths; | 21 | private ITaskItem[] projectReferencePaths; |
| 25 | 22 | ||
| @@ -54,14 +51,14 @@ namespace WixToolset.BuildTasks | |||
| 54 | { | 51 | { |
| 55 | ITaskItem item = this.ProjectReferencePaths[i]; | 52 | ITaskItem item = this.ProjectReferencePaths[i]; |
| 56 | 53 | ||
| 57 | if (!String.IsNullOrEmpty(item.GetMetadata(Common.DoNotHarvest))) | 54 | if (!String.IsNullOrEmpty(item.GetMetadata(ToolsCommon.DoNotHarvest))) |
| 58 | { | 55 | { |
| 59 | continue; | 56 | continue; |
| 60 | } | 57 | } |
| 61 | 58 | ||
| 62 | string projectPath = CreateProjectReferenceDefineConstants.GetProjectPath(this.ProjectReferencePaths, i); | 59 | string projectPath = CreateProjectReferenceDefineConstants.GetProjectPath(this.ProjectReferencePaths, i); |
| 63 | string projectName = Path.GetFileNameWithoutExtension(projectPath); | 60 | string projectName = Path.GetFileNameWithoutExtension(projectPath); |
| 64 | string referenceName = Common.GetIdentifierFromName(CreateProjectReferenceDefineConstants.GetReferenceName(item, projectName)); | 61 | string referenceName = ToolsCommon.GetIdentifierFromName(CreateProjectReferenceDefineConstants.GetReferenceName(item, projectName)); |
| 65 | 62 | ||
| 66 | string[] pogs = item.GetMetadata("RefProjectOutputGroups").Split(';'); | 63 | string[] pogs = item.GetMetadata("RefProjectOutputGroups").Split(';'); |
| 67 | foreach (string pog in pogs) | 64 | foreach (string pog in pogs) |
diff --git a/src/WixToolset.BuildTasks/WixToolset.BuildTasks.csproj b/src/WixToolset.BuildTasks/WixToolset.BuildTasks.csproj index 8a5c388d..39c8824c 100644 --- a/src/WixToolset.BuildTasks/WixToolset.BuildTasks.csproj +++ b/src/WixToolset.BuildTasks/WixToolset.BuildTasks.csproj | |||
| @@ -28,6 +28,10 @@ | |||
| 28 | </ItemGroup> | 28 | </ItemGroup> |
| 29 | 29 | ||
| 30 | <ItemGroup> | 30 | <ItemGroup> |
| 31 | <ProjectReference Include="..\WixToolset.Tools.Core\WixToolset.Tools.Core.csproj" /> | ||
| 32 | </ItemGroup> | ||
| 33 | |||
| 34 | <ItemGroup> | ||
| 31 | <ProjectReference Include="$(WixToolsetRootFolder)\Core\src\WixToolset.Core\WixToolset.Core.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Core\README.md') " /> | 35 | <ProjectReference Include="$(WixToolsetRootFolder)\Core\src\WixToolset.Core\WixToolset.Core.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Core\README.md') " /> |
| 32 | <PackageReference Include="WixToolset.Core" Version="4.0.*" Condition=" '$(Configuration)' == 'Release' Or !Exists('$(WixToolsetRootFolder)\Core\README.md') " /> | 36 | <PackageReference Include="WixToolset.Core" Version="4.0.*" Condition=" '$(Configuration)' == 'Release' Or !Exists('$(WixToolsetRootFolder)\Core\README.md') " /> |
| 33 | 37 | ||
diff --git a/src/WixToolset.Tools.Core/ConsoleMessageListener.cs b/src/WixToolset.Tools.Core/ConsoleMessageListener.cs new file mode 100644 index 00000000..5b1fb988 --- /dev/null +++ b/src/WixToolset.Tools.Core/ConsoleMessageListener.cs | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | using System; | ||
| 2 | using System.Globalization; | ||
| 3 | using System.Text; | ||
| 4 | using System.Threading; | ||
| 5 | using WixToolset.Data; | ||
| 6 | using WixToolset.Extensibility; | ||
| 7 | |||
| 8 | namespace WixToolset.Tools.Core | ||
| 9 | { | ||
| 10 | public sealed class ConsoleMessageListener : IMessageListener | ||
| 11 | { | ||
| 12 | public ConsoleMessageListener(string shortName, string longName) | ||
| 13 | { | ||
| 14 | this.ShortAppName = shortName; | ||
| 15 | this.LongAppName = longName; | ||
| 16 | |||
| 17 | PrepareConsoleForLocalization(); | ||
| 18 | } | ||
| 19 | |||
| 20 | public string LongAppName { get; } | ||
| 21 | |||
| 22 | public string ShortAppName { get; } | ||
| 23 | |||
| 24 | public void Write(Message message) | ||
| 25 | { | ||
| 26 | var filename = message.SourceLineNumbers?.FileName ?? this.LongAppName; | ||
| 27 | var line = message.SourceLineNumbers?.LineNumber ?? -1; | ||
| 28 | var type = message.Level.ToString().ToLowerInvariant(); | ||
| 29 | var output = message.Level >= MessageLevel.Warning ? Console.Out : Console.Error; | ||
| 30 | |||
| 31 | if (line > 0) | ||
| 32 | { | ||
| 33 | filename = String.Concat(filename, "(", line, ")"); | ||
| 34 | } | ||
| 35 | |||
| 36 | output.WriteLine("{0} : {1} {2}{3:0000}: {4}", filename, type, this.ShortAppName, message.Id, message.ToString()); | ||
| 37 | } | ||
| 38 | |||
| 39 | public void Write(string message) | ||
| 40 | { | ||
| 41 | Console.Out.WriteLine(message); | ||
| 42 | } | ||
| 43 | |||
| 44 | private static void PrepareConsoleForLocalization() | ||
| 45 | { | ||
| 46 | Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentUICulture.GetConsoleFallbackUICulture(); | ||
| 47 | |||
| 48 | if (Console.OutputEncoding.CodePage != Encoding.UTF8.CodePage && | ||
| 49 | Console.OutputEncoding.CodePage != Thread.CurrentThread.CurrentUICulture.TextInfo.OEMCodePage && | ||
| 50 | Console.OutputEncoding.CodePage != Thread.CurrentThread.CurrentUICulture.TextInfo.ANSICodePage) | ||
| 51 | { | ||
| 52 | Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US"); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
diff --git a/src/WixToolset.Tools.Core/WixToolset.Tools.Core.csproj b/src/WixToolset.Tools.Core/WixToolset.Tools.Core.csproj new file mode 100644 index 00000000..8be70e6b --- /dev/null +++ b/src/WixToolset.Tools.Core/WixToolset.Tools.Core.csproj | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> | ||
| 3 | |||
| 4 | <Project Sdk="Microsoft.NET.Sdk"> | ||
| 5 | <PropertyGroup> | ||
| 6 | <TargetFramework>netstandard2.0</TargetFramework> | ||
| 7 | <Description>Tools Core</Description> | ||
| 8 | <Title>WiX Toolset Tools Core</Title> | ||
| 9 | <DebugType>embedded</DebugType> | ||
| 10 | <PublishRepositoryUrl>true</PublishRepositoryUrl> | ||
| 11 | </PropertyGroup> | ||
| 12 | |||
| 13 | <ItemGroup> | ||
| 14 | <ProjectReference Include="$(WixToolsetRootFolder)\Data\src\WixToolset.Data\WixToolset.Data.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Data\README.md') " /> | ||
| 15 | <PackageReference Include="WixToolset.Data" Version="4.0.*" Condition=" '$(Configuration)' == 'Release' Or !Exists('$(WixToolsetRootFolder)\Data\README.md') " /> | ||
| 16 | |||
| 17 | <ProjectReference Include="$(WixToolsetRootFolder)\Extensibility\src\WixToolset.Extensibility\WixToolset.Extensibility.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Extensibility\README.md') " /> | ||
| 18 | <PackageReference Include="WixToolset.Extensibility" Version="4.0.*" Condition=" '$(Configuration)' == 'Release' Or !Exists('$(WixToolsetRootFolder)\Extensibility\README.md') " /> | ||
| 19 | |||
| 20 | <ProjectReference Include="$(WixToolsetRootFolder)\Core\src\WixToolset.Core\WixToolset.Core.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Core\README.md') " /> | ||
| 21 | <PackageReference Include="WixToolset.Core" Version="4.0.*" Condition=" '$(Configuration)' == 'Release' Or !Exists('$(WixToolsetRootFolder)\Core\README.md') " /> | ||
| 22 | </ItemGroup> | ||
| 23 | |||
| 24 | <ItemGroup> | ||
| 25 | <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta-63102-01" PrivateAssets="All"/> | ||
| 26 | <PackageReference Include="Nerdbank.GitVersioning" Version="2.1.65" PrivateAssets="All" /> | ||
| 27 | </ItemGroup> | ||
| 28 | </Project> | ||
diff --git a/src/WixToolset.BuildTasks/Common.cs b/src/Wixtoolset.Tools.Core/ToolsCommon.cs index 803e9d14..37d89f3c 100644 --- a/src/WixToolset.BuildTasks/Common.cs +++ b/src/Wixtoolset.Tools.Core/ToolsCommon.cs | |||
| @@ -1,16 +1,14 @@ | |||
| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. |
| 2 | 2 | ||
| 3 | namespace WixToolset | 3 | namespace WixToolset.Tools.Core |
| 4 | { | 4 | { |
| 5 | using System; | 5 | using System; |
| 6 | using System.Globalization; | ||
| 7 | using System.Text; | ||
| 8 | using System.Text.RegularExpressions; | 6 | using System.Text.RegularExpressions; |
| 9 | 7 | ||
| 10 | /// <summary> | 8 | /// <summary> |
| 11 | /// Common WixTasks utility methods and types. | 9 | /// Common WixTasks utility methods and types. |
| 12 | /// </summary> | 10 | /// </summary> |
| 13 | internal static class Common | 11 | public static class ToolsCommon |
| 14 | { | 12 | { |
| 15 | /// <summary>Metadata key name to turn off harvesting of project references.</summary> | 13 | /// <summary>Metadata key name to turn off harvesting of project references.</summary> |
| 16 | public const string DoNotHarvest = "DoNotHarvest"; | 14 | public const string DoNotHarvest = "DoNotHarvest"; |
| @@ -24,7 +22,7 @@ namespace WixToolset | |||
| 24 | /// <param name="name">File/directory name to generate identifer from</param> | 22 | /// <param name="name">File/directory name to generate identifer from</param> |
| 25 | /// <returns>A version of the name that is a legal identifier.</returns> | 23 | /// <returns>A version of the name that is a legal identifier.</returns> |
| 26 | /// <remarks>This is duplicated from WiX's Common class.</remarks> | 24 | /// <remarks>This is duplicated from WiX's Common class.</remarks> |
| 27 | internal static string GetIdentifierFromName(string name) | 25 | public static string GetIdentifierFromName(string name) |
| 28 | { | 26 | { |
| 29 | string result = IllegalIdentifierCharacters.Replace(name, "_"); // replace illegal characters with "_". | 27 | string result = IllegalIdentifierCharacters.Replace(name, "_"); // replace illegal characters with "_". |
| 30 | 28 | ||
diff --git a/src/test/wixcop/ConverterFixture.cs b/src/test/wixcop/ConverterFixture.cs new file mode 100644 index 00000000..45ccc33e --- /dev/null +++ b/src/test/wixcop/ConverterFixture.cs | |||
| @@ -0,0 +1,418 @@ | |||
| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
| 2 | |||
| 3 | namespace WixTest.WixUnitTest | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.IO; | ||
| 7 | using System.Text; | ||
| 8 | using System.Xml.Linq; | ||
| 9 | using WixCop; | ||
| 10 | using WixToolset; | ||
| 11 | using WixToolset.Data; | ||
| 12 | using WixToolset.Extensibility; | ||
| 13 | using WixToolset.Extensibility.Services; | ||
| 14 | using Xunit; | ||
| 15 | |||
| 16 | public class ConverterFixture | ||
| 17 | { | ||
| 18 | private static readonly XNamespace Wix4Namespace = "http://wixtoolset.org/schemas/v4/wxs"; | ||
| 19 | |||
| 20 | [Fact] | ||
| 21 | public void EnsuresDeclaration() | ||
| 22 | { | ||
| 23 | string parse = String.Join(Environment.NewLine, | ||
| 24 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 25 | " <Fragment />", | ||
| 26 | "</Wix>"); | ||
| 27 | |||
| 28 | string expected = String.Join(Environment.NewLine, | ||
| 29 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 30 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 31 | " <Fragment />", | ||
| 32 | "</Wix>"); | ||
| 33 | |||
| 34 | XDocument document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 35 | |||
| 36 | var messaging = new DummyMessaging(); | ||
| 37 | Converter converter = new Converter(messaging, 2, null, null); | ||
| 38 | |||
| 39 | int errors = converter.ConvertDocument(document); | ||
| 40 | |||
| 41 | string actual = UnformattedDocumentString(document); | ||
| 42 | |||
| 43 | Assert.Equal(1, errors); | ||
| 44 | Assert.Equal(expected, actual); | ||
| 45 | } | ||
| 46 | |||
| 47 | [Fact] | ||
| 48 | public void EnsuresUtf8Declaration() | ||
| 49 | { | ||
| 50 | string parse = String.Join(Environment.NewLine, | ||
| 51 | "<?xml version='1.0'?>", | ||
| 52 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 53 | " <Fragment />", | ||
| 54 | "</Wix>"); | ||
| 55 | |||
| 56 | XDocument document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 57 | |||
| 58 | var messaging = new DummyMessaging(); | ||
| 59 | Converter converter = new Converter(messaging, 4, null, null); | ||
| 60 | |||
| 61 | int errors = converter.ConvertDocument(document); | ||
| 62 | |||
| 63 | Assert.Equal(1, errors); | ||
| 64 | Assert.Equal("1.0", document.Declaration.Version); | ||
| 65 | Assert.Equal("utf-8", document.Declaration.Encoding); | ||
| 66 | } | ||
| 67 | |||
| 68 | [Fact] | ||
| 69 | public void CanFixWhitespace() | ||
| 70 | { | ||
| 71 | string parse = String.Join(Environment.NewLine, | ||
| 72 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 73 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 74 | " <Fragment>", | ||
| 75 | " <Property Id='Prop'", | ||
| 76 | " Value='Val'>", | ||
| 77 | " </Property>", | ||
| 78 | " </Fragment>", | ||
| 79 | "</Wix>"); | ||
| 80 | |||
| 81 | string expected = String.Join(Environment.NewLine, | ||
| 82 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 83 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 84 | " <Fragment>", | ||
| 85 | " <Property Id=\"Prop\" Value=\"Val\" />", | ||
| 86 | " </Fragment>", | ||
| 87 | "</Wix>"); | ||
| 88 | |||
| 89 | XDocument document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 90 | |||
| 91 | var messaging = new DummyMessaging(); | ||
| 92 | Converter converter = new Converter(messaging, 4, null, null); | ||
| 93 | |||
| 94 | int errors = converter.ConvertDocument(document); | ||
| 95 | |||
| 96 | string actual = UnformattedDocumentString(document); | ||
| 97 | |||
| 98 | Assert.Equal(4, errors); | ||
| 99 | Assert.Equal(expected, actual); | ||
| 100 | } | ||
| 101 | |||
| 102 | [Fact] | ||
| 103 | public void CanFixCdataWhitespace() | ||
| 104 | { | ||
| 105 | string parse = String.Join(Environment.NewLine, | ||
| 106 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 107 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 108 | " <Fragment>", | ||
| 109 | " <Property Id='Prop'>", | ||
| 110 | " <![CDATA[1<2]]>", | ||
| 111 | " </Property>", | ||
| 112 | " </Fragment>", | ||
| 113 | "</Wix>"); | ||
| 114 | |||
| 115 | string expected = String.Join(Environment.NewLine, | ||
| 116 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 117 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 118 | " <Fragment>", | ||
| 119 | " <Property Id=\"Prop\"><![CDATA[1<2]]></Property>", | ||
| 120 | " </Fragment>", | ||
| 121 | "</Wix>"); | ||
| 122 | |||
| 123 | XDocument document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 124 | |||
| 125 | var messaging = new DummyMessaging(); | ||
| 126 | Converter converter = new Converter(messaging, 2, null, null); | ||
| 127 | |||
| 128 | int errors = converter.ConvertDocument(document); | ||
| 129 | |||
| 130 | string actual = UnformattedDocumentString(document); | ||
| 131 | |||
| 132 | Assert.Equal(2, errors); | ||
| 133 | Assert.Equal(expected, actual); | ||
| 134 | } | ||
| 135 | |||
| 136 | [Fact] | ||
| 137 | public void CanConvertMainNamespace() | ||
| 138 | { | ||
| 139 | string parse = String.Join(Environment.NewLine, | ||
| 140 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 141 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 142 | " <Fragment />", | ||
| 143 | "</Wix>"); | ||
| 144 | |||
| 145 | string expected = String.Join(Environment.NewLine, | ||
| 146 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 147 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 148 | " <Fragment />", | ||
| 149 | "</Wix>"); | ||
| 150 | |||
| 151 | XDocument document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 152 | |||
| 153 | var messaging = new DummyMessaging(); | ||
| 154 | Converter converter = new Converter(messaging, 2, null, null); | ||
| 155 | |||
| 156 | int errors = converter.ConvertDocument(document); | ||
| 157 | |||
| 158 | string actual = UnformattedDocumentString(document); | ||
| 159 | |||
| 160 | Assert.Equal(1, errors); | ||
| 161 | //Assert.Equal(Wix4Namespace, document.Root.GetDefaultNamespace()); | ||
| 162 | Assert.Equal(expected, actual); | ||
| 163 | } | ||
| 164 | |||
| 165 | [Fact] | ||
| 166 | public void CanConvertNamedMainNamespace() | ||
| 167 | { | ||
| 168 | string parse = String.Join(Environment.NewLine, | ||
| 169 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 170 | "<w:Wix xmlns:w='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 171 | " <w:Fragment />", | ||
| 172 | "</w:Wix>"); | ||
| 173 | |||
| 174 | string expected = String.Join(Environment.NewLine, | ||
| 175 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 176 | "<w:Wix xmlns:w=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 177 | " <w:Fragment />", | ||
| 178 | "</w:Wix>"); | ||
| 179 | |||
| 180 | XDocument document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 181 | |||
| 182 | var messaging = new DummyMessaging(); | ||
| 183 | Converter converter = new Converter(messaging, 2, null, null); | ||
| 184 | |||
| 185 | int errors = converter.ConvertDocument(document); | ||
| 186 | |||
| 187 | string actual = UnformattedDocumentString(document); | ||
| 188 | |||
| 189 | Assert.Equal(1, errors); | ||
| 190 | Assert.Equal(expected, actual); | ||
| 191 | Assert.Equal(Wix4Namespace, document.Root.GetNamespaceOfPrefix("w")); | ||
| 192 | } | ||
| 193 | |||
| 194 | [Fact] | ||
| 195 | public void CanConvertNonWixDefaultNamespace() | ||
| 196 | { | ||
| 197 | string parse = String.Join(Environment.NewLine, | ||
| 198 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 199 | "<w:Wix xmlns:w='http://schemas.microsoft.com/wix/2006/wi' xmlns='http://schemas.microsoft.com/wix/UtilExtension'>", | ||
| 200 | " <w:Fragment>", | ||
| 201 | " <Test />", | ||
| 202 | " </w:Fragment>", | ||
| 203 | "</w:Wix>"); | ||
| 204 | |||
| 205 | string expected = String.Join(Environment.NewLine, | ||
| 206 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 207 | "<w:Wix xmlns:w=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns=\"http://wixtoolset.org/schemas/v4/wxs/util\">", | ||
| 208 | " <w:Fragment>", | ||
| 209 | " <Test />", | ||
| 210 | " </w:Fragment>", | ||
| 211 | "</w:Wix>"); | ||
| 212 | |||
| 213 | XDocument document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 214 | |||
| 215 | var messaging = new DummyMessaging(); | ||
| 216 | Converter converter = new Converter(messaging, 2, null, null); | ||
| 217 | |||
| 218 | int errors = converter.ConvertDocument(document); | ||
| 219 | |||
| 220 | string actual = UnformattedDocumentString(document); | ||
| 221 | |||
| 222 | Assert.Equal(2, errors); | ||
| 223 | Assert.Equal(expected, actual); | ||
| 224 | Assert.Equal(Wix4Namespace, document.Root.GetNamespaceOfPrefix("w")); | ||
| 225 | Assert.Equal("http://wixtoolset.org/schemas/v4/wxs/util", document.Root.GetDefaultNamespace()); | ||
| 226 | } | ||
| 227 | |||
| 228 | [Fact] | ||
| 229 | public void CanConvertExtensionNamespace() | ||
| 230 | { | ||
| 231 | string parse = String.Join(Environment.NewLine, | ||
| 232 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 233 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:util='http://schemas.microsoft.com/wix/UtilExtension'>", | ||
| 234 | " <Fragment />", | ||
| 235 | "</Wix>"); | ||
| 236 | |||
| 237 | string expected = String.Join(Environment.NewLine, | ||
| 238 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 239 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:util=\"http://wixtoolset.org/schemas/v4/wxs/util\">", | ||
| 240 | " <Fragment />", | ||
| 241 | "</Wix>"); | ||
| 242 | |||
| 243 | XDocument document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 244 | |||
| 245 | var messaging = new DummyMessaging(); | ||
| 246 | Converter converter = new Converter(messaging, 2, null, null); | ||
| 247 | |||
| 248 | int errors = converter.ConvertDocument(document); | ||
| 249 | |||
| 250 | string actual = UnformattedDocumentString(document); | ||
| 251 | |||
| 252 | Assert.Equal(2, errors); | ||
| 253 | Assert.Equal(expected, actual); | ||
| 254 | Assert.Equal(Wix4Namespace, document.Root.GetDefaultNamespace()); | ||
| 255 | } | ||
| 256 | |||
| 257 | [Fact] | ||
| 258 | public void CanConvertMissingNamespace() | ||
| 259 | { | ||
| 260 | string parse = String.Join(Environment.NewLine, | ||
| 261 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 262 | "<Wix>", | ||
| 263 | " <Fragment />", | ||
| 264 | "</Wix>"); | ||
| 265 | |||
| 266 | string expected = String.Join(Environment.NewLine, | ||
| 267 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 268 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 269 | " <Fragment />", | ||
| 270 | "</Wix>"); | ||
| 271 | |||
| 272 | XDocument document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 273 | |||
| 274 | var messaging = new DummyMessaging(); | ||
| 275 | Converter converter = new Converter(messaging, 2, null, null); | ||
| 276 | |||
| 277 | int errors = converter.ConvertDocument(document); | ||
| 278 | |||
| 279 | string actual = UnformattedDocumentString(document); | ||
| 280 | |||
| 281 | Assert.Equal(1, errors); | ||
| 282 | Assert.Equal(expected, actual); | ||
| 283 | Assert.Equal(Wix4Namespace, document.Root.GetDefaultNamespace()); | ||
| 284 | } | ||
| 285 | |||
| 286 | [Fact] | ||
| 287 | public void CanConvertAnonymousFile() | ||
| 288 | { | ||
| 289 | string parse = String.Join(Environment.NewLine, | ||
| 290 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 291 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 292 | " <File Source='path\\to\\foo.txt' />", | ||
| 293 | "</Wix>"); | ||
| 294 | |||
| 295 | string expected = String.Join(Environment.NewLine, | ||
| 296 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 297 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 298 | " <File Id=\"foo.txt\" Source=\"path\\to\\foo.txt\" />", | ||
| 299 | "</Wix>"); | ||
| 300 | |||
| 301 | XDocument document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 302 | |||
| 303 | var messaging = new DummyMessaging(); | ||
| 304 | Converter converter = new Converter(messaging, 2, null, null); | ||
| 305 | |||
| 306 | int errors = converter.ConvertDocument(document); | ||
| 307 | |||
| 308 | string actual = UnformattedDocumentString(document); | ||
| 309 | |||
| 310 | Assert.Equal(1, errors); | ||
| 311 | Assert.Equal(expected, actual); | ||
| 312 | } | ||
| 313 | |||
| 314 | [Fact] | ||
| 315 | public void CanConvertSuppressSignatureValidationNo() | ||
| 316 | { | ||
| 317 | string parse = String.Join(Environment.NewLine, | ||
| 318 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 319 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 320 | " <MsiPackage SuppressSignatureValidation='no' />", | ||
| 321 | "</Wix>"); | ||
| 322 | |||
| 323 | string expected = String.Join(Environment.NewLine, | ||
| 324 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 325 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 326 | " <MsiPackage EnableSignatureValidation=\"yes\" />", | ||
| 327 | "</Wix>"); | ||
| 328 | |||
| 329 | XDocument document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 330 | |||
| 331 | var messaging = new DummyMessaging(); | ||
| 332 | Converter converter = new Converter(messaging, 2, null, null); | ||
| 333 | |||
| 334 | int errors = converter.ConvertDocument(document); | ||
| 335 | |||
| 336 | string actual = UnformattedDocumentString(document); | ||
| 337 | |||
| 338 | Assert.Equal(1, errors); | ||
| 339 | Assert.Equal(expected, actual); | ||
| 340 | } | ||
| 341 | |||
| 342 | [Fact] | ||
| 343 | public void CanConvertSuppressSignatureValidationYes() | ||
| 344 | { | ||
| 345 | string parse = String.Join(Environment.NewLine, | ||
| 346 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 347 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 348 | " <Payload SuppressSignatureValidation='yes' />", | ||
| 349 | "</Wix>"); | ||
| 350 | |||
| 351 | string expected = String.Join(Environment.NewLine, | ||
| 352 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 353 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 354 | " <Payload />", | ||
| 355 | "</Wix>"); | ||
| 356 | |||
| 357 | XDocument document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 358 | |||
| 359 | var messaging = new DummyMessaging(); | ||
| 360 | Converter converter = new Converter(messaging, 2, null, null); | ||
| 361 | |||
| 362 | int errors = converter.ConvertDocument(document); | ||
| 363 | |||
| 364 | string actual = UnformattedDocumentString(document); | ||
| 365 | |||
| 366 | Assert.Equal(1, errors); | ||
| 367 | Assert.Equal(expected, actual); | ||
| 368 | } | ||
| 369 | |||
| 370 | private static string UnformattedDocumentString(XDocument document) | ||
| 371 | { | ||
| 372 | StringBuilder sb = new StringBuilder(); | ||
| 373 | |||
| 374 | using (StringWriter writer = new StringWriter(sb)) | ||
| 375 | { | ||
| 376 | document.Save(writer, SaveOptions.DisableFormatting | SaveOptions.OmitDuplicateNamespaces); | ||
| 377 | } | ||
| 378 | |||
| 379 | return sb.ToString(); | ||
| 380 | } | ||
| 381 | |||
| 382 | private class DummyMessaging : IMessaging | ||
| 383 | { | ||
| 384 | public bool EncounteredError { get; set; } | ||
| 385 | |||
| 386 | public int LastErrorNumber { get; set; } | ||
| 387 | |||
| 388 | public bool ShowVerboseMessages { get; set; } | ||
| 389 | public bool SuppressAllWarnings { get; set; } | ||
| 390 | public bool WarningsAsError { get; set; } | ||
| 391 | |||
| 392 | public void ElevateWarningMessage(int warningNumber) | ||
| 393 | { | ||
| 394 | } | ||
| 395 | |||
| 396 | public string FormatMessage(Message message) | ||
| 397 | { | ||
| 398 | return ""; | ||
| 399 | } | ||
| 400 | |||
| 401 | public void SetListener(IMessageListener listener) | ||
| 402 | { | ||
| 403 | } | ||
| 404 | |||
| 405 | public void SuppressWarningMessage(int warningNumber) | ||
| 406 | { | ||
| 407 | } | ||
| 408 | |||
| 409 | public void Write(Message message) | ||
| 410 | { | ||
| 411 | } | ||
| 412 | |||
| 413 | public void Write(string message, bool verbose = false) | ||
| 414 | { | ||
| 415 | } | ||
| 416 | } | ||
| 417 | } | ||
| 418 | } | ||
diff --git a/src/test/wixcop/TestData/SingleFile/ConvertedSingleFile.wxs b/src/test/wixcop/TestData/SingleFile/ConvertedSingleFile.wxs new file mode 100644 index 00000000..aacb68fa --- /dev/null +++ b/src/test/wixcop/TestData/SingleFile/ConvertedSingleFile.wxs | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> | ||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | <?include WixVer.wxi ?> | ||
| 7 | |||
| 8 | <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:swid="http://wixtoolset.org/schemas/v4/wxs/tag" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util"> | ||
| 9 | <Product Id="*" Name="!(loc.ShortProduct) v$(var.WixMajorMinor) Core" Language="1033" Manufacturer="!(loc.Company)" Version="$(var.WixMsiProductVersion)" UpgradeCode="3618724B-2523-44F9-A908-866AA619504D"> | ||
| 10 | <Package Compressed="yes" InstallerVersion="200" SummaryCodepage="1252" InstallScope="perMachine" /> | ||
| 11 | <swid:Tag Regid="!(loc.Regid)" InstallDirectory="INSTALLFOLDER" /> | ||
| 12 | |||
| 13 | <MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed." /> | ||
| 14 | |||
| 15 | <MediaTemplate CabinetTemplate="core{0}.cab" /> | ||
| 16 | |||
| 17 | <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1"> | ||
| 18 | <Component Id="Licensing" Directory="INSTALLFOLDER"> | ||
| 19 | <File Id="LICENSE.TXT" Source="LICENSE.TXT" /> | ||
| 20 | </Component> | ||
| 21 | |||
| 22 | <Component Id="ProductRegistration" Directory="INSTALLFOLDER"> | ||
| 23 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> | ||
| 24 | <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" /> | ||
| 25 | </RegistryKey> | ||
| 26 | </Component> | ||
| 27 | |||
| 28 | <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER"> | ||
| 29 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x"> | ||
| 30 | <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" /> | ||
| 31 | </RegistryKey> | ||
| 32 | </Component> | ||
| 33 | |||
| 34 | <Component Id="ProductInformation" Directory="BinFolder"> | ||
| 35 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> | ||
| 36 | <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string" /> | ||
| 37 | <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" /> | ||
| 38 | </RegistryKey> | ||
| 39 | |||
| 40 | <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" /> | ||
| 41 | </Component> | ||
| 42 | |||
| 43 | <Component Directory="BinFolder"> | ||
| 44 | <File Id="wixtoolset.org.ico" Source="common\wixtoolset.org.ico" /> | ||
| 45 | <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" /> | ||
| 46 | </Component> | ||
| 47 | |||
| 48 | <ComponentGroupRef Id="ToolsetComponents" /> | ||
| 49 | <ComponentGroupRef Id="ExtensionComponents" /> | ||
| 50 | <ComponentGroupRef Id="LuxComponents" /> | ||
| 51 | <ComponentGroupRef Id="DocComponents" /> | ||
| 52 | </Feature> | ||
| 53 | |||
| 54 | <FeatureRef Id="Feature_MSBuild" /> | ||
| 55 | <FeatureRef Id="Feature_Intellisense2010" /> | ||
| 56 | <FeatureRef Id="Feature_Intellisense2012" /> | ||
| 57 | <FeatureRef Id="Feature_Intellisense2013" /> | ||
| 58 | <FeatureRef Id="Feature_Intellisense2015" /> | ||
| 59 | </Product> | ||
| 60 | </Wix> | ||
diff --git a/src/test/wixcop/TestData/SingleFile/SingleFile.wxs b/src/test/wixcop/TestData/SingleFile/SingleFile.wxs new file mode 100644 index 00000000..310ae811 --- /dev/null +++ b/src/test/wixcop/TestData/SingleFile/SingleFile.wxs | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> | ||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | <?include WixVer.wxi ?> | ||
| 7 | |||
| 8 | <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:swid="http://schemas.microsoft.com/wix/TagExtension" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension"> | ||
| 9 | <Product Id="*" Name="!(loc.ShortProduct) v$(var.WixMajorMinor) Core" Language="1033" Manufacturer="!(loc.Company)" | ||
| 10 | Version="$(var.WixMsiProductVersion)" UpgradeCode="3618724B-2523-44F9-A908-866AA619504D"> | ||
| 11 | <Package Compressed="yes" InstallerVersion="200" SummaryCodepage="1252" InstallScope="perMachine" /> | ||
| 12 | <swid:Tag Regid="!(loc.Regid)" InstallDirectory="INSTALLFOLDER" /> | ||
| 13 | |||
| 14 | <MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed." /> | ||
| 15 | |||
| 16 | <MediaTemplate CabinetTemplate="core{0}.cab" /> | ||
| 17 | |||
| 18 | <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1"> | ||
| 19 | <Component Id="Licensing" Directory="INSTALLFOLDER"> | ||
| 20 | <File Source="LICENSE.TXT" /> | ||
| 21 | </Component> | ||
| 22 | |||
| 23 | <Component Id="ProductRegistration" Directory="INSTALLFOLDER"> | ||
| 24 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> | ||
| 25 | <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" /> | ||
| 26 | </RegistryKey> | ||
| 27 | </Component> | ||
| 28 | |||
| 29 | <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER"> | ||
| 30 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x"> | ||
| 31 | <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" /> | ||
| 32 | </RegistryKey> | ||
| 33 | </Component> | ||
| 34 | |||
| 35 | <Component Id="ProductInformation" Directory="BinFolder"> | ||
| 36 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> | ||
| 37 | <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string"/> | ||
| 38 | <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" /> | ||
| 39 | </RegistryKey> | ||
| 40 | |||
| 41 | <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" /> | ||
| 42 | </Component> | ||
| 43 | |||
| 44 | <Component Directory="BinFolder"> | ||
| 45 | <File Source="common\wixtoolset.org.ico" /> | ||
| 46 | <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" /> | ||
| 47 | </Component> | ||
| 48 | |||
| 49 | <ComponentGroupRef Id="ToolsetComponents" /> | ||
| 50 | <ComponentGroupRef Id="ExtensionComponents" /> | ||
| 51 | <ComponentGroupRef Id="LuxComponents" /> | ||
| 52 | <ComponentGroupRef Id="DocComponents" /> | ||
| 53 | </Feature> | ||
| 54 | |||
| 55 | <FeatureRef Id="Feature_MSBuild" /> | ||
| 56 | <FeatureRef Id="Feature_Intellisense2010" /> | ||
| 57 | <FeatureRef Id="Feature_Intellisense2012" /> | ||
| 58 | <FeatureRef Id="Feature_Intellisense2013" /> | ||
| 59 | <FeatureRef Id="Feature_Intellisense2015" /> | ||
| 60 | </Product> | ||
| 61 | </Wix> | ||
diff --git a/src/test/wixcop/WixCopFixture.cs b/src/test/wixcop/WixCopFixture.cs new file mode 100644 index 00000000..12863959 --- /dev/null +++ b/src/test/wixcop/WixCopFixture.cs | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | using System; | ||
| 2 | using System.Collections.Generic; | ||
| 3 | using System.IO; | ||
| 4 | using System.Linq; | ||
| 5 | using WixBuildTools.TestSupport; | ||
| 6 | using WixCop.CommandLine; | ||
| 7 | using WixCop.Interfaces; | ||
| 8 | using WixToolset.Core; | ||
| 9 | using WixToolset.Core.TestPackage; | ||
| 10 | using WixToolset.Extensibility; | ||
| 11 | using WixToolset.Extensibility.Services; | ||
| 12 | using Xunit; | ||
| 13 | |||
| 14 | namespace WixCopTests | ||
| 15 | { | ||
| 16 | public class WixCopFixture | ||
| 17 | { | ||
| 18 | [Fact] | ||
| 19 | public void CanConvertSingleFile() | ||
| 20 | { | ||
| 21 | const string beforeFileName = "SingleFile.wxs"; | ||
| 22 | const string afterFileName = "ConvertedSingleFile.wxs"; | ||
| 23 | var folder = TestData.Get(@"TestData\SingleFile"); | ||
| 24 | |||
| 25 | using (var fs = new DisposableFileSystem()) | ||
| 26 | { | ||
| 27 | var baseFolder = fs.GetFolder(true); | ||
| 28 | var targetFile = Path.Combine(baseFolder, beforeFileName); | ||
| 29 | File.Copy(Path.Combine(folder, beforeFileName), Path.Combine(baseFolder, beforeFileName)); | ||
| 30 | |||
| 31 | var runner = new WixCopRunner | ||
| 32 | { | ||
| 33 | FixErrors = true, | ||
| 34 | SearchPatterns = | ||
| 35 | { | ||
| 36 | targetFile, | ||
| 37 | }, | ||
| 38 | }; | ||
| 39 | |||
| 40 | var result = runner.Execute(out var messages); | ||
| 41 | |||
| 42 | Assert.Equal(2, result); | ||
| 43 | |||
| 44 | var actualLines = File.ReadAllLines(targetFile); | ||
| 45 | var expectedLines = File.ReadAllLines(Path.Combine(folder, afterFileName)); | ||
| 46 | Assert.Equal(expectedLines, actualLines); | ||
| 47 | |||
| 48 | var runner2 = new WixCopRunner | ||
| 49 | { | ||
| 50 | FixErrors = true, | ||
| 51 | SearchPatterns = | ||
| 52 | { | ||
| 53 | targetFile, | ||
| 54 | }, | ||
| 55 | }; | ||
| 56 | |||
| 57 | var result2 = runner2.Execute(out var messages2); | ||
| 58 | |||
| 59 | Assert.Equal(0, result2); | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | private class WixCopRunner | ||
| 64 | { | ||
| 65 | public bool FixErrors { get; set; } | ||
| 66 | |||
| 67 | public List<string> SearchPatterns { get; } = new List<string>(); | ||
| 68 | |||
| 69 | public int Execute(out List<string> messages) | ||
| 70 | { | ||
| 71 | var argList = new List<string>(); | ||
| 72 | if (this.FixErrors) | ||
| 73 | { | ||
| 74 | argList.Add("-f"); | ||
| 75 | } | ||
| 76 | |||
| 77 | foreach (string searchPattern in this.SearchPatterns) | ||
| 78 | { | ||
| 79 | argList.Add(searchPattern); | ||
| 80 | } | ||
| 81 | |||
| 82 | return WixCopRunner.Execute(argList.ToArray(), out messages); | ||
| 83 | } | ||
| 84 | |||
| 85 | public static int Execute(string[] args, out List<string> messages) | ||
| 86 | { | ||
| 87 | var listener = new TestMessageListener(); | ||
| 88 | |||
| 89 | var serviceProvider = new WixToolsetServiceProvider(); | ||
| 90 | serviceProvider.AddService<IMessageListener>((x, y) => listener); | ||
| 91 | serviceProvider.AddService<IWixCopCommandLineParser>((x, y) => new WixCopCommandLineParser(x)); | ||
| 92 | |||
| 93 | var result = Execute(serviceProvider, args); | ||
| 94 | |||
| 95 | var messaging = serviceProvider.GetService<IMessaging>(); | ||
| 96 | messages = listener.Messages.Select(x => messaging.FormatMessage(x)).ToList(); | ||
| 97 | return result; | ||
| 98 | } | ||
| 99 | |||
| 100 | public static int Execute(IServiceProvider serviceProvider, string[] args) | ||
| 101 | { | ||
| 102 | var wixcop = new WixCop.Program(); | ||
| 103 | return wixcop.Run(serviceProvider, args); | ||
| 104 | } | ||
| 105 | } | ||
| 106 | } | ||
| 107 | } | ||
diff --git a/src/test/wixcop/WixCopTests.csproj b/src/test/wixcop/WixCopTests.csproj new file mode 100644 index 00000000..0ae50dc8 --- /dev/null +++ b/src/test/wixcop/WixCopTests.csproj | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> | ||
| 3 | |||
| 4 | <Project Sdk="Microsoft.NET.Sdk"> | ||
| 5 | <PropertyGroup> | ||
| 6 | <TargetFramework>net461</TargetFramework> | ||
| 7 | <IsPackable>false</IsPackable> | ||
| 8 | <DebugType>embedded</DebugType> | ||
| 9 | </PropertyGroup> | ||
| 10 | <ItemGroup> | ||
| 11 | <None Remove="TestData\SingleFile\ConvertedSingleFile.wxs" /> | ||
| 12 | <None Remove="TestData\SingleFile\SingleFile.wxs" /> | ||
| 13 | </ItemGroup> | ||
| 14 | <ItemGroup> | ||
| 15 | <Content Include="TestData\SingleFile\ConvertedSingleFile.wxs"> | ||
| 16 | <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
| 17 | </Content> | ||
| 18 | <Content Include="TestData\SingleFile\SingleFile.wxs"> | ||
| 19 | <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
| 20 | </Content> | ||
| 21 | </ItemGroup> | ||
| 22 | |||
| 23 | <ItemGroup> | ||
| 24 | <ProjectReference Include="..\..\wixcop\WixCop.csproj" /> | ||
| 25 | </ItemGroup> | ||
| 26 | |||
| 27 | <ItemGroup> | ||
| 28 | <ProjectReference Include="$(WixToolsetRootFolder)\Core\src\WixToolset.Core.TestPackage\WixToolset.Core.TestPackage.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Core\README.md') " /> | ||
| 29 | <PackageReference Include="WixToolset.Core.TestPackage" Version="4.0.*" Condition=" '$(Configuration)' == 'Release' Or !Exists('$(WixToolsetRootFolder)\Core\README.md') " /> | ||
| 30 | </ItemGroup> | ||
| 31 | |||
| 32 | <ItemGroup> | ||
| 33 | <PackageReference Include="WixBuildTools.TestSupport" Version="4.0.*" /> | ||
| 34 | </ItemGroup> | ||
| 35 | |||
| 36 | <ItemGroup> | ||
| 37 | <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" /> | ||
| 38 | <PackageReference Include="xunit" Version="2.4.0" /> | ||
| 39 | <PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" /> | ||
| 40 | </ItemGroup> | ||
| 41 | </Project> | ||
diff --git a/src/wix/Program.cs b/src/wix/Program.cs index a95851ee..b0e3bb73 100644 --- a/src/wix/Program.cs +++ b/src/wix/Program.cs | |||
| @@ -13,6 +13,7 @@ namespace WixToolset.Tools | |||
| 13 | using WixToolset.Extensibility; | 13 | using WixToolset.Extensibility; |
| 14 | using WixToolset.Extensibility.Data; | 14 | using WixToolset.Extensibility.Data; |
| 15 | using WixToolset.Extensibility.Services; | 15 | using WixToolset.Extensibility.Services; |
| 16 | using WixToolset.Tools.Core; | ||
| 16 | 17 | ||
| 17 | /// <summary> | 18 | /// <summary> |
| 18 | /// Wix Toolset Command-Line Interface. | 19 | /// Wix Toolset Command-Line Interface. |
| @@ -79,52 +80,5 @@ namespace WixToolset.Tools | |||
| 79 | 80 | ||
| 80 | return extensionManager; | 81 | return extensionManager; |
| 81 | } | 82 | } |
| 82 | |||
| 83 | private class ConsoleMessageListener : IMessageListener | ||
| 84 | { | ||
| 85 | public ConsoleMessageListener(string shortName, string longName) | ||
| 86 | { | ||
| 87 | this.ShortAppName = shortName; | ||
| 88 | this.LongAppName = longName; | ||
| 89 | |||
| 90 | PrepareConsoleForLocalization(); | ||
| 91 | } | ||
| 92 | |||
| 93 | public string LongAppName { get; } | ||
| 94 | |||
| 95 | public string ShortAppName { get; } | ||
| 96 | |||
| 97 | public void Write(Message message) | ||
| 98 | { | ||
| 99 | var filename = message.SourceLineNumbers?.FileName ?? this.LongAppName; | ||
| 100 | var line = message.SourceLineNumbers?.LineNumber ?? -1; | ||
| 101 | var type = message.Level.ToString().ToLowerInvariant(); | ||
| 102 | var output = message.Level >= MessageLevel.Warning ? Console.Out : Console.Error; | ||
| 103 | |||
| 104 | if (line > 0) | ||
| 105 | { | ||
| 106 | filename = String.Concat(filename, "(", line, ")"); | ||
| 107 | } | ||
| 108 | |||
| 109 | output.WriteLine("{0} : {1} {2}{3:0000}: {4}", filename, type, this.ShortAppName, message.Id, message.ToString()); | ||
| 110 | } | ||
| 111 | |||
| 112 | public void Write(string message) | ||
| 113 | { | ||
| 114 | Console.Out.WriteLine(message); | ||
| 115 | } | ||
| 116 | |||
| 117 | private static void PrepareConsoleForLocalization() | ||
| 118 | { | ||
| 119 | Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentUICulture.GetConsoleFallbackUICulture(); | ||
| 120 | |||
| 121 | if (Console.OutputEncoding.CodePage != Encoding.UTF8.CodePage && | ||
| 122 | Console.OutputEncoding.CodePage != Thread.CurrentThread.CurrentUICulture.TextInfo.OEMCodePage && | ||
| 123 | Console.OutputEncoding.CodePage != Thread.CurrentThread.CurrentUICulture.TextInfo.ANSICodePage) | ||
| 124 | { | ||
| 125 | Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US"); | ||
| 126 | } | ||
| 127 | } | ||
| 128 | } | ||
| 129 | } | 83 | } |
| 130 | } | 84 | } |
diff --git a/src/wix/wix.csproj b/src/wix/wix.csproj index 6ec22ec0..2cbcdf3a 100644 --- a/src/wix/wix.csproj +++ b/src/wix/wix.csproj | |||
| @@ -17,6 +17,10 @@ | |||
| 17 | </PropertyGroup> | 17 | </PropertyGroup> |
| 18 | 18 | ||
| 19 | <ItemGroup> | 19 | <ItemGroup> |
| 20 | <ProjectReference Include="..\WixToolset.Tools.Core\WixToolset.Tools.Core.csproj" /> | ||
| 21 | </ItemGroup> | ||
| 22 | |||
| 23 | <ItemGroup> | ||
| 20 | <ProjectReference Include="$(WixToolsetRootFolder)\Core\src\WixToolset.Core\WixToolset.Core.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Core\README.md') " /> | 24 | <ProjectReference Include="$(WixToolsetRootFolder)\Core\src\WixToolset.Core\WixToolset.Core.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Core\README.md') " /> |
| 21 | <PackageReference Include="WixToolset.Core" Version="4.0.*" Condition=" '$(Configuration)' == 'Release' Or !Exists('$(WixToolsetRootFolder)\Core\README.md') " /> | 25 | <PackageReference Include="WixToolset.Core" Version="4.0.*" Condition=" '$(Configuration)' == 'Release' Or !Exists('$(WixToolsetRootFolder)\Core\README.md') " /> |
| 22 | 26 | ||
diff --git a/src/wixcop/CommandLine/ConvertCommand.cs b/src/wixcop/CommandLine/ConvertCommand.cs new file mode 100644 index 00000000..6af7d4ca --- /dev/null +++ b/src/wixcop/CommandLine/ConvertCommand.cs | |||
| @@ -0,0 +1,212 @@ | |||
| 1 | using System; | ||
| 2 | using System.Collections.Generic; | ||
| 3 | using System.IO; | ||
| 4 | using System.Linq; | ||
| 5 | using System.Xml; | ||
| 6 | using WixToolset.Extensibility.Data; | ||
| 7 | using WixToolset.Extensibility.Services; | ||
| 8 | |||
| 9 | namespace WixCop.CommandLine | ||
| 10 | { | ||
| 11 | internal class ConvertCommand : ICommandLineCommand | ||
| 12 | { | ||
| 13 | private const string SettingsFileDefault = "wixcop.settings.xml"; | ||
| 14 | |||
| 15 | public ConvertCommand(IServiceProvider serviceProvider, bool fixErrors, int indentationAmount, List<string> searchPatterns, bool subDirectories, string settingsFile1, string settingsFile2) | ||
| 16 | { | ||
| 17 | this.ErrorsAsWarnings = new HashSet<string>(); | ||
| 18 | this.ExemptFiles = new HashSet<string>(); | ||
| 19 | this.FixErrors = fixErrors; | ||
| 20 | this.IndentationAmount = indentationAmount; | ||
| 21 | this.IgnoreErrors = new HashSet<string>(); | ||
| 22 | this.SearchPatternResults = new HashSet<string>(); | ||
| 23 | this.SearchPatterns = searchPatterns; | ||
| 24 | this.ServiceProvider = serviceProvider; | ||
| 25 | this.SettingsFile1 = settingsFile1; | ||
| 26 | this.SettingsFile2 = settingsFile2; | ||
| 27 | this.SubDirectories = subDirectories; | ||
| 28 | } | ||
| 29 | |||
| 30 | private HashSet<string> ErrorsAsWarnings { get; } | ||
| 31 | |||
| 32 | private HashSet<string> ExemptFiles { get; } | ||
| 33 | |||
| 34 | private bool FixErrors { get; } | ||
| 35 | |||
| 36 | private int IndentationAmount { get; } | ||
| 37 | |||
| 38 | private HashSet<string> IgnoreErrors { get; } | ||
| 39 | |||
| 40 | private HashSet<string> SearchPatternResults { get; } | ||
| 41 | |||
| 42 | private List<string> SearchPatterns { get; } | ||
| 43 | |||
| 44 | private IServiceProvider ServiceProvider { get; } | ||
| 45 | |||
| 46 | private string SettingsFile1 { get; } | ||
| 47 | |||
| 48 | private string SettingsFile2 { get; } | ||
| 49 | |||
| 50 | private bool SubDirectories { get; } | ||
| 51 | |||
| 52 | public int Execute() | ||
| 53 | { | ||
| 54 | // parse the settings if any were specified | ||
| 55 | if (null != this.SettingsFile1 || null != this.SettingsFile2) | ||
| 56 | { | ||
| 57 | this.ParseSettingsFiles(this.SettingsFile1, this.SettingsFile2); | ||
| 58 | } | ||
| 59 | else | ||
| 60 | { | ||
| 61 | if (File.Exists(ConvertCommand.SettingsFileDefault)) | ||
| 62 | { | ||
| 63 | this.ParseSettingsFiles(ConvertCommand.SettingsFileDefault, null); | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | var messaging = this.ServiceProvider.GetService<IMessaging>(); | ||
| 68 | var converter = new Converter(messaging, this.IndentationAmount, this.ErrorsAsWarnings, this.IgnoreErrors); | ||
| 69 | |||
| 70 | var errors = this.InspectSubDirectories(converter, Path.GetFullPath(".")); | ||
| 71 | |||
| 72 | foreach (string searchPattern in this.SearchPatterns) | ||
| 73 | { | ||
| 74 | if (!this.SearchPatternResults.Contains(searchPattern)) | ||
| 75 | { | ||
| 76 | Console.Error.WriteLine("Could not find file \"{0}\"", searchPattern); | ||
| 77 | errors++; | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | return errors != 0 ? 2 : 0; | ||
| 82 | } | ||
| 83 | |||
| 84 | /// <summary> | ||
| 85 | /// Get the files that match a search path pattern. | ||
| 86 | /// </summary> | ||
| 87 | /// <param name="baseDir">The base directory at which to begin the search.</param> | ||
| 88 | /// <param name="searchPath">The search path pattern.</param> | ||
| 89 | /// <returns>The files matching the pattern.</returns> | ||
| 90 | private static string[] GetFiles(string baseDir, string searchPath) | ||
| 91 | { | ||
| 92 | // convert alternate directory separators to the standard one | ||
| 93 | var filePath = searchPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); | ||
| 94 | var lastSeparator = filePath.LastIndexOf(Path.DirectorySeparatorChar); | ||
| 95 | string[] files = null; | ||
| 96 | |||
| 97 | try | ||
| 98 | { | ||
| 99 | if (0 > lastSeparator) | ||
| 100 | { | ||
| 101 | files = Directory.GetFiles(baseDir, filePath); | ||
| 102 | } | ||
| 103 | else // found directory separator | ||
| 104 | { | ||
| 105 | var searchPattern = filePath.Substring(lastSeparator + 1); | ||
| 106 | |||
| 107 | files = Directory.GetFiles(filePath.Substring(0, lastSeparator + 1), searchPattern); | ||
| 108 | } | ||
| 109 | } | ||
| 110 | catch (DirectoryNotFoundException) | ||
| 111 | { | ||
| 112 | // don't let this function throw the DirectoryNotFoundException. (this exception | ||
| 113 | // occurs for non-existant directories and invalid characters in the searchPattern) | ||
| 114 | } | ||
| 115 | |||
| 116 | return files; | ||
| 117 | } | ||
| 118 | |||
| 119 | /// <summary> | ||
| 120 | /// Inspect sub-directories. | ||
| 121 | /// </summary> | ||
| 122 | /// <param name="directory">The directory whose sub-directories will be inspected.</param> | ||
| 123 | /// <returns>The number of errors that were found.</returns> | ||
| 124 | private int InspectSubDirectories(Converter converter, string directory) | ||
| 125 | { | ||
| 126 | var errors = 0; | ||
| 127 | |||
| 128 | foreach (var searchPattern in this.SearchPatterns) | ||
| 129 | { | ||
| 130 | foreach (var sourceFilePath in GetFiles(directory, searchPattern)) | ||
| 131 | { | ||
| 132 | var file = new FileInfo(sourceFilePath); | ||
| 133 | |||
| 134 | if (!this.ExemptFiles.Contains(file.Name.ToUpperInvariant())) | ||
| 135 | { | ||
| 136 | this.SearchPatternResults.Add(searchPattern); | ||
| 137 | errors += converter.ConvertFile(file.FullName, this.FixErrors); | ||
| 138 | } | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | if (this.SubDirectories) | ||
| 143 | { | ||
| 144 | foreach (var childDirectoryPath in Directory.GetDirectories(directory)) | ||
| 145 | { | ||
| 146 | errors += this.InspectSubDirectories(converter, childDirectoryPath); | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | return errors; | ||
| 151 | } | ||
| 152 | |||
| 153 | /// <summary> | ||
| 154 | /// Parse the primary and secondary settings files. | ||
| 155 | /// </summary> | ||
| 156 | /// <param name="localSettingsFile1">The primary settings file.</param> | ||
| 157 | /// <param name="localSettingsFile2">The secondary settings file.</param> | ||
| 158 | private void ParseSettingsFiles(string localSettingsFile1, string localSettingsFile2) | ||
| 159 | { | ||
| 160 | if (null == localSettingsFile1 && null != localSettingsFile2) | ||
| 161 | { | ||
| 162 | throw new ArgumentException("Cannot specify a secondary settings file (set2) without a primary settings file (set1).", "localSettingsFile2"); | ||
| 163 | } | ||
| 164 | |||
| 165 | var settingsFile = localSettingsFile1; | ||
| 166 | while (null != settingsFile) | ||
| 167 | { | ||
| 168 | XmlTextReader reader = null; | ||
| 169 | try | ||
| 170 | { | ||
| 171 | reader = new XmlTextReader(settingsFile); | ||
| 172 | var doc = new XmlDocument(); | ||
| 173 | doc.Load(reader); | ||
| 174 | |||
| 175 | // get the types of tests that will have their errors displayed as warnings | ||
| 176 | var testsIgnoredElements = doc.SelectNodes("/Settings/IgnoreErrors/Test"); | ||
| 177 | foreach (XmlElement test in testsIgnoredElements) | ||
| 178 | { | ||
| 179 | var key = test.GetAttribute("Id"); | ||
| 180 | this.IgnoreErrors.Add(key); | ||
| 181 | } | ||
| 182 | |||
| 183 | // get the types of tests that will have their errors displayed as warnings | ||
| 184 | var testsAsWarningsElements = doc.SelectNodes("/Settings/ErrorsAsWarnings/Test"); | ||
| 185 | foreach (XmlElement test in testsAsWarningsElements) | ||
| 186 | { | ||
| 187 | var key = test.GetAttribute("Id"); | ||
| 188 | this.ErrorsAsWarnings.Add(key); | ||
| 189 | } | ||
| 190 | |||
| 191 | // get the exempt files | ||
| 192 | var localExemptFiles = doc.SelectNodes("/Settings/ExemptFiles/File"); | ||
| 193 | foreach (XmlElement file in localExemptFiles) | ||
| 194 | { | ||
| 195 | var key = file.GetAttribute("Name").ToUpperInvariant(); | ||
| 196 | this.ExemptFiles.Add(key); | ||
| 197 | } | ||
| 198 | } | ||
| 199 | finally | ||
| 200 | { | ||
| 201 | if (null != reader) | ||
| 202 | { | ||
| 203 | reader.Close(); | ||
| 204 | } | ||
| 205 | } | ||
| 206 | |||
| 207 | settingsFile = localSettingsFile2; | ||
| 208 | localSettingsFile2 = null; | ||
| 209 | } | ||
| 210 | } | ||
| 211 | } | ||
| 212 | } | ||
diff --git a/src/wixcop/CommandLine/HelpCommand.cs b/src/wixcop/CommandLine/HelpCommand.cs new file mode 100644 index 00000000..a75dac5c --- /dev/null +++ b/src/wixcop/CommandLine/HelpCommand.cs | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | using System; | ||
| 2 | using WixToolset.Extensibility.Data; | ||
| 3 | |||
| 4 | namespace WixCop.CommandLine | ||
| 5 | { | ||
| 6 | internal class HelpCommand : ICommandLineCommand | ||
| 7 | { | ||
| 8 | public int Execute() | ||
| 9 | { | ||
| 10 | Console.WriteLine(" usage: wixcop.exe sourceFile [sourceFile ...]"); | ||
| 11 | Console.WriteLine(); | ||
| 12 | Console.WriteLine(" -f fix errors automatically for writable files"); | ||
| 13 | Console.WriteLine(" -nologo suppress displaying the logo information"); | ||
| 14 | Console.WriteLine(" -s search for matching files in current dir and subdirs"); | ||
| 15 | Console.WriteLine(" -set1<file> primary settings file"); | ||
| 16 | Console.WriteLine(" -set2<file> secondary settings file (overrides primary)"); | ||
| 17 | Console.WriteLine(" -indent:<n> indentation multiple (overrides default of 4)"); | ||
| 18 | Console.WriteLine(" -? this help information"); | ||
| 19 | Console.WriteLine(); | ||
| 20 | Console.WriteLine(" sourceFile may use wildcards like *.wxs"); | ||
| 21 | |||
| 22 | return 0; | ||
| 23 | } | ||
| 24 | } | ||
| 25 | } | ||
diff --git a/src/wixcop/CommandLine/WixCopCommandLineParser.cs b/src/wixcop/CommandLine/WixCopCommandLineParser.cs new file mode 100644 index 00000000..53012cfd --- /dev/null +++ b/src/wixcop/CommandLine/WixCopCommandLineParser.cs | |||
| @@ -0,0 +1,132 @@ | |||
| 1 | using System; | ||
| 2 | using System.Collections.Generic; | ||
| 3 | using WixCop.Interfaces; | ||
| 4 | using WixToolset.Core; | ||
| 5 | using WixToolset.Extensibility.Data; | ||
| 6 | using WixToolset.Extensibility.Services; | ||
| 7 | |||
| 8 | namespace WixCop.CommandLine | ||
| 9 | { | ||
| 10 | public sealed class WixCopCommandLineParser : IWixCopCommandLineParser | ||
| 11 | { | ||
| 12 | private bool fixErrors; | ||
| 13 | private int indentationAmount; | ||
| 14 | private readonly List<string> searchPatterns; | ||
| 15 | private readonly IServiceProvider serviceProvider; | ||
| 16 | private string settingsFile1; | ||
| 17 | private string settingsFile2; | ||
| 18 | private bool showHelp; | ||
| 19 | private bool showLogo; | ||
| 20 | private bool subDirectories; | ||
| 21 | |||
| 22 | public WixCopCommandLineParser(IServiceProvider serviceProvider) | ||
| 23 | { | ||
| 24 | this.serviceProvider = serviceProvider; | ||
| 25 | |||
| 26 | this.indentationAmount = 4; | ||
| 27 | this.searchPatterns = new List<string>(); | ||
| 28 | this.showLogo = true; | ||
| 29 | } | ||
| 30 | |||
| 31 | public ICommandLineArguments Arguments { get; set; } | ||
| 32 | |||
| 33 | public ICommandLineCommand ParseWixCopCommandLine() | ||
| 34 | { | ||
| 35 | this.Parse(); | ||
| 36 | |||
| 37 | if (this.showLogo) | ||
| 38 | { | ||
| 39 | AppCommon.DisplayToolHeader(); | ||
| 40 | Console.WriteLine(); | ||
| 41 | } | ||
| 42 | |||
| 43 | if (this.showHelp) | ||
| 44 | { | ||
| 45 | return new HelpCommand(); | ||
| 46 | } | ||
| 47 | |||
| 48 | return new ConvertCommand( | ||
| 49 | this.serviceProvider, | ||
| 50 | this.fixErrors, | ||
| 51 | this.indentationAmount, | ||
| 52 | this.searchPatterns, | ||
| 53 | this.subDirectories, | ||
| 54 | this.settingsFile1, | ||
| 55 | this.settingsFile2); | ||
| 56 | } | ||
| 57 | |||
| 58 | private void Parse() | ||
| 59 | { | ||
| 60 | this.showHelp = 0 == this.Arguments.Arguments.Length; | ||
| 61 | var parser = this.Arguments.Parse(); | ||
| 62 | |||
| 63 | while (!this.showHelp && | ||
| 64 | String.IsNullOrEmpty(parser.ErrorArgument) && | ||
| 65 | parser.TryGetNextSwitchOrArgument(out var arg)) | ||
| 66 | { | ||
| 67 | if (String.IsNullOrWhiteSpace(arg)) // skip blank arguments. | ||
| 68 | { | ||
| 69 | continue; | ||
| 70 | } | ||
| 71 | |||
| 72 | if (parser.IsSwitch(arg)) | ||
| 73 | { | ||
| 74 | if (!this.ParseArgument(parser, arg)) | ||
| 75 | { | ||
| 76 | parser.ErrorArgument = arg; | ||
| 77 | } | ||
| 78 | } | ||
| 79 | else | ||
| 80 | { | ||
| 81 | this.searchPatterns.Add(arg); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | private bool ParseArgument(IParseCommandLine parser, string arg) | ||
| 87 | { | ||
| 88 | var parameter = arg.Substring(1); | ||
| 89 | |||
| 90 | switch (parameter.ToLowerInvariant()) | ||
| 91 | { | ||
| 92 | case "?": | ||
| 93 | this.showHelp = true; | ||
| 94 | return true; | ||
| 95 | case "f": | ||
| 96 | this.fixErrors = true; | ||
| 97 | return true; | ||
| 98 | case "nologo": | ||
| 99 | this.showLogo = false; | ||
| 100 | return true; | ||
| 101 | case "s": | ||
| 102 | this.subDirectories = true; | ||
| 103 | return true; | ||
| 104 | default: // other parameters | ||
| 105 | if (parameter.StartsWith("set1", StringComparison.Ordinal)) | ||
| 106 | { | ||
| 107 | this.settingsFile1 = parameter.Substring(4); | ||
| 108 | } | ||
| 109 | else if (parameter.StartsWith("set2", StringComparison.Ordinal)) | ||
| 110 | { | ||
| 111 | this.settingsFile2 = parameter.Substring(4); | ||
| 112 | } | ||
| 113 | else if (parameter.StartsWith("indent:", StringComparison.Ordinal)) | ||
| 114 | { | ||
| 115 | try | ||
| 116 | { | ||
| 117 | this.indentationAmount = Convert.ToInt32(parameter.Substring(7)); | ||
| 118 | } | ||
| 119 | catch | ||
| 120 | { | ||
| 121 | throw new ArgumentException("Invalid numeric argument.", parameter); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | else | ||
| 125 | { | ||
| 126 | throw new ArgumentException("Invalid argument.", parameter); | ||
| 127 | } | ||
| 128 | return true; | ||
| 129 | } | ||
| 130 | } | ||
| 131 | } | ||
| 132 | } | ||
diff --git a/src/wixcop/Converter.cs b/src/wixcop/Converter.cs new file mode 100644 index 00000000..a204ebe0 --- /dev/null +++ b/src/wixcop/Converter.cs | |||
| @@ -0,0 +1,633 @@ | |||
| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
| 2 | |||
| 3 | namespace WixCop | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Collections.Generic; | ||
| 7 | using System.Globalization; | ||
| 8 | using System.IO; | ||
| 9 | using System.Linq; | ||
| 10 | using System.Xml; | ||
| 11 | using System.Xml.Linq; | ||
| 12 | using WixToolset.Data; | ||
| 13 | using WixToolset.Extensibility.Services; | ||
| 14 | using WixToolset.Tools.Core; | ||
| 15 | |||
| 16 | /// <summary> | ||
| 17 | /// WiX source code converter. | ||
| 18 | /// </summary> | ||
| 19 | public class Converter | ||
| 20 | { | ||
| 21 | private const string XDocumentNewLine = "\n"; // XDocument normlizes "\r\n" to just "\n". | ||
| 22 | private static readonly XNamespace WixNamespace = "http://wixtoolset.org/schemas/v4/wxs"; | ||
| 23 | |||
| 24 | private static readonly XName FileElementName = WixNamespace + "File"; | ||
| 25 | private static readonly XName ExePackageElementName = WixNamespace + "ExePackage"; | ||
| 26 | private static readonly XName MsiPackageElementName = WixNamespace + "MsiPackage"; | ||
| 27 | private static readonly XName MspPackageElementName = WixNamespace + "MspPackage"; | ||
| 28 | private static readonly XName MsuPackageElementName = WixNamespace + "MsuPackage"; | ||
| 29 | private static readonly XName PayloadElementName = WixNamespace + "Payload"; | ||
| 30 | private static readonly XName WixElementWithoutNamespaceName = XNamespace.None + "Wix"; | ||
| 31 | |||
| 32 | private static readonly Dictionary<string, XNamespace> OldToNewNamespaceMapping = new Dictionary<string, XNamespace>() | ||
| 33 | { | ||
| 34 | { "http://schemas.microsoft.com/wix/BalExtension", "http://wixtoolset.org/schemas/v4/wxs/bal" }, | ||
| 35 | { "http://schemas.microsoft.com/wix/ComPlusExtension", "http://wixtoolset.org/schemas/v4/wxs/complus" }, | ||
| 36 | { "http://schemas.microsoft.com/wix/DependencyExtension", "http://wixtoolset.org/schemas/v4/wxs/dependency" }, | ||
| 37 | { "http://schemas.microsoft.com/wix/DifxAppExtension", "http://wixtoolset.org/schemas/v4/wxs/difxapp" }, | ||
| 38 | { "http://schemas.microsoft.com/wix/FirewallExtension", "http://wixtoolset.org/schemas/v4/wxs/firewall" }, | ||
| 39 | { "http://schemas.microsoft.com/wix/GamingExtension", "http://wixtoolset.org/schemas/v4/wxs/gaming" }, | ||
| 40 | { "http://schemas.microsoft.com/wix/IIsExtension", "http://wixtoolset.org/schemas/v4/wxs/iis" }, | ||
| 41 | { "http://schemas.microsoft.com/wix/MsmqExtension", "http://wixtoolset.org/schemas/v4/wxs/msmq" }, | ||
| 42 | { "http://schemas.microsoft.com/wix/NetFxExtension", "http://wixtoolset.org/schemas/v4/wxs/netfx" }, | ||
| 43 | { "http://schemas.microsoft.com/wix/PSExtension", "http://wixtoolset.org/schemas/v4/wxs/powershell" }, | ||
| 44 | { "http://schemas.microsoft.com/wix/SqlExtension", "http://wixtoolset.org/schemas/v4/wxs/sql" }, | ||
| 45 | { "http://schemas.microsoft.com/wix/TagExtension", "http://wixtoolset.org/schemas/v4/wxs/tag" }, | ||
| 46 | { "http://schemas.microsoft.com/wix/UtilExtension", "http://wixtoolset.org/schemas/v4/wxs/util" }, | ||
| 47 | { "http://schemas.microsoft.com/wix/VSExtension", "http://wixtoolset.org/schemas/v4/wxs/vs" }, | ||
| 48 | { "http://wixtoolset.org/schemas/thmutil/2010", "http://wixtoolset.org/schemas/v4/thmutil" }, | ||
| 49 | { "http://schemas.microsoft.com/wix/2009/Lux", "http://wixtoolset.org/schemas/v4/lux" }, | ||
| 50 | { "http://schemas.microsoft.com/wix/2006/wi", "http://wixtoolset.org/schemas/v4/wxs" }, | ||
| 51 | { "http://schemas.microsoft.com/wix/2006/localization", "http://wixtoolset.org/schemas/v4/wxl" }, | ||
| 52 | { "http://schemas.microsoft.com/wix/2006/libraries", "http://wixtoolset.org/schemas/v4/wixlib" }, | ||
| 53 | { "http://schemas.microsoft.com/wix/2006/objects", "http://wixtoolset.org/schemas/v4/wixobj" }, | ||
| 54 | { "http://schemas.microsoft.com/wix/2006/outputs", "http://wixtoolset.org/schemas/v4/wixout" }, | ||
| 55 | { "http://schemas.microsoft.com/wix/2007/pdbs", "http://wixtoolset.org/schemas/v4/wixpdb" }, | ||
| 56 | { "http://schemas.microsoft.com/wix/2003/04/actions", "http://wixtoolset.org/schemas/v4/wi/actions" }, | ||
| 57 | { "http://schemas.microsoft.com/wix/2006/tables", "http://wixtoolset.org/schemas/v4/wi/tables" }, | ||
| 58 | { "http://schemas.microsoft.com/wix/2006/WixUnit", "http://wixtoolset.org/schemas/v4/wixunit" }, | ||
| 59 | }; | ||
| 60 | |||
| 61 | private Dictionary<XName, Action<XElement>> ConvertElementMapping; | ||
| 62 | |||
| 63 | /// <summary> | ||
| 64 | /// Instantiate a new Converter class. | ||
| 65 | /// </summary> | ||
| 66 | /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param> | ||
| 67 | /// <param name="errorsAsWarnings">Test errors to display as warnings.</param> | ||
| 68 | /// <param name="ignoreErrors">Test errors to ignore.</param> | ||
| 69 | public Converter(IMessaging messaging, int indentationAmount, IEnumerable<string> errorsAsWarnings = null, IEnumerable<string> ignoreErrors = null) | ||
| 70 | { | ||
| 71 | // workaround IDE0009 bug | ||
| 72 | /*this.ConvertElementMapping = new Dictionary<XName, Action<XElement>>() | ||
| 73 | { | ||
| 74 | { Converter.FileElementName, this.ConvertFileElement }, | ||
| 75 | { Converter.ExePackageElementName, this.ConvertSuppressSignatureValidation }, | ||
| 76 | { Converter.MsiPackageElementName, this.ConvertSuppressSignatureValidation }, | ||
| 77 | { Converter.MspPackageElementName, this.ConvertSuppressSignatureValidation }, | ||
| 78 | { Converter.MsuPackageElementName, this.ConvertSuppressSignatureValidation }, | ||
| 79 | { Converter.PayloadElementName, this.ConvertSuppressSignatureValidation }, | ||
| 80 | { Converter.WixElementWithoutNamespaceName, this.ConvertWixElementWithoutNamespace }, | ||
| 81 | };*/ | ||
| 82 | this.ConvertElementMapping = new Dictionary<XName, Action<XElement>>(); | ||
| 83 | this.ConvertElementMapping.Add(Converter.FileElementName, this.ConvertFileElement); | ||
| 84 | this.ConvertElementMapping.Add(Converter.ExePackageElementName, this.ConvertSuppressSignatureValidation); | ||
| 85 | this.ConvertElementMapping.Add(Converter.MsiPackageElementName, this.ConvertSuppressSignatureValidation); | ||
| 86 | this.ConvertElementMapping.Add(Converter.MspPackageElementName, this.ConvertSuppressSignatureValidation); | ||
| 87 | this.ConvertElementMapping.Add(Converter.MsuPackageElementName, this.ConvertSuppressSignatureValidation); | ||
| 88 | this.ConvertElementMapping.Add(Converter.PayloadElementName, this.ConvertSuppressSignatureValidation); | ||
| 89 | this.ConvertElementMapping.Add(Converter.WixElementWithoutNamespaceName, this.ConvertWixElementWithoutNamespace); | ||
| 90 | |||
| 91 | this.Messaging = messaging; | ||
| 92 | |||
| 93 | this.IndentationAmount = indentationAmount; | ||
| 94 | |||
| 95 | this.ErrorsAsWarnings = new HashSet<ConverterTestType>(this.YieldConverterTypes(errorsAsWarnings)); | ||
| 96 | |||
| 97 | this.IgnoreErrors = new HashSet<ConverterTestType>(this.YieldConverterTypes(ignoreErrors)); | ||
| 98 | } | ||
| 99 | |||
| 100 | private int Errors { get; set; } | ||
| 101 | |||
| 102 | private HashSet<ConverterTestType> ErrorsAsWarnings { get; set; } | ||
| 103 | |||
| 104 | private HashSet<ConverterTestType> IgnoreErrors { get; set; } | ||
| 105 | |||
| 106 | private IMessaging Messaging { get; } | ||
| 107 | |||
| 108 | private int IndentationAmount { get; set; } | ||
| 109 | |||
| 110 | private string SourceFile { get; set; } | ||
| 111 | |||
| 112 | /// <summary> | ||
| 113 | /// Convert a file. | ||
| 114 | /// </summary> | ||
| 115 | /// <param name="sourceFile">The file to convert.</param> | ||
| 116 | /// <param name="saveConvertedFile">Option to save the converted errors that are found.</param> | ||
| 117 | /// <returns>The number of errors found.</returns> | ||
| 118 | public int ConvertFile(string sourceFile, bool saveConvertedFile) | ||
| 119 | { | ||
| 120 | XDocument document; | ||
| 121 | |||
| 122 | // Set the instance info. | ||
| 123 | this.Errors = 0; | ||
| 124 | this.SourceFile = sourceFile; | ||
| 125 | |||
| 126 | try | ||
| 127 | { | ||
| 128 | document = XDocument.Load(this.SourceFile, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 129 | } | ||
| 130 | catch (XmlException e) | ||
| 131 | { | ||
| 132 | this.OnError(ConverterTestType.XmlException, (XObject)null, "The xml is invalid. Detail: '{0}'", e.Message); | ||
| 133 | |||
| 134 | return this.Errors; | ||
| 135 | } | ||
| 136 | |||
| 137 | this.ConvertDocument(document); | ||
| 138 | |||
| 139 | // Fix errors if requested and necessary. | ||
| 140 | if (saveConvertedFile && 0 < this.Errors) | ||
| 141 | { | ||
| 142 | try | ||
| 143 | { | ||
| 144 | using (StreamWriter writer = File.CreateText(this.SourceFile)) | ||
| 145 | { | ||
| 146 | document.Save(writer, SaveOptions.DisableFormatting | SaveOptions.OmitDuplicateNamespaces); | ||
| 147 | } | ||
| 148 | } | ||
| 149 | catch (UnauthorizedAccessException) | ||
| 150 | { | ||
| 151 | this.OnError(ConverterTestType.UnauthorizedAccessException, (XObject)null, "Could not write to file."); | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | return this.Errors; | ||
| 156 | } | ||
| 157 | |||
| 158 | /// <summary> | ||
| 159 | /// Convert a document. | ||
| 160 | /// </summary> | ||
| 161 | /// <param name="document">The document to convert.</param> | ||
| 162 | /// <returns>The number of errors found.</returns> | ||
| 163 | public int ConvertDocument(XDocument document) | ||
| 164 | { | ||
| 165 | XDeclaration declaration = document.Declaration; | ||
| 166 | |||
| 167 | // Convert the declaration. | ||
| 168 | if (null != declaration) | ||
| 169 | { | ||
| 170 | if (!String.Equals("utf-8", declaration.Encoding, StringComparison.OrdinalIgnoreCase)) | ||
| 171 | { | ||
| 172 | if (this.OnError(ConverterTestType.DeclarationEncodingWrong, document.Root, "The XML declaration encoding is not properly set to 'utf-8'.")) | ||
| 173 | { | ||
| 174 | declaration.Encoding = "utf-8"; | ||
| 175 | } | ||
| 176 | } | ||
| 177 | } | ||
| 178 | else // missing declaration | ||
| 179 | { | ||
| 180 | if (this.OnError(ConverterTestType.DeclarationMissing, (XNode)null, "This file is missing an XML declaration on the first line.")) | ||
| 181 | { | ||
| 182 | document.Declaration = new XDeclaration("1.0", "utf-8", null); | ||
| 183 | document.Root.AddBeforeSelf(new XText(XDocumentNewLine)); | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | // Start converting the nodes at the top. | ||
| 188 | this.ConvertNode(document.Root, 0); | ||
| 189 | |||
| 190 | return this.Errors; | ||
| 191 | } | ||
| 192 | |||
| 193 | /// <summary> | ||
| 194 | /// Convert a single xml node. | ||
| 195 | /// </summary> | ||
| 196 | /// <param name="node">The node to convert.</param> | ||
| 197 | /// <param name="level">The depth level of the node.</param> | ||
| 198 | /// <returns>The converted node.</returns> | ||
| 199 | private void ConvertNode(XNode node, int level) | ||
| 200 | { | ||
| 201 | // Convert this node's whitespace. | ||
| 202 | if ((XmlNodeType.Comment == node.NodeType && 0 > ((XComment)node).Value.IndexOf(XDocumentNewLine, StringComparison.Ordinal)) || | ||
| 203 | XmlNodeType.CDATA == node.NodeType || XmlNodeType.Element == node.NodeType || XmlNodeType.ProcessingInstruction == node.NodeType) | ||
| 204 | { | ||
| 205 | this.ConvertWhitespace(node, level); | ||
| 206 | } | ||
| 207 | |||
| 208 | // Convert this node if it is an element. | ||
| 209 | XElement element = node as XElement; | ||
| 210 | |||
| 211 | if (null != element) | ||
| 212 | { | ||
| 213 | this.ConvertElement(element); | ||
| 214 | |||
| 215 | // Convert all children of this element. | ||
| 216 | IEnumerable<XNode> children = element.Nodes().ToList(); | ||
| 217 | |||
| 218 | foreach (XNode child in children) | ||
| 219 | { | ||
| 220 | this.ConvertNode(child, level + 1); | ||
| 221 | } | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | private void ConvertElement(XElement element) | ||
| 226 | { | ||
| 227 | // Gather any deprecated namespaces, then update this element tree based on those deprecations. | ||
| 228 | Dictionary<XNamespace, XNamespace> deprecatedToUpdatedNamespaces = new Dictionary<XNamespace, XNamespace>(); | ||
| 229 | |||
| 230 | foreach (XAttribute declaration in element.Attributes().Where(a => a.IsNamespaceDeclaration)) | ||
| 231 | { | ||
| 232 | XNamespace ns; | ||
| 233 | |||
| 234 | if (Converter.OldToNewNamespaceMapping.TryGetValue(declaration.Value, out ns)) | ||
| 235 | { | ||
| 236 | if (this.OnError(ConverterTestType.XmlnsValueWrong, declaration, "The namespace '{0}' is out of date. It must be '{1}'.", declaration.Value, ns.NamespaceName)) | ||
| 237 | { | ||
| 238 | deprecatedToUpdatedNamespaces.Add(declaration.Value, ns); | ||
| 239 | } | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | if (deprecatedToUpdatedNamespaces.Any()) | ||
| 244 | { | ||
| 245 | Converter.UpdateElementsWithDeprecatedNamespaces(element.DescendantsAndSelf(), deprecatedToUpdatedNamespaces); | ||
| 246 | } | ||
| 247 | |||
| 248 | // Convert the node in much greater detail. | ||
| 249 | Action<XElement> convert; | ||
| 250 | |||
| 251 | if (this.ConvertElementMapping.TryGetValue(element.Name, out convert)) | ||
| 252 | { | ||
| 253 | convert(element); | ||
| 254 | } | ||
| 255 | } | ||
| 256 | |||
| 257 | private void ConvertFileElement(XElement element) | ||
| 258 | { | ||
| 259 | if (null == element.Attribute("Id")) | ||
| 260 | { | ||
| 261 | XAttribute attribute = element.Attribute("Name"); | ||
| 262 | |||
| 263 | if (null == attribute) | ||
| 264 | { | ||
| 265 | attribute = element.Attribute("Source"); | ||
| 266 | } | ||
| 267 | |||
| 268 | if (null != attribute) | ||
| 269 | { | ||
| 270 | string name = Path.GetFileName(attribute.Value); | ||
| 271 | |||
| 272 | if (this.OnError(ConverterTestType.AssignAnonymousFileId, element, "The file id is being updated to '{0}' to ensure it remains the same as the default", name)) | ||
| 273 | { | ||
| 274 | IEnumerable<XAttribute> attributes = element.Attributes().ToList(); | ||
| 275 | element.RemoveAttributes(); | ||
| 276 | element.Add(new XAttribute("Id", ToolsCommon.GetIdentifierFromName(name))); | ||
| 277 | element.Add(attributes); | ||
| 278 | } | ||
| 279 | } | ||
| 280 | } | ||
| 281 | } | ||
| 282 | |||
| 283 | private void ConvertSuppressSignatureValidation(XElement element) | ||
| 284 | { | ||
| 285 | XAttribute suppressSignatureValidation = element.Attribute("SuppressSignatureValidation"); | ||
| 286 | |||
| 287 | if (null != suppressSignatureValidation) | ||
| 288 | { | ||
| 289 | if (this.OnError(ConverterTestType.SuppressSignatureValidationDeprecated, element, "The chain package element contains deprecated '{0}' attribute. Use the 'EnableSignatureValidation' instead.", suppressSignatureValidation)) | ||
| 290 | { | ||
| 291 | if ("no" == suppressSignatureValidation.Value) | ||
| 292 | { | ||
| 293 | element.Add(new XAttribute("EnableSignatureValidation", "yes")); | ||
| 294 | } | ||
| 295 | } | ||
| 296 | |||
| 297 | suppressSignatureValidation.Remove(); | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | /// <summary> | ||
| 302 | /// Converts a Wix element. | ||
| 303 | /// </summary> | ||
| 304 | /// <param name="element">The Wix element to convert.</param> | ||
| 305 | /// <returns>The converted element.</returns> | ||
| 306 | private void ConvertWixElementWithoutNamespace(XElement element) | ||
| 307 | { | ||
| 308 | if (this.OnError(ConverterTestType.XmlnsMissing, element, "The xmlns attribute is missing. It must be present with a value of '{0}'.", WixNamespace.NamespaceName)) | ||
| 309 | { | ||
| 310 | element.Name = WixNamespace.GetName(element.Name.LocalName); | ||
| 311 | |||
| 312 | element.Add(new XAttribute("xmlns", WixNamespace.NamespaceName)); // set the default namespace. | ||
| 313 | |||
| 314 | foreach (XElement elementWithoutNamespace in element.Elements().Where(e => XNamespace.None == e.Name.Namespace)) | ||
| 315 | { | ||
| 316 | elementWithoutNamespace.Name = WixNamespace.GetName(elementWithoutNamespace.Name.LocalName); | ||
| 317 | } | ||
| 318 | } | ||
| 319 | } | ||
| 320 | |||
| 321 | /// <summary> | ||
| 322 | /// Convert the whitespace adjacent to a node. | ||
| 323 | /// </summary> | ||
| 324 | /// <param name="node">The node to convert.</param> | ||
| 325 | /// <param name="level">The depth level of the node.</param> | ||
| 326 | private void ConvertWhitespace(XNode node, int level) | ||
| 327 | { | ||
| 328 | // Fix the whitespace before this node. | ||
| 329 | XText whitespace = node.PreviousNode as XText; | ||
| 330 | |||
| 331 | if (null != whitespace) | ||
| 332 | { | ||
| 333 | if (XmlNodeType.CDATA == node.NodeType) | ||
| 334 | { | ||
| 335 | if (this.OnError(ConverterTestType.WhitespacePrecedingCDATAWrong, node, "There should be no whitespace preceding a CDATA node.")) | ||
| 336 | { | ||
| 337 | whitespace.Remove(); | ||
| 338 | } | ||
| 339 | } | ||
| 340 | else | ||
| 341 | { | ||
| 342 | // TODO: this code complains about whitespace even after the file has been fixed. | ||
| 343 | if (!Converter.IsLegalWhitespace(this.IndentationAmount, level, whitespace.Value)) | ||
| 344 | { | ||
| 345 | if (this.OnError(ConverterTestType.WhitespacePrecedingNodeWrong, node, "The whitespace preceding this node is incorrect.")) | ||
| 346 | { | ||
| 347 | Converter.FixWhitespace(this.IndentationAmount, level, whitespace); | ||
| 348 | } | ||
| 349 | } | ||
| 350 | } | ||
| 351 | } | ||
| 352 | |||
| 353 | // Fix the whitespace after CDATA nodes. | ||
| 354 | XCData cdata = node as XCData; | ||
| 355 | |||
| 356 | if (null != cdata) | ||
| 357 | { | ||
| 358 | whitespace = cdata.NextNode as XText; | ||
| 359 | |||
| 360 | if (null != whitespace) | ||
| 361 | { | ||
| 362 | if (this.OnError(ConverterTestType.WhitespaceFollowingCDATAWrong, node, "There should be no whitespace following a CDATA node.")) | ||
| 363 | { | ||
| 364 | whitespace.Remove(); | ||
| 365 | } | ||
| 366 | } | ||
| 367 | } | ||
| 368 | else | ||
| 369 | { | ||
| 370 | // Fix the whitespace inside and after this node (except for Error which may contain just whitespace). | ||
| 371 | XElement element = node as XElement; | ||
| 372 | |||
| 373 | if (null != element && "Error" != element.Name.LocalName) | ||
| 374 | { | ||
| 375 | if (!element.HasElements && !element.IsEmpty && String.IsNullOrEmpty(element.Value.Trim())) | ||
| 376 | { | ||
| 377 | if (this.OnError(ConverterTestType.NotEmptyElement, element, "This should be an empty element since it contains nothing but whitespace.")) | ||
| 378 | { | ||
| 379 | element.RemoveNodes(); | ||
| 380 | } | ||
| 381 | } | ||
| 382 | |||
| 383 | whitespace = node.NextNode as XText; | ||
| 384 | |||
| 385 | if (null != whitespace) | ||
| 386 | { | ||
| 387 | // TODO: this code crashes when level is 0, | ||
| 388 | // complains about whitespace even after the file has been fixed, | ||
| 389 | // and the error text doesn't match the error. | ||
| 390 | if (!Converter.IsLegalWhitespace(this.IndentationAmount, level - 1, whitespace.Value)) | ||
| 391 | { | ||
| 392 | if (this.OnError(ConverterTestType.WhitespacePrecedingEndElementWrong, whitespace, "The whitespace preceding this end element is incorrect.")) | ||
| 393 | { | ||
| 394 | Converter.FixWhitespace(this.IndentationAmount, level - 1, whitespace); | ||
| 395 | } | ||
| 396 | } | ||
| 397 | } | ||
| 398 | } | ||
| 399 | } | ||
| 400 | } | ||
| 401 | |||
| 402 | private IEnumerable<ConverterTestType> YieldConverterTypes(IEnumerable<string> types) | ||
| 403 | { | ||
| 404 | if (null != types) | ||
| 405 | { | ||
| 406 | foreach (string type in types) | ||
| 407 | { | ||
| 408 | ConverterTestType itt; | ||
| 409 | |||
| 410 | if (Enum.TryParse<ConverterTestType>(type, true, out itt)) | ||
| 411 | { | ||
| 412 | yield return itt; | ||
| 413 | } | ||
| 414 | else // not a known ConverterTestType | ||
| 415 | { | ||
| 416 | this.OnError(ConverterTestType.ConverterTestTypeUnknown, (XObject)null, "Unknown error type: '{0}'.", type); | ||
| 417 | } | ||
| 418 | } | ||
| 419 | } | ||
| 420 | } | ||
| 421 | |||
| 422 | private static void UpdateElementsWithDeprecatedNamespaces(IEnumerable<XElement> elements, Dictionary<XNamespace, XNamespace> deprecatedToUpdatedNamespaces) | ||
| 423 | { | ||
| 424 | foreach (XElement element in elements) | ||
| 425 | { | ||
| 426 | XNamespace ns; | ||
| 427 | |||
| 428 | if (deprecatedToUpdatedNamespaces.TryGetValue(element.Name.Namespace, out ns)) | ||
| 429 | { | ||
| 430 | element.Name = ns.GetName(element.Name.LocalName); | ||
| 431 | } | ||
| 432 | |||
| 433 | // Remove all the attributes and add them back to with their namespace updated (as necessary). | ||
| 434 | IEnumerable<XAttribute> attributes = element.Attributes().ToList(); | ||
| 435 | element.RemoveAttributes(); | ||
| 436 | |||
| 437 | foreach (XAttribute attribute in attributes) | ||
| 438 | { | ||
| 439 | XAttribute convertedAttribute = attribute; | ||
| 440 | |||
| 441 | if (attribute.IsNamespaceDeclaration) | ||
| 442 | { | ||
| 443 | if (deprecatedToUpdatedNamespaces.TryGetValue(attribute.Value, out ns)) | ||
| 444 | { | ||
| 445 | convertedAttribute = ("xmlns" == attribute.Name.LocalName) ? new XAttribute(attribute.Name.LocalName, ns.NamespaceName) : new XAttribute(XNamespace.Xmlns + attribute.Name.LocalName, ns.NamespaceName); | ||
| 446 | } | ||
| 447 | } | ||
| 448 | else if (deprecatedToUpdatedNamespaces.TryGetValue(attribute.Name.Namespace, out ns)) | ||
| 449 | { | ||
| 450 | convertedAttribute = new XAttribute(ns.GetName(attribute.Name.LocalName), attribute.Value); | ||
| 451 | } | ||
| 452 | |||
| 453 | element.Add(convertedAttribute); | ||
| 454 | } | ||
| 455 | } | ||
| 456 | } | ||
| 457 | |||
| 458 | /// <summary> | ||
| 459 | /// Determine if the whitespace preceding a node is appropriate for its depth level. | ||
| 460 | /// </summary> | ||
| 461 | /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param> | ||
| 462 | /// <param name="level">The depth level that should match this whitespace.</param> | ||
| 463 | /// <param name="whitespace">The whitespace to validate.</param> | ||
| 464 | /// <returns>true if the whitespace is legal; false otherwise.</returns> | ||
| 465 | private static bool IsLegalWhitespace(int indentationAmount, int level, string whitespace) | ||
| 466 | { | ||
| 467 | // strip off leading newlines; there can be an arbitrary number of these | ||
| 468 | while (whitespace.StartsWith(XDocumentNewLine, StringComparison.Ordinal)) | ||
| 469 | { | ||
| 470 | whitespace = whitespace.Substring(XDocumentNewLine.Length); | ||
| 471 | } | ||
| 472 | |||
| 473 | // check the length | ||
| 474 | if (whitespace.Length != level * indentationAmount) | ||
| 475 | { | ||
| 476 | return false; | ||
| 477 | } | ||
| 478 | |||
| 479 | // check the spaces | ||
| 480 | foreach (char character in whitespace) | ||
| 481 | { | ||
| 482 | if (' ' != character) | ||
| 483 | { | ||
| 484 | return false; | ||
| 485 | } | ||
| 486 | } | ||
| 487 | |||
| 488 | return true; | ||
| 489 | } | ||
| 490 | |||
| 491 | /// <summary> | ||
| 492 | /// Fix the whitespace in a Whitespace node. | ||
| 493 | /// </summary> | ||
| 494 | /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param> | ||
| 495 | /// <param name="level">The depth level of the desired whitespace.</param> | ||
| 496 | /// <param name="whitespace">The whitespace node to fix.</param> | ||
| 497 | private static void FixWhitespace(int indentationAmount, int level, XText whitespace) | ||
| 498 | { | ||
| 499 | int newLineCount = 0; | ||
| 500 | |||
| 501 | for (int i = 0; i + 1 < whitespace.Value.Length; ++i) | ||
| 502 | { | ||
| 503 | if (XDocumentNewLine == whitespace.Value.Substring(i, 2)) | ||
| 504 | { | ||
| 505 | ++i; // skip an extra character | ||
| 506 | ++newLineCount; | ||
| 507 | } | ||
| 508 | } | ||
| 509 | |||
| 510 | if (0 == newLineCount) | ||
| 511 | { | ||
| 512 | newLineCount = 1; | ||
| 513 | } | ||
| 514 | |||
| 515 | // reset the whitespace value | ||
| 516 | whitespace.Value = String.Empty; | ||
| 517 | |||
| 518 | // add the correct number of newlines | ||
| 519 | for (int i = 0; i < newLineCount; ++i) | ||
| 520 | { | ||
| 521 | whitespace.Value = String.Concat(whitespace.Value, XDocumentNewLine); | ||
| 522 | } | ||
| 523 | |||
| 524 | // add the correct number of spaces based on configured indentation amount | ||
| 525 | whitespace.Value = String.Concat(whitespace.Value, new string(' ', level * indentationAmount)); | ||
| 526 | } | ||
| 527 | |||
| 528 | /// <summary> | ||
| 529 | /// Output an error message to the console. | ||
| 530 | /// </summary> | ||
| 531 | /// <param name="converterTestType">The type of converter test.</param> | ||
| 532 | /// <param name="node">The node that caused the error.</param> | ||
| 533 | /// <param name="message">Detailed error message.</param> | ||
| 534 | /// <param name="args">Additional formatted string arguments.</param> | ||
| 535 | /// <returns>Returns true indicating that action should be taken on this error, and false if it should be ignored.</returns> | ||
| 536 | private bool OnError(ConverterTestType converterTestType, XObject node, string message, params object[] args) | ||
| 537 | { | ||
| 538 | if (this.IgnoreErrors.Contains(converterTestType)) // ignore the error | ||
| 539 | { | ||
| 540 | return false; | ||
| 541 | } | ||
| 542 | |||
| 543 | // Increase the error count. | ||
| 544 | this.Errors++; | ||
| 545 | |||
| 546 | SourceLineNumber sourceLine = (null == node) ? new SourceLineNumber(this.SourceFile ?? "wixcop.exe") : new SourceLineNumber(this.SourceFile, ((IXmlLineInfo)node).LineNumber); | ||
| 547 | bool warning = this.ErrorsAsWarnings.Contains(converterTestType); | ||
| 548 | string display = String.Format(CultureInfo.CurrentCulture, message, args); | ||
| 549 | |||
| 550 | var msg = new Message(sourceLine, warning ? MessageLevel.Warning : MessageLevel.Error, (int)converterTestType, "{0} ({1})", display, converterTestType.ToString()); | ||
| 551 | |||
| 552 | this.Messaging.Write(msg); | ||
| 553 | |||
| 554 | return true; | ||
| 555 | } | ||
| 556 | |||
| 557 | /// <summary> | ||
| 558 | /// Converter test types. These are used to condition error messages down to warnings. | ||
| 559 | /// </summary> | ||
| 560 | private enum ConverterTestType | ||
| 561 | { | ||
| 562 | /// <summary> | ||
| 563 | /// Internal-only: displayed when a string cannot be converted to an ConverterTestType. | ||
| 564 | /// </summary> | ||
| 565 | ConverterTestTypeUnknown, | ||
| 566 | |||
| 567 | /// <summary> | ||
| 568 | /// Displayed when an XML loading exception has occurred. | ||
| 569 | /// </summary> | ||
| 570 | XmlException, | ||
| 571 | |||
| 572 | /// <summary> | ||
| 573 | /// Displayed when a file cannot be accessed; typically when trying to save back a fixed file. | ||
| 574 | /// </summary> | ||
| 575 | UnauthorizedAccessException, | ||
| 576 | |||
| 577 | /// <summary> | ||
| 578 | /// Displayed when the encoding attribute in the XML declaration is not 'UTF-8'. | ||
| 579 | /// </summary> | ||
| 580 | DeclarationEncodingWrong, | ||
| 581 | |||
| 582 | /// <summary> | ||
| 583 | /// Displayed when the XML declaration is missing from the source file. | ||
| 584 | /// </summary> | ||
| 585 | DeclarationMissing, | ||
| 586 | |||
| 587 | /// <summary> | ||
| 588 | /// Displayed when the whitespace preceding a CDATA node is wrong. | ||
| 589 | /// </summary> | ||
| 590 | WhitespacePrecedingCDATAWrong, | ||
| 591 | |||
| 592 | /// <summary> | ||
| 593 | /// Displayed when the whitespace preceding a node is wrong. | ||
| 594 | /// </summary> | ||
| 595 | WhitespacePrecedingNodeWrong, | ||
| 596 | |||
| 597 | /// <summary> | ||
| 598 | /// Displayed when an element is not empty as it should be. | ||
| 599 | /// </summary> | ||
| 600 | NotEmptyElement, | ||
| 601 | |||
| 602 | /// <summary> | ||
| 603 | /// Displayed when the whitespace following a CDATA node is wrong. | ||
| 604 | /// </summary> | ||
| 605 | WhitespaceFollowingCDATAWrong, | ||
| 606 | |||
| 607 | /// <summary> | ||
| 608 | /// Displayed when the whitespace preceding an end element is wrong. | ||
| 609 | /// </summary> | ||
| 610 | WhitespacePrecedingEndElementWrong, | ||
| 611 | |||
| 612 | /// <summary> | ||
| 613 | /// Displayed when the xmlns attribute is missing from the document element. | ||
| 614 | /// </summary> | ||
| 615 | XmlnsMissing, | ||
| 616 | |||
| 617 | /// <summary> | ||
| 618 | /// Displayed when the xmlns attribute on the document element is wrong. | ||
| 619 | /// </summary> | ||
| 620 | XmlnsValueWrong, | ||
| 621 | |||
| 622 | /// <summary> | ||
| 623 | /// Assign an identifier to a File element when on Id attribute is specified. | ||
| 624 | /// </summary> | ||
| 625 | AssignAnonymousFileId, | ||
| 626 | |||
| 627 | /// <summary> | ||
| 628 | /// SuppressSignatureValidation attribute is deprecated and replaced with EnableSignatureValidation. | ||
| 629 | /// </summary> | ||
| 630 | SuppressSignatureValidationDeprecated, | ||
| 631 | } | ||
| 632 | } | ||
| 633 | } | ||
diff --git a/src/wixcop/Interfaces/IWixCopCommandLineParser.cs b/src/wixcop/Interfaces/IWixCopCommandLineParser.cs new file mode 100644 index 00000000..2093f5d8 --- /dev/null +++ b/src/wixcop/Interfaces/IWixCopCommandLineParser.cs | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | using WixToolset.Extensibility.Data; | ||
| 2 | |||
| 3 | namespace WixCop.Interfaces | ||
| 4 | { | ||
| 5 | public interface IWixCopCommandLineParser | ||
| 6 | { | ||
| 7 | ICommandLineArguments Arguments { get; set; } | ||
| 8 | |||
| 9 | ICommandLineCommand ParseWixCopCommandLine(); | ||
| 10 | } | ||
| 11 | } | ||
diff --git a/src/wixcop/Program.cs b/src/wixcop/Program.cs new file mode 100644 index 00000000..b26bd6c9 --- /dev/null +++ b/src/wixcop/Program.cs | |||
| @@ -0,0 +1,67 @@ | |||
| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
| 2 | |||
| 3 | namespace WixCop | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using WixCop.CommandLine; | ||
| 7 | using WixCop.Interfaces; | ||
| 8 | using WixToolset.Core; | ||
| 9 | using WixToolset.Extensibility; | ||
| 10 | using WixToolset.Extensibility.Data; | ||
| 11 | using WixToolset.Extensibility.Services; | ||
| 12 | using WixToolset.Tools.Core; | ||
| 13 | |||
| 14 | /// <summary> | ||
| 15 | /// Wix source code style inspector and converter. | ||
| 16 | /// </summary> | ||
| 17 | public sealed class Program | ||
| 18 | { | ||
| 19 | /// <summary> | ||
| 20 | /// The main entry point for the application. | ||
| 21 | /// </summary> | ||
| 22 | /// <param name="args">The commandline arguments.</param> | ||
| 23 | /// <returns>The number of errors that were found.</returns> | ||
| 24 | [STAThread] | ||
| 25 | public static int Main(string[] args) | ||
| 26 | { | ||
| 27 | var serviceProvider = new WixToolsetServiceProvider(); | ||
| 28 | var listener = new ConsoleMessageListener("WXCP", "wixcop.exe"); | ||
| 29 | |||
| 30 | serviceProvider.AddService<IMessageListener>((x, y) => listener); | ||
| 31 | serviceProvider.AddService<IWixCopCommandLineParser>((x, y) => new WixCopCommandLineParser(x)); | ||
| 32 | |||
| 33 | var program = new Program(); | ||
| 34 | return program.Run(serviceProvider, args); | ||
| 35 | } | ||
| 36 | |||
| 37 | /// <summary> | ||
| 38 | /// Run the application with the given arguments. | ||
| 39 | /// </summary> | ||
| 40 | /// <param name="serviceProvider">Service provider to use throughout this execution.</param> | ||
| 41 | /// <param name="args">The commandline arguments.</param> | ||
| 42 | /// <returns>The number of errors that were found.</returns> | ||
| 43 | public int Run(IServiceProvider serviceProvider, string[] args) | ||
| 44 | { | ||
| 45 | try | ||
| 46 | { | ||
| 47 | var listener = serviceProvider.GetService<IMessageListener>(); | ||
| 48 | var messaging = serviceProvider.GetService<IMessaging>(); | ||
| 49 | messaging.SetListener(listener); | ||
| 50 | |||
| 51 | var arguments = serviceProvider.GetService<ICommandLineArguments>(); | ||
| 52 | arguments.Populate(args); | ||
| 53 | |||
| 54 | var commandLine = serviceProvider.GetService<IWixCopCommandLineParser>(); | ||
| 55 | commandLine.Arguments = arguments; | ||
| 56 | var command = commandLine.ParseWixCopCommandLine(); | ||
| 57 | return command?.Execute() ?? 1; | ||
| 58 | } | ||
| 59 | catch (Exception e) | ||
| 60 | { | ||
| 61 | Console.Error.WriteLine("wixcop.exe : fatal error WXCP0001 : {0}\r\n\n\nStack Trace:\r\n{1}", e.Message, e.StackTrace); | ||
| 62 | |||
| 63 | return 1; | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
| 67 | } | ||
diff --git a/src/wixcop/WixCop.csproj b/src/wixcop/WixCop.csproj new file mode 100644 index 00000000..9bcae177 --- /dev/null +++ b/src/wixcop/WixCop.csproj | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> | ||
| 3 | |||
| 4 | <Project Sdk="Microsoft.NET.Sdk"> | ||
| 5 | <PropertyGroup> | ||
| 6 | <TargetFrameworks>net461;netcoreapp2.1</TargetFrameworks> | ||
| 7 | <OutputType>Exe</OutputType> | ||
| 8 | <Description>Converter</Description> | ||
| 9 | <Title>WiX Error Correction Tool</Title> | ||
| 10 | <DebugType>embedded</DebugType> | ||
| 11 | <PublishRepositoryUrl>true</PublishRepositoryUrl> | ||
| 12 | <!-- <PackAsTool>true</PackAsTool> --> | ||
| 13 | </PropertyGroup> | ||
| 14 | |||
| 15 | <PropertyGroup> | ||
| 16 | <NoWarn>NU1701</NoWarn> | ||
| 17 | </PropertyGroup> | ||
| 18 | |||
| 19 | <ItemGroup> | ||
| 20 | <ProjectReference Include="..\WixToolset.Tools.Core\WixToolset.Tools.Core.csproj" /> | ||
| 21 | </ItemGroup> | ||
| 22 | |||
| 23 | <ItemGroup> | ||
| 24 | <ProjectReference Include="$(WixToolsetRootFolder)\Core\src\WixToolset.Core\WixToolset.Core.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Core\README.md') " /> | ||
| 25 | <PackageReference Include="WixToolset.Core" Version="4.0.*" Condition=" '$(Configuration)' == 'Release' Or !Exists('$(WixToolsetRootFolder)\Core\README.md') " /> | ||
| 26 | </ItemGroup> | ||
| 27 | |||
| 28 | <ItemGroup> | ||
| 29 | <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta-63102-01" PrivateAssets="All"/> | ||
| 30 | <PackageReference Include="Nerdbank.GitVersioning" Version="2.1.65" PrivateAssets="All" /> | ||
| 31 | </ItemGroup> | ||
| 32 | </Project> | ||
