From 6f6c485118796f044a278447722eaf18ac5bf86e Mon Sep 17 00:00:00 2001 From: Rob Mensching <rob@firegiant.com> Date: Wed, 15 May 2019 16:09:26 -0700 Subject: Initial code commit --- .../ConvertTuplesCommand.cs | 609 +++++++++++++++++++ .../WixToolset.Converters.Tupleizer.csproj | 32 + src/WixToolset.Converters/Wix3Converter.cs | 652 +++++++++++++++++++++ .../WixToolset.Converters.csproj | 26 + src/deps/wix.dll | Bin 0 -> 1753088 bytes .../ConvertTuplesFixture.cs | 391 ++++++++++++ .../TestData/Integration/test.wixout | Bin 0 -> 148559 bytes .../TestData/Integration/test.wixproj | 47 ++ .../TestData/Integration/test.wxs | 36 ++ .../WixToolsetTest.Converters.Tupleizer.csproj | 33 ++ .../WixToolsetTest.Converters/ConverterFixture.cs | 554 +++++++++++++++++ .../Preprocessor/ConvertedPreprocessor.wxs | 62 ++ .../TestData/Preprocessor/Preprocessor.wxs | 63 ++ .../TestData/Preprocessor/wixcop.settings.xml | 9 + .../TestData/QtExec.bad/v3.wxs | 65 ++ .../TestData/QtExec.bad/v4_expected.wxs | 64 ++ .../TestData/QtExec/v3.wxs | 64 ++ .../TestData/QtExec/v4_expected.wxs | 63 ++ .../TestData/SingleFile/ConvertedSingleFile.wxs | 60 ++ .../TestData/SingleFile/SingleFile.wxs | 61 ++ .../WixToolsetTest.Converters.csproj | 39 ++ 21 files changed, 2930 insertions(+) create mode 100644 src/WixToolset.Converters.Tupleizer/ConvertTuplesCommand.cs create mode 100644 src/WixToolset.Converters.Tupleizer/WixToolset.Converters.Tupleizer.csproj create mode 100644 src/WixToolset.Converters/Wix3Converter.cs create mode 100644 src/WixToolset.Converters/WixToolset.Converters.csproj create mode 100644 src/deps/wix.dll create mode 100644 src/test/WixToolsetTest.Converters.Tupleizer/ConvertTuplesFixture.cs create mode 100644 src/test/WixToolsetTest.Converters.Tupleizer/TestData/Integration/test.wixout create mode 100644 src/test/WixToolsetTest.Converters.Tupleizer/TestData/Integration/test.wixproj create mode 100644 src/test/WixToolsetTest.Converters.Tupleizer/TestData/Integration/test.wxs create mode 100644 src/test/WixToolsetTest.Converters.Tupleizer/WixToolsetTest.Converters.Tupleizer.csproj create mode 100644 src/test/WixToolsetTest.Converters/ConverterFixture.cs create mode 100644 src/test/WixToolsetTest.Converters/TestData/Preprocessor/ConvertedPreprocessor.wxs create mode 100644 src/test/WixToolsetTest.Converters/TestData/Preprocessor/Preprocessor.wxs create mode 100644 src/test/WixToolsetTest.Converters/TestData/Preprocessor/wixcop.settings.xml create mode 100644 src/test/WixToolsetTest.Converters/TestData/QtExec.bad/v3.wxs create mode 100644 src/test/WixToolsetTest.Converters/TestData/QtExec.bad/v4_expected.wxs create mode 100644 src/test/WixToolsetTest.Converters/TestData/QtExec/v3.wxs create mode 100644 src/test/WixToolsetTest.Converters/TestData/QtExec/v4_expected.wxs create mode 100644 src/test/WixToolsetTest.Converters/TestData/SingleFile/ConvertedSingleFile.wxs create mode 100644 src/test/WixToolsetTest.Converters/TestData/SingleFile/SingleFile.wxs create mode 100644 src/test/WixToolsetTest.Converters/WixToolsetTest.Converters.csproj (limited to 'src') 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 @@ +// 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. + +namespace WixToolset.Converters.Tupleizer +{ + using System; + using System.Linq; + using WixToolset.Data; + using WixToolset.Data.Tuples; + using WixToolset.Data.WindowsInstaller; + using Wix3 = Microsoft.Tools.WindowsInstallerXml; + + public class ConvertTuplesCommand + { + public Intermediate Execute(string path) + { + var output = Wix3.Output.Load(path, suppressVersionCheck: true, suppressSchema: true); + return this.Execute(output); + } + + public Intermediate Execute(Wix3.Output output) + { + var section = new IntermediateSection(String.Empty, OutputType3ToSectionType4(output.Type), output.Codepage); + + foreach (Wix3.Table table in output.Tables) + { + foreach (Wix3.Row row in table.Rows) + { + var tuple = GenerateTupleFromRow(row); + if (tuple != null) + { + section.Tuples.Add(tuple); + } + } + } + + return new Intermediate(String.Empty, new[] { section }, localizationsByCulture: null, embedFilePaths: null); + } + + private static IntermediateTuple GenerateTupleFromRow(Wix3.Row row) + { + var name = row.Table.Name; + switch (name) + { + case "_SummaryInformation": + return DefaultTupleFromRow(typeof(_SummaryInformationTuple), row, columnZeroIsId: false); + case "ActionText": + return DefaultTupleFromRow(typeof(ActionTextTuple), row, columnZeroIsId: false); + case "AdvtExecuteSequence": + return DefaultTupleFromRow(typeof(AdvtExecuteSequenceTuple), row, columnZeroIsId: false); + case "AppId": + return DefaultTupleFromRow(typeof(AppIdTuple), row, columnZeroIsId: false); + case "AppSearch": + return DefaultTupleFromRow(typeof(AppSearchTuple), row, columnZeroIsId: false); + case "Binary": + return DefaultTupleFromRow(typeof(BinaryTuple), row, columnZeroIsId: false); + case "Class": + return DefaultTupleFromRow(typeof(ClassTuple), row, columnZeroIsId: false); + case "CompLocator": + return DefaultTupleFromRow(typeof(CompLocatorTuple), row, columnZeroIsId: true); + case "Component": + { + var attributes = FieldAsNullableInt(row, 3); + + var location = ComponentLocation.LocalOnly; + if ((attributes & WindowsInstallerConstants.MsidbComponentAttributesSourceOnly) == WindowsInstallerConstants.MsidbComponentAttributesSourceOnly) + { + location = ComponentLocation.SourceOnly; + } + else if ((attributes & WindowsInstallerConstants.MsidbComponentAttributesOptional) == WindowsInstallerConstants.MsidbComponentAttributesOptional) + { + location = ComponentLocation.Either; + } + + var keyPathType = ComponentKeyPathType.File; + if ((attributes & WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath) == WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath) + { + keyPathType = ComponentKeyPathType.Registry; + } + else if ((attributes & WindowsInstallerConstants.MsidbComponentAttributesODBCDataSource) == WindowsInstallerConstants.MsidbComponentAttributesODBCDataSource) + { + keyPathType = ComponentKeyPathType.OdbcDataSource; + } + + return new ComponentTuple(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Public, FieldAsString(row, 0))) + { + ComponentId = FieldAsString(row, 1), + Directory_ = FieldAsString(row, 2), + Condition = FieldAsString(row, 4), + KeyPath = FieldAsString(row, 5), + Location = location, + DisableRegistryReflection = (attributes & WindowsInstallerConstants.MsidbComponentAttributesDisableRegistryReflection) == WindowsInstallerConstants.MsidbComponentAttributesDisableRegistryReflection, + NeverOverwrite = (attributes & WindowsInstallerConstants.MsidbComponentAttributesNeverOverwrite) == WindowsInstallerConstants.MsidbComponentAttributesNeverOverwrite, + Permanent = (attributes & WindowsInstallerConstants.MsidbComponentAttributesPermanent) == WindowsInstallerConstants.MsidbComponentAttributesPermanent, + SharedDllRefCount = (attributes & WindowsInstallerConstants.MsidbComponentAttributesSharedDllRefCount) == WindowsInstallerConstants.MsidbComponentAttributesSharedDllRefCount, + Shared = (attributes & WindowsInstallerConstants.MsidbComponentAttributesShared) == WindowsInstallerConstants.MsidbComponentAttributesShared, + Transitive = (attributes & WindowsInstallerConstants.MsidbComponentAttributesTransitive) == WindowsInstallerConstants.MsidbComponentAttributesTransitive, + UninstallWhenSuperseded = (attributes & WindowsInstallerConstants.MsidbComponentAttributesUninstallOnSupersedence) == WindowsInstallerConstants.MsidbComponentAttributesUninstallOnSupersedence, + Win64 = (attributes & WindowsInstallerConstants.MsidbComponentAttributes64bit) == WindowsInstallerConstants.MsidbComponentAttributes64bit, + KeyPathType = keyPathType, + }; + } + + case "Condition": + return DefaultTupleFromRow(typeof(ConditionTuple), row, columnZeroIsId: false); + case "CreateFolder": + return DefaultTupleFromRow(typeof(CreateFolderTuple), row, columnZeroIsId: false); + case "CustomAction": + { + var caType = FieldAsInt(row, 1); + var executionType = DetermineCustomActionExecutionType(caType); + var sourceType = DetermineCustomActionSourceType(caType); + var targetType = DetermineCustomActionTargetType(caType); + + return new CustomActionTuple(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Public, FieldAsString(row, 0))) + { + ExecutionType = executionType, + SourceType = sourceType, + Source = FieldAsString(row, 2), + TargetType = targetType, + Target = FieldAsString(row, 3), + Win64 = (caType & WindowsInstallerConstants.MsidbCustomActionType64BitScript) == WindowsInstallerConstants.MsidbCustomActionType64BitScript, + TSAware = (caType & WindowsInstallerConstants.MsidbCustomActionTypeTSAware) == WindowsInstallerConstants.MsidbCustomActionTypeTSAware, + Impersonate = (caType & WindowsInstallerConstants.MsidbCustomActionTypeNoImpersonate) != WindowsInstallerConstants.MsidbCustomActionTypeNoImpersonate, + IgnoreResult = (caType & WindowsInstallerConstants.MsidbCustomActionTypeContinue) == WindowsInstallerConstants.MsidbCustomActionTypeContinue, + Hidden = (caType & WindowsInstallerConstants.MsidbCustomActionTypeHideTarget) == WindowsInstallerConstants.MsidbCustomActionTypeHideTarget, + Async = (caType & WindowsInstallerConstants.MsidbCustomActionTypeAsync) == WindowsInstallerConstants.MsidbCustomActionTypeAsync, + }; + } + + case "Directory": + return DefaultTupleFromRow(typeof(DirectoryTuple), row, columnZeroIsId: false); + case "DrLocator": + return DefaultTupleFromRow(typeof(DrLocatorTuple), row, columnZeroIsId: false); + case "Error": + return DefaultTupleFromRow(typeof(ErrorTuple), row, columnZeroIsId: false); + case "Extension": + return DefaultTupleFromRow(typeof(ExtensionTuple), row, columnZeroIsId: false); + case "Feature": + { + int attributes = FieldAsInt(row, 7); + var installDefault = FeatureInstallDefault.Local; + if ((attributes & WindowsInstallerConstants.MsidbFeatureAttributesFollowParent) == WindowsInstallerConstants.MsidbFeatureAttributesFollowParent) + { + installDefault = FeatureInstallDefault.FollowParent; + } + else + if ((attributes & WindowsInstallerConstants.MsidbFeatureAttributesFavorSource) == WindowsInstallerConstants.MsidbFeatureAttributesFavorSource) + { + installDefault = FeatureInstallDefault.Source; + } + + return new FeatureTuple(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Public, FieldAsString(row, 0))) + { + Feature_Parent = FieldAsString(row, 1), + Title = FieldAsString(row, 2), + Description = FieldAsString(row, 3), + Display = FieldAsInt(row, 4), // BUGBUGBUG: FieldAsNullableInt(row, 4), + Level = FieldAsInt(row, 5), + Directory_ = FieldAsString(row, 6), + DisallowAbsent = (attributes & WindowsInstallerConstants.MsidbFeatureAttributesUIDisallowAbsent) == WindowsInstallerConstants.MsidbFeatureAttributesUIDisallowAbsent, + DisallowAdvertise = (attributes & WindowsInstallerConstants.MsidbFeatureAttributesDisallowAdvertise) == WindowsInstallerConstants.MsidbFeatureAttributesDisallowAdvertise, + InstallDefault = installDefault, + TypicalDefault = (attributes & WindowsInstallerConstants.MsidbFeatureAttributesFavorAdvertise) == WindowsInstallerConstants.MsidbFeatureAttributesFavorAdvertise ? FeatureTypicalDefault.Advertise : FeatureTypicalDefault.Install, + }; + } + + case "FeatureComponents": + return DefaultTupleFromRow(typeof(FeatureComponentsTuple), row, columnZeroIsId: false); + case "File": + { + var attributes = FieldAsNullableInt(row, 6); + var readOnly = (attributes & WindowsInstallerConstants.MsidbFileAttributesReadOnly) == WindowsInstallerConstants.MsidbFileAttributesReadOnly; + var hidden = (attributes & WindowsInstallerConstants.MsidbFileAttributesHidden) == WindowsInstallerConstants.MsidbFileAttributesHidden; + var system = (attributes & WindowsInstallerConstants.MsidbFileAttributesSystem) == WindowsInstallerConstants.MsidbFileAttributesSystem; + var vital = (attributes & WindowsInstallerConstants.MsidbFileAttributesVital) == WindowsInstallerConstants.MsidbFileAttributesVital; + var checksum = (attributes & WindowsInstallerConstants.MsidbFileAttributesChecksum) == WindowsInstallerConstants.MsidbFileAttributesChecksum; + bool? compressed = null; + if ((attributes & WindowsInstallerConstants.MsidbFileAttributesNoncompressed) == WindowsInstallerConstants.MsidbFileAttributesNoncompressed) + { + compressed = false; + } + else if ((attributes & WindowsInstallerConstants.MsidbFileAttributesCompressed) == WindowsInstallerConstants.MsidbFileAttributesCompressed) + { + compressed = true; + } + + return new FileTuple(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Public, FieldAsString(row, 0))) + { + Component_ = FieldAsString(row, 1), + LongFileName = FieldAsString(row, 2), + FileSize = FieldAsInt(row, 3), + Version = FieldAsString(row, 4), + Language = FieldAsString(row, 5), + ReadOnly = readOnly, + Hidden = hidden, + System = system, + Vital = vital, + Checksum = checksum, + Compressed = compressed, + }; + } + + case "Font": + return DefaultTupleFromRow(typeof(FontTuple), row, columnZeroIsId: false); + case "Icon": + return DefaultTupleFromRow(typeof(IconTuple), row, columnZeroIsId: false); + case "InstallExecuteSequence": + return DefaultTupleFromRow(typeof(InstallExecuteSequenceTuple), row, columnZeroIsId: false); + case "LockPermissions": + return DefaultTupleFromRow(typeof(LockPermissionsTuple), row, columnZeroIsId: false); + case "Media": + return DefaultTupleFromRow(typeof(MediaTuple), row, columnZeroIsId: false); + case "MIME": + return DefaultTupleFromRow(typeof(MIMETuple), row, columnZeroIsId: false); + case "MoveFile": + return DefaultTupleFromRow(typeof(MoveFileTuple), row, columnZeroIsId: false); + case "MsiAssembly": + return DefaultTupleFromRow(typeof(MsiAssemblyTuple), row, columnZeroIsId: false); + case "MsiShortcutProperty": + return DefaultTupleFromRow(typeof(MsiShortcutPropertyTuple), row, columnZeroIsId: false); + case "ProgId": + return DefaultTupleFromRow(typeof(ProgIdTuple), row, columnZeroIsId: false); + case "Property": + return DefaultTupleFromRow(typeof(PropertyTuple), row, columnZeroIsId: false); + case "PublishComponent": + return DefaultTupleFromRow(typeof(PublishComponentTuple), row, columnZeroIsId: false); + case "Registry": + { + var value = FieldAsString(row, 4); + var valueType = RegistryValueType.String; + var valueAction = RegistryValueActionType.Write; + + if (!String.IsNullOrEmpty(value)) + { + if (value.StartsWith("#x", StringComparison.Ordinal)) + { + valueType = RegistryValueType.Binary; + value = value.Substring(2); + } + else if (value.StartsWith("#%", StringComparison.Ordinal)) + { + valueType = RegistryValueType.Expandable; + value = value.Substring(2); + } + else if (value.StartsWith("#", StringComparison.Ordinal)) + { + valueType = RegistryValueType.Integer; + value = value.Substring(1); + } + else if (value.StartsWith("[~]", StringComparison.Ordinal) && value.EndsWith("[~]", StringComparison.Ordinal)) + { + value = value.Substring(3, value.Length - 6); + valueType = RegistryValueType.MultiString; + valueAction = RegistryValueActionType.Write; + } + else if (value.StartsWith("[~]", StringComparison.Ordinal)) + { + value = value.Substring(3); + valueType = RegistryValueType.MultiString; + valueAction = RegistryValueActionType.Append; + } + else if (value.EndsWith("[~]", StringComparison.Ordinal)) + { + value = value.Substring(0, value.Length - 3); + valueType = RegistryValueType.MultiString; + valueAction = RegistryValueActionType.Prepend; + } + } + + return new RegistryTuple(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Public, FieldAsString(row, 0))) + { + Root = (RegistryRootType)FieldAsInt(row, 1), + Key = FieldAsString(row, 2), + Name = FieldAsString(row, 3), + Value = value, + Component_ = FieldAsString(row, 5), + ValueAction = valueAction, + ValueType = valueType, + }; + } + + case "RegLocator": + return DefaultTupleFromRow(typeof(RegLocatorTuple), row, columnZeroIsId: false); + case "RemoveFile": + return DefaultTupleFromRow(typeof(RemoveFileTuple), row, columnZeroIsId: false); + case "RemoveRegistry": + { + return new RemoveRegistryTuple(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Public, FieldAsString(row, 0))) + { + Action = RemoveRegistryActionType.RemoveOnInstall, + Root = (RegistryRootType)FieldAsInt(row, 1), + Key = FieldAsString(row, 2), + Name = FieldAsString(row, 3), + Component_ = FieldAsString(row, 4), + }; + } + + case "ReserveCost": + return DefaultTupleFromRow(typeof(ReserveCostTuple), row, columnZeroIsId: false); + case "ServiceControl": + { + var events = FieldAsInt(row, 2); + var wait = FieldAsNullableInt(row, 4); + return new ServiceControlTuple(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Public, FieldAsString(row, 0))) + { + Name = FieldAsString(row, 1), + Arguments = FieldAsString(row, 3), + Wait = !wait.HasValue || wait.Value == 1, + Component_ = FieldAsString(row, 5), + InstallRemove = (events & WindowsInstallerConstants.MsidbServiceControlEventDelete) == WindowsInstallerConstants.MsidbServiceControlEventDelete, + UninstallRemove = (events & WindowsInstallerConstants.MsidbServiceControlEventUninstallDelete) == WindowsInstallerConstants.MsidbServiceControlEventUninstallDelete, + InstallStart = (events & WindowsInstallerConstants.MsidbServiceControlEventStart) == WindowsInstallerConstants.MsidbServiceControlEventStart, + UninstallStart = (events & WindowsInstallerConstants.MsidbServiceControlEventUninstallStart) == WindowsInstallerConstants.MsidbServiceControlEventUninstallStart, + InstallStop = (events & WindowsInstallerConstants.MsidbServiceControlEventStop) == WindowsInstallerConstants.MsidbServiceControlEventStop, + UninstallStop = (events & WindowsInstallerConstants.MsidbServiceControlEventUninstallStop) == WindowsInstallerConstants.MsidbServiceControlEventUninstallStop, + }; + } + + case "ServiceInstall": + return DefaultTupleFromRow(typeof(ServiceInstallTuple), row, columnZeroIsId: true); + case "Shortcut": + return DefaultTupleFromRow(typeof(ShortcutTuple), row, columnZeroIsId: true); + case "Signature": + return DefaultTupleFromRow(typeof(SignatureTuple), row, columnZeroIsId: false); + case "Upgrade": + { + var attributes = FieldAsInt(row, 4); + return new UpgradeTuple(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Public, FieldAsString(row, 0))) + { + UpgradeCode = FieldAsString(row, 0), + VersionMin = FieldAsString(row, 1), + VersionMax = FieldAsString(row, 2), + Language = FieldAsString(row, 3), + Remove = FieldAsString(row, 5), + ActionProperty = FieldAsString(row, 6), + MigrateFeatures = (attributes & WindowsInstallerConstants.MsidbUpgradeAttributesMigrateFeatures) == WindowsInstallerConstants.MsidbUpgradeAttributesMigrateFeatures, + OnlyDetect = (attributes & WindowsInstallerConstants.MsidbUpgradeAttributesOnlyDetect) == WindowsInstallerConstants.MsidbUpgradeAttributesOnlyDetect, + IgnoreRemoveFailures = (attributes & WindowsInstallerConstants.MsidbUpgradeAttributesIgnoreRemoveFailure) == WindowsInstallerConstants.MsidbUpgradeAttributesIgnoreRemoveFailure, + VersionMinInclusive = (attributes & WindowsInstallerConstants.MsidbUpgradeAttributesVersionMinInclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesVersionMinInclusive, + VersionMaxInclusive = (attributes & WindowsInstallerConstants.MsidbUpgradeAttributesVersionMaxInclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesVersionMaxInclusive, + ExcludeLanguages = (attributes & WindowsInstallerConstants.MsidbUpgradeAttributesLanguagesExclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesLanguagesExclusive, + }; + } + + case "Verb": + return DefaultTupleFromRow(typeof(VerbTuple), row, columnZeroIsId: false); + //case "WixAction": + // return new WixActionTuple(SourceLineNumber4(row.SourceLineNumbers)) + // { + // SequenceTable = (SequenceTable)Enum.Parse(typeof(SequenceTable), FieldAsString(row, 0)), + // Action = FieldAsString(row, 1), + // Condition = FieldAsString(row, 2), + // Sequence = FieldAsInt(row, 3), + // Before = FieldAsString(row, 4), + // After = FieldAsString(row, 5), + // Overridable = FieldAsNullableInt(row, 6) != 0, + // }; + case "WixFile": + var assemblyAttributes3 = FieldAsNullableInt(row, 1); + return new WixFileTuple(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Public, FieldAsString(row, 0))) + { + AssemblyType = assemblyAttributes3 == 0 ? FileAssemblyType.DotNetAssembly : assemblyAttributes3 == 1 ? FileAssemblyType.Win32Assembly : FileAssemblyType.NotAnAssembly, + File_AssemblyManifest = FieldAsString(row, 2), + File_AssemblyApplication = FieldAsString(row, 3), + Directory_ = FieldAsString(row, 4), + DiskId = FieldAsInt(row, 5), // TODO: BUGBUGBUG: AB#2626: DiskId is nullable in WiX v3. + Source = new IntermediateFieldPathValue() { Path = FieldAsString(row, 6) }, + ProcessorArchitecture = FieldAsString(row, 7), + PatchGroup = FieldAsInt(row, 8), + Attributes = FieldAsInt(row, 9), + }; + case "WixProperty": + { + var attributes = FieldAsInt(row, 1); + return new WixPropertyTuple(SourceLineNumber4(row.SourceLineNumbers)) + { + Property_ = FieldAsString(row, 0), + Admin = (attributes & 0x1) == 0x1, + Hidden = (attributes & 0x2) == 0x2, + Secure = (attributes & 0x4) == 0x4, + }; + } + + default: + return GenericTupleFromCustomRow(row, columnZeroIsId: false); + } + } + + private static CustomActionTargetType DetermineCustomActionTargetType(int type) + { + var targetType = default(CustomActionTargetType); + + if ((type & WindowsInstallerConstants.MsidbCustomActionTypeVBScript) == WindowsInstallerConstants.MsidbCustomActionTypeVBScript) + { + targetType = CustomActionTargetType.VBScript; + } + else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeJScript) == WindowsInstallerConstants.MsidbCustomActionTypeJScript) + { + targetType = CustomActionTargetType.JScript; + } + else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeTextData) == WindowsInstallerConstants.MsidbCustomActionTypeTextData) + { + targetType = CustomActionTargetType.TextData; + } + else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeExe) == WindowsInstallerConstants.MsidbCustomActionTypeExe) + { + targetType = CustomActionTargetType.Exe; + } + else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeDll) == WindowsInstallerConstants.MsidbCustomActionTypeDll) + { + targetType = CustomActionTargetType.Dll; + } + + return targetType; + } + + private static CustomActionSourceType DetermineCustomActionSourceType(int type) + { + var sourceType = CustomActionSourceType.Binary; + + if ((type & WindowsInstallerConstants.MsidbCustomActionTypeProperty) == WindowsInstallerConstants.MsidbCustomActionTypeProperty) + { + sourceType = CustomActionSourceType.Property; + } + else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeDirectory) == WindowsInstallerConstants.MsidbCustomActionTypeDirectory) + { + sourceType = CustomActionSourceType.Directory; + } + else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeSourceFile) == WindowsInstallerConstants.MsidbCustomActionTypeSourceFile) + { + sourceType = CustomActionSourceType.File; + } + + return sourceType; + } + + private static CustomActionExecutionType DetermineCustomActionExecutionType(int type) + { + var executionType = CustomActionExecutionType.Immediate; + + if ((type & (WindowsInstallerConstants.MsidbCustomActionTypeInScript | WindowsInstallerConstants.MsidbCustomActionTypeCommit)) == (WindowsInstallerConstants.MsidbCustomActionTypeInScript | WindowsInstallerConstants.MsidbCustomActionTypeCommit)) + { + executionType = CustomActionExecutionType.Commit; + } + else if ((type & (WindowsInstallerConstants.MsidbCustomActionTypeInScript | WindowsInstallerConstants.MsidbCustomActionTypeRollback)) == (WindowsInstallerConstants.MsidbCustomActionTypeInScript | WindowsInstallerConstants.MsidbCustomActionTypeRollback)) + { + executionType = CustomActionExecutionType.Rollback; + } + else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeInScript) == WindowsInstallerConstants.MsidbCustomActionTypeInScript) + { + executionType = CustomActionExecutionType.Deferred; + } + else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeClientRepeat) == WindowsInstallerConstants.MsidbCustomActionTypeClientRepeat) + { + executionType = CustomActionExecutionType.ClientRepeat; + } + else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeOncePerProcess) == WindowsInstallerConstants.MsidbCustomActionTypeOncePerProcess) + { + executionType = CustomActionExecutionType.OncePerProcess; + } + else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeFirstSequence) == WindowsInstallerConstants.MsidbCustomActionTypeFirstSequence) + { + executionType = CustomActionExecutionType.FirstSequence; + } + + return executionType; + } + + private static IntermediateFieldType ColumnType3ToIntermediateFieldType4(Wix3.ColumnType columnType) + { + switch (columnType) + { + case Wix3.ColumnType.Number: + return IntermediateFieldType.Number; + case Wix3.ColumnType.Object: + return IntermediateFieldType.Path; + case Wix3.ColumnType.Unknown: + case Wix3.ColumnType.String: + case Wix3.ColumnType.Localized: + case Wix3.ColumnType.Preserved: + default: + return IntermediateFieldType.String; + } + } + + private static IntermediateTuple DefaultTupleFromRow(Type tupleType, Wix3.Row row, bool columnZeroIsId) + { + var tuple = Activator.CreateInstance(tupleType) as IntermediateTuple; + + SetTupleFieldsFromRow(row, tuple, columnZeroIsId); + + tuple.SourceLineNumbers = SourceLineNumber4(row.SourceLineNumbers); + return tuple; + } + + private static IntermediateTuple GenericTupleFromCustomRow(Wix3.Row row, bool columnZeroIsId) + { + var columnDefinitions = row.Table.Definition.Columns.Cast<Wix3.ColumnDefinition>(); + var fieldDefinitions = columnDefinitions.Select(columnDefinition => + new IntermediateFieldDefinition(columnDefinition.Name, ColumnType3ToIntermediateFieldType4(columnDefinition.Type))).ToArray(); + var tupleDefinition = new IntermediateTupleDefinition(row.Table.Name, fieldDefinitions, null); + var tuple = new IntermediateTuple(tupleDefinition, SourceLineNumber4(row.SourceLineNumbers)); + + SetTupleFieldsFromRow(row, tuple, columnZeroIsId); + + return tuple; + } + + private static void SetTupleFieldsFromRow(Wix3.Row row, IntermediateTuple tuple, bool columnZeroIsId) + { + int offset = 0; + if (columnZeroIsId) + { + tuple.Id = GetIdentifierForRow(row); + offset = 1; + } + + for (var i = offset; i < row.Fields.Length; ++i) + { + var column = row.Fields[i].Column; + switch (column.Type) + { + case Wix3.ColumnType.String: + case Wix3.ColumnType.Localized: + case Wix3.ColumnType.Object: + case Wix3.ColumnType.Preserved: + tuple.Set(i - offset, FieldAsString(row, i)); + break; + case Wix3.ColumnType.Number: + int? nullableValue = FieldAsNullableInt(row, i); + // TODO: Consider whether null values should be coerced to their default value when + // a column is not nullable. For now, just pass through the null. + //int value = FieldAsInt(row, i); + //tuple.Set(i - offset, column.IsNullable ? nullableValue : value); + tuple.Set(i - offset, nullableValue); + break; + case Wix3.ColumnType.Unknown: + break; + } + } + } + + private static Identifier GetIdentifierForRow(Wix3.Row row) + { + var column = row.Fields[0].Column; + switch (column.Type) + { + case Wix3.ColumnType.String: + case Wix3.ColumnType.Localized: + case Wix3.ColumnType.Object: + case Wix3.ColumnType.Preserved: + return new Identifier(AccessModifier.Public, (string)row.Fields[0].Data); + case Wix3.ColumnType.Number: + return new Identifier(AccessModifier.Public, FieldAsInt(row, 0)); + default: + return null; + } + } + + private static SectionType OutputType3ToSectionType4(Wix3.OutputType outputType) + { + switch (outputType) + { + case Wix3.OutputType.Bundle: + return SectionType.Bundle; + case Wix3.OutputType.Module: + return SectionType.Module; + case Wix3.OutputType.Patch: + return SectionType.Patch; + case Wix3.OutputType.PatchCreation: + return SectionType.PatchCreation; + case Wix3.OutputType.Product: + return SectionType.Product; + case Wix3.OutputType.Transform: + case Wix3.OutputType.Unknown: + default: + return SectionType.Unknown; + } + } + + private static SourceLineNumber SourceLineNumber4(Wix3.SourceLineNumberCollection source) + { + return String.IsNullOrEmpty(source?.EncodedSourceLineNumbers) ? null : SourceLineNumber.CreateFromEncoded(source.EncodedSourceLineNumbers); + } + + private static string FieldAsString(Wix3.Row row, int column) + { + return (string)row[column]; + } + + private static int FieldAsInt(Wix3.Row row, int column) + { + return Convert.ToInt32(row[column]); + } + + private static int? FieldAsNullableInt(Wix3.Row row, int column) + { + var field = row.Fields[column]; + if (field.Data == null) + { + return null; + } + else + { + return Convert.ToInt32(field.Data); + } + } + } +} 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. --> + +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>netstandard2.0</TargetFramework> + <Description>Tupleizer</Description> + <Title>WiX Toolset Converters Tuplizer</Title> + <DebugType>embedded</DebugType> + <PublishRepositoryUrl>true</PublishRepositoryUrl> + </PropertyGroup> + + <PropertyGroup> + <NoWarn>NU1701</NoWarn> + </PropertyGroup> + + <ItemGroup> + <Reference Include="wix" HintPath="..\deps\wix.dll" /> + <None Include="..\deps\wix.dll" Pack="true" PackagePath="lib\net461" /> + </ItemGroup> + + <ItemGroup> + <PackageReference Include="WixToolset.Core" Version="4.0.*" /> + <PackageReference Include="WixToolset.Core.WindowsInstaller" Version="4.0.*" /> + </ItemGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-18618-05" PrivateAssets="All"/> + <PackageReference Include="Nerdbank.GitVersioning" Version="2.1.65" PrivateAssets="All" /> + </ItemGroup> +</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 @@ +// 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. + +namespace WixToolset.Converters +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.IO; + using System.Linq; + using System.Text; + using System.Text.RegularExpressions; + using System.Xml; + using System.Xml.Linq; + using WixToolset.Data; + using WixToolset.Extensibility.Services; + + /// <summary> + /// WiX source code converter. + /// </summary> + public class Wix3Converter + { + private static readonly Regex AddPrefix = new Regex(@"^[^a-zA-Z_]", RegexOptions.Compiled); + private static readonly Regex IllegalIdentifierCharacters = new Regex(@"[^A-Za-z0-9_\.]|\.{2,}", RegexOptions.Compiled); // non 'words' and assorted valid characters + + private const char XDocumentNewLine = '\n'; // XDocument normalizes "\r\n" to just "\n". + private static readonly XNamespace WixNamespace = "http://wixtoolset.org/schemas/v4/wxs"; + + private static readonly XName DirectoryElementName = WixNamespace + "Directory"; + private static readonly XName FileElementName = WixNamespace + "File"; + private static readonly XName ExePackageElementName = WixNamespace + "ExePackage"; + private static readonly XName MsiPackageElementName = WixNamespace + "MsiPackage"; + private static readonly XName MspPackageElementName = WixNamespace + "MspPackage"; + private static readonly XName MsuPackageElementName = WixNamespace + "MsuPackage"; + private static readonly XName PayloadElementName = WixNamespace + "Payload"; + private static readonly XName CustomActionElementName = WixNamespace + "CustomAction"; + private static readonly XName PropertyElementName = WixNamespace + "Property"; + private static readonly XName WixElementWithoutNamespaceName = XNamespace.None + "Wix"; + + private static readonly Dictionary<string, XNamespace> OldToNewNamespaceMapping = new Dictionary<string, XNamespace>() + { + { "http://schemas.microsoft.com/wix/BalExtension", "http://wixtoolset.org/schemas/v4/wxs/bal" }, + { "http://schemas.microsoft.com/wix/ComPlusExtension", "http://wixtoolset.org/schemas/v4/wxs/complus" }, + { "http://schemas.microsoft.com/wix/DependencyExtension", "http://wixtoolset.org/schemas/v4/wxs/dependency" }, + { "http://schemas.microsoft.com/wix/DifxAppExtension", "http://wixtoolset.org/schemas/v4/wxs/difxapp" }, + { "http://schemas.microsoft.com/wix/FirewallExtension", "http://wixtoolset.org/schemas/v4/wxs/firewall" }, + { "http://schemas.microsoft.com/wix/GamingExtension", "http://wixtoolset.org/schemas/v4/wxs/gaming" }, + { "http://schemas.microsoft.com/wix/IIsExtension", "http://wixtoolset.org/schemas/v4/wxs/iis" }, + { "http://schemas.microsoft.com/wix/MsmqExtension", "http://wixtoolset.org/schemas/v4/wxs/msmq" }, + { "http://schemas.microsoft.com/wix/NetFxExtension", "http://wixtoolset.org/schemas/v4/wxs/netfx" }, + { "http://schemas.microsoft.com/wix/PSExtension", "http://wixtoolset.org/schemas/v4/wxs/powershell" }, + { "http://schemas.microsoft.com/wix/SqlExtension", "http://wixtoolset.org/schemas/v4/wxs/sql" }, + { "http://schemas.microsoft.com/wix/TagExtension", "http://wixtoolset.org/schemas/v4/wxs/tag" }, + { "http://schemas.microsoft.com/wix/UtilExtension", "http://wixtoolset.org/schemas/v4/wxs/util" }, + { "http://schemas.microsoft.com/wix/VSExtension", "http://wixtoolset.org/schemas/v4/wxs/vs" }, + { "http://wixtoolset.org/schemas/thmutil/2010", "http://wixtoolset.org/schemas/v4/thmutil" }, + { "http://schemas.microsoft.com/wix/2009/Lux", "http://wixtoolset.org/schemas/v4/lux" }, + { "http://schemas.microsoft.com/wix/2006/wi", "http://wixtoolset.org/schemas/v4/wxs" }, + { "http://schemas.microsoft.com/wix/2006/localization", "http://wixtoolset.org/schemas/v4/wxl" }, + { "http://schemas.microsoft.com/wix/2006/libraries", "http://wixtoolset.org/schemas/v4/wixlib" }, + { "http://schemas.microsoft.com/wix/2006/objects", "http://wixtoolset.org/schemas/v4/wixobj" }, + { "http://schemas.microsoft.com/wix/2006/outputs", "http://wixtoolset.org/schemas/v4/wixout" }, + { "http://schemas.microsoft.com/wix/2007/pdbs", "http://wixtoolset.org/schemas/v4/wixpdb" }, + { "http://schemas.microsoft.com/wix/2003/04/actions", "http://wixtoolset.org/schemas/v4/wi/actions" }, + { "http://schemas.microsoft.com/wix/2006/tables", "http://wixtoolset.org/schemas/v4/wi/tables" }, + { "http://schemas.microsoft.com/wix/2006/WixUnit", "http://wixtoolset.org/schemas/v4/wixunit" }, + }; + + private readonly Dictionary<XName, Action<XElement>> ConvertElementMapping; + + /// <summary> + /// Instantiate a new Converter class. + /// </summary> + /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param> + /// <param name="errorsAsWarnings">Test errors to display as warnings.</param> + /// <param name="ignoreErrors">Test errors to ignore.</param> + public Wix3Converter(IMessaging messaging, int indentationAmount, IEnumerable<string> errorsAsWarnings = null, IEnumerable<string> ignoreErrors = null) + { + this.ConvertElementMapping = new Dictionary<XName, Action<XElement>> + { + { Wix3Converter.DirectoryElementName, this.ConvertDirectoryElement }, + { Wix3Converter.FileElementName, this.ConvertFileElement }, + { Wix3Converter.ExePackageElementName, this.ConvertSuppressSignatureValidation }, + { Wix3Converter.MsiPackageElementName, this.ConvertSuppressSignatureValidation }, + { Wix3Converter.MspPackageElementName, this.ConvertSuppressSignatureValidation }, + { Wix3Converter.MsuPackageElementName, this.ConvertSuppressSignatureValidation }, + { Wix3Converter.PayloadElementName, this.ConvertSuppressSignatureValidation }, + { Wix3Converter.CustomActionElementName, this.ConvertCustomActionElement }, + { Wix3Converter.PropertyElementName, this.ConvertPropertyElement }, + { Wix3Converter.WixElementWithoutNamespaceName, this.ConvertWixElementWithoutNamespace }, + }; + + this.Messaging = messaging; + + this.IndentationAmount = indentationAmount; + + this.ErrorsAsWarnings = new HashSet<ConverterTestType>(this.YieldConverterTypes(errorsAsWarnings)); + + this.IgnoreErrors = new HashSet<ConverterTestType>(this.YieldConverterTypes(ignoreErrors)); + } + + private int Errors { get; set; } + + private HashSet<ConverterTestType> ErrorsAsWarnings { get; set; } + + private HashSet<ConverterTestType> IgnoreErrors { get; set; } + + private IMessaging Messaging { get; } + + private int IndentationAmount { get; set; } + + private string SourceFile { get; set; } + + /// <summary> + /// Convert a file. + /// </summary> + /// <param name="sourceFile">The file to convert.</param> + /// <param name="saveConvertedFile">Option to save the converted errors that are found.</param> + /// <returns>The number of errors found.</returns> + public int ConvertFile(string sourceFile, bool saveConvertedFile) + { + XDocument document; + + // Set the instance info. + this.Errors = 0; + this.SourceFile = sourceFile; + + try + { + document = XDocument.Load(this.SourceFile, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + } + catch (XmlException e) + { + this.OnError(ConverterTestType.XmlException, null, "The xml is invalid. Detail: '{0}'", e.Message); + + return this.Errors; + } + + this.ConvertDocument(document); + + // Fix errors if requested and necessary. + if (saveConvertedFile && 0 < this.Errors) + { + try + { + using (var writer = File.CreateText(this.SourceFile)) + { + document.Save(writer, SaveOptions.DisableFormatting | SaveOptions.OmitDuplicateNamespaces); + } + } + catch (UnauthorizedAccessException) + { + this.OnError(ConverterTestType.UnauthorizedAccessException, null, "Could not write to file."); + } + } + + return this.Errors; + } + + /// <summary> + /// Convert a document. + /// </summary> + /// <param name="document">The document to convert.</param> + /// <returns>The number of errors found.</returns> + public int ConvertDocument(XDocument document) + { + var declaration = document.Declaration; + + // Convert the declaration. + if (null != declaration) + { + if (!String.Equals("utf-8", declaration.Encoding, StringComparison.OrdinalIgnoreCase)) + { + if (this.OnError(ConverterTestType.DeclarationEncodingWrong, document.Root, "The XML declaration encoding is not properly set to 'utf-8'.")) + { + declaration.Encoding = "utf-8"; + } + } + } + else // missing declaration + { + if (this.OnError(ConverterTestType.DeclarationMissing, null, "This file is missing an XML declaration on the first line.")) + { + document.Declaration = new XDeclaration("1.0", "utf-8", null); + document.Root.AddBeforeSelf(new XText(XDocumentNewLine.ToString())); + } + } + + // Start converting the nodes at the top. + this.ConvertNodes(document.Nodes(), 0); + + return this.Errors; + } + + private void ConvertNodes(IEnumerable<XNode> nodes, int level) + { + // Note we operate on a copy of the node list since we may + // remove some whitespace nodes during this processing. + foreach (var node in nodes.ToList()) + { + if (node is XText text) + { + if (!String.IsNullOrWhiteSpace(text.Value)) + { + text.Value = text.Value.Trim(); + } + else if (node.NextNode is XCData cdata) + { + this.EnsurePrecedingWhitespaceRemoved(text, node, ConverterTestType.WhitespacePrecedingNodeWrong); + } + else if (node.NextNode is XElement element) + { + this.EnsurePrecedingWhitespaceCorrect(text, node, level, ConverterTestType.WhitespacePrecedingNodeWrong); + } + else if (node.NextNode is null) // this is the space before the close element + { + if (node.PreviousNode is null || node.PreviousNode is XCData) + { + this.EnsurePrecedingWhitespaceRemoved(text, node.Parent, ConverterTestType.WhitespacePrecedingEndElementWrong); + } + else if (level == 0) // root element's close tag + { + this.EnsurePrecedingWhitespaceCorrect(text, node, 0, ConverterTestType.WhitespacePrecedingEndElementWrong); + } + else + { + this.EnsurePrecedingWhitespaceCorrect(text, node, level - 1, ConverterTestType.WhitespacePrecedingEndElementWrong); + } + } + } + else if (node is XElement element) + { + this.ConvertElement(element); + + this.ConvertNodes(element.Nodes(), level + 1); + } + } + } + + private void EnsurePrecedingWhitespaceCorrect(XText whitespace, XNode node, int level, ConverterTestType testType) + { + if (!Wix3Converter.LeadingWhitespaceValid(this.IndentationAmount, level, whitespace.Value)) + { + var message = testType == ConverterTestType.WhitespacePrecedingEndElementWrong ? "The whitespace preceding this end element is incorrect." : "The whitespace preceding this node is incorrect."; + + if (this.OnError(testType, node, message)) + { + Wix3Converter.FixupWhitespace(this.IndentationAmount, level, whitespace); + } + } + } + + private void EnsurePrecedingWhitespaceRemoved(XText whitespace, XNode node, ConverterTestType testType) + { + if (!String.IsNullOrEmpty(whitespace.Value)) + { + var message = testType == ConverterTestType.WhitespacePrecedingEndElementWrong ? "The whitespace preceding this end element is incorrect." : "The whitespace preceding this node is incorrect."; + + if (this.OnError(testType, node, message)) + { + whitespace.Remove(); + } + } + } + + private void ConvertElement(XElement element) + { + // Gather any deprecated namespaces, then update this element tree based on those deprecations. + var deprecatedToUpdatedNamespaces = new Dictionary<XNamespace, XNamespace>(); + + foreach (var declaration in element.Attributes().Where(a => a.IsNamespaceDeclaration)) + { + if (Wix3Converter.OldToNewNamespaceMapping.TryGetValue(declaration.Value, out var ns)) + { + if (this.OnError(ConverterTestType.XmlnsValueWrong, declaration, "The namespace '{0}' is out of date. It must be '{1}'.", declaration.Value, ns.NamespaceName)) + { + deprecatedToUpdatedNamespaces.Add(declaration.Value, ns); + } + } + } + + if (deprecatedToUpdatedNamespaces.Any()) + { + Wix3Converter.UpdateElementsWithDeprecatedNamespaces(element.DescendantsAndSelf(), deprecatedToUpdatedNamespaces); + } + + // Apply any specialized conversion actions. + if (this.ConvertElementMapping.TryGetValue(element.Name, out var convert)) + { + convert(element); + } + } + + private void ConvertDirectoryElement(XElement element) + { + if (null == element.Attribute("Name")) + { + var attribute = element.Attribute("ShortName"); + if (null != attribute) + { + var shortName = attribute.Value; + if (this.OnError(ConverterTestType.AssignDirectoryNameFromShortName, element, "The directory ShortName attribute is being renamed to Name since Name wasn't specified for value '{0}'", shortName)) + { + element.Add(new XAttribute("Name", shortName)); + attribute.Remove(); + } + } + } + } + + private void ConvertFileElement(XElement element) + { + if (null == element.Attribute("Id")) + { + var attribute = element.Attribute("Name"); + + if (null == attribute) + { + attribute = element.Attribute("Source"); + } + + if (null != attribute) + { + var name = Path.GetFileName(attribute.Value); + + if (this.OnError(ConverterTestType.AssignAnonymousFileId, element, "The file id is being updated to '{0}' to ensure it remains the same as the default", name)) + { + IEnumerable<XAttribute> attributes = element.Attributes().ToList(); + element.RemoveAttributes(); + element.Add(new XAttribute("Id", GetIdentifierFromName(name))); + element.Add(attributes); + } + } + } + } + + private void ConvertSuppressSignatureValidation(XElement element) + { + var suppressSignatureValidation = element.Attribute("SuppressSignatureValidation"); + + if (null != suppressSignatureValidation) + { + if (this.OnError(ConverterTestType.SuppressSignatureValidationDeprecated, element, "The chain package element contains deprecated '{0}' attribute. Use the 'EnableSignatureValidation' attribute instead.", suppressSignatureValidation)) + { + if ("no" == suppressSignatureValidation.Value) + { + element.Add(new XAttribute("EnableSignatureValidation", "yes")); + } + } + + suppressSignatureValidation.Remove(); + } + } + + private void ConvertCustomActionElement(XElement xCustomAction) + { + var xBinaryKey = xCustomAction.Attribute("BinaryKey"); + + if (xBinaryKey?.Value == "WixCA") + { + if (this.OnError(ConverterTestType.WixCABinaryIdRenamed, xCustomAction, "The WixCA custom action DLL Binary table id has been renamed. Use the id 'UtilCA' instead.")) + { + xBinaryKey.Value = "UtilCA"; + } + } + + var xDllEntry = xCustomAction.Attribute("DllEntry"); + + if (xDllEntry?.Value == "CAQuietExec" || xDllEntry?.Value == "CAQuietExec64") + { + if (this.OnError(ConverterTestType.QuietExecCustomActionsRenamed, xCustomAction, "The CAQuietExec and CAQuietExec64 custom action ids have been renamed. Use the ids 'WixQuietExec' and 'WixQuietExec64' instead.")) + { + xDllEntry.Value = xDllEntry.Value.Replace("CAQuietExec", "WixQuietExec"); + } + } + + var xProperty = xCustomAction.Attribute("Property"); + + if (xProperty?.Value == "QtExecCmdLine" || xProperty?.Value == "QtExec64CmdLine") + { + if (this.OnError(ConverterTestType.QuietExecCustomActionsRenamed, xCustomAction, "The QtExecCmdLine and QtExec64CmdLine property ids have been renamed. Use the ids 'WixQuietExecCmdLine' and 'WixQuietExec64CmdLine' instead.")) + { + xProperty.Value = xProperty.Value.Replace("QtExec", "WixQuietExec"); + } + } + } + + private void ConvertPropertyElement(XElement xProperty) + { + var xId = xProperty.Attribute("Id"); + + if (xId.Value == "QtExecCmdTimeout") + { + this.OnError(ConverterTestType.QtExecCmdTimeoutAmbiguous, xProperty, "QtExecCmdTimeout was previously used for both CAQuietExec and CAQuietExec64. For WixQuietExec, use WixQuietExecCmdTimeout. For WixQuietExec64, use WixQuietExec64CmdTimeout."); + } + } + + /// <summary> + /// Converts a Wix element. + /// </summary> + /// <param name="element">The Wix element to convert.</param> + /// <returns>The converted element.</returns> + private void ConvertWixElementWithoutNamespace(XElement element) + { + if (this.OnError(ConverterTestType.XmlnsMissing, element, "The xmlns attribute is missing. It must be present with a value of '{0}'.", WixNamespace.NamespaceName)) + { + element.Name = WixNamespace.GetName(element.Name.LocalName); + + element.Add(new XAttribute("xmlns", WixNamespace.NamespaceName)); // set the default namespace. + + foreach (var elementWithoutNamespace in element.Elements().Where(e => XNamespace.None == e.Name.Namespace)) + { + elementWithoutNamespace.Name = WixNamespace.GetName(elementWithoutNamespace.Name.LocalName); + } + } + } + + private IEnumerable<ConverterTestType> YieldConverterTypes(IEnumerable<string> types) + { + if (null != types) + { + foreach (var type in types) + { + + if (Enum.TryParse<ConverterTestType>(type, true, out var itt)) + { + yield return itt; + } + else // not a known ConverterTestType + { + this.OnError(ConverterTestType.ConverterTestTypeUnknown, null, "Unknown error type: '{0}'.", type); + } + } + } + } + + private static void UpdateElementsWithDeprecatedNamespaces(IEnumerable<XElement> elements, Dictionary<XNamespace, XNamespace> deprecatedToUpdatedNamespaces) + { + foreach (var element in elements) + { + + if (deprecatedToUpdatedNamespaces.TryGetValue(element.Name.Namespace, out var ns)) + { + element.Name = ns.GetName(element.Name.LocalName); + } + + // Remove all the attributes and add them back to with their namespace updated (as necessary). + IEnumerable<XAttribute> attributes = element.Attributes().ToList(); + element.RemoveAttributes(); + + foreach (var attribute in attributes) + { + var convertedAttribute = attribute; + + if (attribute.IsNamespaceDeclaration) + { + if (deprecatedToUpdatedNamespaces.TryGetValue(attribute.Value, out ns)) + { + convertedAttribute = ("xmlns" == attribute.Name.LocalName) ? new XAttribute(attribute.Name.LocalName, ns.NamespaceName) : new XAttribute(XNamespace.Xmlns + attribute.Name.LocalName, ns.NamespaceName); + } + } + else if (deprecatedToUpdatedNamespaces.TryGetValue(attribute.Name.Namespace, out ns)) + { + convertedAttribute = new XAttribute(ns.GetName(attribute.Name.LocalName), attribute.Value); + } + + element.Add(convertedAttribute); + } + } + } + + /// <summary> + /// Determine if the whitespace preceding a node is appropriate for its depth level. + /// </summary> + /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param> + /// <param name="level">The depth level that should match this whitespace.</param> + /// <param name="whitespace">The whitespace to validate.</param> + /// <returns>true if the whitespace is legal; false otherwise.</returns> + private static bool LeadingWhitespaceValid(int indentationAmount, int level, string whitespace) + { + // Strip off leading newlines; there can be an arbitrary number of these. + whitespace = whitespace.TrimStart(XDocumentNewLine); + + var indentation = new string(' ', level * indentationAmount); + + return whitespace == indentation; + } + + /// <summary> + /// Fix the whitespace in a whitespace node. + /// </summary> + /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param> + /// <param name="level">The depth level of the desired whitespace.</param> + /// <param name="whitespace">The whitespace node to fix.</param> + private static void FixupWhitespace(int indentationAmount, int level, XText whitespace) + { + var value = new StringBuilder(whitespace.Value.Length); + + // Keep any previous preceeding new lines. + var newlines = whitespace.Value.TakeWhile(c => c == XDocumentNewLine).Count(); + + // Ensure there is always at least one new line before the indentation. + value.Append(XDocumentNewLine, newlines == 0 ? 1 : newlines); + + whitespace.Value = value.Append(' ', level * indentationAmount).ToString(); + } + + /// <summary> + /// Output an error message to the console. + /// </summary> + /// <param name="converterTestType">The type of converter test.</param> + /// <param name="node">The node that caused the error.</param> + /// <param name="message">Detailed error message.</param> + /// <param name="args">Additional formatted string arguments.</param> + /// <returns>Returns true indicating that action should be taken on this error, and false if it should be ignored.</returns> + private bool OnError(ConverterTestType converterTestType, XObject node, string message, params object[] args) + { + if (this.IgnoreErrors.Contains(converterTestType)) // ignore the error + { + return false; + } + + // Increase the error count. + this.Errors++; + + var sourceLine = (null == node) ? new SourceLineNumber(this.SourceFile ?? "wixcop.exe") : new SourceLineNumber(this.SourceFile, ((IXmlLineInfo)node).LineNumber); + var warning = this.ErrorsAsWarnings.Contains(converterTestType); + var display = String.Format(CultureInfo.CurrentCulture, message, args); + + var msg = new Message(sourceLine, warning ? MessageLevel.Warning : MessageLevel.Error, (int)converterTestType, "{0} ({1})", display, converterTestType.ToString()); + + this.Messaging.Write(msg); + + return true; + } + + /// <summary> + /// Return an identifier based on passed file/directory name + /// </summary> + /// <param name="name">File/directory name to generate identifer from</param> + /// <returns>A version of the name that is a legal identifier.</returns> + /// <remarks>This is duplicated from WiX's Common class.</remarks> + private static string GetIdentifierFromName(string name) + { + string result = IllegalIdentifierCharacters.Replace(name, "_"); // replace illegal characters with "_". + + // MSI identifiers must begin with an alphabetic character or an + // underscore. Prefix all other values with an underscore. + if (AddPrefix.IsMatch(name)) + { + result = String.Concat("_", result); + } + + return result; + } + + /// <summary> + /// Converter test types. These are used to condition error messages down to warnings. + /// </summary> + private enum ConverterTestType + { + /// <summary> + /// Internal-only: displayed when a string cannot be converted to an ConverterTestType. + /// </summary> + ConverterTestTypeUnknown, + + /// <summary> + /// Displayed when an XML loading exception has occurred. + /// </summary> + XmlException, + + /// <summary> + /// Displayed when a file cannot be accessed; typically when trying to save back a fixed file. + /// </summary> + UnauthorizedAccessException, + + /// <summary> + /// Displayed when the encoding attribute in the XML declaration is not 'UTF-8'. + /// </summary> + DeclarationEncodingWrong, + + /// <summary> + /// Displayed when the XML declaration is missing from the source file. + /// </summary> + DeclarationMissing, + + /// <summary> + /// Displayed when the whitespace preceding a CDATA node is wrong. + /// </summary> + WhitespacePrecedingCDATAWrong, + + /// <summary> + /// Displayed when the whitespace preceding a node is wrong. + /// </summary> + WhitespacePrecedingNodeWrong, + + /// <summary> + /// Displayed when an element is not empty as it should be. + /// </summary> + NotEmptyElement, + + /// <summary> + /// Displayed when the whitespace following a CDATA node is wrong. + /// </summary> + WhitespaceFollowingCDATAWrong, + + /// <summary> + /// Displayed when the whitespace preceding an end element is wrong. + /// </summary> + WhitespacePrecedingEndElementWrong, + + /// <summary> + /// Displayed when the xmlns attribute is missing from the document element. + /// </summary> + XmlnsMissing, + + /// <summary> + /// Displayed when the xmlns attribute on the document element is wrong. + /// </summary> + XmlnsValueWrong, + + /// <summary> + /// Assign an identifier to a File element when on Id attribute is specified. + /// </summary> + AssignAnonymousFileId, + + /// <summary> + /// SuppressSignatureValidation attribute is deprecated and replaced with EnableSignatureValidation. + /// </summary> + SuppressSignatureValidationDeprecated, + + /// <summary> + /// WixCA Binary/@Id has been renamed to UtilCA. + /// </summary> + WixCABinaryIdRenamed, + + /// <summary> + /// QtExec custom actions have been renamed. + /// </summary> + QuietExecCustomActionsRenamed, + + /// <summary> + /// QtExecCmdTimeout was previously used for both CAQuietExec and CAQuietExec64. For WixQuietExec, use WixQuietExecCmdTimeout. For WixQuietExec64, use WixQuietExec64CmdTimeout. + /// </summary> + QtExecCmdTimeoutAmbiguous, + + /// <summary> + /// Directory/@ShortName may only be specified with Directory/@Name. + /// </summary> + AssignDirectoryNameFromShortName, + } + } +} 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. --> + +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>netstandard2.0</TargetFramework> + <Description>Converter</Description> + <Title>WiX Toolset Converters</Title> + <DebugType>embedded</DebugType> + <PublishRepositoryUrl>true</PublishRepositoryUrl> + </PropertyGroup> + + <PropertyGroup> + <NoWarn>NU1701</NoWarn> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="WixToolset.Core" Version="4.0.*" /> + </ItemGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-18618-05" PrivateAssets="All"/> + <PackageReference Include="Nerdbank.GitVersioning" Version="2.1.65" PrivateAssets="All" /> + </ItemGroup> +</Project> diff --git a/src/deps/wix.dll b/src/deps/wix.dll new file mode 100644 index 00000000..64f70f75 Binary files /dev/null and b/src/deps/wix.dll 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 @@ +// 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. + +namespace WixToolsetTest.Converters.Tupleizer +{ + using System; + using System.IO; + using System.Linq; + using WixBuildTools.TestSupport; + using Wix3 = Microsoft.Tools.WindowsInstallerXml; + using WixToolset.Converters.Tupleizer; + using WixToolset.Data; + using WixToolset.Data.WindowsInstaller; + using WixToolset.Data.Tuples; + using Xunit; + + public class ConvertTuplesFixture + { + [Fact] + public void CanLoadWixoutAndConvertToIntermediate() + { + var rootFolder = TestData.Get(); + var dataFolder = TestData.Get(@"TestData\Integration"); + + using (var fs = new DisposableFileSystem()) + { + var intermediateFolder = fs.GetFolder(); + + var path = Path.Combine(dataFolder, "test.wixout"); + var output = Wix3.Output.Load(path, suppressVersionCheck: true, suppressSchema: true); + + var command = new ConvertTuplesCommand(); + var intermediate = command.Execute(output); + + Assert.NotNull(intermediate); + Assert.Single(intermediate.Sections); + Assert.Equal(String.Empty, intermediate.Id); + + // Save and load to guarantee round-tripping support. + // + var wixiplFile = Path.Combine(intermediateFolder, "test.wixipl"); + intermediate.Save(wixiplFile); + + intermediate = Intermediate.Load(wixiplFile); + + // Dump to text for easy diffing, with some massaging to keep v3 and v4 diffable. + // + var tables = output.Tables.Cast<Wix3.Table>(); + var wix3Dump = tables + .SelectMany(table => table.Rows.Cast<Wix3.Row>() + .Select(row => RowToString(row))) + .ToArray(); + + var tuples = intermediate.Sections.SelectMany(s => s.Tuples); + var wix4Dump = tuples.Select(tuple => TupleToString(tuple)).ToArray(); + + Assert.Equal(wix3Dump, wix4Dump); + + // Useful when you want to diff the outputs with another diff tool... + // + //var wix3TextDump = String.Join(Environment.NewLine, wix3Dump.OrderBy(val => val)); + //var wix4TextDump = String.Join(Environment.NewLine, wix4Dump.OrderBy(val => val)); + //Assert.Equal(wix3TextDump, wix4TextDump); + } + } + + private static string RowToString(Wix3.Row row) + { + var fields = String.Join(",", row.Fields.Select(field => field.Data?.ToString())); + + // Massage output to match WiX v3 rows and v4 tuples. + // + switch (row.Table.Name) + { + case "File": + var fieldValues = row.Fields.Take(7).Select(field => field.Data?.ToString()).ToArray(); + if (fieldValues[3] == null) + { + // "Somebody" sometimes writes out a null field even when the column definition says + // it's non-nullable. Not naming names or anything. (SWID tags.) + fieldValues[3] = "0"; + } + fields = String.Join(",", fieldValues); + break; + case "WixFile": + fields = String.Join(",", row.Fields.Take(8).Select(field => field.Data?.ToString())); + break; + } + + return $"{row.Table.Name},{fields}"; + } + + private static string TupleToString(WixToolset.Data.IntermediateTuple tuple) + { + var fields = String.Join(",", tuple.Fields.Select(field => field?.AsString())); + + switch (tuple.Definition.Name) + { + // Massage output to match WiX v3 rows and v4 tuples. + // + case "Component": + { + var componentTuple = (ComponentTuple)tuple; + var attributes = ComponentLocation.Either == componentTuple.Location ? WindowsInstallerConstants.MsidbComponentAttributesOptional : 0; + attributes |= ComponentLocation.SourceOnly == componentTuple.Location ? WindowsInstallerConstants.MsidbComponentAttributesSourceOnly : 0; + attributes |= ComponentKeyPathType.Registry == componentTuple.KeyPathType ? WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath : 0; + attributes |= ComponentKeyPathType.OdbcDataSource == componentTuple.KeyPathType ? WindowsInstallerConstants.MsidbComponentAttributesODBCDataSource : 0; + attributes |= componentTuple.DisableRegistryReflection ? WindowsInstallerConstants.MsidbComponentAttributesDisableRegistryReflection : 0; + attributes |= componentTuple.NeverOverwrite ? WindowsInstallerConstants.MsidbComponentAttributesNeverOverwrite : 0; + attributes |= componentTuple.Permanent ? WindowsInstallerConstants.MsidbComponentAttributesPermanent : 0; + attributes |= componentTuple.SharedDllRefCount ? WindowsInstallerConstants.MsidbComponentAttributesSharedDllRefCount : 0; + attributes |= componentTuple.Shared ? WindowsInstallerConstants.MsidbComponentAttributesShared : 0; + attributes |= componentTuple.Transitive ? WindowsInstallerConstants.MsidbComponentAttributesTransitive : 0; + attributes |= componentTuple.UninstallWhenSuperseded ? WindowsInstallerConstants.MsidbComponentAttributes64bit : 0; + attributes |= componentTuple.Win64 ? WindowsInstallerConstants.MsidbComponentAttributes64bit : 0; + + fields = String.Join(",", + componentTuple.ComponentId, + componentTuple.Directory_, + attributes.ToString(), + componentTuple.Condition, + componentTuple.KeyPath + ); + break; + } + case "CustomAction": + { + var customActionTuple = (CustomActionTuple)tuple; + var type = customActionTuple.Win64 ? WindowsInstallerConstants.MsidbCustomActionType64BitScript : 0; + type |= customActionTuple.TSAware ? WindowsInstallerConstants.MsidbCustomActionTypeTSAware : 0; + type |= customActionTuple.Impersonate ? 0 : WindowsInstallerConstants.MsidbCustomActionTypeNoImpersonate; + type |= customActionTuple.IgnoreResult ? WindowsInstallerConstants.MsidbCustomActionTypeContinue : 0; + type |= customActionTuple.Hidden ? WindowsInstallerConstants.MsidbCustomActionTypeHideTarget : 0; + type |= customActionTuple.Async ? WindowsInstallerConstants.MsidbCustomActionTypeAsync : 0; + type |= CustomActionExecutionType.FirstSequence == customActionTuple.ExecutionType ? WindowsInstallerConstants.MsidbCustomActionTypeFirstSequence : 0; + type |= CustomActionExecutionType.OncePerProcess == customActionTuple.ExecutionType ? WindowsInstallerConstants.MsidbCustomActionTypeOncePerProcess : 0; + type |= CustomActionExecutionType.ClientRepeat == customActionTuple.ExecutionType ? WindowsInstallerConstants.MsidbCustomActionTypeClientRepeat : 0; + type |= CustomActionExecutionType.Deferred == customActionTuple.ExecutionType ? WindowsInstallerConstants.MsidbCustomActionTypeInScript : 0; + type |= CustomActionExecutionType.Rollback == customActionTuple.ExecutionType ? WindowsInstallerConstants.MsidbCustomActionTypeInScript | WindowsInstallerConstants.MsidbCustomActionTypeRollback : 0; + type |= CustomActionExecutionType.Commit == customActionTuple.ExecutionType ? WindowsInstallerConstants.MsidbCustomActionTypeInScript | WindowsInstallerConstants.MsidbCustomActionTypeCommit : 0; + type |= CustomActionSourceType.File == customActionTuple.SourceType ? WindowsInstallerConstants.MsidbCustomActionTypeSourceFile : 0; + type |= CustomActionSourceType.Directory == customActionTuple.SourceType ? WindowsInstallerConstants.MsidbCustomActionTypeDirectory : 0; + type |= CustomActionSourceType.Property == customActionTuple.SourceType ? WindowsInstallerConstants.MsidbCustomActionTypeProperty : 0; + type |= CustomActionTargetType.Dll == customActionTuple.TargetType ? WindowsInstallerConstants.MsidbCustomActionTypeDll : 0; + type |= CustomActionTargetType.Exe == customActionTuple.TargetType ? WindowsInstallerConstants.MsidbCustomActionTypeExe : 0; + type |= CustomActionTargetType.TextData == customActionTuple.TargetType ? WindowsInstallerConstants.MsidbCustomActionTypeTextData : 0; + type |= CustomActionTargetType.JScript == customActionTuple.TargetType ? WindowsInstallerConstants.MsidbCustomActionTypeJScript : 0; + type |= CustomActionTargetType.VBScript == customActionTuple.TargetType ? WindowsInstallerConstants.MsidbCustomActionTypeVBScript : 0; + + fields = String.Join(",", + type.ToString(), + customActionTuple.Source, + customActionTuple.Target, + customActionTuple.PatchUninstall ? WindowsInstallerConstants.MsidbCustomActionTypePatchUninstall.ToString() : null + ); + break; + } + case "Feature": + { + var featureTuple = (FeatureTuple)tuple; + var attributes = featureTuple.DisallowAbsent ? WindowsInstallerConstants.MsidbFeatureAttributesUIDisallowAbsent : 0; + attributes |= featureTuple.DisallowAdvertise ? WindowsInstallerConstants.MsidbFeatureAttributesDisallowAdvertise : 0; + attributes |= FeatureInstallDefault.FollowParent == featureTuple.InstallDefault ? WindowsInstallerConstants.MsidbFeatureAttributesFollowParent : 0; + attributes |= FeatureInstallDefault.Source == featureTuple.InstallDefault ? WindowsInstallerConstants.MsidbFeatureAttributesFavorSource : 0; + attributes |= FeatureTypicalDefault.Advertise == featureTuple.TypicalDefault ? WindowsInstallerConstants.MsidbFeatureAttributesFavorAdvertise : 0; + + fields = String.Join(",", + featureTuple.Feature_Parent, + featureTuple.Title, + featureTuple.Description, + featureTuple.Display.ToString(), + featureTuple.Level.ToString(), + featureTuple.Directory_, + attributes.ToString()); + break; + } + case "File": + { + var fileTuple = (FileTuple)tuple; + fields = String.Join(",", + fileTuple.Component_, + fileTuple.LongFileName, + fileTuple.FileSize.ToString(), + fileTuple.Version, + fileTuple.Language, + ((fileTuple.ReadOnly ? WindowsInstallerConstants.MsidbFileAttributesReadOnly : 0) + | (fileTuple.Hidden ? WindowsInstallerConstants.MsidbFileAttributesHidden : 0) + | (fileTuple.System ? WindowsInstallerConstants.MsidbFileAttributesSystem : 0) + | (fileTuple.Vital ? WindowsInstallerConstants.MsidbFileAttributesVital : 0) + | (fileTuple.Checksum ? WindowsInstallerConstants.MsidbFileAttributesChecksum : 0) + | ((fileTuple.Compressed.HasValue && fileTuple.Compressed.Value) ? WindowsInstallerConstants.MsidbFileAttributesCompressed : 0) + | ((fileTuple.Compressed.HasValue && !fileTuple.Compressed.Value) ? WindowsInstallerConstants.MsidbFileAttributesNoncompressed : 0)) + .ToString()); + break; + } + + case "Registry": + { + var registryTuple = (RegistryTuple)tuple; + var value = registryTuple.Value; + + switch (registryTuple.ValueType) + { + case RegistryValueType.Binary: + value = String.Concat("#x", value); + break; + case RegistryValueType.Expandable: + value = String.Concat("#%", value); + break; + case RegistryValueType.Integer: + value = String.Concat("#", value); + break; + case RegistryValueType.MultiString: + switch (registryTuple.ValueAction) + { + case RegistryValueActionType.Append: + value = String.Concat("[~]", value); + break; + case RegistryValueActionType.Prepend: + value = String.Concat(value, "[~]"); + break; + case RegistryValueActionType.Write: + default: + if (null != value && -1 == value.IndexOf("[~]", StringComparison.Ordinal)) + { + value = String.Concat("[~]", value, "[~]"); + } + break; + } + break; + case RegistryValueType.String: + // escape the leading '#' character for string registry keys + if (null != value && value.StartsWith("#", StringComparison.Ordinal)) + { + value = String.Concat("#", value); + } + break; + } + + fields = String.Join(",", + ((int)registryTuple.Root).ToString(), + registryTuple.Key, + registryTuple.Name, + value, + registryTuple.Component_ + ); + break; + } + + case "RemoveRegistry": + { + var removeRegistryTuple = (RemoveRegistryTuple)tuple; + fields = String.Join(",", + ((int)removeRegistryTuple.Root).ToString(), + removeRegistryTuple.Key, + removeRegistryTuple.Name, + removeRegistryTuple.Component_ + ); + break; + } + + case "ServiceControl": + { + var serviceControlTuple = (ServiceControlTuple)tuple; + + var events = serviceControlTuple.InstallRemove ? WindowsInstallerConstants.MsidbServiceControlEventDelete : 0; + events |= serviceControlTuple.UninstallRemove ? WindowsInstallerConstants.MsidbServiceControlEventUninstallDelete : 0; + events |= serviceControlTuple.InstallStart ? WindowsInstallerConstants.MsidbServiceControlEventStart : 0; + events |= serviceControlTuple.UninstallStart ? WindowsInstallerConstants.MsidbServiceControlEventUninstallStart : 0; + events |= serviceControlTuple.InstallStop ? WindowsInstallerConstants.MsidbServiceControlEventStop : 0; + events |= serviceControlTuple.UninstallStop ? WindowsInstallerConstants.MsidbServiceControlEventUninstallStop : 0; + + fields = String.Join(",", + serviceControlTuple.Name, + events.ToString(), + serviceControlTuple.Arguments, + serviceControlTuple.Wait == true ? "1" : "0", + serviceControlTuple.Component_ + ); + break; + } + + case "ServiceInstall": + { + var serviceInstallTuple = (ServiceInstallTuple)tuple; + + var errorControl = (int)serviceInstallTuple.ErrorControl; + errorControl |= serviceInstallTuple.Vital ? WindowsInstallerConstants.MsidbServiceInstallErrorControlVital : 0; + + var serviceType = (int)serviceInstallTuple.ServiceType; + serviceType |= serviceInstallTuple.Interactive ? WindowsInstallerConstants.MsidbServiceInstallInteractive : 0; + + fields = String.Join(",", + serviceInstallTuple.Name, + serviceInstallTuple.DisplayName, + serviceType.ToString(), + ((int)serviceInstallTuple.StartType).ToString(), + errorControl.ToString(), + serviceInstallTuple.LoadOrderGroup, + serviceInstallTuple.Dependencies, + serviceInstallTuple.StartName, + serviceInstallTuple.Password, + serviceInstallTuple.Arguments, + serviceInstallTuple.Component_, + serviceInstallTuple.Description + ); + break; + } + + case "Upgrade": + { + var upgradeTuple = (UpgradeTuple)tuple; + + var attributes = upgradeTuple.MigrateFeatures ? WindowsInstallerConstants.MsidbUpgradeAttributesMigrateFeatures : 0; + attributes |= upgradeTuple.OnlyDetect ? WindowsInstallerConstants.MsidbUpgradeAttributesOnlyDetect : 0; + attributes |= upgradeTuple.IgnoreRemoveFailures ? WindowsInstallerConstants.MsidbUpgradeAttributesIgnoreRemoveFailure : 0; + attributes |= upgradeTuple.VersionMinInclusive ? WindowsInstallerConstants.MsidbUpgradeAttributesVersionMinInclusive : 0; + attributes |= upgradeTuple.VersionMaxInclusive ? WindowsInstallerConstants.MsidbUpgradeAttributesVersionMaxInclusive : 0; + attributes |= upgradeTuple.ExcludeLanguages ? WindowsInstallerConstants.MsidbUpgradeAttributesLanguagesExclusive : 0; + + fields = String.Join(",", + upgradeTuple.VersionMin, + upgradeTuple.VersionMax, + upgradeTuple.Language, + attributes.ToString(), + upgradeTuple.Remove, + upgradeTuple.ActionProperty + ); + break; + } + + case "WixAction": + { + var wixActionTuple = (WixActionTuple)tuple; + fields = String.Join(",", + wixActionTuple.SequenceTable, + wixActionTuple.Action, + wixActionTuple.Condition, + // BUGBUGBUG: AB#2626 + wixActionTuple.Sequence == 0 ? String.Empty : wixActionTuple.Sequence.ToString(), + wixActionTuple.Before, + wixActionTuple.After, + wixActionTuple.Overridable == true ? "1" : "0" + ); + break; + } + + case "WixComplexReference": + { + var wixComplexReferenceTuple = (WixComplexReferenceTuple)tuple; + fields = String.Join(",", + wixComplexReferenceTuple.Parent, + ((int)wixComplexReferenceTuple.ParentType).ToString(), + wixComplexReferenceTuple.ParentLanguage, + wixComplexReferenceTuple.Child, + ((int)wixComplexReferenceTuple.ChildType).ToString(), + wixComplexReferenceTuple.IsPrimary ? "1" : "0" + ); + break; + } + + case "WixFile": + { + var wixFileTuple = (WixFileTuple)tuple; + fields = String.Concat( + wixFileTuple.AssemblyType == FileAssemblyType.DotNetAssembly ? "0" : wixFileTuple.AssemblyType == FileAssemblyType.Win32Assembly ? "1" : String.Empty, ",", + String.Join(",", tuple.Fields.Skip(2).Take(6).Select(field => (string)field).ToArray())); + break; + } + + case "WixProperty": + { + var wixPropertyTuple = (WixPropertyTuple)tuple; + var attributes = 0; + attributes |= wixPropertyTuple.Admin ? 0x1 : 0; + attributes |= wixPropertyTuple.Hidden ? 0x2 : 0; + attributes |= wixPropertyTuple.Secure ? 0x4 : 0; + + fields = String.Join(",", + wixPropertyTuple.Property_, + attributes.ToString() + ); + break; + } + + } + + var id = tuple.Id == null ? String.Empty : String.Concat(",", tuple.Id.Id); + return $"{tuple.Definition.Name}{id},{fields}"; + } + } +} 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 Binary files /dev/null and b/src/test/WixToolsetTest.Converters.Tupleizer/TestData/Integration/test.wixout 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" DefaultTargets="Build" InitialTargets="EnsureWixToolsetInstalled" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">x86</Platform> + <ProductVersion>3.10</ProductVersion> + <ProjectGuid>d59f1c1e-9238-49fa-bfa2-ec1d9c2dda1d</ProjectGuid> + <SchemaVersion>2.0</SchemaVersion> + <OutputName>TupleizerWixout</OutputName> + <OutputType>Package</OutputType> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> + <OutputPath>bin\$(Configuration)\</OutputPath> + <IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath> + <DefineConstants>Debug</DefineConstants> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> + <OutputPath>bin\$(Configuration)\</OutputPath> + <IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath> + </PropertyGroup> + <ItemGroup> + <Compile Include="Product.wxs" /> + </ItemGroup> + <ItemGroup> + <WixExtension Include="WixUtilExtension"> + <HintPath>$(WixExtDir)\WixUtilExtension.dll</HintPath> + <Name>WixUtilExtension</Name> + </WixExtension> + <WixExtension Include="WixNetFxExtension"> + <HintPath>$(WixExtDir)\WixNetFxExtension.dll</HintPath> + <Name>WixNetFxExtension</Name> + </WixExtension> + </ItemGroup> + <Import Project="$(WixTargetsPath)" Condition=" '$(WixTargetsPath)' != '' " /> + <Import Project="$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets" Condition=" '$(WixTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets') " /> + <Target Name="EnsureWixToolsetInstalled" Condition=" '$(WixTargetsImported)' != 'true' "> + <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/" /> + </Target> + <!-- + To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Wix.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> + <Product Id="*" Name="TupleizerWixout" Language="1033" Version="1.0.0.0" Manufacturer="FireGiant" UpgradeCode="14a02d7f-9b32-4c92-b1f1-da518bf4e32a"> + <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" /> + + <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." AllowSameVersionUpgrades="yes" IgnoreRemoveFailure="yes" /> + <MediaTemplate /> + + <Feature Id="ProductFeature" Title="TupleizerWixout" Level="1"> + <Feature Id="ChildFeature"> + <ComponentGroupRef Id="ProductComponents" /> + </Feature> + </Feature> + + <PropertyRef Id="WIX_IS_NETFRAMEWORK_462_OR_LATER_INSTALLED" /> + <CustomActionRef Id="WixFailWhenDeferred" /> + <Property Id="WIXFAILWHENDEFERRED" Value="1" Secure="yes" /> + </Product> + + <Fragment> + <Directory Id="TARGETDIR" Name="SourceDir"> + <Directory Id="ProgramFilesFolder"> + <Directory Id="INSTALLFOLDER" Name="TupleizerWixout" /> + </Directory> + </Directory> + </Fragment> + + <Fragment> + <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER"> + <Component Id="ProductComponent"> + <File Checksum="yes" ReadOnly="yes" Source="$(env.WIX)\bin\candle.exe" Assembly=".net" AssemblyApplication="candle.exe" /> + <RegistryValue Root="HKLM" Key="SOFTWARE\WiX Toolset" Name="[ProductName]Installed" Value="1" Type="integer" /> + </Component> + </ComponentGroup> + </Fragment> +</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 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. --> + +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net461</TargetFramework> + <IsPackable>false</IsPackable> + </PropertyGroup> + + <ItemGroup> + <Content Include="TestData\Integration\test.wixout" CopyToOutputDirectory="PreserveNewest" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\WixToolset.Converters.Tupleizer\WixToolset.Converters.Tupleizer.csproj" /> + </ItemGroup> + + <ItemGroup> + <PackageReference Include="WixToolset.Data" Version="4.0.*" /> + <PackageReference Include="WixBuildTools.TestSupport" Version="4.0.*" /> + </ItemGroup> + + <ItemGroup> + <Reference Include="wix" HintPath="..\..\deps\wix.dll" /> + </ItemGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.0" /> + <PackageReference Include="xunit" Version="2.4.1" /> + <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" PrivateAssets="All" /> + </ItemGroup> +</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 @@ +// 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. + +namespace WixToolsetTest.Converters +{ + using System; + using System.IO; + using System.Text; + using System.Xml.Linq; + using WixToolset.Converters; + using WixToolset.Data; + using WixToolset.Extensibility; + using WixToolset.Extensibility.Services; + using Xunit; + + public class ConverterFixture + { + private static readonly XNamespace Wix4Namespace = "http://wixtoolset.org/schemas/v4/wxs"; + + [Fact] + public void EnsuresDeclaration() + { + var parse = String.Join(Environment.NewLine, + "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", + " <Fragment />", + "</Wix>"); + + var expected = String.Join(Environment.NewLine, + "<?xml version=\"1.0\" encoding=\"utf-16\"?>", + "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", + " <Fragment />", + "</Wix>"); + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new DummyMessaging(); + var converter = new Wix3Converter(messaging, 2, null, null); + + var errors = converter.ConvertDocument(document); + + var actual = UnformattedDocumentString(document); + + Assert.Equal(1, errors); + Assert.Equal(expected, actual); + } + + [Fact] + public void EnsuresUtf8Declaration() + { + var parse = String.Join(Environment.NewLine, + "<?xml version='1.0'?>", + "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", + " <Fragment />", + "</Wix>"); + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new DummyMessaging(); + var converter = new Wix3Converter(messaging, 4, null, null); + + var errors = converter.ConvertDocument(document); + + Assert.Equal(1, errors); + Assert.Equal("1.0", document.Declaration.Version); + Assert.Equal("utf-8", document.Declaration.Encoding); + } + + [Fact] + public void CanFixWhitespace() + { + var parse = String.Join(Environment.NewLine, + "<?xml version='1.0' encoding='utf-8'?>", + "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", + " <Fragment>", + " <Property Id='Prop'", + " Value='Val'>", + " </Property>", + " </Fragment>", + "</Wix>"); + + var expected = String.Join(Environment.NewLine, + "<?xml version=\"1.0\" encoding=\"utf-16\"?>", + "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", + " <Fragment>", + " <Property Id=\"Prop\" Value=\"Val\" />", + " </Fragment>", + "</Wix>"); + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new DummyMessaging(); + var converter = new Wix3Converter(messaging, 4, null, null); + + var errors = converter.ConvertDocument(document); + + var actual = UnformattedDocumentString(document); + + Assert.Equal(expected, actual); + Assert.Equal(4, errors); + } + + [Fact] + public void CanPreserveNewLines() + { + var parse = String.Join(Environment.NewLine, + "<?xml version='1.0' encoding='utf-8'?>", + "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", + " <Fragment>", + "", + " <Property Id='Prop' Value='Val' />", + "", + " </Fragment>", + "</Wix>"); + + var expected = String.Join(Environment.NewLine, + "<?xml version=\"1.0\" encoding=\"utf-16\"?>", + "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", + " <Fragment>", + "", + " <Property Id=\"Prop\" Value=\"Val\" />", + "", + " </Fragment>", + "</Wix>"); + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new DummyMessaging(); + var converter = new Wix3Converter(messaging, 4, null, null); + + var conversions = converter.ConvertDocument(document); + + var actual = UnformattedDocumentString(document); + + Assert.Equal(expected, actual); + Assert.Equal(3, conversions); + } + + [Fact] + public void CanConvertWithNewLineAtEndOfFile() + { + var parse = String.Join(Environment.NewLine, + "<?xml version='1.0' encoding='utf-8'?>", + "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", + " <Fragment>", + "", + " <Property Id='Prop' Value='Val' />", + "", + " </Fragment>", + "</Wix>", + ""); + + var expected = String.Join(Environment.NewLine, + "<?xml version=\"1.0\" encoding=\"utf-16\"?>", + "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", + " <Fragment>", + "", + " <Property Id=\"Prop\" Value=\"Val\" />", + "", + " </Fragment>", + "</Wix>", + ""); + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new DummyMessaging(); + var converter = new Wix3Converter(messaging, 4, null, null); + + var conversions = converter.ConvertDocument(document); + + var actual = UnformattedDocumentString(document); + + Assert.Equal(expected, actual); + Assert.Equal(3, conversions); + } + + [Fact] + public void CanFixCdataWhitespace() + { + var parse = String.Join(Environment.NewLine, + "<?xml version='1.0' encoding='utf-8'?>", + "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", + " <Fragment>", + " <Property Id='Prop'>", + " <![CDATA[1<2]]>", + " </Property>", + " </Fragment>", + "</Wix>"); + + var expected = String.Join(Environment.NewLine, + "<?xml version=\"1.0\" encoding=\"utf-16\"?>", + "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", + " <Fragment>", + " <Property Id=\"Prop\"><![CDATA[1<2]]></Property>", + " </Fragment>", + "</Wix>"); + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new DummyMessaging(); + var converter = new Wix3Converter(messaging, 2, null, null); + + var errors = converter.ConvertDocument(document); + + var actual = UnformattedDocumentString(document); + + Assert.Equal(expected, actual); + Assert.Equal(2, errors); + } + + [Fact] + public void CanFixCdataWithWhitespace() + { + var parse = String.Join(Environment.NewLine, + "<?xml version='1.0' encoding='utf-8'?>", + "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", + " <Fragment>", + " <Property Id='Prop'>", + " <![CDATA[", + " 1<2", + " ]]>", + " </Property>", + " </Fragment>", + "</Wix>"); + + var expected = String.Join(Environment.NewLine, + "<?xml version=\"1.0\" encoding=\"utf-16\"?>", + "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", + " <Fragment>", + " <Property Id=\"Prop\"><![CDATA[1<2]]></Property>", + " </Fragment>", + "</Wix>"); + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new DummyMessaging(); + var converter = new Wix3Converter(messaging, 2, null, null); + + var errors = converter.ConvertDocument(document); + + var actual = UnformattedDocumentString(document); + + Assert.Equal(expected, actual); + Assert.Equal(2, errors); + } + + [Fact] + public void CanConvertMainNamespace() + { + var parse = String.Join(Environment.NewLine, + "<?xml version='1.0' encoding='utf-8'?>", + "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", + " <Fragment />", + "</Wix>"); + + var expected = String.Join(Environment.NewLine, + "<?xml version=\"1.0\" encoding=\"utf-16\"?>", + "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", + " <Fragment />", + "</Wix>"); + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new DummyMessaging(); + var converter = new Wix3Converter(messaging, 2, null, null); + + var errors = converter.ConvertDocument(document); + + var actual = UnformattedDocumentString(document); + + Assert.Equal(1, errors); + //Assert.Equal(Wix4Namespace, document.Root.GetDefaultNamespace()); + Assert.Equal(expected, actual); + } + + [Fact] + public void CanConvertNamedMainNamespace() + { + var parse = String.Join(Environment.NewLine, + "<?xml version='1.0' encoding='utf-8'?>", + "<w:Wix xmlns:w='http://schemas.microsoft.com/wix/2006/wi'>", + " <w:Fragment />", + "</w:Wix>"); + + var expected = String.Join(Environment.NewLine, + "<?xml version=\"1.0\" encoding=\"utf-16\"?>", + "<w:Wix xmlns:w=\"http://wixtoolset.org/schemas/v4/wxs\">", + " <w:Fragment />", + "</w:Wix>"); + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new DummyMessaging(); + var converter = new Wix3Converter(messaging, 2, null, null); + + var errors = converter.ConvertDocument(document); + + var actual = UnformattedDocumentString(document); + + Assert.Equal(1, errors); + Assert.Equal(expected, actual); + Assert.Equal(Wix4Namespace, document.Root.GetNamespaceOfPrefix("w")); + } + + [Fact] + public void CanConvertNonWixDefaultNamespace() + { + var parse = String.Join(Environment.NewLine, + "<?xml version='1.0' encoding='utf-8'?>", + "<w:Wix xmlns:w='http://schemas.microsoft.com/wix/2006/wi' xmlns='http://schemas.microsoft.com/wix/UtilExtension'>", + " <w:Fragment>", + " <Test />", + " </w:Fragment>", + "</w:Wix>"); + + var expected = String.Join(Environment.NewLine, + "<?xml version=\"1.0\" encoding=\"utf-16\"?>", + "<w:Wix xmlns:w=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns=\"http://wixtoolset.org/schemas/v4/wxs/util\">", + " <w:Fragment>", + " <Test />", + " </w:Fragment>", + "</w:Wix>"); + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new DummyMessaging(); + var converter = new Wix3Converter(messaging, 2, null, null); + + var errors = converter.ConvertDocument(document); + + var actual = UnformattedDocumentString(document); + + Assert.Equal(expected, actual); + Assert.Equal(2, errors); + Assert.Equal(Wix4Namespace, document.Root.GetNamespaceOfPrefix("w")); + Assert.Equal("http://wixtoolset.org/schemas/v4/wxs/util", document.Root.GetDefaultNamespace()); + } + + [Fact] + public void CanConvertExtensionNamespace() + { + var parse = String.Join(Environment.NewLine, + "<?xml version='1.0' encoding='utf-8'?>", + "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:util='http://schemas.microsoft.com/wix/UtilExtension'>", + " <Fragment />", + "</Wix>"); + + var expected = String.Join(Environment.NewLine, + "<?xml version=\"1.0\" encoding=\"utf-16\"?>", + "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:util=\"http://wixtoolset.org/schemas/v4/wxs/util\">", + " <Fragment />", + "</Wix>"); + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new DummyMessaging(); + var converter = new Wix3Converter(messaging, 2, null, null); + + var errors = converter.ConvertDocument(document); + + var actual = UnformattedDocumentString(document); + + Assert.Equal(2, errors); + Assert.Equal(expected, actual); + Assert.Equal(Wix4Namespace, document.Root.GetDefaultNamespace()); + } + + [Fact] + public void CanConvertMissingNamespace() + { + var parse = String.Join(Environment.NewLine, + "<?xml version='1.0' encoding='utf-8'?>", + "<Wix>", + " <Fragment />", + "</Wix>"); + + var expected = String.Join(Environment.NewLine, + "<?xml version=\"1.0\" encoding=\"utf-16\"?>", + "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", + " <Fragment />", + "</Wix>"); + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new DummyMessaging(); + var converter = new Wix3Converter(messaging, 2, null, null); + + var errors = converter.ConvertDocument(document); + + var actual = UnformattedDocumentString(document); + + Assert.Equal(1, errors); + Assert.Equal(expected, actual); + Assert.Equal(Wix4Namespace, document.Root.GetDefaultNamespace()); + } + + [Fact] + public void CanConvertAnonymousFile() + { + var parse = String.Join(Environment.NewLine, + "<?xml version='1.0' encoding='utf-8'?>", + "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", + " <File Source='path\\to\\foo.txt' />", + "</Wix>"); + + var expected = String.Join(Environment.NewLine, + "<?xml version=\"1.0\" encoding=\"utf-16\"?>", + "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", + " <File Id=\"foo.txt\" Source=\"path\\to\\foo.txt\" />", + "</Wix>"); + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new DummyMessaging(); + var converter = new Wix3Converter(messaging, 2, null, null); + + var errors = converter.ConvertDocument(document); + + var actual = UnformattedDocumentString(document); + + Assert.Equal(1, errors); + Assert.Equal(expected, actual); + } + + [Fact] + public void CanConvertShortNameDirectoryWithoutName() + { + var parse = String.Join(Environment.NewLine, + "<?xml version='1.0' encoding='utf-8'?>", + "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", + " <Directory ShortName='iamshort' />", + "</Wix>"); + + var expected = String.Join(Environment.NewLine, + "<?xml version=\"1.0\" encoding=\"utf-16\"?>", + "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", + " <Directory Name=\"iamshort\" />", + "</Wix>"); + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new DummyMessaging(); + var converter = new Wix3Converter(messaging, 2, null, null); + + var errors = converter.ConvertDocument(document); + + var actual = UnformattedDocumentString(document); + + Assert.Equal(1, errors); + Assert.Equal(expected, actual); + } + + [Fact] + public void CanConvertSuppressSignatureValidationNo() + { + var parse = String.Join(Environment.NewLine, + "<?xml version='1.0' encoding='utf-8'?>", + "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", + " <MsiPackage SuppressSignatureValidation='no' />", + "</Wix>"); + + var expected = String.Join(Environment.NewLine, + "<?xml version=\"1.0\" encoding=\"utf-16\"?>", + "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", + " <MsiPackage EnableSignatureValidation=\"yes\" />", + "</Wix>"); + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new DummyMessaging(); + var converter = new Wix3Converter(messaging, 2, null, null); + + var errors = converter.ConvertDocument(document); + + var actual = UnformattedDocumentString(document); + + Assert.Equal(1, errors); + Assert.Equal(expected, actual); + } + + [Fact] + public void CanConvertSuppressSignatureValidationYes() + { + var parse = String.Join(Environment.NewLine, + "<?xml version='1.0' encoding='utf-8'?>", + "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", + " <Payload SuppressSignatureValidation='yes' />", + "</Wix>"); + + var expected = String.Join(Environment.NewLine, + "<?xml version=\"1.0\" encoding=\"utf-16\"?>", + "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", + " <Payload />", + "</Wix>"); + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new DummyMessaging(); + var converter = new Wix3Converter(messaging, 2, null, null); + + var errors = converter.ConvertDocument(document); + + var actual = UnformattedDocumentString(document); + + Assert.Equal(1, errors); + Assert.Equal(expected, actual); + } + + private static string UnformattedDocumentString(XDocument document) + { + var sb = new StringBuilder(); + + using (var writer = new StringWriter(sb)) + { + document.Save(writer, SaveOptions.DisableFormatting); + } + + return sb.ToString(); + } + + private class DummyMessaging : IMessaging + { + public bool EncounteredError { get; set; } + + public int LastErrorNumber { get; set; } + + public bool ShowVerboseMessages { get; set; } + + public bool SuppressAllWarnings { get; set; } + + public bool WarningsAsError { get; set; } + + public void ElevateWarningMessage(int warningNumber) + { + } + + public string FormatMessage(Message message) => String.Empty; + + public void SetListener(IMessageListener listener) + { + } + + public void SuppressWarningMessage(int warningNumber) + { + } + + public void Write(Message message) + { + } + + public void Write(string message, bool verbose = false) + { + } + } + } +} 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. --> + + + +<?include WixVer.wxi ?> + +<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"> + <Product Id="*" Name="!(loc.ShortProduct) v$(var.WixMajorMinor) Core" Language="1033" Manufacturer="!(loc.Company)" Version="$(var.WixMsiProductVersion)" UpgradeCode="3618724B-2523-44F9-A908-866AA619504D"> + <Package Compressed="yes" InstallerVersion="200" SummaryCodepage="1252" InstallScope="perMachine" /> + <swid:Tag Regid="!(loc.Regid)" InstallDirectory="INSTALLFOLDER" /> + + <MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed." /> + + <MediaTemplate CabinetTemplate="core{0}.cab" /> + + <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1"> + <Component Id="Licensing" Directory="INSTALLFOLDER"> + <File Id="LICENSE.TXT" Source="LICENSE.TXT" /> + </Component> + + <Component Id="ProductRegistration" Directory="INSTALLFOLDER"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> + <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" /> + </RegistryKey> + </Component> + + <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x"> + <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" /> + </RegistryKey> + </Component> + + <Component Id="ProductInformation" Directory="BinFolder"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> + <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string" /> + <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" /> + </RegistryKey> + + <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" /> + </Component> + + <Component Directory="BinFolder"> + <File Id="wixtoolset.org.ico" Source="common\wixtoolset.org.ico"> + <?include ComRegistration.wxi ?> + </File> + <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" /> + </Component> + + <ComponentGroupRef Id="ToolsetComponents" /> + <ComponentGroupRef Id="ExtensionComponents" /> + <ComponentGroupRef Id="LuxComponents" /> + <ComponentGroupRef Id="DocComponents" /> + </Feature> + + <FeatureRef Id="Feature_MSBuild" /> + <FeatureRef Id="Feature_Intellisense2010" /> + <FeatureRef Id="Feature_Intellisense2012" /> + <FeatureRef Id="Feature_Intellisense2013" /> + <FeatureRef Id="Feature_Intellisense2015" /> + </Product> +</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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- 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. --> + + + +<?include WixVer.wxi ?> + +<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"> + <Product Id="*" Name="!(loc.ShortProduct) v$(var.WixMajorMinor) Core" Language="1033" Manufacturer="!(loc.Company)" + Version="$(var.WixMsiProductVersion)" UpgradeCode="3618724B-2523-44F9-A908-866AA619504D"> + <Package Compressed="yes" InstallerVersion="200" SummaryCodepage="1252" InstallScope="perMachine" /> + <swid:Tag Regid="!(loc.Regid)" InstallDirectory="INSTALLFOLDER" /> + + <MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed." /> + + <MediaTemplate CabinetTemplate="core{0}.cab" /> + + <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1"> + <Component Id="Licensing" Directory="INSTALLFOLDER"> + <File Source="LICENSE.TXT" /> + </Component> + + <Component Id="ProductRegistration" Directory="INSTALLFOLDER"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> + <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" /> + </RegistryKey> + </Component> + + <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x"> + <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" /> + </RegistryKey> + </Component> + + <Component Id="ProductInformation" Directory="BinFolder"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> + <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string"/> + <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" /> + </RegistryKey> + + <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" /> + </Component> + + <Component Directory="BinFolder"> + <File Source="common\wixtoolset.org.ico"> + <?include ComRegistration.wxi ?> + </File> + <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" /> + </Component> + + <ComponentGroupRef Id="ToolsetComponents" /> + <ComponentGroupRef Id="ExtensionComponents" /> + <ComponentGroupRef Id="LuxComponents" /> + <ComponentGroupRef Id="DocComponents" /> + </Feature> + + <FeatureRef Id="Feature_MSBuild" /> + <FeatureRef Id="Feature_Intellisense2010" /> + <FeatureRef Id="Feature_Intellisense2012" /> + <FeatureRef Id="Feature_Intellisense2013" /> + <FeatureRef Id="Feature_Intellisense2015" /> + </Product> +</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 @@ +<?xml version="1.0"?> +<Settings> + <IgnoreErrors> + <Test Id="WhitespacePrecedingNodeWrong"/> + <Test Id="WhitespacePrecedingEndElementWrong"/> + </IgnoreErrors> + <ErrorsAsWarnings/> + <ExemptFiles/> +</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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- 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. --> + + + +<?include WixVer.wxi ?> + +<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"> + <Product Id="*" Name="!(loc.ShortProduct) v$(var.WixMajorMinor) Core" Language="1033" Manufacturer="!(loc.Company)" + Version="$(var.WixMsiProductVersion)" UpgradeCode="3618724B-2523-44F9-A908-866AA619504D"> + <Package Compressed="yes" InstallerVersion="200" SummaryCodepage="1252" InstallScope="perMachine" /> + <swid:Tag Regid="!(loc.Regid)" InstallDirectory="INSTALLFOLDER" /> + + <MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed." /> + + <MediaTemplate CabinetTemplate="core{0}.cab" /> + + <Property Id="QtExecCmdTimeout" Value="600000" /> + <CustomAction Id="InstallVSTemplateCommand" Property="QtExecCmdLine" Value=""[VSENVPRODUCT80]\devenv.exe" /setup" /> + <CustomAction Id="InstallVSTemplate" BinaryKey="WixCA" DllEntry="CAQuietExec" Return="asyncWait" /> + + <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1"> + <Component Id="Licensing" Directory="INSTALLFOLDER"> + <File Source="LICENSE.TXT" /> + </Component> + + <Component Id="ProductRegistration" Directory="INSTALLFOLDER"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> + <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" /> + </RegistryKey> + </Component> + + <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x"> + <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" /> + </RegistryKey> + </Component> + + <Component Id="ProductInformation" Directory="BinFolder"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> + <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string"/> + <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" /> + </RegistryKey> + + <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" /> + </Component> + + <Component Directory="BinFolder"> + <File Source="common\wixtoolset.org.ico" /> + <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" /> + </Component> + + <ComponentGroupRef Id="ToolsetComponents" /> + <ComponentGroupRef Id="ExtensionComponents" /> + <ComponentGroupRef Id="LuxComponents" /> + <ComponentGroupRef Id="DocComponents" /> + </Feature> + + <FeatureRef Id="Feature_MSBuild" /> + <FeatureRef Id="Feature_Intellisense2010" /> + <FeatureRef Id="Feature_Intellisense2012" /> + <FeatureRef Id="Feature_Intellisense2013" /> + <FeatureRef Id="Feature_Intellisense2015" /> + </Product> +</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 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. --> + + + +<?include WixVer.wxi ?> + +<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"> + <Product Id="*" Name="!(loc.ShortProduct) v$(var.WixMajorMinor) Core" Language="1033" Manufacturer="!(loc.Company)" Version="$(var.WixMsiProductVersion)" UpgradeCode="3618724B-2523-44F9-A908-866AA619504D"> + <Package Compressed="yes" InstallerVersion="200" SummaryCodepage="1252" InstallScope="perMachine" /> + <swid:Tag Regid="!(loc.Regid)" InstallDirectory="INSTALLFOLDER" /> + + <MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed." /> + + <MediaTemplate CabinetTemplate="core{0}.cab" /> + + <Property Id="QtExecCmdTimeout" Value="600000" /> + <CustomAction Id="InstallVSTemplateCommand" Property="WixQuietExecCmdLine" Value=""[VSENVPRODUCT80]\devenv.exe" /setup" /> + <CustomAction Id="InstallVSTemplate" BinaryKey="UtilCA" DllEntry="WixQuietExec" Return="asyncWait" /> + + <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1"> + <Component Id="Licensing" Directory="INSTALLFOLDER"> + <File Id="LICENSE.TXT" Source="LICENSE.TXT" /> + </Component> + + <Component Id="ProductRegistration" Directory="INSTALLFOLDER"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> + <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" /> + </RegistryKey> + </Component> + + <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x"> + <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" /> + </RegistryKey> + </Component> + + <Component Id="ProductInformation" Directory="BinFolder"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> + <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string" /> + <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" /> + </RegistryKey> + + <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" /> + </Component> + + <Component Directory="BinFolder"> + <File Id="wixtoolset.org.ico" Source="common\wixtoolset.org.ico" /> + <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" /> + </Component> + + <ComponentGroupRef Id="ToolsetComponents" /> + <ComponentGroupRef Id="ExtensionComponents" /> + <ComponentGroupRef Id="LuxComponents" /> + <ComponentGroupRef Id="DocComponents" /> + </Feature> + + <FeatureRef Id="Feature_MSBuild" /> + <FeatureRef Id="Feature_Intellisense2010" /> + <FeatureRef Id="Feature_Intellisense2012" /> + <FeatureRef Id="Feature_Intellisense2013" /> + <FeatureRef Id="Feature_Intellisense2015" /> + </Product> +</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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- 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. --> + + + +<?include WixVer.wxi ?> + +<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"> + <Product Id="*" Name="!(loc.ShortProduct) v$(var.WixMajorMinor) Core" Language="1033" Manufacturer="!(loc.Company)" + Version="$(var.WixMsiProductVersion)" UpgradeCode="3618724B-2523-44F9-A908-866AA619504D"> + <Package Compressed="yes" InstallerVersion="200" SummaryCodepage="1252" InstallScope="perMachine" /> + <swid:Tag Regid="!(loc.Regid)" InstallDirectory="INSTALLFOLDER" /> + + <MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed." /> + + <MediaTemplate CabinetTemplate="core{0}.cab" /> + + <CustomAction Id="InstallVSTemplateCommand" Property="QtExecCmdLine" Value=""[VSENVPRODUCT80]\devenv.exe" /setup" /> + <CustomAction Id="InstallVSTemplate" BinaryKey="WixCA" DllEntry="CAQuietExec" Return="asyncWait" /> + + <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1"> + <Component Id="Licensing" Directory="INSTALLFOLDER"> + <File Source="LICENSE.TXT" /> + </Component> + + <Component Id="ProductRegistration" Directory="INSTALLFOLDER"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> + <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" /> + </RegistryKey> + </Component> + + <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x"> + <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" /> + </RegistryKey> + </Component> + + <Component Id="ProductInformation" Directory="BinFolder"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> + <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string"/> + <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" /> + </RegistryKey> + + <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" /> + </Component> + + <Component Directory="BinFolder"> + <File Source="common\wixtoolset.org.ico" /> + <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" /> + </Component> + + <ComponentGroupRef Id="ToolsetComponents" /> + <ComponentGroupRef Id="ExtensionComponents" /> + <ComponentGroupRef Id="LuxComponents" /> + <ComponentGroupRef Id="DocComponents" /> + </Feature> + + <FeatureRef Id="Feature_MSBuild" /> + <FeatureRef Id="Feature_Intellisense2010" /> + <FeatureRef Id="Feature_Intellisense2012" /> + <FeatureRef Id="Feature_Intellisense2013" /> + <FeatureRef Id="Feature_Intellisense2015" /> + </Product> +</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 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. --> + + + +<?include WixVer.wxi ?> + +<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"> + <Product Id="*" Name="!(loc.ShortProduct) v$(var.WixMajorMinor) Core" Language="1033" Manufacturer="!(loc.Company)" Version="$(var.WixMsiProductVersion)" UpgradeCode="3618724B-2523-44F9-A908-866AA619504D"> + <Package Compressed="yes" InstallerVersion="200" SummaryCodepage="1252" InstallScope="perMachine" /> + <swid:Tag Regid="!(loc.Regid)" InstallDirectory="INSTALLFOLDER" /> + + <MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed." /> + + <MediaTemplate CabinetTemplate="core{0}.cab" /> + + <CustomAction Id="InstallVSTemplateCommand" Property="WixQuietExecCmdLine" Value=""[VSENVPRODUCT80]\devenv.exe" /setup" /> + <CustomAction Id="InstallVSTemplate" BinaryKey="UtilCA" DllEntry="WixQuietExec" Return="asyncWait" /> + + <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1"> + <Component Id="Licensing" Directory="INSTALLFOLDER"> + <File Id="LICENSE.TXT" Source="LICENSE.TXT" /> + </Component> + + <Component Id="ProductRegistration" Directory="INSTALLFOLDER"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> + <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" /> + </RegistryKey> + </Component> + + <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x"> + <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" /> + </RegistryKey> + </Component> + + <Component Id="ProductInformation" Directory="BinFolder"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> + <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string" /> + <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" /> + </RegistryKey> + + <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" /> + </Component> + + <Component Directory="BinFolder"> + <File Id="wixtoolset.org.ico" Source="common\wixtoolset.org.ico" /> + <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" /> + </Component> + + <ComponentGroupRef Id="ToolsetComponents" /> + <ComponentGroupRef Id="ExtensionComponents" /> + <ComponentGroupRef Id="LuxComponents" /> + <ComponentGroupRef Id="DocComponents" /> + </Feature> + + <FeatureRef Id="Feature_MSBuild" /> + <FeatureRef Id="Feature_Intellisense2010" /> + <FeatureRef Id="Feature_Intellisense2012" /> + <FeatureRef Id="Feature_Intellisense2013" /> + <FeatureRef Id="Feature_Intellisense2015" /> + </Product> +</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 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. --> + + + +<?include WixVer.wxi ?> + +<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"> + <Product Id="*" Name="!(loc.ShortProduct) v$(var.WixMajorMinor) Core" Language="1033" Manufacturer="!(loc.Company)" Version="$(var.WixMsiProductVersion)" UpgradeCode="3618724B-2523-44F9-A908-866AA619504D"> + <Package Compressed="yes" InstallerVersion="200" SummaryCodepage="1252" InstallScope="perMachine" /> + <swid:Tag Regid="!(loc.Regid)" InstallDirectory="INSTALLFOLDER" /> + + <MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed." /> + + <MediaTemplate CabinetTemplate="core{0}.cab" /> + + <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1"> + <Component Id="Licensing" Directory="INSTALLFOLDER"> + <File Id="LICENSE.TXT" Source="LICENSE.TXT" /> + </Component> + + <Component Id="ProductRegistration" Directory="INSTALLFOLDER"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> + <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" /> + </RegistryKey> + </Component> + + <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x"> + <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" /> + </RegistryKey> + </Component> + + <Component Id="ProductInformation" Directory="BinFolder"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> + <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string" /> + <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" /> + </RegistryKey> + + <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" /> + </Component> + + <Component Directory="BinFolder"> + <File Id="wixtoolset.org.ico" Source="common\wixtoolset.org.ico" /> + <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" /> + </Component> + + <ComponentGroupRef Id="ToolsetComponents" /> + <ComponentGroupRef Id="ExtensionComponents" /> + <ComponentGroupRef Id="LuxComponents" /> + <ComponentGroupRef Id="DocComponents" /> + </Feature> + + <FeatureRef Id="Feature_MSBuild" /> + <FeatureRef Id="Feature_Intellisense2010" /> + <FeatureRef Id="Feature_Intellisense2012" /> + <FeatureRef Id="Feature_Intellisense2013" /> + <FeatureRef Id="Feature_Intellisense2015" /> + </Product> +</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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- 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. --> + + + +<?include WixVer.wxi ?> + +<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"> + <Product Id="*" Name="!(loc.ShortProduct) v$(var.WixMajorMinor) Core" Language="1033" Manufacturer="!(loc.Company)" + Version="$(var.WixMsiProductVersion)" UpgradeCode="3618724B-2523-44F9-A908-866AA619504D"> + <Package Compressed="yes" InstallerVersion="200" SummaryCodepage="1252" InstallScope="perMachine" /> + <swid:Tag Regid="!(loc.Regid)" InstallDirectory="INSTALLFOLDER" /> + + <MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed." /> + + <MediaTemplate CabinetTemplate="core{0}.cab" /> + + <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1"> + <Component Id="Licensing" Directory="INSTALLFOLDER"> + <File Source="LICENSE.TXT" /> + </Component> + + <Component Id="ProductRegistration" Directory="INSTALLFOLDER"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> + <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" /> + </RegistryKey> + </Component> + + <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x"> + <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" /> + </RegistryKey> + </Component> + + <Component Id="ProductInformation" Directory="BinFolder"> + <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> + <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string"/> + <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" /> + </RegistryKey> + + <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" /> + </Component> + + <Component Directory="BinFolder"> + <File Source="common\wixtoolset.org.ico" /> + <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" /> + </Component> + + <ComponentGroupRef Id="ToolsetComponents" /> + <ComponentGroupRef Id="ExtensionComponents" /> + <ComponentGroupRef Id="LuxComponents" /> + <ComponentGroupRef Id="DocComponents" /> + </Feature> + + <FeatureRef Id="Feature_MSBuild" /> + <FeatureRef Id="Feature_Intellisense2010" /> + <FeatureRef Id="Feature_Intellisense2012" /> + <FeatureRef Id="Feature_Intellisense2013" /> + <FeatureRef Id="Feature_Intellisense2015" /> + </Product> +</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 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. --> + +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <TargetFramework>netcoreapp2.1</TargetFramework> + <IsPackable>false</IsPackable> + </PropertyGroup> + + <ItemGroup> + <None Remove="TestData\SingleFile\ConvertedSingleFile.wxs" /> + <None Remove="TestData\SingleFile\SingleFile.wxs" /> + </ItemGroup> + <ItemGroup> + <Content Include="TestData\SingleFile\ConvertedSingleFile.wxs" CopyToOutputDirectory="PreserveNewest" /> + <Content Include="TestData\SingleFile\SingleFile.wxs" CopyToOutputDirectory="PreserveNewest" /> + <Content Include="TestData\Preprocessor\ConvertedPreprocessor.wxs" CopyToOutputDirectory="PreserveNewest" /> + <Content Include="TestData\Preprocessor\Preprocessor.wxs" CopyToOutputDirectory="PreserveNewest" /> + <Content Include="TestData\Preprocessor\wixcop.settings.xml" CopyToOutputDirectory="PreserveNewest" /> + <Content Include="TestData\QtExec\v3.wxs" CopyToOutputDirectory="PreserveNewest" /> + <Content Include="TestData\QtExec\v4_expected.wxs" CopyToOutputDirectory="PreserveNewest" /> + <Content Include="TestData\QtExec.bad\v3.wxs" CopyToOutputDirectory="PreserveNewest" /> + <Content Include="TestData\QtExec.bad\v4_expected.wxs" CopyToOutputDirectory="PreserveNewest" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\WixToolset.Converters\WixToolset.Converters.csproj" /> + </ItemGroup> + + <ItemGroup> + <PackageReference Include="WixBuildTools.TestSupport" Version="4.0.*" /> + </ItemGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.0" /> + <PackageReference Include="xunit" Version="2.4.1" /> + <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" PrivateAssets="All" /> + </ItemGroup> +</Project> -- cgit v1.2.3-55-g6feb