aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2019-05-15 16:09:26 -0700
committerRob Mensching <rob@firegiant.com>2019-05-15 16:12:21 -0700
commit6f6c485118796f044a278447722eaf18ac5bf86e (patch)
treede580265227abc257ee85a8973367f5cdce09bd9 /src
parent8d3f778c61a1a0a576445e8dc7312613363b787d (diff)
downloadwix-6f6c485118796f044a278447722eaf18ac5bf86e.tar.gz
wix-6f6c485118796f044a278447722eaf18ac5bf86e.tar.bz2
wix-6f6c485118796f044a278447722eaf18ac5bf86e.zip
Initial code commit
Diffstat (limited to 'src')
-rw-r--r--src/WixToolset.Converters.Tupleizer/ConvertTuplesCommand.cs609
-rw-r--r--src/WixToolset.Converters.Tupleizer/WixToolset.Converters.Tupleizer.csproj32
-rw-r--r--src/WixToolset.Converters/Wix3Converter.cs652
-rw-r--r--src/WixToolset.Converters/WixToolset.Converters.csproj26
-rw-r--r--src/deps/wix.dllbin0 -> 1753088 bytes
-rw-r--r--src/test/WixToolsetTest.Converters.Tupleizer/ConvertTuplesFixture.cs391
-rw-r--r--src/test/WixToolsetTest.Converters.Tupleizer/TestData/Integration/test.wixoutbin0 -> 148559 bytes
-rw-r--r--src/test/WixToolsetTest.Converters.Tupleizer/TestData/Integration/test.wixproj47
-rw-r--r--src/test/WixToolsetTest.Converters.Tupleizer/TestData/Integration/test.wxs36
-rw-r--r--src/test/WixToolsetTest.Converters.Tupleizer/WixToolsetTest.Converters.Tupleizer.csproj33
-rw-r--r--src/test/WixToolsetTest.Converters/ConverterFixture.cs554
-rw-r--r--src/test/WixToolsetTest.Converters/TestData/Preprocessor/ConvertedPreprocessor.wxs62
-rw-r--r--src/test/WixToolsetTest.Converters/TestData/Preprocessor/Preprocessor.wxs63
-rw-r--r--src/test/WixToolsetTest.Converters/TestData/Preprocessor/wixcop.settings.xml9
-rw-r--r--src/test/WixToolsetTest.Converters/TestData/QtExec.bad/v3.wxs65
-rw-r--r--src/test/WixToolsetTest.Converters/TestData/QtExec.bad/v4_expected.wxs64
-rw-r--r--src/test/WixToolsetTest.Converters/TestData/QtExec/v3.wxs64
-rw-r--r--src/test/WixToolsetTest.Converters/TestData/QtExec/v4_expected.wxs63
-rw-r--r--src/test/WixToolsetTest.Converters/TestData/SingleFile/ConvertedSingleFile.wxs60
-rw-r--r--src/test/WixToolsetTest.Converters/TestData/SingleFile/SingleFile.wxs61
-rw-r--r--src/test/WixToolsetTest.Converters/WixToolsetTest.Converters.csproj39
21 files changed, 2930 insertions, 0 deletions
diff --git a/src/WixToolset.Converters.Tupleizer/ConvertTuplesCommand.cs b/src/WixToolset.Converters.Tupleizer/ConvertTuplesCommand.cs
new file mode 100644
index 00000000..c07dd42e
--- /dev/null
+++ b/src/WixToolset.Converters.Tupleizer/ConvertTuplesCommand.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Converters.Tupleizer/WixToolset.Converters.Tupleizer.csproj b/src/WixToolset.Converters.Tupleizer/WixToolset.Converters.Tupleizer.csproj
new file mode 100644
index 00000000..a162807a
--- /dev/null
+++ b/src/WixToolset.Converters.Tupleizer/WixToolset.Converters.Tupleizer.csproj
@@ -0,0 +1,32 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project Sdk="Microsoft.NET.Sdk">
5
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>
diff --git a/src/WixToolset.Converters/Wix3Converter.cs b/src/WixToolset.Converters/Wix3Converter.cs
new file mode 100644
index 00000000..c23930b6
--- /dev/null
+++ b/src/WixToolset.Converters/Wix3Converter.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Converters/WixToolset.Converters.csproj b/src/WixToolset.Converters/WixToolset.Converters.csproj
new file mode 100644
index 00000000..94a956d5
--- /dev/null
+++ b/src/WixToolset.Converters/WixToolset.Converters.csproj
@@ -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>
diff --git a/src/deps/wix.dll b/src/deps/wix.dll
new file mode 100644
index 00000000..64f70f75
--- /dev/null
+++ b/src/deps/wix.dll
Binary files differ
diff --git a/src/test/WixToolsetTest.Converters.Tupleizer/ConvertTuplesFixture.cs b/src/test/WixToolsetTest.Converters.Tupleizer/ConvertTuplesFixture.cs
new file mode 100644
index 00000000..ae33d6b1
--- /dev/null
+++ b/src/test/WixToolsetTest.Converters.Tupleizer/ConvertTuplesFixture.cs
@@ -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
3namespace 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}
diff --git a/src/test/WixToolsetTest.Converters.Tupleizer/TestData/Integration/test.wixout b/src/test/WixToolsetTest.Converters.Tupleizer/TestData/Integration/test.wixout
new file mode 100644
index 00000000..da64b8af
--- /dev/null
+++ b/src/test/WixToolsetTest.Converters.Tupleizer/TestData/Integration/test.wixout
Binary files differ
diff --git a/src/test/WixToolsetTest.Converters.Tupleizer/TestData/Integration/test.wixproj b/src/test/WixToolsetTest.Converters.Tupleizer/TestData/Integration/test.wixproj
new file mode 100644
index 00000000..8af13dc8
--- /dev/null
+++ b/src/test/WixToolsetTest.Converters.Tupleizer/TestData/Integration/test.wixproj
@@ -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
diff --git a/src/test/WixToolsetTest.Converters.Tupleizer/TestData/Integration/test.wxs b/src/test/WixToolsetTest.Converters.Tupleizer/TestData/Integration/test.wxs
new file mode 100644
index 00000000..1006a254
--- /dev/null
+++ b/src/test/WixToolsetTest.Converters.Tupleizer/TestData/Integration/test.wxs
@@ -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>
diff --git a/src/test/WixToolsetTest.Converters.Tupleizer/WixToolsetTest.Converters.Tupleizer.csproj b/src/test/WixToolsetTest.Converters.Tupleizer/WixToolsetTest.Converters.Tupleizer.csproj
new file mode 100644
index 00000000..fa6a6bcf
--- /dev/null
+++ b/src/test/WixToolsetTest.Converters.Tupleizer/WixToolsetTest.Converters.Tupleizer.csproj
@@ -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>
diff --git a/src/test/WixToolsetTest.Converters/ConverterFixture.cs b/src/test/WixToolsetTest.Converters/ConverterFixture.cs
new file mode 100644
index 00000000..97769cd6
--- /dev/null
+++ b/src/test/WixToolsetTest.Converters/ConverterFixture.cs
@@ -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
3namespace 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}
diff --git a/src/test/WixToolsetTest.Converters/TestData/Preprocessor/ConvertedPreprocessor.wxs b/src/test/WixToolsetTest.Converters/TestData/Preprocessor/ConvertedPreprocessor.wxs
new file mode 100644
index 00000000..dcd43e35
--- /dev/null
+++ b/src/test/WixToolsetTest.Converters/TestData/Preprocessor/ConvertedPreprocessor.wxs
@@ -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>
diff --git a/src/test/WixToolsetTest.Converters/TestData/Preprocessor/Preprocessor.wxs b/src/test/WixToolsetTest.Converters/TestData/Preprocessor/Preprocessor.wxs
new file mode 100644
index 00000000..2eb908c2
--- /dev/null
+++ b/src/test/WixToolsetTest.Converters/TestData/Preprocessor/Preprocessor.wxs
@@ -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>
diff --git a/src/test/WixToolsetTest.Converters/TestData/Preprocessor/wixcop.settings.xml b/src/test/WixToolsetTest.Converters/TestData/Preprocessor/wixcop.settings.xml
new file mode 100644
index 00000000..9d3ad496
--- /dev/null
+++ b/src/test/WixToolsetTest.Converters/TestData/Preprocessor/wixcop.settings.xml
@@ -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
diff --git a/src/test/WixToolsetTest.Converters/TestData/QtExec.bad/v3.wxs b/src/test/WixToolsetTest.Converters/TestData/QtExec.bad/v3.wxs
new file mode 100644
index 00000000..b0630f65
--- /dev/null
+++ b/src/test/WixToolsetTest.Converters/TestData/QtExec.bad/v3.wxs
@@ -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="&quot;[VSENVPRODUCT80]\devenv.exe&quot; /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>
diff --git a/src/test/WixToolsetTest.Converters/TestData/QtExec.bad/v4_expected.wxs b/src/test/WixToolsetTest.Converters/TestData/QtExec.bad/v4_expected.wxs
new file mode 100644
index 00000000..be487147
--- /dev/null
+++ b/src/test/WixToolsetTest.Converters/TestData/QtExec.bad/v4_expected.wxs
@@ -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="&quot;[VSENVPRODUCT80]\devenv.exe&quot; /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>
diff --git a/src/test/WixToolsetTest.Converters/TestData/QtExec/v3.wxs b/src/test/WixToolsetTest.Converters/TestData/QtExec/v3.wxs
new file mode 100644
index 00000000..8d81a758
--- /dev/null
+++ b/src/test/WixToolsetTest.Converters/TestData/QtExec/v3.wxs
@@ -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="&quot;[VSENVPRODUCT80]\devenv.exe&quot; /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>
diff --git a/src/test/WixToolsetTest.Converters/TestData/QtExec/v4_expected.wxs b/src/test/WixToolsetTest.Converters/TestData/QtExec/v4_expected.wxs
new file mode 100644
index 00000000..22a961b2
--- /dev/null
+++ b/src/test/WixToolsetTest.Converters/TestData/QtExec/v4_expected.wxs
@@ -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="&quot;[VSENVPRODUCT80]\devenv.exe&quot; /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>
diff --git a/src/test/WixToolsetTest.Converters/TestData/SingleFile/ConvertedSingleFile.wxs b/src/test/WixToolsetTest.Converters/TestData/SingleFile/ConvertedSingleFile.wxs
new file mode 100644
index 00000000..aacb68fa
--- /dev/null
+++ b/src/test/WixToolsetTest.Converters/TestData/SingleFile/ConvertedSingleFile.wxs
@@ -0,0 +1,60 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4
5
6<?include WixVer.wxi ?>
7
8<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:swid="http://wixtoolset.org/schemas/v4/wxs/tag" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
9 <Product Id="*" Name="!(loc.ShortProduct) v$(var.WixMajorMinor) Core" Language="1033" Manufacturer="!(loc.Company)" Version="$(var.WixMsiProductVersion)" UpgradeCode="3618724B-2523-44F9-A908-866AA619504D">
10 <Package Compressed="yes" InstallerVersion="200" SummaryCodepage="1252" InstallScope="perMachine" />
11 <swid:Tag Regid="!(loc.Regid)" InstallDirectory="INSTALLFOLDER" />
12
13 <MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed." />
14
15 <MediaTemplate CabinetTemplate="core{0}.cab" />
16
17 <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1">
18 <Component Id="Licensing" Directory="INSTALLFOLDER">
19 <File Id="LICENSE.TXT" Source="LICENSE.TXT" />
20 </Component>
21
22 <Component Id="ProductRegistration" Directory="INSTALLFOLDER">
23 <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)">
24 <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" />
25 </RegistryKey>
26 </Component>
27
28 <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER">
29 <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x">
30 <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" />
31 </RegistryKey>
32 </Component>
33
34 <Component Id="ProductInformation" Directory="BinFolder">
35 <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)">
36 <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string" />
37 <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" />
38 </RegistryKey>
39
40 <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" />
41 </Component>
42
43 <Component Directory="BinFolder">
44 <File Id="wixtoolset.org.ico" Source="common\wixtoolset.org.ico" />
45 <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" />
46 </Component>
47
48 <ComponentGroupRef Id="ToolsetComponents" />
49 <ComponentGroupRef Id="ExtensionComponents" />
50 <ComponentGroupRef Id="LuxComponents" />
51 <ComponentGroupRef Id="DocComponents" />
52 </Feature>
53
54 <FeatureRef Id="Feature_MSBuild" />
55 <FeatureRef Id="Feature_Intellisense2010" />
56 <FeatureRef Id="Feature_Intellisense2012" />
57 <FeatureRef Id="Feature_Intellisense2013" />
58 <FeatureRef Id="Feature_Intellisense2015" />
59 </Product>
60</Wix>
diff --git a/src/test/WixToolsetTest.Converters/TestData/SingleFile/SingleFile.wxs b/src/test/WixToolsetTest.Converters/TestData/SingleFile/SingleFile.wxs
new file mode 100644
index 00000000..310ae811
--- /dev/null
+++ b/src/test/WixToolsetTest.Converters/TestData/SingleFile/SingleFile.wxs
@@ -0,0 +1,61 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4
5
6<?include WixVer.wxi ?>
7
8<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:swid="http://schemas.microsoft.com/wix/TagExtension" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
9 <Product Id="*" Name="!(loc.ShortProduct) v$(var.WixMajorMinor) Core" Language="1033" Manufacturer="!(loc.Company)"
10 Version="$(var.WixMsiProductVersion)" UpgradeCode="3618724B-2523-44F9-A908-866AA619504D">
11 <Package Compressed="yes" InstallerVersion="200" SummaryCodepage="1252" InstallScope="perMachine" />
12 <swid:Tag Regid="!(loc.Regid)" InstallDirectory="INSTALLFOLDER" />
13
14 <MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed." />
15
16 <MediaTemplate CabinetTemplate="core{0}.cab" />
17
18 <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1">
19 <Component Id="Licensing" Directory="INSTALLFOLDER">
20 <File Source="LICENSE.TXT" />
21 </Component>
22
23 <Component Id="ProductRegistration" Directory="INSTALLFOLDER">
24 <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)">
25 <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" />
26 </RegistryKey>
27 </Component>
28
29 <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER">
30 <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x">
31 <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" />
32 </RegistryKey>
33 </Component>
34
35 <Component Id="ProductInformation" Directory="BinFolder">
36 <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)">
37 <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string"/>
38 <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" />
39 </RegistryKey>
40
41 <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" />
42 </Component>
43
44 <Component Directory="BinFolder">
45 <File Source="common\wixtoolset.org.ico" />
46 <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" />
47 </Component>
48
49 <ComponentGroupRef Id="ToolsetComponents" />
50 <ComponentGroupRef Id="ExtensionComponents" />
51 <ComponentGroupRef Id="LuxComponents" />
52 <ComponentGroupRef Id="DocComponents" />
53 </Feature>
54
55 <FeatureRef Id="Feature_MSBuild" />
56 <FeatureRef Id="Feature_Intellisense2010" />
57 <FeatureRef Id="Feature_Intellisense2012" />
58 <FeatureRef Id="Feature_Intellisense2013" />
59 <FeatureRef Id="Feature_Intellisense2015" />
60 </Product>
61</Wix>
diff --git a/src/test/WixToolsetTest.Converters/WixToolsetTest.Converters.csproj b/src/test/WixToolsetTest.Converters/WixToolsetTest.Converters.csproj
new file mode 100644
index 00000000..d16c3d16
--- /dev/null
+++ b/src/test/WixToolsetTest.Converters/WixToolsetTest.Converters.csproj
@@ -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>