Initial code commit
Rob Mensching committed
May 15, 2019 at 16:09 UTC
6f6c485118796f044a278447722eaf18ac5bf86e
24 files changed
+3015
WixToolset.Converters.sln
+67
@@ -3,6 +3,16 @@ Microsoft Visual Studio Solution File, Format Version 12.00
3
# Visual Studio 15
4
VisualStudioVersion = 15.0.26124.0
5
MinimumVisualStudioVersion = 15.0.26124.0
6
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Converters", "src\WixToolset.Converters\WixToolset.Converters.csproj", "{6FAF6385-6598-4B89-972B-C31AFCA14538}"
7
+EndProject
8
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Converters.Tupleizer", "src\WixToolset.Converters.Tupleizer\WixToolset.Converters.Tupleizer.csproj", "{F051BCAF-698C-41D5-8427-164537CE5C5C}"
9
+EndProject
10
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{1B16A6C1-2B5D-4F9A-9DD5-FBC89B3CE31E}"
11
+EndProject
12
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolsetTest.Converters", "src\test\WixToolsetTest.Converters\WixToolsetTest.Converters.csproj", "{485C5038-97E1-4729-A54D-848CC69569FD}"
13
+EndProject
14
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolsetTest.Converters.Tupleizer", "src\test\WixToolsetTest.Converters.Tupleizer\WixToolsetTest.Converters.Tupleizer.csproj", "{9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}"
15
+EndProject
16
Global
17
GlobalSection(SolutionConfigurationPlatforms) = preSolution
18
Debug|Any CPU = Debug|Any CPU
@@ -12,7 +22,64 @@ Global
22
Release|x64 = Release|x64
23
Release|x86 = Release|x86
24
EndGlobalSection
25
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
26
+ {F051BCAF-698C-41D5-8427-164537CE5C5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27
+ {F051BCAF-698C-41D5-8427-164537CE5C5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
28
+ {F051BCAF-698C-41D5-8427-164537CE5C5C}.Debug|x64.ActiveCfg = Debug|Any CPU
29
+ {F051BCAF-698C-41D5-8427-164537CE5C5C}.Debug|x64.Build.0 = Debug|Any CPU
30
+ {F051BCAF-698C-41D5-8427-164537CE5C5C}.Debug|x86.ActiveCfg = Debug|Any CPU
31
+ {F051BCAF-698C-41D5-8427-164537CE5C5C}.Debug|x86.Build.0 = Debug|Any CPU
32
+ {F051BCAF-698C-41D5-8427-164537CE5C5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
33
+ {F051BCAF-698C-41D5-8427-164537CE5C5C}.Release|Any CPU.Build.0 = Release|Any CPU
34
+ {F051BCAF-698C-41D5-8427-164537CE5C5C}.Release|x64.ActiveCfg = Release|Any CPU
35
+ {F051BCAF-698C-41D5-8427-164537CE5C5C}.Release|x64.Build.0 = Release|Any CPU
36
+ {F051BCAF-698C-41D5-8427-164537CE5C5C}.Release|x86.ActiveCfg = Release|Any CPU
37
+ {F051BCAF-698C-41D5-8427-164537CE5C5C}.Release|x86.Build.0 = Release|Any CPU
38
+ {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39
+ {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Debug|Any CPU.Build.0 = Debug|Any CPU
40
+ {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Debug|x64.ActiveCfg = Debug|Any CPU
41
+ {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Debug|x64.Build.0 = Debug|Any CPU
42
+ {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Debug|x86.ActiveCfg = Debug|Any CPU
43
+ {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Debug|x86.Build.0 = Debug|Any CPU
44
+ {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Release|Any CPU.ActiveCfg = Release|Any CPU
45
+ {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Release|Any CPU.Build.0 = Release|Any CPU
46
+ {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Release|x64.ActiveCfg = Release|Any CPU
47
+ {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Release|x64.Build.0 = Release|Any CPU
48
+ {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Release|x86.ActiveCfg = Release|Any CPU
49
+ {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Release|x86.Build.0 = Release|Any CPU
50
+ {6FAF6385-6598-4B89-972B-C31AFCA14538}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
51
+ {6FAF6385-6598-4B89-972B-C31AFCA14538}.Debug|Any CPU.Build.0 = Debug|Any CPU
52
+ {6FAF6385-6598-4B89-972B-C31AFCA14538}.Debug|x64.ActiveCfg = Debug|Any CPU
53
+ {6FAF6385-6598-4B89-972B-C31AFCA14538}.Debug|x64.Build.0 = Debug|Any CPU
54
+ {6FAF6385-6598-4B89-972B-C31AFCA14538}.Debug|x86.ActiveCfg = Debug|Any CPU
55
+ {6FAF6385-6598-4B89-972B-C31AFCA14538}.Debug|x86.Build.0 = Debug|Any CPU
56
+ {6FAF6385-6598-4B89-972B-C31AFCA14538}.Release|Any CPU.ActiveCfg = Release|Any CPU
57
+ {6FAF6385-6598-4B89-972B-C31AFCA14538}.Release|Any CPU.Build.0 = Release|Any CPU
58
+ {6FAF6385-6598-4B89-972B-C31AFCA14538}.Release|x64.ActiveCfg = Release|Any CPU
59
+ {6FAF6385-6598-4B89-972B-C31AFCA14538}.Release|x64.Build.0 = Release|Any CPU
60
+ {6FAF6385-6598-4B89-972B-C31AFCA14538}.Release|x86.ActiveCfg = Release|Any CPU
61
+ {6FAF6385-6598-4B89-972B-C31AFCA14538}.Release|x86.Build.0 = Release|Any CPU
62
+ {485C5038-97E1-4729-A54D-848CC69569FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
63
+ {485C5038-97E1-4729-A54D-848CC69569FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
64
+ {485C5038-97E1-4729-A54D-848CC69569FD}.Debug|x64.ActiveCfg = Debug|Any CPU
65
+ {485C5038-97E1-4729-A54D-848CC69569FD}.Debug|x64.Build.0 = Debug|Any CPU
66
+ {485C5038-97E1-4729-A54D-848CC69569FD}.Debug|x86.ActiveCfg = Debug|Any CPU
67
+ {485C5038-97E1-4729-A54D-848CC69569FD}.Debug|x86.Build.0 = Debug|Any CPU
68
+ {485C5038-97E1-4729-A54D-848CC69569FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
69
+ {485C5038-97E1-4729-A54D-848CC69569FD}.Release|Any CPU.Build.0 = Release|Any CPU
70
+ {485C5038-97E1-4729-A54D-848CC69569FD}.Release|x64.ActiveCfg = Release|Any CPU
71
+ {485C5038-97E1-4729-A54D-848CC69569FD}.Release|x64.Build.0 = Release|Any CPU
72
+ {485C5038-97E1-4729-A54D-848CC69569FD}.Release|x86.ActiveCfg = Release|Any CPU
73
+ {485C5038-97E1-4729-A54D-848CC69569FD}.Release|x86.Build.0 = Release|Any CPU
74
+ EndGlobalSection
75
GlobalSection(SolutionProperties) = preSolution
76
HideSolutionNode = FALSE
77
EndGlobalSection
78
+ GlobalSection(NestedProjects) = preSolution
79
+ {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C} = {1B16A6C1-2B5D-4F9A-9DD5-FBC89B3CE31E}
80
+ {485C5038-97E1-4729-A54D-848CC69569FD} = {1B16A6C1-2B5D-4F9A-9DD5-FBC89B3CE31E}
81
+ EndGlobalSection
82
+ GlobalSection(ExtensibilityGlobals) = postSolution
83
+ SolutionGuid = {2E71F0EC-CF75-44DA-8353-7066EBD06564}
84
+ EndGlobalSection
85
EndGlobal
WixToolset.Converters.v3.ncrunchsolution
new
+6
@@ -0,0 +1,6 @@
1
+<SolutionConfiguration>
2
+ <Settings>
3
+ <AllowParallelTestExecution>False</AllowParallelTestExecution>
4
+ <SolutionConfigured>True</SolutionConfigured>
5
+ </Settings>
6
+</SolutionConfiguration>
\ No newline at end of file
appveyor.cmd
new
+12
@@ -0,0 +1,12 @@
1
+@setlocal
2
+@pushd %~dp0
3
+@set _P=%~dp0build\Release\publish
4
+
5
+dotnet pack -c Release src\WixToolset.Converters
6
+dotnet pack -c Release src\WixToolset.Converters.Tupleizer
7
+
8
+dotnet build -c Release src\test\WixToolsetTest.Converters
9
+dotnet build -c Release src\test\WixToolsetTest.Converters.Tupleizer
10
+
11
+@popd
12
+@endlocal
src/WixToolset.Converters.Tupleizer/ConvertTuplesCommand.cs
new
+609
@@ -0,0 +1,609 @@
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 WixToolset.Converters.Tupleizer
4
+{
5
+ using System;
6
+ using System.Linq;
7
+ using WixToolset.Data;
8
+ using WixToolset.Data.Tuples;
9
+ using WixToolset.Data.WindowsInstaller;
10
+ using Wix3 = Microsoft.Tools.WindowsInstallerXml;
11
+
12
+ public class ConvertTuplesCommand
13
+ {
14
+ public Intermediate Execute(string path)
15
+ {
16
+ var output = Wix3.Output.Load(path, suppressVersionCheck: true, suppressSchema: true);
17
+ return this.Execute(output);
18
+ }
19
+
20
+ public Intermediate Execute(Wix3.Output output)
21
+ {
22
+ var section = new IntermediateSection(String.Empty, OutputType3ToSectionType4(output.Type), output.Codepage);
23
+
24
+ foreach (Wix3.Table table in output.Tables)
25
+ {
26
+ foreach (Wix3.Row row in table.Rows)
27
+ {
28
+ var tuple = GenerateTupleFromRow(row);
29
+ if (tuple != null)
30
+ {
31
+ section.Tuples.Add(tuple);
32
+ }
33
+ }
34
+ }
35
+
36
+ return new Intermediate(String.Empty, new[] { section }, localizationsByCulture: null, embedFilePaths: null);
37
+ }
38
+
39
+ private static IntermediateTuple GenerateTupleFromRow(Wix3.Row row)
40
+ {
41
+ var name = row.Table.Name;
42
+ switch (name)
43
+ {
44
+ case "_SummaryInformation":
45
+ return DefaultTupleFromRow(typeof(_SummaryInformationTuple), row, columnZeroIsId: false);
46
+ case "ActionText":
47
+ return DefaultTupleFromRow(typeof(ActionTextTuple), row, columnZeroIsId: false);
48
+ case "AdvtExecuteSequence":
49
+ return DefaultTupleFromRow(typeof(AdvtExecuteSequenceTuple), row, columnZeroIsId: false);
50
+ case "AppId":
51
+ return DefaultTupleFromRow(typeof(AppIdTuple), row, columnZeroIsId: false);
52
+ case "AppSearch":
53
+ return DefaultTupleFromRow(typeof(AppSearchTuple), row, columnZeroIsId: false);
54
+ case "Binary":
55
+ return DefaultTupleFromRow(typeof(BinaryTuple), row, columnZeroIsId: false);
56
+ case "Class":
57
+ return DefaultTupleFromRow(typeof(ClassTuple), row, columnZeroIsId: false);
58
+ case "CompLocator":
59
+ return DefaultTupleFromRow(typeof(CompLocatorTuple), row, columnZeroIsId: true);
60
+ case "Component":
61
+ {
62
+ var attributes = FieldAsNullableInt(row, 3);
63
+
64
+ var location = ComponentLocation.LocalOnly;
65
+ if ((attributes & WindowsInstallerConstants.MsidbComponentAttributesSourceOnly) == WindowsInstallerConstants.MsidbComponentAttributesSourceOnly)
66
+ {
67
+ location = ComponentLocation.SourceOnly;
68
+ }
69
+ else if ((attributes & WindowsInstallerConstants.MsidbComponentAttributesOptional) == WindowsInstallerConstants.MsidbComponentAttributesOptional)
70
+ {
71
+ location = ComponentLocation.Either;
72
+ }
73
+
74
+ var keyPathType = ComponentKeyPathType.File;
75
+ if ((attributes & WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath) == WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath)
76
+ {
77
+ keyPathType = ComponentKeyPathType.Registry;
78
+ }
79
+ else if ((attributes & WindowsInstallerConstants.MsidbComponentAttributesODBCDataSource) == WindowsInstallerConstants.MsidbComponentAttributesODBCDataSource)
80
+ {
81
+ keyPathType = ComponentKeyPathType.OdbcDataSource;
82
+ }
83
+
84
+ return new ComponentTuple(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Public, FieldAsString(row, 0)))
85
+ {
86
+ ComponentId = FieldAsString(row, 1),
87
+ Directory_ = FieldAsString(row, 2),
88
+ Condition = FieldAsString(row, 4),
89
+ KeyPath = FieldAsString(row, 5),
90
+ Location = location,
91
+ DisableRegistryReflection = (attributes & WindowsInstallerConstants.MsidbComponentAttributesDisableRegistryReflection) == WindowsInstallerConstants.MsidbComponentAttributesDisableRegistryReflection,
92
+ NeverOverwrite = (attributes & WindowsInstallerConstants.MsidbComponentAttributesNeverOverwrite) == WindowsInstallerConstants.MsidbComponentAttributesNeverOverwrite,
93
+ Permanent = (attributes & WindowsInstallerConstants.MsidbComponentAttributesPermanent) == WindowsInstallerConstants.MsidbComponentAttributesPermanent,
94
+ SharedDllRefCount = (attributes & WindowsInstallerConstants.MsidbComponentAttributesSharedDllRefCount) == WindowsInstallerConstants.MsidbComponentAttributesSharedDllRefCount,
95
+ Shared = (attributes & WindowsInstallerConstants.MsidbComponentAttributesShared) == WindowsInstallerConstants.MsidbComponentAttributesShared,
96
+ Transitive = (attributes & WindowsInstallerConstants.MsidbComponentAttributesTransitive) == WindowsInstallerConstants.MsidbComponentAttributesTransitive,
97
+ UninstallWhenSuperseded = (attributes & WindowsInstallerConstants.MsidbComponentAttributesUninstallOnSupersedence) == WindowsInstallerConstants.MsidbComponentAttributesUninstallOnSupersedence,
98
+ Win64 = (attributes & WindowsInstallerConstants.MsidbComponentAttributes64bit) == WindowsInstallerConstants.MsidbComponentAttributes64bit,
99
+ KeyPathType = keyPathType,
100
+ };
101
+ }
102
+
103
+ case "Condition":
104
+ return DefaultTupleFromRow(typeof(ConditionTuple), row, columnZeroIsId: false);
105
+ case "CreateFolder":
106
+ return DefaultTupleFromRow(typeof(CreateFolderTuple), row, columnZeroIsId: false);
107
+ case "CustomAction":
108
+ {
109
+ var caType = FieldAsInt(row, 1);
110
+ var executionType = DetermineCustomActionExecutionType(caType);
111
+ var sourceType = DetermineCustomActionSourceType(caType);
112
+ var targetType = DetermineCustomActionTargetType(caType);
113
+
114
+ return new CustomActionTuple(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Public, FieldAsString(row, 0)))
115
+ {
116
+ ExecutionType = executionType,
117
+ SourceType = sourceType,
118
+ Source = FieldAsString(row, 2),
119
+ TargetType = targetType,
120
+ Target = FieldAsString(row, 3),
121
+ Win64 = (caType & WindowsInstallerConstants.MsidbCustomActionType64BitScript) == WindowsInstallerConstants.MsidbCustomActionType64BitScript,
122
+ TSAware = (caType & WindowsInstallerConstants.MsidbCustomActionTypeTSAware) == WindowsInstallerConstants.MsidbCustomActionTypeTSAware,
123
+ Impersonate = (caType & WindowsInstallerConstants.MsidbCustomActionTypeNoImpersonate) != WindowsInstallerConstants.MsidbCustomActionTypeNoImpersonate,
124
+ IgnoreResult = (caType & WindowsInstallerConstants.MsidbCustomActionTypeContinue) == WindowsInstallerConstants.MsidbCustomActionTypeContinue,
125
+ Hidden = (caType & WindowsInstallerConstants.MsidbCustomActionTypeHideTarget) == WindowsInstallerConstants.MsidbCustomActionTypeHideTarget,
126
+ Async = (caType & WindowsInstallerConstants.MsidbCustomActionTypeAsync) == WindowsInstallerConstants.MsidbCustomActionTypeAsync,
127
+ };
128
+ }
129
+
130
+ case "Directory":
131
+ return DefaultTupleFromRow(typeof(DirectoryTuple), row, columnZeroIsId: false);
132
+ case "DrLocator":
133
+ return DefaultTupleFromRow(typeof(DrLocatorTuple), row, columnZeroIsId: false);
134
+ case "Error":
135
+ return DefaultTupleFromRow(typeof(ErrorTuple), row, columnZeroIsId: false);
136
+ case "Extension":
137
+ return DefaultTupleFromRow(typeof(ExtensionTuple), row, columnZeroIsId: false);
138
+ case "Feature":
139
+ {
140
+ int attributes = FieldAsInt(row, 7);
141
+ var installDefault = FeatureInstallDefault.Local;
142
+ if ((attributes & WindowsInstallerConstants.MsidbFeatureAttributesFollowParent) == WindowsInstallerConstants.MsidbFeatureAttributesFollowParent)
143
+ {
144
+ installDefault = FeatureInstallDefault.FollowParent;
145
+ }
146
+ else
147
+ if ((attributes & WindowsInstallerConstants.MsidbFeatureAttributesFavorSource) == WindowsInstallerConstants.MsidbFeatureAttributesFavorSource)
148
+ {
149
+ installDefault = FeatureInstallDefault.Source;
150
+ }
151
+
152
+ return new FeatureTuple(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Public, FieldAsString(row, 0)))
153
+ {
154
+ Feature_Parent = FieldAsString(row, 1),
155
+ Title = FieldAsString(row, 2),
156
+ Description = FieldAsString(row, 3),
157
+ Display = FieldAsInt(row, 4), // BUGBUGBUG: FieldAsNullableInt(row, 4),
158
+ Level = FieldAsInt(row, 5),
159
+ Directory_ = FieldAsString(row, 6),
160
+ DisallowAbsent = (attributes & WindowsInstallerConstants.MsidbFeatureAttributesUIDisallowAbsent) == WindowsInstallerConstants.MsidbFeatureAttributesUIDisallowAbsent,
161
+ DisallowAdvertise = (attributes & WindowsInstallerConstants.MsidbFeatureAttributesDisallowAdvertise) == WindowsInstallerConstants.MsidbFeatureAttributesDisallowAdvertise,
162
+ InstallDefault = installDefault,
163
+ TypicalDefault = (attributes & WindowsInstallerConstants.MsidbFeatureAttributesFavorAdvertise) == WindowsInstallerConstants.MsidbFeatureAttributesFavorAdvertise ? FeatureTypicalDefault.Advertise : FeatureTypicalDefault.Install,
164
+ };
165
+ }
166
+
167
+ case "FeatureComponents":
168
+ return DefaultTupleFromRow(typeof(FeatureComponentsTuple), row, columnZeroIsId: false);
169
+ case "File":
170
+ {
171
+ var attributes = FieldAsNullableInt(row, 6);
172
+ var readOnly = (attributes & WindowsInstallerConstants.MsidbFileAttributesReadOnly) == WindowsInstallerConstants.MsidbFileAttributesReadOnly;
173
+ var hidden = (attributes & WindowsInstallerConstants.MsidbFileAttributesHidden) == WindowsInstallerConstants.MsidbFileAttributesHidden;
174
+ var system = (attributes & WindowsInstallerConstants.MsidbFileAttributesSystem) == WindowsInstallerConstants.MsidbFileAttributesSystem;
175
+ var vital = (attributes & WindowsInstallerConstants.MsidbFileAttributesVital) == WindowsInstallerConstants.MsidbFileAttributesVital;
176
+ var checksum = (attributes & WindowsInstallerConstants.MsidbFileAttributesChecksum) == WindowsInstallerConstants.MsidbFileAttributesChecksum;
177
+ bool? compressed = null;
178
+ if ((attributes & WindowsInstallerConstants.MsidbFileAttributesNoncompressed) == WindowsInstallerConstants.MsidbFileAttributesNoncompressed)
179
+ {
180
+ compressed = false;
181
+ }
182
+ else if ((attributes & WindowsInstallerConstants.MsidbFileAttributesCompressed) == WindowsInstallerConstants.MsidbFileAttributesCompressed)
183
+ {
184
+ compressed = true;
185
+ }
186
+
187
+ return new FileTuple(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Public, FieldAsString(row, 0)))
188
+ {
189
+ Component_ = FieldAsString(row, 1),
190
+ LongFileName = FieldAsString(row, 2),
191
+ FileSize = FieldAsInt(row, 3),
192
+ Version = FieldAsString(row, 4),
193
+ Language = FieldAsString(row, 5),
194
+ ReadOnly = readOnly,
195
+ Hidden = hidden,
196
+ System = system,
197
+ Vital = vital,
198
+ Checksum = checksum,
199
+ Compressed = compressed,
200
+ };
201
+ }
202
+
203
+ case "Font":
204
+ return DefaultTupleFromRow(typeof(FontTuple), row, columnZeroIsId: false);
205
+ case "Icon":
206
+ return DefaultTupleFromRow(typeof(IconTuple), row, columnZeroIsId: false);
207
+ case "InstallExecuteSequence":
208
+ return DefaultTupleFromRow(typeof(InstallExecuteSequenceTuple), row, columnZeroIsId: false);
209
+ case "LockPermissions":
210
+ return DefaultTupleFromRow(typeof(LockPermissionsTuple), row, columnZeroIsId: false);
211
+ case "Media":
212
+ return DefaultTupleFromRow(typeof(MediaTuple), row, columnZeroIsId: false);
213
+ case "MIME":
214
+ return DefaultTupleFromRow(typeof(MIMETuple), row, columnZeroIsId: false);
215
+ case "MoveFile":
216
+ return DefaultTupleFromRow(typeof(MoveFileTuple), row, columnZeroIsId: false);
217
+ case "MsiAssembly":
218
+ return DefaultTupleFromRow(typeof(MsiAssemblyTuple), row, columnZeroIsId: false);
219
+ case "MsiShortcutProperty":
220
+ return DefaultTupleFromRow(typeof(MsiShortcutPropertyTuple), row, columnZeroIsId: false);
221
+ case "ProgId":
222
+ return DefaultTupleFromRow(typeof(ProgIdTuple), row, columnZeroIsId: false);
223
+ case "Property":
224
+ return DefaultTupleFromRow(typeof(PropertyTuple), row, columnZeroIsId: false);
225
+ case "PublishComponent":
226
+ return DefaultTupleFromRow(typeof(PublishComponentTuple), row, columnZeroIsId: false);
227
+ case "Registry":
228
+ {
229
+ var value = FieldAsString(row, 4);
230
+ var valueType = RegistryValueType.String;
231
+ var valueAction = RegistryValueActionType.Write;
232
+
233
+ if (!String.IsNullOrEmpty(value))
234
+ {
235
+ if (value.StartsWith("#x", StringComparison.Ordinal))
236
+ {
237
+ valueType = RegistryValueType.Binary;
238
+ value = value.Substring(2);
239
+ }
240
+ else if (value.StartsWith("#%", StringComparison.Ordinal))
241
+ {
242
+ valueType = RegistryValueType.Expandable;
243
+ value = value.Substring(2);
244
+ }
245
+ else if (value.StartsWith("#", StringComparison.Ordinal))
246
+ {
247
+ valueType = RegistryValueType.Integer;
248
+ value = value.Substring(1);
249
+ }
250
+ else if (value.StartsWith("[~]", StringComparison.Ordinal) && value.EndsWith("[~]", StringComparison.Ordinal))
251
+ {
252
+ value = value.Substring(3, value.Length - 6);
253
+ valueType = RegistryValueType.MultiString;
254
+ valueAction = RegistryValueActionType.Write;
255
+ }
256
+ else if (value.StartsWith("[~]", StringComparison.Ordinal))
257
+ {
258
+ value = value.Substring(3);
259
+ valueType = RegistryValueType.MultiString;
260
+ valueAction = RegistryValueActionType.Append;
261
+ }
262
+ else if (value.EndsWith("[~]", StringComparison.Ordinal))
263
+ {
264
+ value = value.Substring(0, value.Length - 3);
265
+ valueType = RegistryValueType.MultiString;
266
+ valueAction = RegistryValueActionType.Prepend;
267
+ }
268
+ }
269
+
270
+ return new RegistryTuple(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Public, FieldAsString(row, 0)))
271
+ {
272
+ Root = (RegistryRootType)FieldAsInt(row, 1),
273
+ Key = FieldAsString(row, 2),
274
+ Name = FieldAsString(row, 3),
275
+ Value = value,
276
+ Component_ = FieldAsString(row, 5),
277
+ ValueAction = valueAction,
278
+ ValueType = valueType,
279
+ };
280
+ }
281
+
282
+ case "RegLocator":
283
+ return DefaultTupleFromRow(typeof(RegLocatorTuple), row, columnZeroIsId: false);
284
+ case "RemoveFile":
285
+ return DefaultTupleFromRow(typeof(RemoveFileTuple), row, columnZeroIsId: false);
286
+ case "RemoveRegistry":
287
+ {
288
+ return new RemoveRegistryTuple(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Public, FieldAsString(row, 0)))
289
+ {
290
+ Action = RemoveRegistryActionType.RemoveOnInstall,
291
+ Root = (RegistryRootType)FieldAsInt(row, 1),
292
+ Key = FieldAsString(row, 2),
293
+ Name = FieldAsString(row, 3),
294
+ Component_ = FieldAsString(row, 4),
295
+ };
296
+ }
297
+
298
+ case "ReserveCost":
299
+ return DefaultTupleFromRow(typeof(ReserveCostTuple), row, columnZeroIsId: false);
300
+ case "ServiceControl":
301
+ {
302
+ var events = FieldAsInt(row, 2);
303
+ var wait = FieldAsNullableInt(row, 4);
304
+ return new ServiceControlTuple(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Public, FieldAsString(row, 0)))
305
+ {
306
+ Name = FieldAsString(row, 1),
307
+ Arguments = FieldAsString(row, 3),
308
+ Wait = !wait.HasValue || wait.Value == 1,
309
+ Component_ = FieldAsString(row, 5),
310
+ InstallRemove = (events & WindowsInstallerConstants.MsidbServiceControlEventDelete) == WindowsInstallerConstants.MsidbServiceControlEventDelete,
311
+ UninstallRemove = (events & WindowsInstallerConstants.MsidbServiceControlEventUninstallDelete) == WindowsInstallerConstants.MsidbServiceControlEventUninstallDelete,
312
+ InstallStart = (events & WindowsInstallerConstants.MsidbServiceControlEventStart) == WindowsInstallerConstants.MsidbServiceControlEventStart,
313
+ UninstallStart = (events & WindowsInstallerConstants.MsidbServiceControlEventUninstallStart) == WindowsInstallerConstants.MsidbServiceControlEventUninstallStart,
314
+ InstallStop = (events & WindowsInstallerConstants.MsidbServiceControlEventStop) == WindowsInstallerConstants.MsidbServiceControlEventStop,
315
+ UninstallStop = (events & WindowsInstallerConstants.MsidbServiceControlEventUninstallStop) == WindowsInstallerConstants.MsidbServiceControlEventUninstallStop,
316
+ };
317
+ }
318
+
319
+ case "ServiceInstall":
320
+ return DefaultTupleFromRow(typeof(ServiceInstallTuple), row, columnZeroIsId: true);
321
+ case "Shortcut":
322
+ return DefaultTupleFromRow(typeof(ShortcutTuple), row, columnZeroIsId: true);
323
+ case "Signature":
324
+ return DefaultTupleFromRow(typeof(SignatureTuple), row, columnZeroIsId: false);
325
+ case "Upgrade":
326
+ {
327
+ var attributes = FieldAsInt(row, 4);
328
+ return new UpgradeTuple(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Public, FieldAsString(row, 0)))
329
+ {
330
+ UpgradeCode = FieldAsString(row, 0),
331
+ VersionMin = FieldAsString(row, 1),
332
+ VersionMax = FieldAsString(row, 2),
333
+ Language = FieldAsString(row, 3),
334
+ Remove = FieldAsString(row, 5),
335
+ ActionProperty = FieldAsString(row, 6),
336
+ MigrateFeatures = (attributes & WindowsInstallerConstants.MsidbUpgradeAttributesMigrateFeatures) == WindowsInstallerConstants.MsidbUpgradeAttributesMigrateFeatures,
337
+ OnlyDetect = (attributes & WindowsInstallerConstants.MsidbUpgradeAttributesOnlyDetect) == WindowsInstallerConstants.MsidbUpgradeAttributesOnlyDetect,
338
+ IgnoreRemoveFailures = (attributes & WindowsInstallerConstants.MsidbUpgradeAttributesIgnoreRemoveFailure) == WindowsInstallerConstants.MsidbUpgradeAttributesIgnoreRemoveFailure,
339
+ VersionMinInclusive = (attributes & WindowsInstallerConstants.MsidbUpgradeAttributesVersionMinInclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesVersionMinInclusive,
340
+ VersionMaxInclusive = (attributes & WindowsInstallerConstants.MsidbUpgradeAttributesVersionMaxInclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesVersionMaxInclusive,
341
+ ExcludeLanguages = (attributes & WindowsInstallerConstants.MsidbUpgradeAttributesLanguagesExclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesLanguagesExclusive,
342
+ };
343
+ }
344
+
345
+ case "Verb":
346
+ return DefaultTupleFromRow(typeof(VerbTuple), row, columnZeroIsId: false);
347
+ //case "WixAction":
348
+ // return new WixActionTuple(SourceLineNumber4(row.SourceLineNumbers))
349
+ // {
350
+ // SequenceTable = (SequenceTable)Enum.Parse(typeof(SequenceTable), FieldAsString(row, 0)),
351
+ // Action = FieldAsString(row, 1),
352
+ // Condition = FieldAsString(row, 2),
353
+ // Sequence = FieldAsInt(row, 3),
354
+ // Before = FieldAsString(row, 4),
355
+ // After = FieldAsString(row, 5),
356
+ // Overridable = FieldAsNullableInt(row, 6) != 0,
357
+ // };
358
+ case "WixFile":
359
+ var assemblyAttributes3 = FieldAsNullableInt(row, 1);
360
+ return new WixFileTuple(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Public, FieldAsString(row, 0)))
361
+ {
362
+ AssemblyType = assemblyAttributes3 == 0 ? FileAssemblyType.DotNetAssembly : assemblyAttributes3 == 1 ? FileAssemblyType.Win32Assembly : FileAssemblyType.NotAnAssembly,
363
+ File_AssemblyManifest = FieldAsString(row, 2),
364
+ File_AssemblyApplication = FieldAsString(row, 3),
365
+ Directory_ = FieldAsString(row, 4),
366
+ DiskId = FieldAsInt(row, 5), // TODO: BUGBUGBUG: AB#2626: DiskId is nullable in WiX v3.
367
+ Source = new IntermediateFieldPathValue() { Path = FieldAsString(row, 6) },
368
+ ProcessorArchitecture = FieldAsString(row, 7),
369
+ PatchGroup = FieldAsInt(row, 8),
370
+ Attributes = FieldAsInt(row, 9),
371
+ };
372
+ case "WixProperty":
373
+ {
374
+ var attributes = FieldAsInt(row, 1);
375
+ return new WixPropertyTuple(SourceLineNumber4(row.SourceLineNumbers))
376
+ {
377
+ Property_ = FieldAsString(row, 0),
378
+ Admin = (attributes & 0x1) == 0x1,
379
+ Hidden = (attributes & 0x2) == 0x2,
380
+ Secure = (attributes & 0x4) == 0x4,
381
+ };
382
+ }
383
+
384
+ default:
385
+ return GenericTupleFromCustomRow(row, columnZeroIsId: false);
386
+ }
387
+ }
388
+
389
+ private static CustomActionTargetType DetermineCustomActionTargetType(int type)
390
+ {
391
+ var targetType = default(CustomActionTargetType);
392
+
393
+ if ((type & WindowsInstallerConstants.MsidbCustomActionTypeVBScript) == WindowsInstallerConstants.MsidbCustomActionTypeVBScript)
394
+ {
395
+ targetType = CustomActionTargetType.VBScript;
396
+ }
397
+ else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeJScript) == WindowsInstallerConstants.MsidbCustomActionTypeJScript)
398
+ {
399
+ targetType = CustomActionTargetType.JScript;
400
+ }
401
+ else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeTextData) == WindowsInstallerConstants.MsidbCustomActionTypeTextData)
402
+ {
403
+ targetType = CustomActionTargetType.TextData;
404
+ }
405
+ else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeExe) == WindowsInstallerConstants.MsidbCustomActionTypeExe)
406
+ {
407
+ targetType = CustomActionTargetType.Exe;
408
+ }
409
+ else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeDll) == WindowsInstallerConstants.MsidbCustomActionTypeDll)
410
+ {
411
+ targetType = CustomActionTargetType.Dll;
412
+ }
413
+
414
+ return targetType;
415
+ }
416
+
417
+ private static CustomActionSourceType DetermineCustomActionSourceType(int type)
418
+ {
419
+ var sourceType = CustomActionSourceType.Binary;
420
+
421
+ if ((type & WindowsInstallerConstants.MsidbCustomActionTypeProperty) == WindowsInstallerConstants.MsidbCustomActionTypeProperty)
422
+ {
423
+ sourceType = CustomActionSourceType.Property;
424
+ }
425
+ else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeDirectory) == WindowsInstallerConstants.MsidbCustomActionTypeDirectory)
426
+ {
427
+ sourceType = CustomActionSourceType.Directory;
428
+ }
429
+ else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeSourceFile) == WindowsInstallerConstants.MsidbCustomActionTypeSourceFile)
430
+ {
431
+ sourceType = CustomActionSourceType.File;
432
+ }
433
+
434
+ return sourceType;
435
+ }
436
+
437
+ private static CustomActionExecutionType DetermineCustomActionExecutionType(int type)
438
+ {
439
+ var executionType = CustomActionExecutionType.Immediate;
440
+
441
+ if ((type & (WindowsInstallerConstants.MsidbCustomActionTypeInScript | WindowsInstallerConstants.MsidbCustomActionTypeCommit)) == (WindowsInstallerConstants.MsidbCustomActionTypeInScript | WindowsInstallerConstants.MsidbCustomActionTypeCommit))
442
+ {
443
+ executionType = CustomActionExecutionType.Commit;
444
+ }
445
+ else if ((type & (WindowsInstallerConstants.MsidbCustomActionTypeInScript | WindowsInstallerConstants.MsidbCustomActionTypeRollback)) == (WindowsInstallerConstants.MsidbCustomActionTypeInScript | WindowsInstallerConstants.MsidbCustomActionTypeRollback))
446
+ {
447
+ executionType = CustomActionExecutionType.Rollback;
448
+ }
449
+ else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeInScript) == WindowsInstallerConstants.MsidbCustomActionTypeInScript)
450
+ {
451
+ executionType = CustomActionExecutionType.Deferred;
452
+ }
453
+ else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeClientRepeat) == WindowsInstallerConstants.MsidbCustomActionTypeClientRepeat)
454
+ {
455
+ executionType = CustomActionExecutionType.ClientRepeat;
456
+ }
457
+ else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeOncePerProcess) == WindowsInstallerConstants.MsidbCustomActionTypeOncePerProcess)
458
+ {
459
+ executionType = CustomActionExecutionType.OncePerProcess;
460
+ }
461
+ else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeFirstSequence) == WindowsInstallerConstants.MsidbCustomActionTypeFirstSequence)
462
+ {
463
+ executionType = CustomActionExecutionType.FirstSequence;
464
+ }
465
+
466
+ return executionType;
467
+ }
468
+
469
+ private static IntermediateFieldType ColumnType3ToIntermediateFieldType4(Wix3.ColumnType columnType)
470
+ {
471
+ switch (columnType)
472
+ {
473
+ case Wix3.ColumnType.Number:
474
+ return IntermediateFieldType.Number;
475
+ case Wix3.ColumnType.Object:
476
+ return IntermediateFieldType.Path;
477
+ case Wix3.ColumnType.Unknown:
478
+ case Wix3.ColumnType.String:
479
+ case Wix3.ColumnType.Localized:
480
+ case Wix3.ColumnType.Preserved:
481
+ default:
482
+ return IntermediateFieldType.String;
483
+ }
484
+ }
485
+
486
+ private static IntermediateTuple DefaultTupleFromRow(Type tupleType, Wix3.Row row, bool columnZeroIsId)
487
+ {
488
+ var tuple = Activator.CreateInstance(tupleType) as IntermediateTuple;
489
+
490
+ SetTupleFieldsFromRow(row, tuple, columnZeroIsId);
491
+
492
+ tuple.SourceLineNumbers = SourceLineNumber4(row.SourceLineNumbers);
493
+ return tuple;
494
+ }
495
+
496
+ private static IntermediateTuple GenericTupleFromCustomRow(Wix3.Row row, bool columnZeroIsId)
497
+ {
498
+ var columnDefinitions = row.Table.Definition.Columns.Cast<Wix3.ColumnDefinition>();
499
+ var fieldDefinitions = columnDefinitions.Select(columnDefinition =>
500
+ new IntermediateFieldDefinition(columnDefinition.Name, ColumnType3ToIntermediateFieldType4(columnDefinition.Type))).ToArray();
501
+ var tupleDefinition = new IntermediateTupleDefinition(row.Table.Name, fieldDefinitions, null);
502
+ var tuple = new IntermediateTuple(tupleDefinition, SourceLineNumber4(row.SourceLineNumbers));
503
+
504
+ SetTupleFieldsFromRow(row, tuple, columnZeroIsId);
505
+
506
+ return tuple;
507
+ }
508
+
509
+ private static void SetTupleFieldsFromRow(Wix3.Row row, IntermediateTuple tuple, bool columnZeroIsId)
510
+ {
511
+ int offset = 0;
512
+ if (columnZeroIsId)
513
+ {
514
+ tuple.Id = GetIdentifierForRow(row);
515
+ offset = 1;
516
+ }
517
+
518
+ for (var i = offset; i < row.Fields.Length; ++i)
519
+ {
520
+ var column = row.Fields[i].Column;
521
+ switch (column.Type)
522
+ {
523
+ case Wix3.ColumnType.String:
524
+ case Wix3.ColumnType.Localized:
525
+ case Wix3.ColumnType.Object:
526
+ case Wix3.ColumnType.Preserved:
527
+ tuple.Set(i - offset, FieldAsString(row, i));
528
+ break;
529
+ case Wix3.ColumnType.Number:
530
+ int? nullableValue = FieldAsNullableInt(row, i);
531
+ // TODO: Consider whether null values should be coerced to their default value when
532
+ // a column is not nullable. For now, just pass through the null.
533
+ //int value = FieldAsInt(row, i);
534
+ //tuple.Set(i - offset, column.IsNullable ? nullableValue : value);
535
+ tuple.Set(i - offset, nullableValue);
536
+ break;
537
+ case Wix3.ColumnType.Unknown:
538
+ break;
539
+ }
540
+ }
541
+ }
542
+
543
+ private static Identifier GetIdentifierForRow(Wix3.Row row)
544
+ {
545
+ var column = row.Fields[0].Column;
546
+ switch (column.Type)
547
+ {
548
+ case Wix3.ColumnType.String:
549
+ case Wix3.ColumnType.Localized:
550
+ case Wix3.ColumnType.Object:
551
+ case Wix3.ColumnType.Preserved:
552
+ return new Identifier(AccessModifier.Public, (string)row.Fields[0].Data);
553
+ case Wix3.ColumnType.Number:
554
+ return new Identifier(AccessModifier.Public, FieldAsInt(row, 0));
555
+ default:
556
+ return null;
557
+ }
558
+ }
559
+
560
+ private static SectionType OutputType3ToSectionType4(Wix3.OutputType outputType)
561
+ {
562
+ switch (outputType)
563
+ {
564
+ case Wix3.OutputType.Bundle:
565
+ return SectionType.Bundle;
566
+ case Wix3.OutputType.Module:
567
+ return SectionType.Module;
568
+ case Wix3.OutputType.Patch:
569
+ return SectionType.Patch;
570
+ case Wix3.OutputType.PatchCreation:
571
+ return SectionType.PatchCreation;
572
+ case Wix3.OutputType.Product:
573
+ return SectionType.Product;
574
+ case Wix3.OutputType.Transform:
575
+ case Wix3.OutputType.Unknown:
576
+ default:
577
+ return SectionType.Unknown;
578
+ }
579
+ }
580
+
581
+ private static SourceLineNumber SourceLineNumber4(Wix3.SourceLineNumberCollection source)
582
+ {
583
+ return String.IsNullOrEmpty(source?.EncodedSourceLineNumbers) ? null : SourceLineNumber.CreateFromEncoded(source.EncodedSourceLineNumbers);
584
+ }
585
+
586
+ private static string FieldAsString(Wix3.Row row, int column)
587
+ {
588
+ return (string)row[column];
589
+ }
590
+
591
+ private static int FieldAsInt(Wix3.Row row, int column)
592
+ {
593
+ return Convert.ToInt32(row[column]);
594
+ }
595
+
596
+ private static int? FieldAsNullableInt(Wix3.Row row, int column)
597
+ {
598
+ var field = row.Fields[column];
599
+ if (field.Data == null)
600
+ {
601
+ return null;
602
+ }
603
+ else
604
+ {
605
+ return Convert.ToInt32(field.Data);
606
+ }
607
+ }
608
+ }
609
+}
src/WixToolset.Converters.Tupleizer/WixToolset.Converters.Tupleizer.csproj
new
+32
@@ -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
+
6
+ <PropertyGroup>
7
+ <TargetFramework>netstandard2.0</TargetFramework>
8
+ <Description>Tupleizer</Description>
9
+ <Title>WiX Toolset Converters Tuplizer</Title>
10
+ <DebugType>embedded</DebugType>
11
+ <PublishRepositoryUrl>true</PublishRepositoryUrl>
12
+ </PropertyGroup>
13
+
14
+ <PropertyGroup>
15
+ <NoWarn>NU1701</NoWarn>
16
+ </PropertyGroup>
17
+
18
+ <ItemGroup>
19
+ <Reference Include="wix" HintPath="..\deps\wix.dll" />
20
+ <None Include="..\deps\wix.dll" Pack="true" PackagePath="lib\net461" />
21
+ </ItemGroup>
22
+
23
+ <ItemGroup>
24
+ <PackageReference Include="WixToolset.Core" Version="4.0.*" />
25
+ <PackageReference Include="WixToolset.Core.WindowsInstaller" Version="4.0.*" />
26
+ </ItemGroup>
27
+
28
+ <ItemGroup>
29
+ <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-18618-05" PrivateAssets="All"/>
30
+ <PackageReference Include="Nerdbank.GitVersioning" Version="2.1.65" PrivateAssets="All" />
31
+ </ItemGroup>
32
+</Project>
src/WixToolset.Converters/Wix3Converter.cs
new
+652
@@ -0,0 +1,652 @@
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 WixToolset.Converters
4
+{
5
+ using System;
6
+ using System.Collections.Generic;
7
+ using System.Globalization;
8
+ using System.IO;
9
+ using System.Linq;
10
+ using System.Text;
11
+ using System.Text.RegularExpressions;
12
+ using System.Xml;
13
+ using System.Xml.Linq;
14
+ using WixToolset.Data;
15
+ using WixToolset.Extensibility.Services;
16
+
17
+ /// <summary>
18
+ /// WiX source code converter.
19
+ /// </summary>
20
+ public class Wix3Converter
21
+ {
22
+ private static readonly Regex AddPrefix = new Regex(@"^[^a-zA-Z_]", RegexOptions.Compiled);
23
+ private static readonly Regex IllegalIdentifierCharacters = new Regex(@"[^A-Za-z0-9_\.]|\.{2,}", RegexOptions.Compiled); // non 'words' and assorted valid characters
24
+
25
+ private const char XDocumentNewLine = '\n'; // XDocument normalizes "\r\n" to just "\n".
26
+ private static readonly XNamespace WixNamespace = "http://wixtoolset.org/schemas/v4/wxs";
27
+
28
+ private static readonly XName DirectoryElementName = WixNamespace + "Directory";
29
+ private static readonly XName FileElementName = WixNamespace + "File";
30
+ private static readonly XName ExePackageElementName = WixNamespace + "ExePackage";
31
+ private static readonly XName MsiPackageElementName = WixNamespace + "MsiPackage";
32
+ private static readonly XName MspPackageElementName = WixNamespace + "MspPackage";
33
+ private static readonly XName MsuPackageElementName = WixNamespace + "MsuPackage";
34
+ private static readonly XName PayloadElementName = WixNamespace + "Payload";
35
+ private static readonly XName CustomActionElementName = WixNamespace + "CustomAction";
36
+ private static readonly XName PropertyElementName = WixNamespace + "Property";
37
+ private static readonly XName WixElementWithoutNamespaceName = XNamespace.None + "Wix";
38
+
39
+ private static readonly Dictionary<string, XNamespace> OldToNewNamespaceMapping = new Dictionary<string, XNamespace>()
40
+ {
41
+ { "http://schemas.microsoft.com/wix/BalExtension", "http://wixtoolset.org/schemas/v4/wxs/bal" },
42
+ { "http://schemas.microsoft.com/wix/ComPlusExtension", "http://wixtoolset.org/schemas/v4/wxs/complus" },
43
+ { "http://schemas.microsoft.com/wix/DependencyExtension", "http://wixtoolset.org/schemas/v4/wxs/dependency" },
44
+ { "http://schemas.microsoft.com/wix/DifxAppExtension", "http://wixtoolset.org/schemas/v4/wxs/difxapp" },
45
+ { "http://schemas.microsoft.com/wix/FirewallExtension", "http://wixtoolset.org/schemas/v4/wxs/firewall" },
46
+ { "http://schemas.microsoft.com/wix/GamingExtension", "http://wixtoolset.org/schemas/v4/wxs/gaming" },
47
+ { "http://schemas.microsoft.com/wix/IIsExtension", "http://wixtoolset.org/schemas/v4/wxs/iis" },
48
+ { "http://schemas.microsoft.com/wix/MsmqExtension", "http://wixtoolset.org/schemas/v4/wxs/msmq" },
49
+ { "http://schemas.microsoft.com/wix/NetFxExtension", "http://wixtoolset.org/schemas/v4/wxs/netfx" },
50
+ { "http://schemas.microsoft.com/wix/PSExtension", "http://wixtoolset.org/schemas/v4/wxs/powershell" },
51
+ { "http://schemas.microsoft.com/wix/SqlExtension", "http://wixtoolset.org/schemas/v4/wxs/sql" },
52
+ { "http://schemas.microsoft.com/wix/TagExtension", "http://wixtoolset.org/schemas/v4/wxs/tag" },
53
+ { "http://schemas.microsoft.com/wix/UtilExtension", "http://wixtoolset.org/schemas/v4/wxs/util" },
54
+ { "http://schemas.microsoft.com/wix/VSExtension", "http://wixtoolset.org/schemas/v4/wxs/vs" },
55
+ { "http://wixtoolset.org/schemas/thmutil/2010", "http://wixtoolset.org/schemas/v4/thmutil" },
56
+ { "http://schemas.microsoft.com/wix/2009/Lux", "http://wixtoolset.org/schemas/v4/lux" },
57
+ { "http://schemas.microsoft.com/wix/2006/wi", "http://wixtoolset.org/schemas/v4/wxs" },
58
+ { "http://schemas.microsoft.com/wix/2006/localization", "http://wixtoolset.org/schemas/v4/wxl" },
59
+ { "http://schemas.microsoft.com/wix/2006/libraries", "http://wixtoolset.org/schemas/v4/wixlib" },
60
+ { "http://schemas.microsoft.com/wix/2006/objects", "http://wixtoolset.org/schemas/v4/wixobj" },
61
+ { "http://schemas.microsoft.com/wix/2006/outputs", "http://wixtoolset.org/schemas/v4/wixout" },
62
+ { "http://schemas.microsoft.com/wix/2007/pdbs", "http://wixtoolset.org/schemas/v4/wixpdb" },
63
+ { "http://schemas.microsoft.com/wix/2003/04/actions", "http://wixtoolset.org/schemas/v4/wi/actions" },
64
+ { "http://schemas.microsoft.com/wix/2006/tables", "http://wixtoolset.org/schemas/v4/wi/tables" },
65
+ { "http://schemas.microsoft.com/wix/2006/WixUnit", "http://wixtoolset.org/schemas/v4/wixunit" },
66
+ };
67
+
68
+ private readonly Dictionary<XName, Action<XElement>> ConvertElementMapping;
69
+
70
+ /// <summary>
71
+ /// Instantiate a new Converter class.
72
+ /// </summary>
73
+ /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param>
74
+ /// <param name="errorsAsWarnings">Test errors to display as warnings.</param>
75
+ /// <param name="ignoreErrors">Test errors to ignore.</param>
76
+ public Wix3Converter(IMessaging messaging, int indentationAmount, IEnumerable<string> errorsAsWarnings = null, IEnumerable<string> ignoreErrors = null)
77
+ {
78
+ this.ConvertElementMapping = new Dictionary<XName, Action<XElement>>
79
+ {
80
+ { Wix3Converter.DirectoryElementName, this.ConvertDirectoryElement },
81
+ { Wix3Converter.FileElementName, this.ConvertFileElement },
82
+ { Wix3Converter.ExePackageElementName, this.ConvertSuppressSignatureValidation },
83
+ { Wix3Converter.MsiPackageElementName, this.ConvertSuppressSignatureValidation },
84
+ { Wix3Converter.MspPackageElementName, this.ConvertSuppressSignatureValidation },
85
+ { Wix3Converter.MsuPackageElementName, this.ConvertSuppressSignatureValidation },
86
+ { Wix3Converter.PayloadElementName, this.ConvertSuppressSignatureValidation },
87
+ { Wix3Converter.CustomActionElementName, this.ConvertCustomActionElement },
88
+ { Wix3Converter.PropertyElementName, this.ConvertPropertyElement },
89
+ { Wix3Converter.WixElementWithoutNamespaceName, this.ConvertWixElementWithoutNamespace },
90
+ };
91
+
92
+ this.Messaging = messaging;
93
+
94
+ this.IndentationAmount = indentationAmount;
95
+
96
+ this.ErrorsAsWarnings = new HashSet<ConverterTestType>(this.YieldConverterTypes(errorsAsWarnings));
97
+
98
+ this.IgnoreErrors = new HashSet<ConverterTestType>(this.YieldConverterTypes(ignoreErrors));
99
+ }
100
+
101
+ private int Errors { get; set; }
102
+
103
+ private HashSet<ConverterTestType> ErrorsAsWarnings { get; set; }
104
+
105
+ private HashSet<ConverterTestType> IgnoreErrors { get; set; }
106
+
107
+ private IMessaging Messaging { get; }
108
+
109
+ private int IndentationAmount { get; set; }
110
+
111
+ private string SourceFile { get; set; }
112
+
113
+ /// <summary>
114
+ /// Convert a file.
115
+ /// </summary>
116
+ /// <param name="sourceFile">The file to convert.</param>
117
+ /// <param name="saveConvertedFile">Option to save the converted errors that are found.</param>
118
+ /// <returns>The number of errors found.</returns>
119
+ public int ConvertFile(string sourceFile, bool saveConvertedFile)
120
+ {
121
+ XDocument document;
122
+
123
+ // Set the instance info.
124
+ this.Errors = 0;
125
+ this.SourceFile = sourceFile;
126
+
127
+ try
128
+ {
129
+ document = XDocument.Load(this.SourceFile, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
130
+ }
131
+ catch (XmlException e)
132
+ {
133
+ this.OnError(ConverterTestType.XmlException, null, "The xml is invalid. Detail: '{0}'", e.Message);
134
+
135
+ return this.Errors;
136
+ }
137
+
138
+ this.ConvertDocument(document);
139
+
140
+ // Fix errors if requested and necessary.
141
+ if (saveConvertedFile && 0 < this.Errors)
142
+ {
143
+ try
144
+ {
145
+ using (var writer = File.CreateText(this.SourceFile))
146
+ {
147
+ document.Save(writer, SaveOptions.DisableFormatting | SaveOptions.OmitDuplicateNamespaces);
148
+ }
149
+ }
150
+ catch (UnauthorizedAccessException)
151
+ {
152
+ this.OnError(ConverterTestType.UnauthorizedAccessException, null, "Could not write to file.");
153
+ }
154
+ }
155
+
156
+ return this.Errors;
157
+ }
158
+
159
+ /// <summary>
160
+ /// Convert a document.
161
+ /// </summary>
162
+ /// <param name="document">The document to convert.</param>
163
+ /// <returns>The number of errors found.</returns>
164
+ public int ConvertDocument(XDocument document)
165
+ {
166
+ var declaration = document.Declaration;
167
+
168
+ // Convert the declaration.
169
+ if (null != declaration)
170
+ {
171
+ if (!String.Equals("utf-8", declaration.Encoding, StringComparison.OrdinalIgnoreCase))
172
+ {
173
+ if (this.OnError(ConverterTestType.DeclarationEncodingWrong, document.Root, "The XML declaration encoding is not properly set to 'utf-8'."))
174
+ {
175
+ declaration.Encoding = "utf-8";
176
+ }
177
+ }
178
+ }
179
+ else // missing declaration
180
+ {
181
+ if (this.OnError(ConverterTestType.DeclarationMissing, null, "This file is missing an XML declaration on the first line."))
182
+ {
183
+ document.Declaration = new XDeclaration("1.0", "utf-8", null);
184
+ document.Root.AddBeforeSelf(new XText(XDocumentNewLine.ToString()));
185
+ }
186
+ }
187
+
188
+ // Start converting the nodes at the top.
189
+ this.ConvertNodes(document.Nodes(), 0);
190
+
191
+ return this.Errors;
192
+ }
193
+
194
+ private void ConvertNodes(IEnumerable<XNode> nodes, int level)
195
+ {
196
+ // Note we operate on a copy of the node list since we may
197
+ // remove some whitespace nodes during this processing.
198
+ foreach (var node in nodes.ToList())
199
+ {
200
+ if (node is XText text)
201
+ {
202
+ if (!String.IsNullOrWhiteSpace(text.Value))
203
+ {
204
+ text.Value = text.Value.Trim();
205
+ }
206
+ else if (node.NextNode is XCData cdata)
207
+ {
208
+ this.EnsurePrecedingWhitespaceRemoved(text, node, ConverterTestType.WhitespacePrecedingNodeWrong);
209
+ }
210
+ else if (node.NextNode is XElement element)
211
+ {
212
+ this.EnsurePrecedingWhitespaceCorrect(text, node, level, ConverterTestType.WhitespacePrecedingNodeWrong);
213
+ }
214
+ else if (node.NextNode is null) // this is the space before the close element
215
+ {
216
+ if (node.PreviousNode is null || node.PreviousNode is XCData)
217
+ {
218
+ this.EnsurePrecedingWhitespaceRemoved(text, node.Parent, ConverterTestType.WhitespacePrecedingEndElementWrong);
219
+ }
220
+ else if (level == 0) // root element's close tag
221
+ {
222
+ this.EnsurePrecedingWhitespaceCorrect(text, node, 0, ConverterTestType.WhitespacePrecedingEndElementWrong);
223
+ }
224
+ else
225
+ {
226
+ this.EnsurePrecedingWhitespaceCorrect(text, node, level - 1, ConverterTestType.WhitespacePrecedingEndElementWrong);
227
+ }
228
+ }
229
+ }
230
+ else if (node is XElement element)
231
+ {
232
+ this.ConvertElement(element);
233
+
234
+ this.ConvertNodes(element.Nodes(), level + 1);
235
+ }
236
+ }
237
+ }
238
+
239
+ private void EnsurePrecedingWhitespaceCorrect(XText whitespace, XNode node, int level, ConverterTestType testType)
240
+ {
241
+ if (!Wix3Converter.LeadingWhitespaceValid(this.IndentationAmount, level, whitespace.Value))
242
+ {
243
+ var message = testType == ConverterTestType.WhitespacePrecedingEndElementWrong ? "The whitespace preceding this end element is incorrect." : "The whitespace preceding this node is incorrect.";
244
+
245
+ if (this.OnError(testType, node, message))
246
+ {
247
+ Wix3Converter.FixupWhitespace(this.IndentationAmount, level, whitespace);
248
+ }
249
+ }
250
+ }
251
+
252
+ private void EnsurePrecedingWhitespaceRemoved(XText whitespace, XNode node, ConverterTestType testType)
253
+ {
254
+ if (!String.IsNullOrEmpty(whitespace.Value))
255
+ {
256
+ var message = testType == ConverterTestType.WhitespacePrecedingEndElementWrong ? "The whitespace preceding this end element is incorrect." : "The whitespace preceding this node is incorrect.";
257
+
258
+ if (this.OnError(testType, node, message))
259
+ {
260
+ whitespace.Remove();
261
+ }
262
+ }
263
+ }
264
+
265
+ private void ConvertElement(XElement element)
266
+ {
267
+ // Gather any deprecated namespaces, then update this element tree based on those deprecations.
268
+ var deprecatedToUpdatedNamespaces = new Dictionary<XNamespace, XNamespace>();
269
+
270
+ foreach (var declaration in element.Attributes().Where(a => a.IsNamespaceDeclaration))
271
+ {
272
+ if (Wix3Converter.OldToNewNamespaceMapping.TryGetValue(declaration.Value, out var ns))
273
+ {
274
+ if (this.OnError(ConverterTestType.XmlnsValueWrong, declaration, "The namespace '{0}' is out of date. It must be '{1}'.", declaration.Value, ns.NamespaceName))
275
+ {
276
+ deprecatedToUpdatedNamespaces.Add(declaration.Value, ns);
277
+ }
278
+ }
279
+ }
280
+
281
+ if (deprecatedToUpdatedNamespaces.Any())
282
+ {
283
+ Wix3Converter.UpdateElementsWithDeprecatedNamespaces(element.DescendantsAndSelf(), deprecatedToUpdatedNamespaces);
284
+ }
285
+
286
+ // Apply any specialized conversion actions.
287
+ if (this.ConvertElementMapping.TryGetValue(element.Name, out var convert))
288
+ {
289
+ convert(element);
290
+ }
291
+ }
292
+
293
+ private void ConvertDirectoryElement(XElement element)
294
+ {
295
+ if (null == element.Attribute("Name"))
296
+ {
297
+ var attribute = element.Attribute("ShortName");
298
+ if (null != attribute)
299
+ {
300
+ var shortName = attribute.Value;
301
+ if (this.OnError(ConverterTestType.AssignDirectoryNameFromShortName, element, "The directory ShortName attribute is being renamed to Name since Name wasn't specified for value '{0}'", shortName))
302
+ {
303
+ element.Add(new XAttribute("Name", shortName));
304
+ attribute.Remove();
305
+ }
306
+ }
307
+ }
308
+ }
309
+
310
+ private void ConvertFileElement(XElement element)
311
+ {
312
+ if (null == element.Attribute("Id"))
313
+ {
314
+ var attribute = element.Attribute("Name");
315
+
316
+ if (null == attribute)
317
+ {
318
+ attribute = element.Attribute("Source");
319
+ }
320
+
321
+ if (null != attribute)
322
+ {
323
+ var name = Path.GetFileName(attribute.Value);
324
+
325
+ if (this.OnError(ConverterTestType.AssignAnonymousFileId, element, "The file id is being updated to '{0}' to ensure it remains the same as the default", name))
326
+ {
327
+ IEnumerable<XAttribute> attributes = element.Attributes().ToList();
328
+ element.RemoveAttributes();
329
+ element.Add(new XAttribute("Id", GetIdentifierFromName(name)));
330
+ element.Add(attributes);
331
+ }
332
+ }
333
+ }
334
+ }
335
+
336
+ private void ConvertSuppressSignatureValidation(XElement element)
337
+ {
338
+ var suppressSignatureValidation = element.Attribute("SuppressSignatureValidation");
339
+
340
+ if (null != suppressSignatureValidation)
341
+ {
342
+ if (this.OnError(ConverterTestType.SuppressSignatureValidationDeprecated, element, "The chain package element contains deprecated '{0}' attribute. Use the 'EnableSignatureValidation' attribute instead.", suppressSignatureValidation))
343
+ {
344
+ if ("no" == suppressSignatureValidation.Value)
345
+ {
346
+ element.Add(new XAttribute("EnableSignatureValidation", "yes"));
347
+ }
348
+ }
349
+
350
+ suppressSignatureValidation.Remove();
351
+ }
352
+ }
353
+
354
+ private void ConvertCustomActionElement(XElement xCustomAction)
355
+ {
356
+ var xBinaryKey = xCustomAction.Attribute("BinaryKey");
357
+
358
+ if (xBinaryKey?.Value == "WixCA")
359
+ {
360
+ if (this.OnError(ConverterTestType.WixCABinaryIdRenamed, xCustomAction, "The WixCA custom action DLL Binary table id has been renamed. Use the id 'UtilCA' instead."))
361
+ {
362
+ xBinaryKey.Value = "UtilCA";
363
+ }
364
+ }
365
+
366
+ var xDllEntry = xCustomAction.Attribute("DllEntry");
367
+
368
+ if (xDllEntry?.Value == "CAQuietExec" || xDllEntry?.Value == "CAQuietExec64")
369
+ {
370
+ if (this.OnError(ConverterTestType.QuietExecCustomActionsRenamed, xCustomAction, "The CAQuietExec and CAQuietExec64 custom action ids have been renamed. Use the ids 'WixQuietExec' and 'WixQuietExec64' instead."))
371
+ {
372
+ xDllEntry.Value = xDllEntry.Value.Replace("CAQuietExec", "WixQuietExec");
373
+ }
374
+ }
375
+
376
+ var xProperty = xCustomAction.Attribute("Property");
377
+
378
+ if (xProperty?.Value == "QtExecCmdLine" || xProperty?.Value == "QtExec64CmdLine")
379
+ {
380
+ if (this.OnError(ConverterTestType.QuietExecCustomActionsRenamed, xCustomAction, "The QtExecCmdLine and QtExec64CmdLine property ids have been renamed. Use the ids 'WixQuietExecCmdLine' and 'WixQuietExec64CmdLine' instead."))
381
+ {
382
+ xProperty.Value = xProperty.Value.Replace("QtExec", "WixQuietExec");
383
+ }
384
+ }
385
+ }
386
+
387
+ private void ConvertPropertyElement(XElement xProperty)
388
+ {
389
+ var xId = xProperty.Attribute("Id");
390
+
391
+ if (xId.Value == "QtExecCmdTimeout")
392
+ {
393
+ this.OnError(ConverterTestType.QtExecCmdTimeoutAmbiguous, xProperty, "QtExecCmdTimeout was previously used for both CAQuietExec and CAQuietExec64. For WixQuietExec, use WixQuietExecCmdTimeout. For WixQuietExec64, use WixQuietExec64CmdTimeout.");
394
+ }
395
+ }
396
+
397
+ /// <summary>
398
+ /// Converts a Wix element.
399
+ /// </summary>
400
+ /// <param name="element">The Wix element to convert.</param>
401
+ /// <returns>The converted element.</returns>
402
+ private void ConvertWixElementWithoutNamespace(XElement element)
403
+ {
404
+ if (this.OnError(ConverterTestType.XmlnsMissing, element, "The xmlns attribute is missing. It must be present with a value of '{0}'.", WixNamespace.NamespaceName))
405
+ {
406
+ element.Name = WixNamespace.GetName(element.Name.LocalName);
407
+
408
+ element.Add(new XAttribute("xmlns", WixNamespace.NamespaceName)); // set the default namespace.
409
+
410
+ foreach (var elementWithoutNamespace in element.Elements().Where(e => XNamespace.None == e.Name.Namespace))
411
+ {
412
+ elementWithoutNamespace.Name = WixNamespace.GetName(elementWithoutNamespace.Name.LocalName);
413
+ }
414
+ }
415
+ }
416
+
417
+ private IEnumerable<ConverterTestType> YieldConverterTypes(IEnumerable<string> types)
418
+ {
419
+ if (null != types)
420
+ {
421
+ foreach (var type in types)
422
+ {
423
+
424
+ if (Enum.TryParse<ConverterTestType>(type, true, out var itt))
425
+ {
426
+ yield return itt;
427
+ }
428
+ else // not a known ConverterTestType
429
+ {
430
+ this.OnError(ConverterTestType.ConverterTestTypeUnknown, null, "Unknown error type: '{0}'.", type);
431
+ }
432
+ }
433
+ }
434
+ }
435
+
436
+ private static void UpdateElementsWithDeprecatedNamespaces(IEnumerable<XElement> elements, Dictionary<XNamespace, XNamespace> deprecatedToUpdatedNamespaces)
437
+ {
438
+ foreach (var element in elements)
439
+ {
440
+
441
+ if (deprecatedToUpdatedNamespaces.TryGetValue(element.Name.Namespace, out var ns))
442
+ {
443
+ element.Name = ns.GetName(element.Name.LocalName);
444
+ }
445
+
446
+ // Remove all the attributes and add them back to with their namespace updated (as necessary).
447
+ IEnumerable<XAttribute> attributes = element.Attributes().ToList();
448
+ element.RemoveAttributes();
449
+
450
+ foreach (var attribute in attributes)
451
+ {
452
+ var convertedAttribute = attribute;
453
+
454
+ if (attribute.IsNamespaceDeclaration)
455
+ {
456
+ if (deprecatedToUpdatedNamespaces.TryGetValue(attribute.Value, out ns))
457
+ {
458
+ convertedAttribute = ("xmlns" == attribute.Name.LocalName) ? new XAttribute(attribute.Name.LocalName, ns.NamespaceName) : new XAttribute(XNamespace.Xmlns + attribute.Name.LocalName, ns.NamespaceName);
459
+ }
460
+ }
461
+ else if (deprecatedToUpdatedNamespaces.TryGetValue(attribute.Name.Namespace, out ns))
462
+ {
463
+ convertedAttribute = new XAttribute(ns.GetName(attribute.Name.LocalName), attribute.Value);
464
+ }
465
+
466
+ element.Add(convertedAttribute);
467
+ }
468
+ }
469
+ }
470
+
471
+ /// <summary>
472
+ /// Determine if the whitespace preceding a node is appropriate for its depth level.
473
+ /// </summary>
474
+ /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param>
475
+ /// <param name="level">The depth level that should match this whitespace.</param>
476
+ /// <param name="whitespace">The whitespace to validate.</param>
477
+ /// <returns>true if the whitespace is legal; false otherwise.</returns>
478
+ private static bool LeadingWhitespaceValid(int indentationAmount, int level, string whitespace)
479
+ {
480
+ // Strip off leading newlines; there can be an arbitrary number of these.
481
+ whitespace = whitespace.TrimStart(XDocumentNewLine);
482
+
483
+ var indentation = new string(' ', level * indentationAmount);
484
+
485
+ return whitespace == indentation;
486
+ }
487
+
488
+ /// <summary>
489
+ /// Fix the whitespace in a whitespace node.
490
+ /// </summary>
491
+ /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param>
492
+ /// <param name="level">The depth level of the desired whitespace.</param>
493
+ /// <param name="whitespace">The whitespace node to fix.</param>
494
+ private static void FixupWhitespace(int indentationAmount, int level, XText whitespace)
495
+ {
496
+ var value = new StringBuilder(whitespace.Value.Length);
497
+
498
+ // Keep any previous preceeding new lines.
499
+ var newlines = whitespace.Value.TakeWhile(c => c == XDocumentNewLine).Count();
500
+
501
+ // Ensure there is always at least one new line before the indentation.
502
+ value.Append(XDocumentNewLine, newlines == 0 ? 1 : newlines);
503
+
504
+ whitespace.Value = value.Append(' ', level * indentationAmount).ToString();
505
+ }
506
+
507
+ /// <summary>
508
+ /// Output an error message to the console.
509
+ /// </summary>
510
+ /// <param name="converterTestType">The type of converter test.</param>
511
+ /// <param name="node">The node that caused the error.</param>
512
+ /// <param name="message">Detailed error message.</param>
513
+ /// <param name="args">Additional formatted string arguments.</param>
514
+ /// <returns>Returns true indicating that action should be taken on this error, and false if it should be ignored.</returns>
515
+ private bool OnError(ConverterTestType converterTestType, XObject node, string message, params object[] args)
516
+ {
517
+ if (this.IgnoreErrors.Contains(converterTestType)) // ignore the error
518
+ {
519
+ return false;
520
+ }
521
+
522
+ // Increase the error count.
523
+ this.Errors++;
524
+
525
+ var sourceLine = (null == node) ? new SourceLineNumber(this.SourceFile ?? "wixcop.exe") : new SourceLineNumber(this.SourceFile, ((IXmlLineInfo)node).LineNumber);
526
+ var warning = this.ErrorsAsWarnings.Contains(converterTestType);
527
+ var display = String.Format(CultureInfo.CurrentCulture, message, args);
528
+
529
+ var msg = new Message(sourceLine, warning ? MessageLevel.Warning : MessageLevel.Error, (int)converterTestType, "{0} ({1})", display, converterTestType.ToString());
530
+
531
+ this.Messaging.Write(msg);
532
+
533
+ return true;
534
+ }
535
+
536
+ /// <summary>
537
+ /// Return an identifier based on passed file/directory name
538
+ /// </summary>
539
+ /// <param name="name">File/directory name to generate identifer from</param>
540
+ /// <returns>A version of the name that is a legal identifier.</returns>
541
+ /// <remarks>This is duplicated from WiX's Common class.</remarks>
542
+ private static string GetIdentifierFromName(string name)
543
+ {
544
+ string result = IllegalIdentifierCharacters.Replace(name, "_"); // replace illegal characters with "_".
545
+
546
+ // MSI identifiers must begin with an alphabetic character or an
547
+ // underscore. Prefix all other values with an underscore.
548
+ if (AddPrefix.IsMatch(name))
549
+ {
550
+ result = String.Concat("_", result);
551
+ }
552
+
553
+ return result;
554
+ }
555
+
556
+ /// <summary>
557
+ /// Converter test types. These are used to condition error messages down to warnings.
558
+ /// </summary>
559
+ private enum ConverterTestType
560
+ {
561
+ /// <summary>
562
+ /// Internal-only: displayed when a string cannot be converted to an ConverterTestType.
563
+ /// </summary>
564
+ ConverterTestTypeUnknown,
565
+
566
+ /// <summary>
567
+ /// Displayed when an XML loading exception has occurred.
568
+ /// </summary>
569
+ XmlException,
570
+
571
+ /// <summary>
572
+ /// Displayed when a file cannot be accessed; typically when trying to save back a fixed file.
573
+ /// </summary>
574
+ UnauthorizedAccessException,
575
+
576
+ /// <summary>
577
+ /// Displayed when the encoding attribute in the XML declaration is not 'UTF-8'.
578
+ /// </summary>
579
+ DeclarationEncodingWrong,
580
+
581
+ /// <summary>
582
+ /// Displayed when the XML declaration is missing from the source file.
583
+ /// </summary>
584
+ DeclarationMissing,
585
+
586
+ /// <summary>
587
+ /// Displayed when the whitespace preceding a CDATA node is wrong.
588
+ /// </summary>
589
+ WhitespacePrecedingCDATAWrong,
590
+
591
+ /// <summary>
592
+ /// Displayed when the whitespace preceding a node is wrong.
593
+ /// </summary>
594
+ WhitespacePrecedingNodeWrong,
595
+
596
+ /// <summary>
597
+ /// Displayed when an element is not empty as it should be.
598
+ /// </summary>
599
+ NotEmptyElement,
600
+
601
+ /// <summary>
602
+ /// Displayed when the whitespace following a CDATA node is wrong.
603
+ /// </summary>
604
+ WhitespaceFollowingCDATAWrong,
605
+
606
+ /// <summary>
607
+ /// Displayed when the whitespace preceding an end element is wrong.
608
+ /// </summary>
609
+ WhitespacePrecedingEndElementWrong,
610
+
611
+ /// <summary>
612
+ /// Displayed when the xmlns attribute is missing from the document element.
613
+ /// </summary>
614
+ XmlnsMissing,
615
+
616
+ /// <summary>
617
+ /// Displayed when the xmlns attribute on the document element is wrong.
618
+ /// </summary>
619
+ XmlnsValueWrong,
620
+
621
+ /// <summary>
622
+ /// Assign an identifier to a File element when on Id attribute is specified.
623
+ /// </summary>
624
+ AssignAnonymousFileId,
625
+
626
+ /// <summary>
627
+ /// SuppressSignatureValidation attribute is deprecated and replaced with EnableSignatureValidation.
628
+ /// </summary>
629
+ SuppressSignatureValidationDeprecated,
630
+
631
+ /// <summary>
632
+ /// WixCA Binary/@Id has been renamed to UtilCA.
633
+ /// </summary>
634
+ WixCABinaryIdRenamed,
635
+
636
+ /// <summary>
637
+ /// QtExec custom actions have been renamed.
638
+ /// </summary>
639
+ QuietExecCustomActionsRenamed,
640
+
641
+ /// <summary>
642
+ /// QtExecCmdTimeout was previously used for both CAQuietExec and CAQuietExec64. For WixQuietExec, use WixQuietExecCmdTimeout. For WixQuietExec64, use WixQuietExec64CmdTimeout.
643
+ /// </summary>
644
+ QtExecCmdTimeoutAmbiguous,
645
+
646
+ /// <summary>
647
+ /// Directory/@ShortName may only be specified with Directory/@Name.
648
+ /// </summary>
649
+ AssignDirectoryNameFromShortName,
650
+ }
651
+ }
652
+}
src/WixToolset.Converters/WixToolset.Converters.csproj
new
+26
@@ -0,0 +1,26 @@
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
+
6
+ <PropertyGroup>
7
+ <TargetFramework>netstandard2.0</TargetFramework>
8
+ <Description>Converter</Description>
9
+ <Title>WiX Toolset Converters</Title>
10
+ <DebugType>embedded</DebugType>
11
+ <PublishRepositoryUrl>true</PublishRepositoryUrl>
12
+ </PropertyGroup>
13
+
14
+ <PropertyGroup>
15
+ <NoWarn>NU1701</NoWarn>
16
+ </PropertyGroup>
17
+
18
+ <ItemGroup>
19
+ <PackageReference Include="WixToolset.Core" Version="4.0.*" />
20
+ </ItemGroup>
21
+
22
+ <ItemGroup>
23
+ <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-18618-05" PrivateAssets="All"/>
24
+ <PackageReference Include="Nerdbank.GitVersioning" Version="2.1.65" PrivateAssets="All" />
25
+ </ItemGroup>
26
+</Project>
src/deps/wix.dll
Binary files /dev/null and b/src/deps/wix.dll differ
src/test/WixToolsetTest.Converters.Tupleizer/ConvertTuplesFixture.cs
new
+391
@@ -0,0 +1,391 @@
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 WixToolsetTest.Converters.Tupleizer
4
+{
5
+ using System;
6
+ using System.IO;
7
+ using System.Linq;
8
+ using WixBuildTools.TestSupport;
9
+ using Wix3 = Microsoft.Tools.WindowsInstallerXml;
10
+ using WixToolset.Converters.Tupleizer;
11
+ using WixToolset.Data;
12
+ using WixToolset.Data.WindowsInstaller;
13
+ using WixToolset.Data.Tuples;
14
+ using Xunit;
15
+
16
+ public class ConvertTuplesFixture
17
+ {
18
+ [Fact]
19
+ public void CanLoadWixoutAndConvertToIntermediate()
20
+ {
21
+ var rootFolder = TestData.Get();
22
+ var dataFolder = TestData.Get(@"TestData\Integration");
23
+
24
+ using (var fs = new DisposableFileSystem())
25
+ {
26
+ var intermediateFolder = fs.GetFolder();
27
+
28
+ var path = Path.Combine(dataFolder, "test.wixout");
29
+ var output = Wix3.Output.Load(path, suppressVersionCheck: true, suppressSchema: true);
30
+
31
+ var command = new ConvertTuplesCommand();
32
+ var intermediate = command.Execute(output);
33
+
34
+ Assert.NotNull(intermediate);
35
+ Assert.Single(intermediate.Sections);
36
+ Assert.Equal(String.Empty, intermediate.Id);
37
+
38
+ // Save and load to guarantee round-tripping support.
39
+ //
40
+ var wixiplFile = Path.Combine(intermediateFolder, "test.wixipl");
41
+ intermediate.Save(wixiplFile);
42
+
43
+ intermediate = Intermediate.Load(wixiplFile);
44
+
45
+ // Dump to text for easy diffing, with some massaging to keep v3 and v4 diffable.
46
+ //
47
+ var tables = output.Tables.Cast<Wix3.Table>();
48
+ var wix3Dump = tables
49
+ .SelectMany(table => table.Rows.Cast<Wix3.Row>()
50
+ .Select(row => RowToString(row)))
51
+ .ToArray();
52
+
53
+ var tuples = intermediate.Sections.SelectMany(s => s.Tuples);
54
+ var wix4Dump = tuples.Select(tuple => TupleToString(tuple)).ToArray();
55
+
56
+ Assert.Equal(wix3Dump, wix4Dump);
57
+
58
+ // Useful when you want to diff the outputs with another diff tool...
59
+ //
60
+ //var wix3TextDump = String.Join(Environment.NewLine, wix3Dump.OrderBy(val => val));
61
+ //var wix4TextDump = String.Join(Environment.NewLine, wix4Dump.OrderBy(val => val));
62
+ //Assert.Equal(wix3TextDump, wix4TextDump);
63
+ }
64
+ }
65
+
66
+ private static string RowToString(Wix3.Row row)
67
+ {
68
+ var fields = String.Join(",", row.Fields.Select(field => field.Data?.ToString()));
69
+
70
+ // Massage output to match WiX v3 rows and v4 tuples.
71
+ //
72
+ switch (row.Table.Name)
73
+ {
74
+ case "File":
75
+ var fieldValues = row.Fields.Take(7).Select(field => field.Data?.ToString()).ToArray();
76
+ if (fieldValues[3] == null)
77
+ {
78
+ // "Somebody" sometimes writes out a null field even when the column definition says
79
+ // it's non-nullable. Not naming names or anything. (SWID tags.)
80
+ fieldValues[3] = "0";
81
+ }
82
+ fields = String.Join(",", fieldValues);
83
+ break;
84
+ case "WixFile":
85
+ fields = String.Join(",", row.Fields.Take(8).Select(field => field.Data?.ToString()));
86
+ break;
87
+ }
88
+
89
+ return $"{row.Table.Name},{fields}";
90
+ }
91
+
92
+ private static string TupleToString(WixToolset.Data.IntermediateTuple tuple)
93
+ {
94
+ var fields = String.Join(",", tuple.Fields.Select(field => field?.AsString()));
95
+
96
+ switch (tuple.Definition.Name)
97
+ {
98
+ // Massage output to match WiX v3 rows and v4 tuples.
99
+ //
100
+ case "Component":
101
+ {
102
+ var componentTuple = (ComponentTuple)tuple;
103
+ var attributes = ComponentLocation.Either == componentTuple.Location ? WindowsInstallerConstants.MsidbComponentAttributesOptional : 0;
104
+ attributes |= ComponentLocation.SourceOnly == componentTuple.Location ? WindowsInstallerConstants.MsidbComponentAttributesSourceOnly : 0;
105
+ attributes |= ComponentKeyPathType.Registry == componentTuple.KeyPathType ? WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath : 0;
106
+ attributes |= ComponentKeyPathType.OdbcDataSource == componentTuple.KeyPathType ? WindowsInstallerConstants.MsidbComponentAttributesODBCDataSource : 0;
107
+ attributes |= componentTuple.DisableRegistryReflection ? WindowsInstallerConstants.MsidbComponentAttributesDisableRegistryReflection : 0;
108
+ attributes |= componentTuple.NeverOverwrite ? WindowsInstallerConstants.MsidbComponentAttributesNeverOverwrite : 0;
109
+ attributes |= componentTuple.Permanent ? WindowsInstallerConstants.MsidbComponentAttributesPermanent : 0;
110
+ attributes |= componentTuple.SharedDllRefCount ? WindowsInstallerConstants.MsidbComponentAttributesSharedDllRefCount : 0;
111
+ attributes |= componentTuple.Shared ? WindowsInstallerConstants.MsidbComponentAttributesShared : 0;
112
+ attributes |= componentTuple.Transitive ? WindowsInstallerConstants.MsidbComponentAttributesTransitive : 0;
113
+ attributes |= componentTuple.UninstallWhenSuperseded ? WindowsInstallerConstants.MsidbComponentAttributes64bit : 0;
114
+ attributes |= componentTuple.Win64 ? WindowsInstallerConstants.MsidbComponentAttributes64bit : 0;
115
+
116
+ fields = String.Join(",",
117
+ componentTuple.ComponentId,
118
+ componentTuple.Directory_,
119
+ attributes.ToString(),
120
+ componentTuple.Condition,
121
+ componentTuple.KeyPath
122
+ );
123
+ break;
124
+ }
125
+ case "CustomAction":
126
+ {
127
+ var customActionTuple = (CustomActionTuple)tuple;
128
+ var type = customActionTuple.Win64 ? WindowsInstallerConstants.MsidbCustomActionType64BitScript : 0;
129
+ type |= customActionTuple.TSAware ? WindowsInstallerConstants.MsidbCustomActionTypeTSAware : 0;
130
+ type |= customActionTuple.Impersonate ? 0 : WindowsInstallerConstants.MsidbCustomActionTypeNoImpersonate;
131
+ type |= customActionTuple.IgnoreResult ? WindowsInstallerConstants.MsidbCustomActionTypeContinue : 0;
132
+ type |= customActionTuple.Hidden ? WindowsInstallerConstants.MsidbCustomActionTypeHideTarget : 0;
133
+ type |= customActionTuple.Async ? WindowsInstallerConstants.MsidbCustomActionTypeAsync : 0;
134
+ type |= CustomActionExecutionType.FirstSequence == customActionTuple.ExecutionType ? WindowsInstallerConstants.MsidbCustomActionTypeFirstSequence : 0;
135
+ type |= CustomActionExecutionType.OncePerProcess == customActionTuple.ExecutionType ? WindowsInstallerConstants.MsidbCustomActionTypeOncePerProcess : 0;
136
+ type |= CustomActionExecutionType.ClientRepeat == customActionTuple.ExecutionType ? WindowsInstallerConstants.MsidbCustomActionTypeClientRepeat : 0;
137
+ type |= CustomActionExecutionType.Deferred == customActionTuple.ExecutionType ? WindowsInstallerConstants.MsidbCustomActionTypeInScript : 0;
138
+ type |= CustomActionExecutionType.Rollback == customActionTuple.ExecutionType ? WindowsInstallerConstants.MsidbCustomActionTypeInScript | WindowsInstallerConstants.MsidbCustomActionTypeRollback : 0;
139
+ type |= CustomActionExecutionType.Commit == customActionTuple.ExecutionType ? WindowsInstallerConstants.MsidbCustomActionTypeInScript | WindowsInstallerConstants.MsidbCustomActionTypeCommit : 0;
140
+ type |= CustomActionSourceType.File == customActionTuple.SourceType ? WindowsInstallerConstants.MsidbCustomActionTypeSourceFile : 0;
141
+ type |= CustomActionSourceType.Directory == customActionTuple.SourceType ? WindowsInstallerConstants.MsidbCustomActionTypeDirectory : 0;
142
+ type |= CustomActionSourceType.Property == customActionTuple.SourceType ? WindowsInstallerConstants.MsidbCustomActionTypeProperty : 0;
143
+ type |= CustomActionTargetType.Dll == customActionTuple.TargetType ? WindowsInstallerConstants.MsidbCustomActionTypeDll : 0;
144
+ type |= CustomActionTargetType.Exe == customActionTuple.TargetType ? WindowsInstallerConstants.MsidbCustomActionTypeExe : 0;
145
+ type |= CustomActionTargetType.TextData == customActionTuple.TargetType ? WindowsInstallerConstants.MsidbCustomActionTypeTextData : 0;
146
+ type |= CustomActionTargetType.JScript == customActionTuple.TargetType ? WindowsInstallerConstants.MsidbCustomActionTypeJScript : 0;
147
+ type |= CustomActionTargetType.VBScript == customActionTuple.TargetType ? WindowsInstallerConstants.MsidbCustomActionTypeVBScript : 0;
148
+
149
+ fields = String.Join(",",
150
+ type.ToString(),
151
+ customActionTuple.Source,
152
+ customActionTuple.Target,
153
+ customActionTuple.PatchUninstall ? WindowsInstallerConstants.MsidbCustomActionTypePatchUninstall.ToString() : null
154
+ );
155
+ break;
156
+ }
157
+ case "Feature":
158
+ {
159
+ var featureTuple = (FeatureTuple)tuple;
160
+ var attributes = featureTuple.DisallowAbsent ? WindowsInstallerConstants.MsidbFeatureAttributesUIDisallowAbsent : 0;
161
+ attributes |= featureTuple.DisallowAdvertise ? WindowsInstallerConstants.MsidbFeatureAttributesDisallowAdvertise : 0;
162
+ attributes |= FeatureInstallDefault.FollowParent == featureTuple.InstallDefault ? WindowsInstallerConstants.MsidbFeatureAttributesFollowParent : 0;
163
+ attributes |= FeatureInstallDefault.Source == featureTuple.InstallDefault ? WindowsInstallerConstants.MsidbFeatureAttributesFavorSource : 0;
164
+ attributes |= FeatureTypicalDefault.Advertise == featureTuple.TypicalDefault ? WindowsInstallerConstants.MsidbFeatureAttributesFavorAdvertise : 0;
165
+
166
+ fields = String.Join(",",
167
+ featureTuple.Feature_Parent,
168
+ featureTuple.Title,
169
+ featureTuple.Description,
170
+ featureTuple.Display.ToString(),
171
+ featureTuple.Level.ToString(),
172
+ featureTuple.Directory_,
173
+ attributes.ToString());
174
+ break;
175
+ }
176
+ case "File":
177
+ {
178
+ var fileTuple = (FileTuple)tuple;
179
+ fields = String.Join(",",
180
+ fileTuple.Component_,
181
+ fileTuple.LongFileName,
182
+ fileTuple.FileSize.ToString(),
183
+ fileTuple.Version,
184
+ fileTuple.Language,
185
+ ((fileTuple.ReadOnly ? WindowsInstallerConstants.MsidbFileAttributesReadOnly : 0)
186
+ | (fileTuple.Hidden ? WindowsInstallerConstants.MsidbFileAttributesHidden : 0)
187
+ | (fileTuple.System ? WindowsInstallerConstants.MsidbFileAttributesSystem : 0)
188
+ | (fileTuple.Vital ? WindowsInstallerConstants.MsidbFileAttributesVital : 0)
189
+ | (fileTuple.Checksum ? WindowsInstallerConstants.MsidbFileAttributesChecksum : 0)
190
+ | ((fileTuple.Compressed.HasValue && fileTuple.Compressed.Value) ? WindowsInstallerConstants.MsidbFileAttributesCompressed : 0)
191
+ | ((fileTuple.Compressed.HasValue && !fileTuple.Compressed.Value) ? WindowsInstallerConstants.MsidbFileAttributesNoncompressed : 0))
192
+ .ToString());
193
+ break;
194
+ }
195
+
196
+ case "Registry":
197
+ {
198
+ var registryTuple = (RegistryTuple)tuple;
199
+ var value = registryTuple.Value;
200
+
201
+ switch (registryTuple.ValueType)
202
+ {
203
+ case RegistryValueType.Binary:
204
+ value = String.Concat("#x", value);
205
+ break;
206
+ case RegistryValueType.Expandable:
207
+ value = String.Concat("#%", value);
208
+ break;
209
+ case RegistryValueType.Integer:
210
+ value = String.Concat("#", value);
211
+ break;
212
+ case RegistryValueType.MultiString:
213
+ switch (registryTuple.ValueAction)
214
+ {
215
+ case RegistryValueActionType.Append:
216
+ value = String.Concat("[~]", value);
217
+ break;
218
+ case RegistryValueActionType.Prepend:
219
+ value = String.Concat(value, "[~]");
220
+ break;
221
+ case RegistryValueActionType.Write:
222
+ default:
223
+ if (null != value && -1 == value.IndexOf("[~]", StringComparison.Ordinal))
224
+ {
225
+ value = String.Concat("[~]", value, "[~]");
226
+ }
227
+ break;
228
+ }
229
+ break;
230
+ case RegistryValueType.String:
231
+ // escape the leading '#' character for string registry keys
232
+ if (null != value && value.StartsWith("#", StringComparison.Ordinal))
233
+ {
234
+ value = String.Concat("#", value);
235
+ }
236
+ break;
237
+ }
238
+
239
+ fields = String.Join(",",
240
+ ((int)registryTuple.Root).ToString(),
241
+ registryTuple.Key,
242
+ registryTuple.Name,
243
+ value,
244
+ registryTuple.Component_
245
+ );
246
+ break;
247
+ }
248
+
249
+ case "RemoveRegistry":
250
+ {
251
+ var removeRegistryTuple = (RemoveRegistryTuple)tuple;
252
+ fields = String.Join(",",
253
+ ((int)removeRegistryTuple.Root).ToString(),
254
+ removeRegistryTuple.Key,
255
+ removeRegistryTuple.Name,
256
+ removeRegistryTuple.Component_
257
+ );
258
+ break;
259
+ }
260
+
261
+ case "ServiceControl":
262
+ {
263
+ var serviceControlTuple = (ServiceControlTuple)tuple;
264
+
265
+ var events = serviceControlTuple.InstallRemove ? WindowsInstallerConstants.MsidbServiceControlEventDelete : 0;
266
+ events |= serviceControlTuple.UninstallRemove ? WindowsInstallerConstants.MsidbServiceControlEventUninstallDelete : 0;
267
+ events |= serviceControlTuple.InstallStart ? WindowsInstallerConstants.MsidbServiceControlEventStart : 0;
268
+ events |= serviceControlTuple.UninstallStart ? WindowsInstallerConstants.MsidbServiceControlEventUninstallStart : 0;
269
+ events |= serviceControlTuple.InstallStop ? WindowsInstallerConstants.MsidbServiceControlEventStop : 0;
270
+ events |= serviceControlTuple.UninstallStop ? WindowsInstallerConstants.MsidbServiceControlEventUninstallStop : 0;
271
+
272
+ fields = String.Join(",",
273
+ serviceControlTuple.Name,
274
+ events.ToString(),
275
+ serviceControlTuple.Arguments,
276
+ serviceControlTuple.Wait == true ? "1" : "0",
277
+ serviceControlTuple.Component_
278
+ );
279
+ break;
280
+ }
281
+
282
+ case "ServiceInstall":
283
+ {
284
+ var serviceInstallTuple = (ServiceInstallTuple)tuple;
285
+
286
+ var errorControl = (int)serviceInstallTuple.ErrorControl;
287
+ errorControl |= serviceInstallTuple.Vital ? WindowsInstallerConstants.MsidbServiceInstallErrorControlVital : 0;
288
+
289
+ var serviceType = (int)serviceInstallTuple.ServiceType;
290
+ serviceType |= serviceInstallTuple.Interactive ? WindowsInstallerConstants.MsidbServiceInstallInteractive : 0;
291
+
292
+ fields = String.Join(",",
293
+ serviceInstallTuple.Name,
294
+ serviceInstallTuple.DisplayName,
295
+ serviceType.ToString(),
296
+ ((int)serviceInstallTuple.StartType).ToString(),
297
+ errorControl.ToString(),
298
+ serviceInstallTuple.LoadOrderGroup,
299
+ serviceInstallTuple.Dependencies,
300
+ serviceInstallTuple.StartName,
301
+ serviceInstallTuple.Password,
302
+ serviceInstallTuple.Arguments,
303
+ serviceInstallTuple.Component_,
304
+ serviceInstallTuple.Description
305
+ );
306
+ break;
307
+ }
308
+
309
+ case "Upgrade":
310
+ {
311
+ var upgradeTuple = (UpgradeTuple)tuple;
312
+
313
+ var attributes = upgradeTuple.MigrateFeatures ? WindowsInstallerConstants.MsidbUpgradeAttributesMigrateFeatures : 0;
314
+ attributes |= upgradeTuple.OnlyDetect ? WindowsInstallerConstants.MsidbUpgradeAttributesOnlyDetect : 0;
315
+ attributes |= upgradeTuple.IgnoreRemoveFailures ? WindowsInstallerConstants.MsidbUpgradeAttributesIgnoreRemoveFailure : 0;
316
+ attributes |= upgradeTuple.VersionMinInclusive ? WindowsInstallerConstants.MsidbUpgradeAttributesVersionMinInclusive : 0;
317
+ attributes |= upgradeTuple.VersionMaxInclusive ? WindowsInstallerConstants.MsidbUpgradeAttributesVersionMaxInclusive : 0;
318
+ attributes |= upgradeTuple.ExcludeLanguages ? WindowsInstallerConstants.MsidbUpgradeAttributesLanguagesExclusive : 0;
319
+
320
+ fields = String.Join(",",
321
+ upgradeTuple.VersionMin,
322
+ upgradeTuple.VersionMax,
323
+ upgradeTuple.Language,
324
+ attributes.ToString(),
325
+ upgradeTuple.Remove,
326
+ upgradeTuple.ActionProperty
327
+ );
328
+ break;
329
+ }
330
+
331
+ case "WixAction":
332
+ {
333
+ var wixActionTuple = (WixActionTuple)tuple;
334
+ fields = String.Join(",",
335
+ wixActionTuple.SequenceTable,
336
+ wixActionTuple.Action,
337
+ wixActionTuple.Condition,
338
+ // BUGBUGBUG: AB#2626
339
+ wixActionTuple.Sequence == 0 ? String.Empty : wixActionTuple.Sequence.ToString(),
340
+ wixActionTuple.Before,
341
+ wixActionTuple.After,
342
+ wixActionTuple.Overridable == true ? "1" : "0"
343
+ );
344
+ break;
345
+ }
346
+
347
+ case "WixComplexReference":
348
+ {
349
+ var wixComplexReferenceTuple = (WixComplexReferenceTuple)tuple;
350
+ fields = String.Join(",",
351
+ wixComplexReferenceTuple.Parent,
352
+ ((int)wixComplexReferenceTuple.ParentType).ToString(),
353
+ wixComplexReferenceTuple.ParentLanguage,
354
+ wixComplexReferenceTuple.Child,
355
+ ((int)wixComplexReferenceTuple.ChildType).ToString(),
356
+ wixComplexReferenceTuple.IsPrimary ? "1" : "0"
357
+ );
358
+ break;
359
+ }
360
+
361
+ case "WixFile":
362
+ {
363
+ var wixFileTuple = (WixFileTuple)tuple;
364
+ fields = String.Concat(
365
+ wixFileTuple.AssemblyType == FileAssemblyType.DotNetAssembly ? "0" : wixFileTuple.AssemblyType == FileAssemblyType.Win32Assembly ? "1" : String.Empty, ",",
366
+ String.Join(",", tuple.Fields.Skip(2).Take(6).Select(field => (string)field).ToArray()));
367
+ break;
368
+ }
369
+
370
+ case "WixProperty":
371
+ {
372
+ var wixPropertyTuple = (WixPropertyTuple)tuple;
373
+ var attributes = 0;
374
+ attributes |= wixPropertyTuple.Admin ? 0x1 : 0;
375
+ attributes |= wixPropertyTuple.Hidden ? 0x2 : 0;
376
+ attributes |= wixPropertyTuple.Secure ? 0x4 : 0;
377
+
378
+ fields = String.Join(",",
379
+ wixPropertyTuple.Property_,
380
+ attributes.ToString()
381
+ );
382
+ break;
383
+ }
384
+
385
+ }
386
+
387
+ var id = tuple.Id == null ? String.Empty : String.Concat(",", tuple.Id.Id);
388
+ return $"{tuple.Definition.Name}{id},{fields}";
389
+ }
390
+ }
391
+}
src/test/WixToolsetTest.Converters.Tupleizer/TestData/Integration/test.wixout
Binary files /dev/null and b/src/test/WixToolsetTest.Converters.Tupleizer/TestData/Integration/test.wixout differ
src/test/WixToolsetTest.Converters.Tupleizer/TestData/Integration/test.wixproj
new
+47
@@ -0,0 +1,47 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<Project ToolsVersion="4.0" DefaultTargets="Build" InitialTargets="EnsureWixToolsetInstalled" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3
+ <PropertyGroup>
4
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5
+ <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
6
+ <ProductVersion>3.10</ProductVersion>
7
+ <ProjectGuid>d59f1c1e-9238-49fa-bfa2-ec1d9c2dda1d</ProjectGuid>
8
+ <SchemaVersion>2.0</SchemaVersion>
9
+ <OutputName>TupleizerWixout</OutputName>
10
+ <OutputType>Package</OutputType>
11
+ </PropertyGroup>
12
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
13
+ <OutputPath>bin\$(Configuration)\</OutputPath>
14
+ <IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
15
+ <DefineConstants>Debug</DefineConstants>
16
+ </PropertyGroup>
17
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
18
+ <OutputPath>bin\$(Configuration)\</OutputPath>
19
+ <IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
20
+ </PropertyGroup>
21
+ <ItemGroup>
22
+ <Compile Include="Product.wxs" />
23
+ </ItemGroup>
24
+ <ItemGroup>
25
+ <WixExtension Include="WixUtilExtension">
26
+ <HintPath>$(WixExtDir)\WixUtilExtension.dll</HintPath>
27
+ <Name>WixUtilExtension</Name>
28
+ </WixExtension>
29
+ <WixExtension Include="WixNetFxExtension">
30
+ <HintPath>$(WixExtDir)\WixNetFxExtension.dll</HintPath>
31
+ <Name>WixNetFxExtension</Name>
32
+ </WixExtension>
33
+ </ItemGroup>
34
+ <Import Project="$(WixTargetsPath)" Condition=" '$(WixTargetsPath)' != '' " />
35
+ <Import Project="$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets" Condition=" '$(WixTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets') " />
36
+ <Target Name="EnsureWixToolsetInstalled" Condition=" '$(WixTargetsImported)' != 'true' ">
37
+ <Error Text="The WiX Toolset v3.11 (or newer) build tools must be installed to build this project. To download the WiX Toolset, see http://wixtoolset.org/releases/" />
38
+ </Target>
39
+ <!--
40
+ To modify your build process, add your task inside one of the targets below and uncomment it.
41
+ Other similar extension points exist, see Wix.targets.
42
+ <Target Name="BeforeBuild">
43
+ </Target>
44
+ <Target Name="AfterBuild">
45
+ </Target>
46
+ -->
47
+</Project>
\ No newline at end of file
src/test/WixToolsetTest.Converters.Tupleizer/TestData/Integration/test.wxs
new
+36
@@ -0,0 +1,36 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
3
+ <Product Id="*" Name="TupleizerWixout" Language="1033" Version="1.0.0.0" Manufacturer="FireGiant" UpgradeCode="14a02d7f-9b32-4c92-b1f1-da518bf4e32a">
4
+ <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
5
+
6
+ <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." AllowSameVersionUpgrades="yes" IgnoreRemoveFailure="yes" />
7
+ <MediaTemplate />
8
+
9
+ <Feature Id="ProductFeature" Title="TupleizerWixout" Level="1">
10
+ <Feature Id="ChildFeature">
11
+ <ComponentGroupRef Id="ProductComponents" />
12
+ </Feature>
13
+ </Feature>
14
+
15
+ <PropertyRef Id="WIX_IS_NETFRAMEWORK_462_OR_LATER_INSTALLED" />
16
+ <CustomActionRef Id="WixFailWhenDeferred" />
17
+ <Property Id="WIXFAILWHENDEFERRED" Value="1" Secure="yes" />
18
+ </Product>
19
+
20
+ <Fragment>
21
+ <Directory Id="TARGETDIR" Name="SourceDir">
22
+ <Directory Id="ProgramFilesFolder">
23
+ <Directory Id="INSTALLFOLDER" Name="TupleizerWixout" />
24
+ </Directory>
25
+ </Directory>
26
+ </Fragment>
27
+
28
+ <Fragment>
29
+ <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
30
+ <Component Id="ProductComponent">
31
+ <File Checksum="yes" ReadOnly="yes" Source="$(env.WIX)\bin\candle.exe" Assembly=".net" AssemblyApplication="candle.exe" />
32
+ <RegistryValue Root="HKLM" Key="SOFTWARE\WiX Toolset" Name="[ProductName]Installed" Value="1" Type="integer" />
33
+ </Component>
34
+ </ComponentGroup>
35
+ </Fragment>
36
+</Wix>
src/test/WixToolsetTest.Converters.Tupleizer/WixToolsetTest.Converters.Tupleizer.csproj
new
+33
@@ -0,0 +1,33 @@
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
+
6
+ <PropertyGroup>
7
+ <TargetFramework>net461</TargetFramework>
8
+ <IsPackable>false</IsPackable>
9
+ </PropertyGroup>
10
+
11
+ <ItemGroup>
12
+ <Content Include="TestData\Integration\test.wixout" CopyToOutputDirectory="PreserveNewest" />
13
+ </ItemGroup>
14
+
15
+ <ItemGroup>
16
+ <ProjectReference Include="..\..\WixToolset.Converters.Tupleizer\WixToolset.Converters.Tupleizer.csproj" />
17
+ </ItemGroup>
18
+
19
+ <ItemGroup>
20
+ <PackageReference Include="WixToolset.Data" Version="4.0.*" />
21
+ <PackageReference Include="WixBuildTools.TestSupport" Version="4.0.*" />
22
+ </ItemGroup>
23
+
24
+ <ItemGroup>
25
+ <Reference Include="wix" HintPath="..\..\deps\wix.dll" />
26
+ </ItemGroup>
27
+
28
+ <ItemGroup>
29
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.0" />
30
+ <PackageReference Include="xunit" Version="2.4.1" />
31
+ <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" PrivateAssets="All" />
32
+ </ItemGroup>
33
+</Project>
src/test/WixToolsetTest.Converters/ConverterFixture.cs
new
+554
@@ -0,0 +1,554 @@
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 WixToolsetTest.Converters
4
+{
5
+ using System;
6
+ using System.IO;
7
+ using System.Text;
8
+ using System.Xml.Linq;
9
+ using WixToolset.Converters;
10
+ using WixToolset.Data;
11
+ using WixToolset.Extensibility;
12
+ using WixToolset.Extensibility.Services;
13
+ using Xunit;
14
+
15
+ public class ConverterFixture
16
+ {
17
+ private static readonly XNamespace Wix4Namespace = "http://wixtoolset.org/schemas/v4/wxs";
18
+
19
+ [Fact]
20
+ public void EnsuresDeclaration()
21
+ {
22
+ var parse = String.Join(Environment.NewLine,
23
+ "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>",
24
+ " <Fragment />",
25
+ "</Wix>");
26
+
27
+ var expected = String.Join(Environment.NewLine,
28
+ "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
29
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
30
+ " <Fragment />",
31
+ "</Wix>");
32
+
33
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
34
+
35
+ var messaging = new DummyMessaging();
36
+ var converter = new Wix3Converter(messaging, 2, null, null);
37
+
38
+ var errors = converter.ConvertDocument(document);
39
+
40
+ var actual = UnformattedDocumentString(document);
41
+
42
+ Assert.Equal(1, errors);
43
+ Assert.Equal(expected, actual);
44
+ }
45
+
46
+ [Fact]
47
+ public void EnsuresUtf8Declaration()
48
+ {
49
+ var parse = String.Join(Environment.NewLine,
50
+ "<?xml version='1.0'?>",
51
+ "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>",
52
+ " <Fragment />",
53
+ "</Wix>");
54
+
55
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
56
+
57
+ var messaging = new DummyMessaging();
58
+ var converter = new Wix3Converter(messaging, 4, null, null);
59
+
60
+ var errors = converter.ConvertDocument(document);
61
+
62
+ Assert.Equal(1, errors);
63
+ Assert.Equal("1.0", document.Declaration.Version);
64
+ Assert.Equal("utf-8", document.Declaration.Encoding);
65
+ }
66
+
67
+ [Fact]
68
+ public void CanFixWhitespace()
69
+ {
70
+ var parse = String.Join(Environment.NewLine,
71
+ "<?xml version='1.0' encoding='utf-8'?>",
72
+ "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>",
73
+ " <Fragment>",
74
+ " <Property Id='Prop'",
75
+ " Value='Val'>",
76
+ " </Property>",
77
+ " </Fragment>",
78
+ "</Wix>");
79
+
80
+ var expected = String.Join(Environment.NewLine,
81
+ "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
82
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
83
+ " <Fragment>",
84
+ " <Property Id=\"Prop\" Value=\"Val\" />",
85
+ " </Fragment>",
86
+ "</Wix>");
87
+
88
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
89
+
90
+ var messaging = new DummyMessaging();
91
+ var converter = new Wix3Converter(messaging, 4, null, null);
92
+
93
+ var errors = converter.ConvertDocument(document);
94
+
95
+ var actual = UnformattedDocumentString(document);
96
+
97
+ Assert.Equal(expected, actual);
98
+ Assert.Equal(4, errors);
99
+ }
100
+
101
+ [Fact]
102
+ public void CanPreserveNewLines()
103
+ {
104
+ var parse = String.Join(Environment.NewLine,
105
+ "<?xml version='1.0' encoding='utf-8'?>",
106
+ "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>",
107
+ " <Fragment>",
108
+ "",
109
+ " <Property Id='Prop' Value='Val' />",
110
+ "",
111
+ " </Fragment>",
112
+ "</Wix>");
113
+
114
+ var expected = String.Join(Environment.NewLine,
115
+ "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
116
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
117
+ " <Fragment>",
118
+ "",
119
+ " <Property Id=\"Prop\" Value=\"Val\" />",
120
+ "",
121
+ " </Fragment>",
122
+ "</Wix>");
123
+
124
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
125
+
126
+ var messaging = new DummyMessaging();
127
+ var converter = new Wix3Converter(messaging, 4, null, null);
128
+
129
+ var conversions = converter.ConvertDocument(document);
130
+
131
+ var actual = UnformattedDocumentString(document);
132
+
133
+ Assert.Equal(expected, actual);
134
+ Assert.Equal(3, conversions);
135
+ }
136
+
137
+ [Fact]
138
+ public void CanConvertWithNewLineAtEndOfFile()
139
+ {
140
+ var parse = String.Join(Environment.NewLine,
141
+ "<?xml version='1.0' encoding='utf-8'?>",
142
+ "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>",
143
+ " <Fragment>",
144
+ "",
145
+ " <Property Id='Prop' Value='Val' />",
146
+ "",
147
+ " </Fragment>",
148
+ "</Wix>",
149
+ "");
150
+
151
+ var expected = String.Join(Environment.NewLine,
152
+ "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
153
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
154
+ " <Fragment>",
155
+ "",
156
+ " <Property Id=\"Prop\" Value=\"Val\" />",
157
+ "",
158
+ " </Fragment>",
159
+ "</Wix>",
160
+ "");
161
+
162
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
163
+
164
+ var messaging = new DummyMessaging();
165
+ var converter = new Wix3Converter(messaging, 4, null, null);
166
+
167
+ var conversions = converter.ConvertDocument(document);
168
+
169
+ var actual = UnformattedDocumentString(document);
170
+
171
+ Assert.Equal(expected, actual);
172
+ Assert.Equal(3, conversions);
173
+ }
174
+
175
+ [Fact]
176
+ public void CanFixCdataWhitespace()
177
+ {
178
+ var parse = String.Join(Environment.NewLine,
179
+ "<?xml version='1.0' encoding='utf-8'?>",
180
+ "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>",
181
+ " <Fragment>",
182
+ " <Property Id='Prop'>",
183
+ " <![CDATA[1<2]]>",
184
+ " </Property>",
185
+ " </Fragment>",
186
+ "</Wix>");
187
+
188
+ var expected = String.Join(Environment.NewLine,
189
+ "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
190
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
191
+ " <Fragment>",
192
+ " <Property Id=\"Prop\"><![CDATA[1<2]]></Property>",
193
+ " </Fragment>",
194
+ "</Wix>");
195
+
196
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
197
+
198
+ var messaging = new DummyMessaging();
199
+ var converter = new Wix3Converter(messaging, 2, null, null);
200
+
201
+ var errors = converter.ConvertDocument(document);
202
+
203
+ var actual = UnformattedDocumentString(document);
204
+
205
+ Assert.Equal(expected, actual);
206
+ Assert.Equal(2, errors);
207
+ }
208
+
209
+ [Fact]
210
+ public void CanFixCdataWithWhitespace()
211
+ {
212
+ var parse = String.Join(Environment.NewLine,
213
+ "<?xml version='1.0' encoding='utf-8'?>",
214
+ "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>",
215
+ " <Fragment>",
216
+ " <Property Id='Prop'>",
217
+ " <![CDATA[",
218
+ " 1<2",
219
+ " ]]>",
220
+ " </Property>",
221
+ " </Fragment>",
222
+ "</Wix>");
223
+
224
+ var expected = String.Join(Environment.NewLine,
225
+ "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
226
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
227
+ " <Fragment>",
228
+ " <Property Id=\"Prop\"><![CDATA[1<2]]></Property>",
229
+ " </Fragment>",
230
+ "</Wix>");
231
+
232
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
233
+
234
+ var messaging = new DummyMessaging();
235
+ var converter = new Wix3Converter(messaging, 2, null, null);
236
+
237
+ var errors = converter.ConvertDocument(document);
238
+
239
+ var actual = UnformattedDocumentString(document);
240
+
241
+ Assert.Equal(expected, actual);
242
+ Assert.Equal(2, errors);
243
+ }
244
+
245
+ [Fact]
246
+ public void CanConvertMainNamespace()
247
+ {
248
+ var parse = String.Join(Environment.NewLine,
249
+ "<?xml version='1.0' encoding='utf-8'?>",
250
+ "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>",
251
+ " <Fragment />",
252
+ "</Wix>");
253
+
254
+ var expected = String.Join(Environment.NewLine,
255
+ "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
256
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
257
+ " <Fragment />",
258
+ "</Wix>");
259
+
260
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
261
+
262
+ var messaging = new DummyMessaging();
263
+ var converter = new Wix3Converter(messaging, 2, null, null);
264
+
265
+ var errors = converter.ConvertDocument(document);
266
+
267
+ var actual = UnformattedDocumentString(document);
268
+
269
+ Assert.Equal(1, errors);
270
+ //Assert.Equal(Wix4Namespace, document.Root.GetDefaultNamespace());
271
+ Assert.Equal(expected, actual);
272
+ }
273
+
274
+ [Fact]
275
+ public void CanConvertNamedMainNamespace()
276
+ {
277
+ var parse = String.Join(Environment.NewLine,
278
+ "<?xml version='1.0' encoding='utf-8'?>",
279
+ "<w:Wix xmlns:w='http://schemas.microsoft.com/wix/2006/wi'>",
280
+ " <w:Fragment />",
281
+ "</w:Wix>");
282
+
283
+ var expected = String.Join(Environment.NewLine,
284
+ "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
285
+ "<w:Wix xmlns:w=\"http://wixtoolset.org/schemas/v4/wxs\">",
286
+ " <w:Fragment />",
287
+ "</w:Wix>");
288
+
289
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
290
+
291
+ var messaging = new DummyMessaging();
292
+ var converter = new Wix3Converter(messaging, 2, null, null);
293
+
294
+ var errors = converter.ConvertDocument(document);
295
+
296
+ var actual = UnformattedDocumentString(document);
297
+
298
+ Assert.Equal(1, errors);
299
+ Assert.Equal(expected, actual);
300
+ Assert.Equal(Wix4Namespace, document.Root.GetNamespaceOfPrefix("w"));
301
+ }
302
+
303
+ [Fact]
304
+ public void CanConvertNonWixDefaultNamespace()
305
+ {
306
+ var parse = String.Join(Environment.NewLine,
307
+ "<?xml version='1.0' encoding='utf-8'?>",
308
+ "<w:Wix xmlns:w='http://schemas.microsoft.com/wix/2006/wi' xmlns='http://schemas.microsoft.com/wix/UtilExtension'>",
309
+ " <w:Fragment>",
310
+ " <Test />",
311
+ " </w:Fragment>",
312
+ "</w:Wix>");
313
+
314
+ var expected = String.Join(Environment.NewLine,
315
+ "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
316
+ "<w:Wix xmlns:w=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns=\"http://wixtoolset.org/schemas/v4/wxs/util\">",
317
+ " <w:Fragment>",
318
+ " <Test />",
319
+ " </w:Fragment>",
320
+ "</w:Wix>");
321
+
322
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
323
+
324
+ var messaging = new DummyMessaging();
325
+ var converter = new Wix3Converter(messaging, 2, null, null);
326
+
327
+ var errors = converter.ConvertDocument(document);
328
+
329
+ var actual = UnformattedDocumentString(document);
330
+
331
+ Assert.Equal(expected, actual);
332
+ Assert.Equal(2, errors);
333
+ Assert.Equal(Wix4Namespace, document.Root.GetNamespaceOfPrefix("w"));
334
+ Assert.Equal("http://wixtoolset.org/schemas/v4/wxs/util", document.Root.GetDefaultNamespace());
335
+ }
336
+
337
+ [Fact]
338
+ public void CanConvertExtensionNamespace()
339
+ {
340
+ var parse = String.Join(Environment.NewLine,
341
+ "<?xml version='1.0' encoding='utf-8'?>",
342
+ "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:util='http://schemas.microsoft.com/wix/UtilExtension'>",
343
+ " <Fragment />",
344
+ "</Wix>");
345
+
346
+ var expected = String.Join(Environment.NewLine,
347
+ "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
348
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:util=\"http://wixtoolset.org/schemas/v4/wxs/util\">",
349
+ " <Fragment />",
350
+ "</Wix>");
351
+
352
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
353
+
354
+ var messaging = new DummyMessaging();
355
+ var converter = new Wix3Converter(messaging, 2, null, null);
356
+
357
+ var errors = converter.ConvertDocument(document);
358
+
359
+ var actual = UnformattedDocumentString(document);
360
+
361
+ Assert.Equal(2, errors);
362
+ Assert.Equal(expected, actual);
363
+ Assert.Equal(Wix4Namespace, document.Root.GetDefaultNamespace());
364
+ }
365
+
366
+ [Fact]
367
+ public void CanConvertMissingNamespace()
368
+ {
369
+ var parse = String.Join(Environment.NewLine,
370
+ "<?xml version='1.0' encoding='utf-8'?>",
371
+ "<Wix>",
372
+ " <Fragment />",
373
+ "</Wix>");
374
+
375
+ var expected = String.Join(Environment.NewLine,
376
+ "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
377
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
378
+ " <Fragment />",
379
+ "</Wix>");
380
+
381
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
382
+
383
+ var messaging = new DummyMessaging();
384
+ var converter = new Wix3Converter(messaging, 2, null, null);
385
+
386
+ var errors = converter.ConvertDocument(document);
387
+
388
+ var actual = UnformattedDocumentString(document);
389
+
390
+ Assert.Equal(1, errors);
391
+ Assert.Equal(expected, actual);
392
+ Assert.Equal(Wix4Namespace, document.Root.GetDefaultNamespace());
393
+ }
394
+
395
+ [Fact]
396
+ public void CanConvertAnonymousFile()
397
+ {
398
+ var parse = String.Join(Environment.NewLine,
399
+ "<?xml version='1.0' encoding='utf-8'?>",
400
+ "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>",
401
+ " <File Source='path\\to\\foo.txt' />",
402
+ "</Wix>");
403
+
404
+ var expected = String.Join(Environment.NewLine,
405
+ "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
406
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
407
+ " <File Id=\"foo.txt\" Source=\"path\\to\\foo.txt\" />",
408
+ "</Wix>");
409
+
410
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
411
+
412
+ var messaging = new DummyMessaging();
413
+ var converter = new Wix3Converter(messaging, 2, null, null);
414
+
415
+ var errors = converter.ConvertDocument(document);
416
+
417
+ var actual = UnformattedDocumentString(document);
418
+
419
+ Assert.Equal(1, errors);
420
+ Assert.Equal(expected, actual);
421
+ }
422
+
423
+ [Fact]
424
+ public void CanConvertShortNameDirectoryWithoutName()
425
+ {
426
+ var parse = String.Join(Environment.NewLine,
427
+ "<?xml version='1.0' encoding='utf-8'?>",
428
+ "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>",
429
+ " <Directory ShortName='iamshort' />",
430
+ "</Wix>");
431
+
432
+ var expected = String.Join(Environment.NewLine,
433
+ "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
434
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
435
+ " <Directory Name=\"iamshort\" />",
436
+ "</Wix>");
437
+
438
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
439
+
440
+ var messaging = new DummyMessaging();
441
+ var converter = new Wix3Converter(messaging, 2, null, null);
442
+
443
+ var errors = converter.ConvertDocument(document);
444
+
445
+ var actual = UnformattedDocumentString(document);
446
+
447
+ Assert.Equal(1, errors);
448
+ Assert.Equal(expected, actual);
449
+ }
450
+
451
+ [Fact]
452
+ public void CanConvertSuppressSignatureValidationNo()
453
+ {
454
+ var parse = String.Join(Environment.NewLine,
455
+ "<?xml version='1.0' encoding='utf-8'?>",
456
+ "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>",
457
+ " <MsiPackage SuppressSignatureValidation='no' />",
458
+ "</Wix>");
459
+
460
+ var expected = String.Join(Environment.NewLine,
461
+ "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
462
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
463
+ " <MsiPackage EnableSignatureValidation=\"yes\" />",
464
+ "</Wix>");
465
+
466
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
467
+
468
+ var messaging = new DummyMessaging();
469
+ var converter = new Wix3Converter(messaging, 2, null, null);
470
+
471
+ var errors = converter.ConvertDocument(document);
472
+
473
+ var actual = UnformattedDocumentString(document);
474
+
475
+ Assert.Equal(1, errors);
476
+ Assert.Equal(expected, actual);
477
+ }
478
+
479
+ [Fact]
480
+ public void CanConvertSuppressSignatureValidationYes()
481
+ {
482
+ var parse = String.Join(Environment.NewLine,
483
+ "<?xml version='1.0' encoding='utf-8'?>",
484
+ "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>",
485
+ " <Payload SuppressSignatureValidation='yes' />",
486
+ "</Wix>");
487
+
488
+ var expected = String.Join(Environment.NewLine,
489
+ "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
490
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
491
+ " <Payload />",
492
+ "</Wix>");
493
+
494
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
495
+
496
+ var messaging = new DummyMessaging();
497
+ var converter = new Wix3Converter(messaging, 2, null, null);
498
+
499
+ var errors = converter.ConvertDocument(document);
500
+
501
+ var actual = UnformattedDocumentString(document);
502
+
503
+ Assert.Equal(1, errors);
504
+ Assert.Equal(expected, actual);
505
+ }
506
+
507
+ private static string UnformattedDocumentString(XDocument document)
508
+ {
509
+ var sb = new StringBuilder();
510
+
511
+ using (var writer = new StringWriter(sb))
512
+ {
513
+ document.Save(writer, SaveOptions.DisableFormatting);
514
+ }
515
+
516
+ return sb.ToString();
517
+ }
518
+
519
+ private class DummyMessaging : IMessaging
520
+ {
521
+ public bool EncounteredError { get; set; }
522
+
523
+ public int LastErrorNumber { get; set; }
524
+
525
+ public bool ShowVerboseMessages { get; set; }
526
+
527
+ public bool SuppressAllWarnings { get; set; }
528
+
529
+ public bool WarningsAsError { get; set; }
530
+
531
+ public void ElevateWarningMessage(int warningNumber)
532
+ {
533
+ }
534
+
535
+ public string FormatMessage(Message message) => String.Empty;
536
+
537
+ public void SetListener(IMessageListener listener)
538
+ {
539
+ }
540
+
541
+ public void SuppressWarningMessage(int warningNumber)
542
+ {
543
+ }
544
+
545
+ public void Write(Message message)
546
+ {
547
+ }
548
+
549
+ public void Write(string message, bool verbose = false)
550
+ {
551
+ }
552
+ }
553
+ }
554
+}
src/test/WixToolsetTest.Converters/TestData/Preprocessor/ConvertedPreprocessor.wxs
new
+62
@@ -0,0 +1,62 @@
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
+ <?include ComRegistration.wxi ?>
46
+ </File>
47
+ <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" />
48
+ </Component>
49
+
50
+ <ComponentGroupRef Id="ToolsetComponents" />
51
+ <ComponentGroupRef Id="ExtensionComponents" />
52
+ <ComponentGroupRef Id="LuxComponents" />
53
+ <ComponentGroupRef Id="DocComponents" />
54
+ </Feature>
55
+
56
+ <FeatureRef Id="Feature_MSBuild" />
57
+ <FeatureRef Id="Feature_Intellisense2010" />
58
+ <FeatureRef Id="Feature_Intellisense2012" />
59
+ <FeatureRef Id="Feature_Intellisense2013" />
60
+ <FeatureRef Id="Feature_Intellisense2015" />
61
+ </Product>
62
+</Wix>
src/test/WixToolsetTest.Converters/TestData/Preprocessor/Preprocessor.wxs
new
+63
@@ -0,0 +1,63 @@
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
+ <?include ComRegistration.wxi ?>
47
+ </File>
48
+ <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" />
49
+ </Component>
50
+
51
+ <ComponentGroupRef Id="ToolsetComponents" />
52
+ <ComponentGroupRef Id="ExtensionComponents" />
53
+ <ComponentGroupRef Id="LuxComponents" />
54
+ <ComponentGroupRef Id="DocComponents" />
55
+ </Feature>
56
+
57
+ <FeatureRef Id="Feature_MSBuild" />
58
+ <FeatureRef Id="Feature_Intellisense2010" />
59
+ <FeatureRef Id="Feature_Intellisense2012" />
60
+ <FeatureRef Id="Feature_Intellisense2013" />
61
+ <FeatureRef Id="Feature_Intellisense2015" />
62
+ </Product>
63
+</Wix>
src/test/WixToolsetTest.Converters/TestData/Preprocessor/wixcop.settings.xml
new
+9
@@ -0,0 +1,9 @@
1
+<?xml version="1.0"?>
2
+<Settings>
3
+ <IgnoreErrors>
4
+ <Test Id="WhitespacePrecedingNodeWrong"/>
5
+ <Test Id="WhitespacePrecedingEndElementWrong"/>
6
+ </IgnoreErrors>
7
+ <ErrorsAsWarnings/>
8
+ <ExemptFiles/>
9
+</Settings>
\ No newline at end of file
src/test/WixToolsetTest.Converters/TestData/QtExec.bad/v3.wxs
new
+65
@@ -0,0 +1,65 @@
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
+ <Property Id="QtExecCmdTimeout" Value="600000" />
19
+ <CustomAction Id="InstallVSTemplateCommand" Property="QtExecCmdLine" Value=""[VSENVPRODUCT80]\devenv.exe" /setup" />
20
+ <CustomAction Id="InstallVSTemplate" BinaryKey="WixCA" DllEntry="CAQuietExec" Return="asyncWait" />
21
+
22
+ <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1">
23
+ <Component Id="Licensing" Directory="INSTALLFOLDER">
24
+ <File Source="LICENSE.TXT" />
25
+ </Component>
26
+
27
+ <Component Id="ProductRegistration" Directory="INSTALLFOLDER">
28
+ <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)">
29
+ <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" />
30
+ </RegistryKey>
31
+ </Component>
32
+
33
+ <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER">
34
+ <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x">
35
+ <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" />
36
+ </RegistryKey>
37
+ </Component>
38
+
39
+ <Component Id="ProductInformation" Directory="BinFolder">
40
+ <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)">
41
+ <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string"/>
42
+ <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" />
43
+ </RegistryKey>
44
+
45
+ <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" />
46
+ </Component>
47
+
48
+ <Component Directory="BinFolder">
49
+ <File Source="common\wixtoolset.org.ico" />
50
+ <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" />
51
+ </Component>
52
+
53
+ <ComponentGroupRef Id="ToolsetComponents" />
54
+ <ComponentGroupRef Id="ExtensionComponents" />
55
+ <ComponentGroupRef Id="LuxComponents" />
56
+ <ComponentGroupRef Id="DocComponents" />
57
+ </Feature>
58
+
59
+ <FeatureRef Id="Feature_MSBuild" />
60
+ <FeatureRef Id="Feature_Intellisense2010" />
61
+ <FeatureRef Id="Feature_Intellisense2012" />
62
+ <FeatureRef Id="Feature_Intellisense2013" />
63
+ <FeatureRef Id="Feature_Intellisense2015" />
64
+ </Product>
65
+</Wix>
src/test/WixToolsetTest.Converters/TestData/QtExec.bad/v4_expected.wxs
new
+64
@@ -0,0 +1,64 @@
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
+ <Property Id="QtExecCmdTimeout" Value="600000" />
18
+ <CustomAction Id="InstallVSTemplateCommand" Property="WixQuietExecCmdLine" Value=""[VSENVPRODUCT80]\devenv.exe" /setup" />
19
+ <CustomAction Id="InstallVSTemplate" BinaryKey="UtilCA" DllEntry="WixQuietExec" Return="asyncWait" />
20
+
21
+ <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1">
22
+ <Component Id="Licensing" Directory="INSTALLFOLDER">
23
+ <File Id="LICENSE.TXT" Source="LICENSE.TXT" />
24
+ </Component>
25
+
26
+ <Component Id="ProductRegistration" Directory="INSTALLFOLDER">
27
+ <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)">
28
+ <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" />
29
+ </RegistryKey>
30
+ </Component>
31
+
32
+ <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER">
33
+ <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x">
34
+ <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" />
35
+ </RegistryKey>
36
+ </Component>
37
+
38
+ <Component Id="ProductInformation" Directory="BinFolder">
39
+ <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)">
40
+ <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string" />
41
+ <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" />
42
+ </RegistryKey>
43
+
44
+ <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" />
45
+ </Component>
46
+
47
+ <Component Directory="BinFolder">
48
+ <File Id="wixtoolset.org.ico" Source="common\wixtoolset.org.ico" />
49
+ <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" />
50
+ </Component>
51
+
52
+ <ComponentGroupRef Id="ToolsetComponents" />
53
+ <ComponentGroupRef Id="ExtensionComponents" />
54
+ <ComponentGroupRef Id="LuxComponents" />
55
+ <ComponentGroupRef Id="DocComponents" />
56
+ </Feature>
57
+
58
+ <FeatureRef Id="Feature_MSBuild" />
59
+ <FeatureRef Id="Feature_Intellisense2010" />
60
+ <FeatureRef Id="Feature_Intellisense2012" />
61
+ <FeatureRef Id="Feature_Intellisense2013" />
62
+ <FeatureRef Id="Feature_Intellisense2015" />
63
+ </Product>
64
+</Wix>
src/test/WixToolsetTest.Converters/TestData/QtExec/v3.wxs
new
+64
@@ -0,0 +1,64 @@
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
+ <CustomAction Id="InstallVSTemplateCommand" Property="QtExecCmdLine" Value=""[VSENVPRODUCT80]\devenv.exe" /setup" />
19
+ <CustomAction Id="InstallVSTemplate" BinaryKey="WixCA" DllEntry="CAQuietExec" Return="asyncWait" />
20
+
21
+ <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1">
22
+ <Component Id="Licensing" Directory="INSTALLFOLDER">
23
+ <File Source="LICENSE.TXT" />
24
+ </Component>
25
+
26
+ <Component Id="ProductRegistration" Directory="INSTALLFOLDER">
27
+ <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)">
28
+ <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" />
29
+ </RegistryKey>
30
+ </Component>
31
+
32
+ <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER">
33
+ <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x">
34
+ <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" />
35
+ </RegistryKey>
36
+ </Component>
37
+
38
+ <Component Id="ProductInformation" Directory="BinFolder">
39
+ <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)">
40
+ <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string"/>
41
+ <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" />
42
+ </RegistryKey>
43
+
44
+ <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" />
45
+ </Component>
46
+
47
+ <Component Directory="BinFolder">
48
+ <File Source="common\wixtoolset.org.ico" />
49
+ <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" />
50
+ </Component>
51
+
52
+ <ComponentGroupRef Id="ToolsetComponents" />
53
+ <ComponentGroupRef Id="ExtensionComponents" />
54
+ <ComponentGroupRef Id="LuxComponents" />
55
+ <ComponentGroupRef Id="DocComponents" />
56
+ </Feature>
57
+
58
+ <FeatureRef Id="Feature_MSBuild" />
59
+ <FeatureRef Id="Feature_Intellisense2010" />
60
+ <FeatureRef Id="Feature_Intellisense2012" />
61
+ <FeatureRef Id="Feature_Intellisense2013" />
62
+ <FeatureRef Id="Feature_Intellisense2015" />
63
+ </Product>
64
+</Wix>
src/test/WixToolsetTest.Converters/TestData/QtExec/v4_expected.wxs
new
+63
@@ -0,0 +1,63 @@
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
+ <CustomAction Id="InstallVSTemplateCommand" Property="WixQuietExecCmdLine" Value=""[VSENVPRODUCT80]\devenv.exe" /setup" />
18
+ <CustomAction Id="InstallVSTemplate" BinaryKey="UtilCA" DllEntry="WixQuietExec" Return="asyncWait" />
19
+
20
+ <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1">
21
+ <Component Id="Licensing" Directory="INSTALLFOLDER">
22
+ <File Id="LICENSE.TXT" Source="LICENSE.TXT" />
23
+ </Component>
24
+
25
+ <Component Id="ProductRegistration" Directory="INSTALLFOLDER">
26
+ <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)">
27
+ <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" />
28
+ </RegistryKey>
29
+ </Component>
30
+
31
+ <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER">
32
+ <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x">
33
+ <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" />
34
+ </RegistryKey>
35
+ </Component>
36
+
37
+ <Component Id="ProductInformation" Directory="BinFolder">
38
+ <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)">
39
+ <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string" />
40
+ <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" />
41
+ </RegistryKey>
42
+
43
+ <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" />
44
+ </Component>
45
+
46
+ <Component Directory="BinFolder">
47
+ <File Id="wixtoolset.org.ico" Source="common\wixtoolset.org.ico" />
48
+ <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" />
49
+ </Component>
50
+
51
+ <ComponentGroupRef Id="ToolsetComponents" />
52
+ <ComponentGroupRef Id="ExtensionComponents" />
53
+ <ComponentGroupRef Id="LuxComponents" />
54
+ <ComponentGroupRef Id="DocComponents" />
55
+ </Feature>
56
+
57
+ <FeatureRef Id="Feature_MSBuild" />
58
+ <FeatureRef Id="Feature_Intellisense2010" />
59
+ <FeatureRef Id="Feature_Intellisense2012" />
60
+ <FeatureRef Id="Feature_Intellisense2013" />
61
+ <FeatureRef Id="Feature_Intellisense2015" />
62
+ </Product>
63
+</Wix>
src/test/WixToolsetTest.Converters/TestData/SingleFile/ConvertedSingleFile.wxs
new
+60
@@ -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>
src/test/WixToolsetTest.Converters/TestData/SingleFile/SingleFile.wxs
new
+61
@@ -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>
src/test/WixToolsetTest.Converters/WixToolsetTest.Converters.csproj
new
+39
@@ -0,0 +1,39 @@
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>netcoreapp2.1</TargetFramework>
7
+ <IsPackable>false</IsPackable>
8
+ </PropertyGroup>
9
+
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" CopyToOutputDirectory="PreserveNewest" />
16
+ <Content Include="TestData\SingleFile\SingleFile.wxs" CopyToOutputDirectory="PreserveNewest" />
17
+ <Content Include="TestData\Preprocessor\ConvertedPreprocessor.wxs" CopyToOutputDirectory="PreserveNewest" />
18
+ <Content Include="TestData\Preprocessor\Preprocessor.wxs" CopyToOutputDirectory="PreserveNewest" />
19
+ <Content Include="TestData\Preprocessor\wixcop.settings.xml" CopyToOutputDirectory="PreserveNewest" />
20
+ <Content Include="TestData\QtExec\v3.wxs" CopyToOutputDirectory="PreserveNewest" />
21
+ <Content Include="TestData\QtExec\v4_expected.wxs" CopyToOutputDirectory="PreserveNewest" />
22
+ <Content Include="TestData\QtExec.bad\v3.wxs" CopyToOutputDirectory="PreserveNewest" />
23
+ <Content Include="TestData\QtExec.bad\v4_expected.wxs" CopyToOutputDirectory="PreserveNewest" />
24
+ </ItemGroup>
25
+
26
+ <ItemGroup>
27
+ <ProjectReference Include="..\..\WixToolset.Converters\WixToolset.Converters.csproj" />
28
+ </ItemGroup>
29
+
30
+ <ItemGroup>
31
+ <PackageReference Include="WixBuildTools.TestSupport" Version="4.0.*" />
32
+ </ItemGroup>
33
+
34
+ <ItemGroup>
35
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.0" />
36
+ <PackageReference Include="xunit" Version="2.4.1" />
37
+ <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" PrivateAssets="All" />
38
+ </ItemGroup>
39
+</Project>