diff options
author | Rob Mensching <rob@firegiant.com> | 2022-03-05 11:09:13 -0800 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2022-03-14 12:50:55 -0700 |
commit | 91cd2d65121a163e625d2f029025123b0f8467d2 (patch) | |
tree | f5684229d7971595d6f457913a988c1b81139ceb | |
parent | 8b72be6a36104497a0fe1606c47fb625b097972c (diff) | |
download | wix-91cd2d65121a163e625d2f029025123b0f8467d2.tar.gz wix-91cd2d65121a163e625d2f029025123b0f8467d2.tar.bz2 wix-91cd2d65121a163e625d2f029025123b0f8467d2.zip |
Implement "wix msi transform"
Brings the functionality of torch into the WindowsInstallerBackend
as the "transform" subcommand.
Fixes 4602
16 files changed, 653 insertions, 201 deletions
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs index 3379ec5d..3d8e7595 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs | |||
@@ -62,18 +62,19 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
62 | 62 | ||
63 | if (this.Transform.TryGetTable("Property", out var propertyTable)) | 63 | if (this.Transform.TryGetTable("Property", out var propertyTable)) |
64 | { | 64 | { |
65 | for (int i = propertyTable.Rows.Count - 1; i >= 0; i--) | 65 | for (var i = propertyTable.Rows.Count - 1; i >= 0; i--) |
66 | { | 66 | { |
67 | Row row = propertyTable.Rows[i]; | 67 | var row = propertyTable.Rows[i]; |
68 | var id = row.FieldAsString(0); | ||
68 | 69 | ||
69 | if ("ProductCode" == (string)row[0] || "ProductLanguage" == (string)row[0] || "ProductVersion" == (string)row[0] || "UpgradeCode" == (string)row[0]) | 70 | if ("ProductCode" == id || "ProductLanguage" == id || "ProductVersion" == id) |
70 | { | 71 | { |
71 | propertyTable.Rows.RemoveAt(i); | 72 | propertyTable.Rows.RemoveAt(i); |
72 | 73 | } | |
73 | if ("UpgradeCode" == (string)row[0]) | 74 | else if ("UpgradeCode" == id) |
74 | { | 75 | { |
75 | updatedUpgradeCode = (string)row[1]; | 76 | updatedUpgradeCode = id; |
76 | } | 77 | propertyTable.Rows.RemoveAt(i); |
77 | } | 78 | } |
78 | } | 79 | } |
79 | } | 80 | } |
@@ -383,11 +384,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
383 | } | 384 | } |
384 | } | 385 | } |
385 | 386 | ||
386 | //foreach (BinderExtension extension in this.Extensions) | ||
387 | //{ | ||
388 | // extension.PostBind(this.Context); | ||
389 | //} | ||
390 | |||
391 | // Any errors encountered up to this point can cause errors during generation. | 387 | // Any errors encountered up to this point can cause errors during generation. |
392 | if (this.Messaging.EncounteredError) | 388 | if (this.Messaging.EncounteredError) |
393 | { | 389 | { |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/LoadTableDefinitionsCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/LoadTableDefinitionsCommand.cs index 2eb95bc5..475a88f9 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/LoadTableDefinitionsCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/LoadTableDefinitionsCommand.cs | |||
@@ -2,9 +2,7 @@ | |||
2 | 2 | ||
3 | namespace WixToolset.Core.WindowsInstaller.Bind | 3 | namespace WixToolset.Core.WindowsInstaller.Bind |
4 | { | 4 | { |
5 | using System; | ||
6 | using System.Collections.Generic; | 5 | using System.Collections.Generic; |
7 | using System.Globalization; | ||
8 | using System.Linq; | 6 | using System.Linq; |
9 | using WixToolset.Data; | 7 | using WixToolset.Data; |
10 | using WixToolset.Data.Symbols; | 8 | using WixToolset.Data.Symbols; |
@@ -32,9 +30,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
32 | public TableDefinitionCollection Execute() | 30 | public TableDefinitionCollection Execute() |
33 | { | 31 | { |
34 | var tableDefinitions = new TableDefinitionCollection(WindowsInstallerTableDefinitions.All); | 32 | var tableDefinitions = new TableDefinitionCollection(WindowsInstallerTableDefinitions.All); |
35 | var customColumnsById = this.Section.Symbols.OfType<WixCustomTableColumnSymbol>().ToDictionary(t => t.Id.Id); | 33 | var customColumnsById = this.Section?.Symbols.OfType<WixCustomTableColumnSymbol>().ToDictionary(t => t.Id.Id); |
36 | 34 | ||
37 | if (customColumnsById.Any()) | 35 | if (customColumnsById?.Any() == true) |
38 | { | 36 | { |
39 | foreach (var symbol in this.Section.Symbols.OfType<WixCustomTableSymbol>()) | 37 | foreach (var symbol in this.Section.Symbols.OfType<WixCustomTableSymbol>()) |
40 | { | 38 | { |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs new file mode 100644 index 00000000..7ed41d1a --- /dev/null +++ b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs | |||
@@ -0,0 +1,352 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | namespace WixToolset.Core.WindowsInstaller.CommandLine | ||
4 | { | ||
5 | using System; | ||
6 | using System.IO; | ||
7 | using System.Threading; | ||
8 | using System.Threading.Tasks; | ||
9 | using WixToolset.Core.WindowsInstaller.Bind; | ||
10 | using WixToolset.Core.WindowsInstaller.Unbind; | ||
11 | using WixToolset.Data; | ||
12 | using WixToolset.Data.Symbols; | ||
13 | using WixToolset.Data.WindowsInstaller; | ||
14 | using WixToolset.Extensibility; | ||
15 | using WixToolset.Extensibility.Services; | ||
16 | |||
17 | internal class TransformSubcommand : WindowsInstallerSubcommandBase | ||
18 | { | ||
19 | public TransformSubcommand(IServiceProvider serviceProvider) | ||
20 | { | ||
21 | this.Messaging = serviceProvider.GetService<IMessaging>(); | ||
22 | this.BackendHelper = serviceProvider.GetService<IBackendHelper>(); | ||
23 | this.ExtensionManager = serviceProvider.GetService<IExtensionManager>(); | ||
24 | } | ||
25 | |||
26 | private IMessaging Messaging { get; } | ||
27 | |||
28 | private IBackendHelper BackendHelper { get; } | ||
29 | |||
30 | private IExtensionManager ExtensionManager { get; } | ||
31 | |||
32 | private string OutputPath { get; set; } | ||
33 | |||
34 | private string TargetPath { get; set; } | ||
35 | |||
36 | private string UpdatedPath { get; set; } | ||
37 | |||
38 | private string ExportBasePath { get; set; } | ||
39 | |||
40 | private string IntermediateFolder { get; set; } | ||
41 | |||
42 | private bool IsAdminImage { get; set; } | ||
43 | |||
44 | private bool PreserveUnchangedRows { get; set; } | ||
45 | |||
46 | private bool ShowPedanticMessages { get; set; } | ||
47 | |||
48 | private bool SuppressKeepingSpecialRows { get; set; } | ||
49 | |||
50 | private bool OutputAsWixout { get; set; } | ||
51 | |||
52 | private TransformFlags ValidationFlags { get; set; } | ||
53 | |||
54 | public override Task<int> ExecuteAsync(CancellationToken cancellationToken) | ||
55 | { | ||
56 | if (String.IsNullOrEmpty(this.TargetPath)) | ||
57 | { | ||
58 | Console.Error.WriteLine("Input file required"); | ||
59 | return Task.FromResult(-1); | ||
60 | } | ||
61 | |||
62 | if (String.IsNullOrEmpty(this.OutputPath)) | ||
63 | { | ||
64 | Console.Error.WriteLine("Output file required"); | ||
65 | return Task.FromResult(-1); | ||
66 | } | ||
67 | |||
68 | if (String.IsNullOrEmpty(this.IntermediateFolder)) | ||
69 | { | ||
70 | this.IntermediateFolder = Path.GetTempPath(); | ||
71 | } | ||
72 | |||
73 | var transform = this.LoadTransform(); | ||
74 | |||
75 | if (!this.Messaging.EncounteredError) | ||
76 | { | ||
77 | this.SaveTransform(transform); | ||
78 | } | ||
79 | |||
80 | return Task.FromResult(this.Messaging.EncounteredError ? 1 : 0); | ||
81 | } | ||
82 | |||
83 | public override bool TryParseArgument(ICommandLineParser parser, string argument) | ||
84 | { | ||
85 | if (parser.IsSwitch(argument)) | ||
86 | { | ||
87 | var parameter = argument.Substring(1); | ||
88 | switch (parameter.ToLowerInvariant()) | ||
89 | { | ||
90 | case "a": | ||
91 | this.IsAdminImage = true; | ||
92 | return true; | ||
93 | |||
94 | case "intermediatefolder": | ||
95 | this.IntermediateFolder = parser.GetNextArgumentAsDirectoryOrError(argument); | ||
96 | return true; | ||
97 | |||
98 | case "o": | ||
99 | case "out": | ||
100 | this.OutputPath = parser.GetNextArgumentAsFilePathOrError(argument); | ||
101 | return true; | ||
102 | |||
103 | case "p": | ||
104 | this.PreserveUnchangedRows = true; | ||
105 | return true; | ||
106 | |||
107 | case "pedantic": | ||
108 | this.ShowPedanticMessages = true; | ||
109 | return true; | ||
110 | |||
111 | case "serr": | ||
112 | { | ||
113 | var serr = parser.GetNextArgumentOrError(argument); | ||
114 | |||
115 | switch (serr.ToLowerInvariant()) | ||
116 | { | ||
117 | case "a": | ||
118 | this.ValidationFlags |= TransformFlags.ErrorAddExistingRow; | ||
119 | return true; | ||
120 | |||
121 | case "b": | ||
122 | this.ValidationFlags |= TransformFlags.ErrorDeleteMissingRow; | ||
123 | return true; | ||
124 | |||
125 | case "c": | ||
126 | this.ValidationFlags |= TransformFlags.ErrorAddExistingTable; | ||
127 | return true; | ||
128 | |||
129 | case "d": | ||
130 | this.ValidationFlags |= TransformFlags.ErrorDeleteMissingTable; | ||
131 | return true; | ||
132 | |||
133 | case "e": | ||
134 | this.ValidationFlags |= TransformFlags.ErrorUpdateMissingRow; | ||
135 | return true; | ||
136 | |||
137 | case "f": | ||
138 | this.ValidationFlags |= TransformFlags.ErrorChangeCodePage; | ||
139 | return true; | ||
140 | |||
141 | default: | ||
142 | this.Messaging.Write(ErrorMessages.ExpectedArgument(serr)); | ||
143 | return true; | ||
144 | } | ||
145 | } | ||
146 | |||
147 | case "val": | ||
148 | { | ||
149 | var val = parser.GetNextArgumentOrError(argument); | ||
150 | |||
151 | switch (val.ToLowerInvariant()) | ||
152 | { | ||
153 | case "language": | ||
154 | this.ValidationFlags |= TransformFlags.LanguageTransformDefault; | ||
155 | return true; | ||
156 | |||
157 | case "instance": | ||
158 | this.ValidationFlags |= TransformFlags.InstanceTransformDefault; | ||
159 | return true; | ||
160 | |||
161 | case "patch": | ||
162 | this.ValidationFlags |= TransformFlags.PatchTransformDefault; | ||
163 | return true; | ||
164 | |||
165 | case "g": | ||
166 | this.ValidationFlags |= TransformFlags.ValidateUpgradeCode; | ||
167 | return true; | ||
168 | |||
169 | case "l": | ||
170 | this.ValidationFlags |= TransformFlags.ValidateLanguage; | ||
171 | return true; | ||
172 | |||
173 | case "r": | ||
174 | this.ValidationFlags |= TransformFlags.ValidateProduct; | ||
175 | return true; | ||
176 | |||
177 | case "s": | ||
178 | this.ValidationFlags |= TransformFlags.ValidateMajorVersion; | ||
179 | return true; | ||
180 | |||
181 | case "t": | ||
182 | this.ValidationFlags |= TransformFlags.ValidateMinorVersion; | ||
183 | return true; | ||
184 | |||
185 | case "u": | ||
186 | this.ValidationFlags |= TransformFlags.ValidateUpdateVersion; | ||
187 | return true; | ||
188 | |||
189 | case "v": | ||
190 | this.ValidationFlags |= TransformFlags.ValidateNewLessBaseVersion; | ||
191 | return true; | ||
192 | |||
193 | case "w": | ||
194 | this.ValidationFlags |= TransformFlags.ValidateNewLessEqualBaseVersion; | ||
195 | return true; | ||
196 | |||
197 | case "x": | ||
198 | this.ValidationFlags |= TransformFlags.ValidateNewEqualBaseVersion; | ||
199 | return true; | ||
200 | |||
201 | case "y": | ||
202 | this.ValidationFlags |= TransformFlags.ValidateNewGreaterEqualBaseVersion; | ||
203 | return true; | ||
204 | |||
205 | case "z": | ||
206 | this.ValidationFlags |= TransformFlags.ValidateNewGreaterBaseVersion; | ||
207 | return true; | ||
208 | |||
209 | default: | ||
210 | this.Messaging.Write(ErrorMessages.ExpectedArgument(val)); | ||
211 | return true; | ||
212 | } | ||
213 | } | ||
214 | |||
215 | case "x": | ||
216 | this.ExportBasePath = parser.GetNextArgumentAsDirectoryOrError(argument); | ||
217 | return true; | ||
218 | |||
219 | case "xo": | ||
220 | this.OutputAsWixout = true; | ||
221 | return true; | ||
222 | } | ||
223 | } | ||
224 | else if (String.IsNullOrEmpty(this.TargetPath)) | ||
225 | { | ||
226 | this.TargetPath = argument; | ||
227 | return true; | ||
228 | } | ||
229 | else if (String.IsNullOrEmpty(this.UpdatedPath)) | ||
230 | { | ||
231 | this.UpdatedPath = argument; | ||
232 | return true; | ||
233 | } | ||
234 | |||
235 | return false; | ||
236 | } | ||
237 | |||
238 | private WindowsInstallerData LoadTransform() | ||
239 | { | ||
240 | WindowsInstallerData transform; | ||
241 | |||
242 | if (String.IsNullOrEmpty(this.UpdatedPath)) | ||
243 | { | ||
244 | Exception exception; | ||
245 | |||
246 | (transform, exception) = LoadWindowsInstallerDataSafely(this.TargetPath); | ||
247 | |||
248 | if (transform?.Type != OutputType.Transform) | ||
249 | { | ||
250 | this.Messaging.Write(WindowsInstallerBackendErrors.CannotLoadWixoutAsTransform(new SourceLineNumber(this.TargetPath), exception)); | ||
251 | } | ||
252 | } | ||
253 | else | ||
254 | { | ||
255 | transform = this.CreateTransform(); | ||
256 | |||
257 | if (null == transform.Tables || 0 >= transform.Tables.Count) | ||
258 | { | ||
259 | this.Messaging.Write(ErrorMessages.NoDifferencesInTransform(new SourceLineNumber(this.OutputPath))); | ||
260 | } | ||
261 | } | ||
262 | |||
263 | return transform; | ||
264 | } | ||
265 | |||
266 | private void SaveTransform(WindowsInstallerData transform) | ||
267 | { | ||
268 | if (this.OutputAsWixout) | ||
269 | { | ||
270 | using (var output = WixOutput.Create(this.OutputPath)) | ||
271 | { | ||
272 | transform.Save(output); | ||
273 | } | ||
274 | } | ||
275 | else | ||
276 | { | ||
277 | var fileSystemExtensions = this.ExtensionManager.GetServices<IFileSystemExtension>(); | ||
278 | var fileSystemManager = new FileSystemManager(fileSystemExtensions); | ||
279 | |||
280 | var tableDefinitions = this.GetTableDefinitions(); | ||
281 | |||
282 | var bindCommand = new BindTransformCommand(this.Messaging, this.BackendHelper, fileSystemManager, this.IntermediateFolder, transform, this.OutputPath, tableDefinitions); | ||
283 | bindCommand.Execute(); | ||
284 | } | ||
285 | } | ||
286 | |||
287 | private WindowsInstallerData CreateTransform() | ||
288 | { | ||
289 | if (!TryLoadWindowsInstallerData(this.TargetPath, out var targetOutput)) | ||
290 | { | ||
291 | var unbindCommand = new UnbindMsiOrMsmCommand(this.Messaging, this.BackendHelper, this.TargetPath, this.ExportBasePath, this.IntermediateFolder, this.IsAdminImage, suppressDemodularization: true, suppressExtractCabinets: true); | ||
292 | targetOutput = unbindCommand.Execute(); | ||
293 | } | ||
294 | |||
295 | if (!TryLoadWindowsInstallerData(this.TargetPath, out var updatedOutput)) | ||
296 | { | ||
297 | var unbindCommand = new UnbindMsiOrMsmCommand(this.Messaging, this.BackendHelper, this.UpdatedPath, this.ExportBasePath, this.IntermediateFolder, this.IsAdminImage, suppressDemodularization: true, suppressExtractCabinets: true); | ||
298 | updatedOutput = unbindCommand.Execute(); | ||
299 | } | ||
300 | |||
301 | var differ = new Differ(this.Messaging) | ||
302 | { | ||
303 | PreserveUnchangedRows = this.PreserveUnchangedRows, | ||
304 | ShowPedanticMessages = this.ShowPedanticMessages, | ||
305 | SuppressKeepingSpecialRows = this.SuppressKeepingSpecialRows | ||
306 | }; | ||
307 | |||
308 | return differ.Diff(targetOutput, updatedOutput, this.ValidationFlags); | ||
309 | } | ||
310 | |||
311 | private TableDefinitionCollection GetTableDefinitions() | ||
312 | { | ||
313 | var backendExtensions = this.ExtensionManager.GetServices<IWindowsInstallerBackendBinderExtension>(); | ||
314 | |||
315 | var loadTableDefinitions = new LoadTableDefinitionsCommand(this.Messaging, null, backendExtensions); | ||
316 | return loadTableDefinitions.Execute(); | ||
317 | } | ||
318 | |||
319 | private static bool TryLoadWindowsInstallerData(string path, out WindowsInstallerData data) | ||
320 | { | ||
321 | data = null; | ||
322 | |||
323 | var extension = Path.GetExtension(path); | ||
324 | |||
325 | // If the path is _not_ obviously a Windows Installer database, let's try opening it as | ||
326 | // our own data file format. | ||
327 | if (!extension.Equals(".msi", StringComparison.OrdinalIgnoreCase) && !extension.Equals(".msm", StringComparison.OrdinalIgnoreCase)) | ||
328 | { | ||
329 | (data, _) = LoadWindowsInstallerDataSafely(path); | ||
330 | } | ||
331 | |||
332 | return data != null; | ||
333 | } | ||
334 | |||
335 | private static (WindowsInstallerData, Exception) LoadWindowsInstallerDataSafely(string path) | ||
336 | { | ||
337 | WindowsInstallerData data = null; | ||
338 | Exception exception = null; | ||
339 | |||
340 | try | ||
341 | { | ||
342 | data = WindowsInstallerData.Load(path); | ||
343 | } | ||
344 | catch (Exception e) | ||
345 | { | ||
346 | exception = e; | ||
347 | } | ||
348 | |||
349 | return (data, exception); | ||
350 | } | ||
351 | } | ||
352 | } | ||
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerCommand.cs index a0da7fa4..ed0c0658 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerCommand.cs | |||
@@ -49,6 +49,10 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine | |||
49 | this.Subcommand = new InscribeSubcommand(this.ServiceProvider); | 49 | this.Subcommand = new InscribeSubcommand(this.ServiceProvider); |
50 | return true; | 50 | return true; |
51 | 51 | ||
52 | case "transform": | ||
53 | this.Subcommand = new TransformSubcommand(this.ServiceProvider); | ||
54 | return true; | ||
55 | |||
52 | case "validate": | 56 | case "validate": |
53 | this.Subcommand = new ValidateSubcommand(this.ServiceProvider); | 57 | this.Subcommand = new ValidateSubcommand(this.ServiceProvider); |
54 | return true; | 58 | return true; |
@@ -72,6 +76,7 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine | |||
72 | Console.WriteLine("Commands:"); | 76 | Console.WriteLine("Commands:"); |
73 | Console.WriteLine(); | 77 | Console.WriteLine(); |
74 | Console.WriteLine(" inscribe Updates MSI database with cabinet signature information."); | 78 | Console.WriteLine(" inscribe Updates MSI database with cabinet signature information."); |
79 | Console.WriteLine(" transform Creates an MST transform file."); | ||
75 | Console.WriteLine(" validate Validates MSI database using standard or custom ICEs."); | 80 | Console.WriteLine(" validate Validates MSI database using standard or custom ICEs."); |
76 | } | 81 | } |
77 | } | 82 | } |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Differ.cs b/src/wix/WixToolset.Core.WindowsInstaller/Differ.cs index 304d0152..8b474605 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Differ.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Differ.cs | |||
@@ -1,18 +1,15 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. |
2 | 2 | ||
3 | #if DELETE | ||
4 | |||
5 | namespace WixToolset.Core.WindowsInstaller | 3 | namespace WixToolset.Core.WindowsInstaller |
6 | { | 4 | { |
7 | using System; | 5 | using System; |
8 | using System.Collections; | 6 | using System.Collections; |
9 | using System.Collections.Generic; | 7 | using System.Collections.Generic; |
10 | using System.Globalization; | 8 | using System.Globalization; |
11 | using WixToolset.Core.WindowsInstaller.Msi; | 9 | using WixToolset.Core.Native.Msi; |
12 | using WixToolset.Data; | 10 | using WixToolset.Data; |
13 | using WixToolset.Data.Symbols; | 11 | using WixToolset.Data.Symbols; |
14 | using WixToolset.Data.WindowsInstaller; | 12 | using WixToolset.Data.WindowsInstaller; |
15 | using WixToolset.Data.WindowsInstaller.Rows; | ||
16 | using WixToolset.Extensibility; | 13 | using WixToolset.Extensibility; |
17 | using WixToolset.Extensibility.Services; | 14 | using WixToolset.Extensibility.Services; |
18 | 15 | ||
@@ -22,9 +19,6 @@ namespace WixToolset.Core.WindowsInstaller | |||
22 | public sealed class Differ | 19 | public sealed class Differ |
23 | { | 20 | { |
24 | private readonly List<IInspectorExtension> inspectorExtensions; | 21 | private readonly List<IInspectorExtension> inspectorExtensions; |
25 | private bool showPedanticMessages; | ||
26 | private bool suppressKeepingSpecialRows; | ||
27 | private bool preserveUnchangedRows; | ||
28 | private const char sectionDelimiter = '/'; | 22 | private const char sectionDelimiter = '/'; |
29 | private readonly IMessaging messaging; | 23 | private readonly IMessaging messaging; |
30 | private SummaryInformationStreams transformSummaryInfo; | 24 | private SummaryInformationStreams transformSummaryInfo; |
@@ -42,31 +36,19 @@ namespace WixToolset.Core.WindowsInstaller | |||
42 | /// Gets or sets the option to show pedantic messages. | 36 | /// Gets or sets the option to show pedantic messages. |
43 | /// </summary> | 37 | /// </summary> |
44 | /// <value>The option to show pedantic messages.</value> | 38 | /// <value>The option to show pedantic messages.</value> |
45 | public bool ShowPedanticMessages | 39 | public bool ShowPedanticMessages { get; set; } |
46 | { | ||
47 | get { return this.showPedanticMessages; } | ||
48 | set { this.showPedanticMessages = value; } | ||
49 | } | ||
50 | 40 | ||
51 | /// <summary> | 41 | /// <summary> |
52 | /// Gets or sets the option to suppress keeping special rows. | 42 | /// Gets or sets the option to suppress keeping special rows. |
53 | /// </summary> | 43 | /// </summary> |
54 | /// <value>The option to suppress keeping special rows.</value> | 44 | /// <value>The option to suppress keeping special rows.</value> |
55 | public bool SuppressKeepingSpecialRows | 45 | public bool SuppressKeepingSpecialRows { get; set; } |
56 | { | ||
57 | get { return this.suppressKeepingSpecialRows; } | ||
58 | set { this.suppressKeepingSpecialRows = value; } | ||
59 | } | ||
60 | 46 | ||
61 | /// <summary> | 47 | /// <summary> |
62 | /// Gets or sets the flag to determine if all rows, even unchanged ones will be persisted in the output. | 48 | /// Gets or sets the flag to determine if all rows, even unchanged ones will be persisted in the output. |
63 | /// </summary> | 49 | /// </summary> |
64 | /// <value>The option to keep all rows including unchanged rows.</value> | 50 | /// <value>The option to keep all rows including unchanged rows.</value> |
65 | public bool PreserveUnchangedRows | 51 | public bool PreserveUnchangedRows { get; set; } |
66 | { | ||
67 | get { return this.preserveUnchangedRows; } | ||
68 | set { this.preserveUnchangedRows = value; } | ||
69 | } | ||
70 | 52 | ||
71 | /// <summary> | 53 | /// <summary> |
72 | /// Adds an extension. | 54 | /// Adds an extension. |
@@ -97,7 +79,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
97 | /// <returns>The transform.</returns> | 79 | /// <returns>The transform.</returns> |
98 | public WindowsInstallerData Diff(WindowsInstallerData targetOutput, WindowsInstallerData updatedOutput, TransformFlags validationFlags) | 80 | public WindowsInstallerData Diff(WindowsInstallerData targetOutput, WindowsInstallerData updatedOutput, TransformFlags validationFlags) |
99 | { | 81 | { |
100 | WindowsInstallerData transform = new WindowsInstallerData(null); | 82 | var transform = new WindowsInstallerData(null); |
101 | transform.Type = OutputType.Transform; | 83 | transform.Type = OutputType.Transform; |
102 | transform.Codepage = updatedOutput.Codepage; | 84 | transform.Codepage = updatedOutput.Codepage; |
103 | this.transformSummaryInfo = new SummaryInformationStreams(); | 85 | this.transformSummaryInfo = new SummaryInformationStreams(); |
@@ -119,34 +101,34 @@ namespace WixToolset.Core.WindowsInstaller | |||
119 | } | 101 | } |
120 | 102 | ||
121 | // compare the contents of the tables | 103 | // compare the contents of the tables |
122 | foreach (Table targetTable in targetOutput.Tables) | 104 | foreach (var targetTable in targetOutput.Tables) |
123 | { | 105 | { |
124 | Table updatedTable = updatedOutput.Tables[targetTable.Name]; | 106 | var updatedTable = updatedOutput.Tables[targetTable.Name]; |
125 | TableOperation operation = TableOperation.None; | 107 | var operation = TableOperation.None; |
126 | 108 | ||
127 | List<Row> rows = this.CompareTables(targetOutput, targetTable, updatedTable, out operation); | 109 | var rows = this.CompareTables(targetOutput, targetTable, updatedTable, out operation); |
128 | 110 | ||
129 | if (TableOperation.Drop == operation) | 111 | if (TableOperation.Drop == operation) |
130 | { | 112 | { |
131 | Table droppedTable = transform.EnsureTable(targetTable.Definition); | 113 | var droppedTable = transform.EnsureTable(targetTable.Definition); |
132 | droppedTable.Operation = TableOperation.Drop; | 114 | droppedTable.Operation = TableOperation.Drop; |
133 | } | 115 | } |
134 | else if (TableOperation.None == operation) | 116 | else if (TableOperation.None == operation) |
135 | { | 117 | { |
136 | Table modified = transform.EnsureTable(updatedTable.Definition); | 118 | var modified = transform.EnsureTable(updatedTable.Definition); |
137 | rows.ForEach(r => modified.Rows.Add(r)); | 119 | rows.ForEach(r => modified.Rows.Add(r)); |
138 | } | 120 | } |
139 | } | 121 | } |
140 | 122 | ||
141 | // added tables | 123 | // added tables |
142 | foreach (Table updatedTable in updatedOutput.Tables) | 124 | foreach (var updatedTable in updatedOutput.Tables) |
143 | { | 125 | { |
144 | if (null == targetOutput.Tables[updatedTable.Name]) | 126 | if (null == targetOutput.Tables[updatedTable.Name]) |
145 | { | 127 | { |
146 | Table addedTable = transform.EnsureTable(updatedTable.Definition); | 128 | var addedTable = transform.EnsureTable(updatedTable.Definition); |
147 | addedTable.Operation = TableOperation.Add; | 129 | addedTable.Operation = TableOperation.Add; |
148 | 130 | ||
149 | foreach (Row updatedRow in updatedTable.Rows) | 131 | foreach (var updatedRow in updatedTable.Rows) |
150 | { | 132 | { |
151 | updatedRow.Operation = RowOperation.Add; | 133 | updatedRow.Operation = RowOperation.Add; |
152 | updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId; | 134 | updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId; |
@@ -156,9 +138,9 @@ namespace WixToolset.Core.WindowsInstaller | |||
156 | } | 138 | } |
157 | 139 | ||
158 | // set summary information properties | 140 | // set summary information properties |
159 | if (!this.suppressKeepingSpecialRows) | 141 | if (!this.SuppressKeepingSpecialRows) |
160 | { | 142 | { |
161 | Table summaryInfoTable = transform.Tables["_SummaryInformation"]; | 143 | var summaryInfoTable = transform.Tables["_SummaryInformation"]; |
162 | this.UpdateTransformSummaryInformationTable(summaryInfoTable, validationFlags); | 144 | this.UpdateTransformSummaryInformationTable(summaryInfoTable, validationFlags); |
163 | } | 145 | } |
164 | 146 | ||
@@ -170,53 +152,56 @@ namespace WixToolset.Core.WindowsInstaller | |||
170 | /// </summary> | 152 | /// </summary> |
171 | /// <param name="index">The indexed rows.</param> | 153 | /// <param name="index">The indexed rows.</param> |
172 | /// <param name="row">The row to index.</param> | 154 | /// <param name="row">The row to index.</param> |
173 | private void AddIndexedRow(IDictionary index, Row row) | 155 | private void AddIndexedRow(IDictionary<string, Row> index, Row row) |
174 | { | 156 | { |
175 | string primaryKey = row.GetPrimaryKey('/'); | 157 | var primaryKey = row.GetPrimaryKey('/'); |
176 | if (null != primaryKey) | 158 | |
159 | // If there is no primary, use the string representation of the row as its | ||
160 | // primary key (even though it may not be unique). | ||
161 | if (String.IsNullOrEmpty(primaryKey)) | ||
177 | { | 162 | { |
178 | // Overriding WixActionRows have a primary key defined and take precedence in the index. | 163 | // This is provided for compatibility with unreal tables with no primary key |
179 | if (row is WixActionRow) | 164 | // all real tables must specify at least one column as the primary key. |
165 | primaryKey = row.ToString(); | ||
166 | index[primaryKey] = row; | ||
167 | } | ||
168 | else | ||
169 | { | ||
170 | if (!index.TryGetValue(primaryKey, out var existingRow)) | ||
171 | { | ||
172 | index.Add(primaryKey, row); | ||
173 | } | ||
174 | else | ||
180 | { | 175 | { |
181 | WixActionRow currentRow = (WixActionRow)row; | 176 | #if TODO |
182 | if (index.Contains(primaryKey)) | 177 | // Overriding WixActionRows have a primary key defined and take precedence in the index. |
178 | if (row is WixActionRow currentActionRow) | ||
183 | { | 179 | { |
184 | // If the current row is not overridable, see if the indexed row is. | 180 | // If the current row is not overridable, see if the indexed row is. |
185 | if (!currentRow.Overridable) | 181 | if (!currentActionRow.Overridable) |
186 | { | 182 | { |
187 | WixActionRow indexedRow = index[primaryKey] as WixActionRow; | 183 | if (existingRow is WixActionRow existingActionRow && existingActionRow.Overridable) |
188 | if (null != indexedRow && indexedRow.Overridable) | ||
189 | { | 184 | { |
190 | // The indexed key is overridable and should be replaced | 185 | // The indexed key is overridable and should be replaced |
191 | // (not removed and re-added which results in two Array.Copy | 186 | // (not removed and re-added which results in two Array.Copy |
192 | // operations for SortedList, or may be re-hashing in other | 187 | // operations for SortedList, or may be re-hashing in other |
193 | // implementations of IDictionary). | 188 | // implementations of IDictionary). |
194 | index[primaryKey] = currentRow; | 189 | index[primaryKey] = currentActionRow; |
195 | } | 190 | } |
196 | } | 191 | } |
197 | 192 | ||
198 | // If we got this far, the row does not need to be indexed. | 193 | // If we got this far, the row does not need to be indexed. |
199 | return; | 194 | return; |
200 | } | 195 | } |
201 | } | 196 | #endif |
202 | 197 | ||
203 | // Nothing else should be added more than once. | 198 | // Nothing else should be added more than once. |
204 | if (!index.Contains(primaryKey)) | 199 | if (this.ShowPedanticMessages) |
205 | { | 200 | { |
206 | index.Add(primaryKey, row); | 201 | this.messaging.Write(ErrorMessages.DuplicatePrimaryKey(row.SourceLineNumbers, primaryKey, row.Table.Name)); |
207 | } | 202 | } |
208 | else if (this.showPedanticMessages) | ||
209 | { | ||
210 | this.messaging.Write(ErrorMessages.DuplicatePrimaryKey(row.SourceLineNumbers, primaryKey, row.Table.Name)); | ||
211 | } | 203 | } |
212 | } | 204 | } |
213 | else // use the string representation of the row as its primary key (it may not be unique) | ||
214 | { | ||
215 | // this is provided for compatibility with unreal tables with no primary key | ||
216 | // all real tables must specify at least one column as the primary key | ||
217 | primaryKey = row.ToString(); | ||
218 | index[primaryKey] = row; | ||
219 | } | ||
220 | } | 205 | } |
221 | 206 | ||
222 | private Row CompareRows(Table targetTable, Row targetRow, Row updatedRow, out RowOperation operation, out bool keepRow) | 207 | private Row CompareRows(Table targetTable, Row targetRow, Row updatedRow, out RowOperation operation, out bool keepRow) |
@@ -235,7 +220,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
235 | else if (null == updatedRow) | 220 | else if (null == updatedRow) |
236 | { | 221 | { |
237 | operation = targetRow.Operation = RowOperation.Delete; | 222 | operation = targetRow.Operation = RowOperation.Delete; |
238 | targetRow.SectionId = targetRow.SectionId + sectionDelimiter; | 223 | targetRow.SectionId += sectionDelimiter; |
239 | comparedRow = targetRow; | 224 | comparedRow = targetRow; |
240 | keepRow = true; | 225 | keepRow = true; |
241 | } | 226 | } |
@@ -243,7 +228,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
243 | else // possibly modified | 228 | else // possibly modified |
244 | { | 229 | { |
245 | updatedRow.Operation = RowOperation.None; | 230 | updatedRow.Operation = RowOperation.None; |
246 | if (!this.suppressKeepingSpecialRows && "_SummaryInformation" == targetTable.Name) | 231 | if (!this.SuppressKeepingSpecialRows && "_SummaryInformation" == targetTable.Name) |
247 | { | 232 | { |
248 | // ignore rows that shouldn't be in a transform | 233 | // ignore rows that shouldn't be in a transform |
249 | if (Enum.IsDefined(typeof(SummaryInformation.Transform), (int)updatedRow[0])) | 234 | if (Enum.IsDefined(typeof(SummaryInformation.Transform), (int)updatedRow[0])) |
@@ -256,18 +241,18 @@ namespace WixToolset.Core.WindowsInstaller | |||
256 | } | 241 | } |
257 | else | 242 | else |
258 | { | 243 | { |
259 | if (this.preserveUnchangedRows) | 244 | if (this.PreserveUnchangedRows) |
260 | { | 245 | { |
261 | keepRow = true; | 246 | keepRow = true; |
262 | } | 247 | } |
263 | 248 | ||
264 | for (int i = 0; i < updatedRow.Fields.Length; i++) | 249 | for (var i = 0; i < updatedRow.Fields.Length; i++) |
265 | { | 250 | { |
266 | ColumnDefinition columnDefinition = updatedRow.Fields[i].Column; | 251 | var columnDefinition = updatedRow.Fields[i].Column; |
267 | 252 | ||
268 | if (!columnDefinition.PrimaryKey) | 253 | if (!columnDefinition.PrimaryKey) |
269 | { | 254 | { |
270 | bool modified = false; | 255 | var modified = false; |
271 | 256 | ||
272 | if (i >= targetRow.Fields.Length) | 257 | if (i >= targetRow.Fields.Length) |
273 | { | 258 | { |
@@ -290,12 +275,12 @@ namespace WixToolset.Core.WindowsInstaller | |||
290 | updatedRow.Fields[i].PreviousData = (string)targetRow.Fields[i].Data; | 275 | updatedRow.Fields[i].PreviousData = (string)targetRow.Fields[i].Data; |
291 | 276 | ||
292 | // keep rows containing preserved fields so the historical data is available to the binder | 277 | // keep rows containing preserved fields so the historical data is available to the binder |
293 | keepRow = !this.suppressKeepingSpecialRows; | 278 | keepRow = !this.SuppressKeepingSpecialRows; |
294 | } | 279 | } |
295 | else if (ColumnType.Object == columnDefinition.Type) | 280 | else if (ColumnType.Object == columnDefinition.Type) |
296 | { | 281 | { |
297 | ObjectField targetObjectField = (ObjectField)targetRow.Fields[i]; | 282 | var targetObjectField = (ObjectField)targetRow.Fields[i]; |
298 | ObjectField updatedObjectField = (ObjectField)updatedRow.Fields[i]; | 283 | var updatedObjectField = (ObjectField)updatedRow.Fields[i]; |
299 | 284 | ||
300 | updatedObjectField.PreviousEmbeddedFileIndex = targetObjectField.EmbeddedFileIndex; | 285 | updatedObjectField.PreviousEmbeddedFileIndex = targetObjectField.EmbeddedFileIndex; |
301 | updatedObjectField.PreviousBaseUri = targetObjectField.BaseUri; | 286 | updatedObjectField.PreviousBaseUri = targetObjectField.BaseUri; |
@@ -308,7 +293,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
308 | updatedObjectField.UnresolvedPreviousData = (string)targetObjectField.UnresolvedData; | 293 | updatedObjectField.UnresolvedPreviousData = (string)targetObjectField.UnresolvedData; |
309 | 294 | ||
310 | // keep rows containing object fields so the files can be compared in the binder | 295 | // keep rows containing object fields so the files can be compared in the binder |
311 | keepRow = !this.suppressKeepingSpecialRows; | 296 | keepRow = !this.SuppressKeepingSpecialRows; |
312 | } | 297 | } |
313 | else | 298 | else |
314 | { | 299 | { |
@@ -342,7 +327,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
342 | 327 | ||
343 | private List<Row> CompareTables(WindowsInstallerData targetOutput, Table targetTable, Table updatedTable, out TableOperation operation) | 328 | private List<Row> CompareTables(WindowsInstallerData targetOutput, Table targetTable, Table updatedTable, out TableOperation operation) |
344 | { | 329 | { |
345 | List<Row> rows = new List<Row>(); | 330 | var rows = new List<Row>(); |
346 | operation = TableOperation.None; | 331 | operation = TableOperation.None; |
347 | 332 | ||
348 | // dropped tables | 333 | // dropped tables |
@@ -360,8 +345,8 @@ namespace WixToolset.Core.WindowsInstaller | |||
360 | } | 345 | } |
361 | else // possibly modified tables | 346 | else // possibly modified tables |
362 | { | 347 | { |
363 | SortedList updatedPrimaryKeys = new SortedList(); | 348 | var updatedPrimaryKeys = new SortedDictionary<string, Row>(); |
364 | SortedList targetPrimaryKeys = new SortedList(); | 349 | var targetPrimaryKeys = new SortedDictionary<string, Row>(); |
365 | 350 | ||
366 | // compare the table definitions | 351 | // compare the table definitions |
367 | if (0 != targetTable.Definition.CompareTo(updatedTable.Definition)) | 352 | if (0 != targetTable.Definition.CompareTo(updatedTable.Definition)) |
@@ -374,13 +359,10 @@ namespace WixToolset.Core.WindowsInstaller | |||
374 | this.IndexPrimaryKeys(targetTable, targetPrimaryKeys, updatedTable, updatedPrimaryKeys); | 359 | this.IndexPrimaryKeys(targetTable, targetPrimaryKeys, updatedTable, updatedPrimaryKeys); |
375 | 360 | ||
376 | // diff the target and updated rows | 361 | // diff the target and updated rows |
377 | foreach (DictionaryEntry targetPrimaryKeyEntry in targetPrimaryKeys) | 362 | foreach (var targetPrimaryKeyEntry in targetPrimaryKeys) |
378 | { | 363 | { |
379 | string targetPrimaryKey = (string)targetPrimaryKeyEntry.Key; | 364 | var targetPrimaryKey = targetPrimaryKeyEntry.Key; |
380 | bool keepRow = false; | 365 | var compared = this.CompareRows(targetTable, targetPrimaryKeyEntry.Value, updatedPrimaryKeys[targetPrimaryKey], out var _, out var keepRow); |
381 | RowOperation rowOperation = RowOperation.None; | ||
382 | |||
383 | Row compared = this.CompareRows(targetTable, targetPrimaryKeyEntry.Value as Row, updatedPrimaryKeys[targetPrimaryKey] as Row, out rowOperation, out keepRow); | ||
384 | 366 | ||
385 | if (keepRow) | 367 | if (keepRow) |
386 | { | 368 | { |
@@ -389,13 +371,13 @@ namespace WixToolset.Core.WindowsInstaller | |||
389 | } | 371 | } |
390 | 372 | ||
391 | // find the inserted rows | 373 | // find the inserted rows |
392 | foreach (DictionaryEntry updatedPrimaryKeyEntry in updatedPrimaryKeys) | 374 | foreach (var updatedPrimaryKeyEntry in updatedPrimaryKeys) |
393 | { | 375 | { |
394 | string updatedPrimaryKey = (string)updatedPrimaryKeyEntry.Key; | 376 | var updatedPrimaryKey = (string)updatedPrimaryKeyEntry.Key; |
395 | 377 | ||
396 | if (!targetPrimaryKeys.Contains(updatedPrimaryKey)) | 378 | if (!targetPrimaryKeys.ContainsKey(updatedPrimaryKey)) |
397 | { | 379 | { |
398 | Row updatedRow = (Row)updatedPrimaryKeyEntry.Value; | 380 | var updatedRow = (Row)updatedPrimaryKeyEntry.Value; |
399 | 381 | ||
400 | updatedRow.Operation = RowOperation.Add; | 382 | updatedRow.Operation = RowOperation.Add; |
401 | updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId; | 383 | updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId; |
@@ -408,10 +390,10 @@ namespace WixToolset.Core.WindowsInstaller | |||
408 | return rows; | 390 | return rows; |
409 | } | 391 | } |
410 | 392 | ||
411 | private void IndexPrimaryKeys(Table targetTable, SortedList targetPrimaryKeys, Table updatedTable, SortedList updatedPrimaryKeys) | 393 | private void IndexPrimaryKeys(Table targetTable, SortedDictionary<string, Row> targetPrimaryKeys, Table updatedTable, SortedDictionary<string, Row> updatedPrimaryKeys) |
412 | { | 394 | { |
413 | // index the target rows | 395 | // index the target rows |
414 | foreach (Row row in targetTable.Rows) | 396 | foreach (var row in targetTable.Rows) |
415 | { | 397 | { |
416 | this.AddIndexedRow(targetPrimaryKeys, row); | 398 | this.AddIndexedRow(targetPrimaryKeys, row); |
417 | 399 | ||
@@ -452,7 +434,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
452 | } | 434 | } |
453 | 435 | ||
454 | // index the updated rows | 436 | // index the updated rows |
455 | foreach (Row row in updatedTable.Rows) | 437 | foreach (var row in updatedTable.Rows) |
456 | { | 438 | { |
457 | this.AddIndexedRow(updatedPrimaryKeys, row); | 439 | this.AddIndexedRow(updatedPrimaryKeys, row); |
458 | 440 | ||
@@ -492,17 +474,15 @@ namespace WixToolset.Core.WindowsInstaller | |||
492 | private void UpdateTransformSummaryInformationTable(Table summaryInfoTable, TransformFlags validationFlags) | 474 | private void UpdateTransformSummaryInformationTable(Table summaryInfoTable, TransformFlags validationFlags) |
493 | { | 475 | { |
494 | // calculate the minimum version of MSI required to process the transform | 476 | // calculate the minimum version of MSI required to process the transform |
495 | int targetMin; | 477 | var minimumVersion = 100; |
496 | int updatedMin; | ||
497 | int minimumVersion = 100; | ||
498 | 478 | ||
499 | if (Int32.TryParse(this.transformSummaryInfo.TargetMinimumVersion, out targetMin) && Int32.TryParse(this.transformSummaryInfo.UpdatedMinimumVersion, out updatedMin)) | 479 | if (Int32.TryParse(this.transformSummaryInfo.TargetMinimumVersion, out var targetMin) && Int32.TryParse(this.transformSummaryInfo.UpdatedMinimumVersion, out var updatedMin)) |
500 | { | 480 | { |
501 | minimumVersion = Math.Max(targetMin, updatedMin); | 481 | minimumVersion = Math.Max(targetMin, updatedMin); |
502 | } | 482 | } |
503 | 483 | ||
504 | Hashtable summaryRows = new Hashtable(summaryInfoTable.Rows.Count); | 484 | var summaryRows = new Hashtable(summaryInfoTable.Rows.Count); |
505 | foreach (Row row in summaryInfoTable.Rows) | 485 | foreach (var row in summaryInfoTable.Rows) |
506 | { | 486 | { |
507 | summaryRows[row[0]] = row; | 487 | summaryRows[row[0]] = row; |
508 | 488 | ||
@@ -535,35 +515,35 @@ namespace WixToolset.Core.WindowsInstaller | |||
535 | 515 | ||
536 | if (!summaryRows.Contains((int)SummaryInformation.Transform.TargetPlatformAndLanguage)) | 516 | if (!summaryRows.Contains((int)SummaryInformation.Transform.TargetPlatformAndLanguage)) |
537 | { | 517 | { |
538 | Row summaryRow = summaryInfoTable.CreateRow(null); | 518 | var summaryRow = summaryInfoTable.CreateRow(null); |
539 | summaryRow[0] = (int)SummaryInformation.Transform.TargetPlatformAndLanguage; | 519 | summaryRow[0] = (int)SummaryInformation.Transform.TargetPlatformAndLanguage; |
540 | summaryRow[1] = this.transformSummaryInfo.TargetPlatformAndLanguage; | 520 | summaryRow[1] = this.transformSummaryInfo.TargetPlatformAndLanguage; |
541 | } | 521 | } |
542 | 522 | ||
543 | if (!summaryRows.Contains((int)SummaryInformation.Transform.UpdatedPlatformAndLanguage)) | 523 | if (!summaryRows.Contains((int)SummaryInformation.Transform.UpdatedPlatformAndLanguage)) |
544 | { | 524 | { |
545 | Row summaryRow = summaryInfoTable.CreateRow(null); | 525 | var summaryRow = summaryInfoTable.CreateRow(null); |
546 | summaryRow[0] = (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage; | 526 | summaryRow[0] = (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage; |
547 | summaryRow[1] = this.transformSummaryInfo.UpdatedPlatformAndLanguage; | 527 | summaryRow[1] = this.transformSummaryInfo.UpdatedPlatformAndLanguage; |
548 | } | 528 | } |
549 | 529 | ||
550 | if (!summaryRows.Contains((int)SummaryInformation.Transform.ValidationFlags)) | 530 | if (!summaryRows.Contains((int)SummaryInformation.Transform.ValidationFlags)) |
551 | { | 531 | { |
552 | Row summaryRow = summaryInfoTable.CreateRow(null); | 532 | var summaryRow = summaryInfoTable.CreateRow(null); |
553 | summaryRow[0] = (int)SummaryInformation.Transform.ValidationFlags; | 533 | summaryRow[0] = (int)SummaryInformation.Transform.ValidationFlags; |
554 | summaryRow[1] = ((int)validationFlags).ToString(CultureInfo.InvariantCulture); | 534 | summaryRow[1] = ((int)validationFlags).ToString(CultureInfo.InvariantCulture); |
555 | } | 535 | } |
556 | 536 | ||
557 | if (!summaryRows.Contains((int)SummaryInformation.Transform.InstallerRequirement)) | 537 | if (!summaryRows.Contains((int)SummaryInformation.Transform.InstallerRequirement)) |
558 | { | 538 | { |
559 | Row summaryRow = summaryInfoTable.CreateRow(null); | 539 | var summaryRow = summaryInfoTable.CreateRow(null); |
560 | summaryRow[0] = (int)SummaryInformation.Transform.InstallerRequirement; | 540 | summaryRow[0] = (int)SummaryInformation.Transform.InstallerRequirement; |
561 | summaryRow[1] = minimumVersion.ToString(CultureInfo.InvariantCulture); | 541 | summaryRow[1] = minimumVersion.ToString(CultureInfo.InvariantCulture); |
562 | } | 542 | } |
563 | 543 | ||
564 | if (!summaryRows.Contains((int)SummaryInformation.Transform.Security)) | 544 | if (!summaryRows.Contains((int)SummaryInformation.Transform.Security)) |
565 | { | 545 | { |
566 | Row summaryRow = summaryInfoTable.CreateRow(null); | 546 | var summaryRow = summaryInfoTable.CreateRow(null); |
567 | summaryRow[0] = (int)SummaryInformation.Transform.Security; | 547 | summaryRow[0] = (int)SummaryInformation.Transform.Security; |
568 | summaryRow[1] = "4"; | 548 | summaryRow[1] = "4"; |
569 | } | 549 | } |
@@ -606,5 +586,3 @@ namespace WixToolset.Core.WindowsInstaller | |||
606 | } | 586 | } |
607 | } | 587 | } |
608 | } | 588 | } |
609 | |||
610 | #endif | ||
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs b/src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs index 33aa7a74..628ad8de 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs | |||
@@ -2,6 +2,7 @@ | |||
2 | 2 | ||
3 | namespace WixToolset.Core.WindowsInstaller | 3 | namespace WixToolset.Core.WindowsInstaller |
4 | { | 4 | { |
5 | using System; | ||
5 | using WixToolset.Core.WindowsInstaller.Bind; | 6 | using WixToolset.Core.WindowsInstaller.Bind; |
6 | using WixToolset.Core.WindowsInstaller.Decompile; | 7 | using WixToolset.Core.WindowsInstaller.Decompile; |
7 | using WixToolset.Core.WindowsInstaller.Unbind; | 8 | using WixToolset.Core.WindowsInstaller.Unbind; |
@@ -71,8 +72,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
71 | 72 | ||
72 | public Intermediate Unbind(IUnbindContext context) | 73 | public Intermediate Unbind(IUnbindContext context) |
73 | { | 74 | { |
74 | var command = new UnbindMsiOrMsmCommand(context); | 75 | throw new NotImplementedException(); |
75 | return command.Execute(); | ||
76 | } | 76 | } |
77 | } | 77 | } |
78 | } | 78 | } |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/MsmBackend.cs b/src/wix/WixToolset.Core.WindowsInstaller/MsmBackend.cs index 02ea5f45..01e3c6d8 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/MsmBackend.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/MsmBackend.cs | |||
@@ -2,6 +2,7 @@ | |||
2 | 2 | ||
3 | namespace WixToolset.Core.WindowsInstaller | 3 | namespace WixToolset.Core.WindowsInstaller |
4 | { | 4 | { |
5 | using System; | ||
5 | using WixToolset.Core.WindowsInstaller.Bind; | 6 | using WixToolset.Core.WindowsInstaller.Bind; |
6 | using WixToolset.Core.WindowsInstaller.Decompile; | 7 | using WixToolset.Core.WindowsInstaller.Decompile; |
7 | using WixToolset.Core.WindowsInstaller.Unbind; | 8 | using WixToolset.Core.WindowsInstaller.Unbind; |
@@ -67,8 +68,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
67 | 68 | ||
68 | public Intermediate Unbind(IUnbindContext context) | 69 | public Intermediate Unbind(IUnbindContext context) |
69 | { | 70 | { |
70 | var command = new UnbindMsiOrMsmCommand(context); | 71 | throw new NotImplementedException(); |
71 | return command.Execute(); | ||
72 | } | 72 | } |
73 | } | 73 | } |
74 | } | 74 | } |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs b/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs index d1f5eb99..398fc780 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs | |||
@@ -4,13 +4,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
4 | { | 4 | { |
5 | using System; | 5 | using System; |
6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
7 | using System.IO; | ||
8 | using System.Linq; | ||
9 | using WixToolset.Core.WindowsInstaller.Bind; | 7 | using WixToolset.Core.WindowsInstaller.Bind; |
10 | using WixToolset.Core.Native.Msi; | ||
11 | using WixToolset.Core.WindowsInstaller.Unbind; | ||
12 | using WixToolset.Data; | ||
13 | using WixToolset.Data.Symbols; | ||
14 | using WixToolset.Data.WindowsInstaller; | 8 | using WixToolset.Data.WindowsInstaller; |
15 | using WixToolset.Extensibility; | 9 | using WixToolset.Extensibility; |
16 | using WixToolset.Extensibility.Data; | 10 | using WixToolset.Extensibility.Data; |
@@ -74,9 +68,9 @@ namespace WixToolset.Core.WindowsInstaller | |||
74 | throw new NotImplementedException(); | 68 | throw new NotImplementedException(); |
75 | } | 69 | } |
76 | 70 | ||
71 | #if TODO_PATCHING | ||
77 | public Intermediate Unbind(IUnbindContext context) | 72 | public Intermediate Unbind(IUnbindContext context) |
78 | { | 73 | { |
79 | #if TODO_PATCHING | ||
80 | Output patch; | 74 | Output patch; |
81 | 75 | ||
82 | // patch files are essentially database files (use a special flag to let the API know its a patch file) | 76 | // patch files are essentially database files (use a special flag to let the API know its a patch file) |
@@ -156,8 +150,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
156 | } | 150 | } |
157 | 151 | ||
158 | return patch; | 152 | return patch; |
159 | #endif | ||
160 | throw new NotImplementedException(); | ||
161 | } | 153 | } |
154 | #endif | ||
162 | } | 155 | } |
163 | } | 156 | } |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/MstBackend.cs b/src/wix/WixToolset.Core.WindowsInstaller/MstBackend.cs deleted file mode 100644 index 8ce75265..00000000 --- a/src/wix/WixToolset.Core.WindowsInstaller/MstBackend.cs +++ /dev/null | |||
@@ -1,39 +0,0 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | namespace WixToolset.Core.WindowsInstaller | ||
4 | { | ||
5 | using System; | ||
6 | using WixToolset.Core.WindowsInstaller.Unbind; | ||
7 | using WixToolset.Data; | ||
8 | using WixToolset.Extensibility; | ||
9 | using WixToolset.Extensibility.Data; | ||
10 | |||
11 | internal class MstBackend : IBackend | ||
12 | { | ||
13 | public IBindResult Bind(IBindContext context) | ||
14 | { | ||
15 | #if TODO_PATCHING | ||
16 | var command = new BindTransformCommand(); | ||
17 | command.Extensions = context.Extensions; | ||
18 | command.TempFilesLocation = context.IntermediateFolder; | ||
19 | command.Transform = context.IntermediateRepresentation; | ||
20 | command.OutputPath = context.OutputPath; | ||
21 | command.Execute(); | ||
22 | |||
23 | return new BindResult(Array.Empty<FileTransfer>(), Array.Empty<string>()); | ||
24 | #endif | ||
25 | throw new NotImplementedException(); | ||
26 | } | ||
27 | |||
28 | public IDecompileResult Decompile(IDecompileContext context) | ||
29 | { | ||
30 | throw new NotImplementedException(); | ||
31 | } | ||
32 | |||
33 | public Intermediate Unbind(IUnbindContext context) | ||
34 | { | ||
35 | var command = new UnbindMsiOrMsmCommand(context); | ||
36 | return command.Execute(); | ||
37 | } | ||
38 | } | ||
39 | } | ||
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs index 75ee6307..82015cf2 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs | |||
@@ -4,52 +4,80 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
4 | { | 4 | { |
5 | using System; | 5 | using System; |
6 | using System.ComponentModel; | 6 | using System.ComponentModel; |
7 | using WixToolset.Core.Native.Msi; | ||
7 | using WixToolset.Data; | 8 | using WixToolset.Data; |
9 | using WixToolset.Data.WindowsInstaller; | ||
8 | using WixToolset.Extensibility.Data; | 10 | using WixToolset.Extensibility.Data; |
9 | using WixToolset.Core.Native.Msi; | 11 | using WixToolset.Extensibility.Services; |
10 | 12 | ||
11 | internal class UnbindMsiOrMsmCommand | 13 | internal class UnbindMsiOrMsmCommand |
12 | { | 14 | { |
15 | public UnbindMsiOrMsmCommand(IMessaging messaging, IBackendHelper backendHelper, string databasePath, string exportBasePath, string intermediateFolder, bool adminImage, bool suppressDemodularization, bool suppressExtractCabinets) | ||
16 | { | ||
17 | this.Messaging = messaging; | ||
18 | this.BackendHelper = backendHelper; | ||
19 | this.DatabasePath = databasePath; | ||
20 | this.ExportBasePath = exportBasePath; | ||
21 | this.IntermediateFolder = intermediateFolder; | ||
22 | this.IsAdminImage = adminImage; | ||
23 | this.SuppressDemodularization = suppressDemodularization; | ||
24 | this.SuppressExtractCabinets = suppressExtractCabinets; | ||
25 | } | ||
26 | |||
13 | public UnbindMsiOrMsmCommand(IUnbindContext context) | 27 | public UnbindMsiOrMsmCommand(IUnbindContext context) |
14 | { | 28 | { |
15 | this.Context = context; | 29 | this.Messaging = context.ServiceProvider.GetService<IMessaging>(); |
30 | this.DatabasePath = context.InputFilePath; | ||
31 | this.ExportBasePath = context.ExportBasePath; | ||
32 | this.IntermediateFolder = context.IntermediateFolder; | ||
33 | this.IsAdminImage = context.IsAdminImage; | ||
34 | this.SuppressDemodularization = context.SuppressDemodularization; | ||
16 | } | 35 | } |
17 | 36 | ||
18 | public IUnbindContext Context { get; } | 37 | private IMessaging Messaging { get; } |
19 | 38 | ||
20 | public Intermediate Execute() | 39 | private IBackendHelper BackendHelper { get; } |
21 | { | 40 | |
22 | #if TODO_PATCHING | 41 | private string DatabasePath { get; } |
23 | Output output; | 42 | |
43 | private string ExportBasePath { get; } | ||
24 | 44 | ||
45 | private string IntermediateFolder { get; } | ||
46 | |||
47 | private bool IsAdminImage { get; } | ||
48 | |||
49 | private bool SuppressDemodularization { get; } | ||
50 | |||
51 | private bool SuppressExtractCabinets { get; } | ||
52 | |||
53 | public WindowsInstallerData Execute() | ||
54 | { | ||
25 | try | 55 | try |
26 | { | 56 | { |
27 | using (Database database = new Database(this.Context.InputFilePath, OpenDatabase.ReadOnly)) | 57 | using (var database = new Database(this.DatabasePath, OpenDatabase.ReadOnly)) |
28 | { | 58 | { |
29 | var unbindCommand = new UnbindDatabaseCommand(this.Context.Messaging, database, this.Context.InputFilePath, OutputType.Product, this.Context.ExportBasePath, this.Context.IntermediateFolder, this.Context.IsAdminImage, this.Context.SuppressDemodularization, skipSummaryInfo: false); | 59 | var unbindCommand = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, database, this.DatabasePath, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, this.IsAdminImage, this.SuppressDemodularization, skipSummaryInfo: false); |
30 | output = unbindCommand.Execute(); | 60 | var data = unbindCommand.Execute(); |
31 | 61 | ||
32 | // extract the files from the cabinets | 62 | // extract the files from the cabinets |
33 | if (!String.IsNullOrEmpty(this.Context.ExportBasePath) && !this.Context.SuppressExtractCabinets) | 63 | if (!String.IsNullOrEmpty(this.ExportBasePath) && !this.SuppressExtractCabinets) |
34 | { | 64 | { |
35 | var extractCommand = new ExtractCabinetsCommand(output, database, this.Context.InputFilePath, this.Context.ExportBasePath, this.Context.IntermediateFolder); | 65 | var extractCommand = new ExtractCabinetsCommand(data, database, this.DatabasePath, this.ExportBasePath, this.IntermediateFolder); |
36 | extractCommand.Execute(); | 66 | extractCommand.Execute(); |
37 | } | 67 | } |
68 | |||
69 | return data; | ||
38 | } | 70 | } |
39 | } | 71 | } |
40 | catch (Win32Exception e) | 72 | catch (Win32Exception e) |
41 | { | 73 | { |
42 | if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED | 74 | if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED |
43 | { | 75 | { |
44 | throw new WixException(WixErrors.OpenDatabaseFailed(this.Context.InputFilePath)); | 76 | //throw new WixException(WixErrors.OpenDatabaseFailed(this.DatabasePath)); |
45 | } | 77 | } |
46 | 78 | ||
47 | throw; | 79 | throw; |
48 | } | 80 | } |
49 | |||
50 | return output; | ||
51 | #endif | ||
52 | throw new NotImplementedException(); | ||
53 | } | 81 | } |
54 | } | 82 | } |
55 | } | 83 | } |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs index f40aed4e..ea40fa9f 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs | |||
@@ -41,7 +41,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
41 | private TableDefinitionCollection TableDefinitions { get; } | 41 | private TableDefinitionCollection TableDefinitions { get; } |
42 | 42 | ||
43 | private string EmptyFile { get; set; } | 43 | private string EmptyFile { get; set; } |
44 | 44 | ||
45 | public WindowsInstallerData Execute() | 45 | public WindowsInstallerData Execute() |
46 | { | 46 | { |
47 | var transform = new WindowsInstallerData(new SourceLineNumber(this.TransformFile)); | 47 | var transform = new WindowsInstallerData(new SourceLineNumber(this.TransformFile)); |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendErrors.cs b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendErrors.cs index 0c15ad05..2efb06f1 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendErrors.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendErrors.cs | |||
@@ -2,14 +2,17 @@ | |||
2 | 2 | ||
3 | namespace WixToolset.Core.WindowsInstaller | 3 | namespace WixToolset.Core.WindowsInstaller |
4 | { | 4 | { |
5 | using System; | ||
5 | using WixToolset.Data; | 6 | using WixToolset.Data; |
6 | 7 | ||
7 | internal static class WindowsInstallerBackendErrors | 8 | internal static class WindowsInstallerBackendErrors |
8 | { | 9 | { |
9 | //public static Message ReplaceThisWithTheFirstError(SourceLineNumber sourceLineNumbers) | 10 | public static Message CannotLoadWixoutAsTransform(SourceLineNumber sourceLineNumbers, Exception exception) |
10 | //{ | 11 | { |
11 | // return Message(sourceLineNumbers, Ids.ReplaceThisWithTheFirstError, "format string", arg1, arg2); | 12 | var additionalDetail = exception == null ? String.Empty : ", detail: " + exception.Message; |
12 | //} | 13 | |
14 | return Message(sourceLineNumbers, Ids.CannotLoadWixoutAsTransform, "Could not load wixout file as a transform{1}", additionalDetail); | ||
15 | } | ||
13 | 16 | ||
14 | private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args) | 17 | private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args) |
15 | { | 18 | { |
@@ -18,7 +21,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
18 | 21 | ||
19 | public enum Ids | 22 | public enum Ids |
20 | { | 23 | { |
21 | // ReplaceThisWithTheFirstError = 7500, | 24 | CannotLoadWixoutAsTransform = 7500, |
22 | } // last available is 7999. 8000 is BurnBackendErrors. | 25 | } // last available is 7999. 8000 is BurnBackendErrors. |
23 | } | 26 | } |
24 | } | 27 | } |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendFactory.cs b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendFactory.cs index f72acb21..d14743e9 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendFactory.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendFactory.cs | |||
@@ -37,11 +37,6 @@ namespace WixToolset.Core.WindowsInstaller | |||
37 | //case "patchcreation": | 37 | //case "patchcreation": |
38 | //case ".pcp": | 38 | //case ".pcp": |
39 | // return new PatchCreationBackend(); | 39 | // return new PatchCreationBackend(); |
40 | |||
41 | case "transform": | ||
42 | case ".mst": | ||
43 | backend = new MstBackend(); | ||
44 | return true; | ||
45 | } | 40 | } |
46 | 41 | ||
47 | backend = null; | 42 | backend = null; |
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.en-us.wxl b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.en-us.wxl index f7453566..d3844e39 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.en-us.wxl +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.en-us.wxl | |||
@@ -1,7 +1,7 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
2 | <WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US"> | 2 | <WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US"> |
3 | 3 | ||
4 | <String Id="DowngradeError">A newer version of [ProductName] is already installed.</String> | 4 | <String Id="DowngradeError">A newer version en-us of [ProductName] is already installed.</String> |
5 | <String Id="FeatureTitle">MsiPackage</String> | 5 | <String Id="FeatureTitle">MsiPackage en-us</String> |
6 | 6 | ||
7 | </WixLocalization> | 7 | </WixLocalization> |
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.ja-jp.wxl b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.ja-jp.wxl index ef287da7..48a339d3 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.ja-jp.wxl +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.ja-jp.wxl | |||
@@ -1,7 +1,7 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
2 | <WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="ja-JP"> | 2 | <WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="ja-JP"> |
3 | 3 | ||
4 | <String Id="DowngradeError">A newer version of [ProductName] is already installed.</String> | 4 | <String Id="DowngradeError">A newer version ja-jp of [ProductName] is already installed.</String> |
5 | <String Id="FeatureTitle">MsiPackage</String> | 5 | <String Id="FeatureTitle">MsiPackage ja-jp</String> |
6 | 6 | ||
7 | </WixLocalization> | 7 | </WixLocalization> |
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TransformFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/TransformFixture.cs new file mode 100644 index 00000000..bdbf5c26 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TransformFixture.cs | |||
@@ -0,0 +1,143 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | namespace WixToolsetTest.CoreIntegration | ||
4 | { | ||
5 | using System.IO; | ||
6 | using System.Linq; | ||
7 | using WixBuildTools.TestSupport; | ||
8 | using WixToolset.Core.TestPackage; | ||
9 | using WixToolset.Data.WindowsInstaller; | ||
10 | using Xunit; | ||
11 | |||
12 | public class TransformFixture | ||
13 | { | ||
14 | [Fact] | ||
15 | public void CanBuildTransformFromEnuToJpn() | ||
16 | { | ||
17 | var folder = TestData.Get(@"TestData", "Language"); | ||
18 | |||
19 | using (var fs = new DisposableFileSystem()) | ||
20 | { | ||
21 | var baseFolder = fs.GetFolder(); | ||
22 | var enuMsiPath = Path.Combine(baseFolder, @"bin\enu.msi"); | ||
23 | var jpnMsiPath = Path.Combine(baseFolder, @"bin\jpn.msi"); | ||
24 | var mstPath = Path.Combine(baseFolder, @"bin\test.mst"); | ||
25 | |||
26 | var result = WixRunner.Execute(new[] | ||
27 | { | ||
28 | "build", | ||
29 | Path.Combine(folder, "Package.wxs"), | ||
30 | "-loc", Path.Combine(folder, "Package.en-us.wxl"), | ||
31 | "-bindpath", Path.Combine(folder, "data"), | ||
32 | "-intermediateFolder", Path.Combine(baseFolder, "obj"), | ||
33 | "-o", enuMsiPath | ||
34 | }); | ||
35 | result.AssertSuccess(); | ||
36 | |||
37 | |||
38 | result = WixRunner.Execute(new[] | ||
39 | { | ||
40 | "build", | ||
41 | Path.Combine(folder, "Package.wxs"), | ||
42 | "-loc", Path.Combine(folder, "Package.ja-jp.wxl"), | ||
43 | "-bindpath", Path.Combine(folder, "data"), | ||
44 | "-intermediateFolder", Path.Combine(baseFolder, "obj"), | ||
45 | "-o", jpnMsiPath | ||
46 | }); | ||
47 | result.AssertSuccess(); | ||
48 | |||
49 | result = WixRunner.Execute(new[] | ||
50 | { | ||
51 | "msi", "transform", | ||
52 | "-intermediateFolder", Path.Combine(baseFolder, "obj"), | ||
53 | "-serr", "f", | ||
54 | "-o", mstPath, | ||
55 | enuMsiPath, | ||
56 | jpnMsiPath | ||
57 | }); | ||
58 | result.AssertSuccess(); | ||
59 | |||
60 | Assert.True(File.Exists(mstPath)); | ||
61 | } | ||
62 | } | ||
63 | |||
64 | [Fact] | ||
65 | public void CanBuildWixoutTransform() | ||
66 | { | ||
67 | var folder = TestData.Get(@"TestData", "Language"); | ||
68 | |||
69 | using (var fs = new DisposableFileSystem()) | ||
70 | { | ||
71 | var baseFolder = fs.GetFolder(); | ||
72 | var enuMsiPath = Path.Combine(baseFolder, @"bin\enu.msi"); | ||
73 | var jpnMsiPath = Path.Combine(baseFolder, @"bin\jpn.msi"); | ||
74 | var wixmstPath = Path.Combine(baseFolder, @"bin\test.wixmst"); | ||
75 | var mstPath = Path.Combine(baseFolder, @"bin\test.mst"); | ||
76 | |||
77 | var result = WixRunner.Execute(new[] | ||
78 | { | ||
79 | "build", | ||
80 | Path.Combine(folder, "Package.wxs"), | ||
81 | "-loc", Path.Combine(folder, "Package.en-us.wxl"), | ||
82 | "-bindpath", Path.Combine(folder, "data"), | ||
83 | "-intermediateFolder", Path.Combine(baseFolder, "obj"), | ||
84 | "-o", enuMsiPath | ||
85 | }); | ||
86 | result.AssertSuccess(); | ||
87 | |||
88 | result = WixRunner.Execute(new[] | ||
89 | { | ||
90 | "build", | ||
91 | Path.Combine(folder, "Package.wxs"), | ||
92 | "-loc", Path.Combine(folder, "Package.ja-jp.wxl"), | ||
93 | "-bindpath", Path.Combine(folder, "data"), | ||
94 | "-intermediateFolder", Path.Combine(baseFolder, "obj"), | ||
95 | "-o", jpnMsiPath | ||
96 | }); | ||
97 | result.AssertSuccess(); | ||
98 | |||
99 | result = WixRunner.Execute(new[] | ||
100 | { | ||
101 | "msi", "transform", | ||
102 | "-intermediateFolder", Path.Combine(baseFolder, "obj"), | ||
103 | "-serr", "f", | ||
104 | "-xo", | ||
105 | "-o", wixmstPath, | ||
106 | enuMsiPath, | ||
107 | jpnMsiPath | ||
108 | }); | ||
109 | result.AssertSuccess(); | ||
110 | |||
111 | var wixmst = WindowsInstallerData.Load(wixmstPath); | ||
112 | var rows = wixmst.Tables.SelectMany(t => t.Rows).Where(r => r.Operation == RowOperation.Modify).ToDictionary(r => r.GetPrimaryKey()); | ||
113 | |||
114 | WixAssert.CompareLineByLine(new[] | ||
115 | { | ||
116 | "NOT WIX_DOWNGRADE_DETECTED", | ||
117 | "ProductCode", | ||
118 | "ProductFeature", | ||
119 | "ProductLanguage" | ||
120 | }, rows.Keys.OrderBy(s => s).ToArray()); | ||
121 | |||
122 | Assert.True(rows.TryGetValue("ProductFeature", out var productFeatureRow)); | ||
123 | Assert.Equal("MsiPackage ja-jp", productFeatureRow.FieldAsString(2)); | ||
124 | |||
125 | Assert.True(rows.TryGetValue("ProductLanguage", out var productLanguageRow)); | ||
126 | Assert.Equal("1041", productLanguageRow.FieldAsString(1)); | ||
127 | |||
128 | Assert.False(File.Exists(mstPath)); | ||
129 | |||
130 | result = WixRunner.Execute(new[] | ||
131 | { | ||
132 | "msi", "transform", | ||
133 | "-intermediateFolder", Path.Combine(baseFolder, "obj"), | ||
134 | "-o", mstPath, | ||
135 | wixmstPath | ||
136 | }); | ||
137 | result.AssertSuccess(); | ||
138 | |||
139 | Assert.True(File.Exists(mstPath)); | ||
140 | } | ||
141 | } | ||
142 | } | ||
143 | } | ||