Implement "wix msi transform"
Brings the functionality of torch into the WindowsInstallerBackend as the "transform" subcommand. Fixes 4602
Rob Mensching committed
Mar 5, 2022 at 11:09 UTC
91cd2d65121a163e625d2f029025123b0f8467d2
16 files changed
+653
-201
src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
+9
-13
@@ -62,18 +62,19 @@ namespace WixToolset.Core.WindowsInstaller.Bind
62
63
if (this.Transform.TryGetTable("Property", out var propertyTable))
64
{
65
- for (int i = propertyTable.Rows.Count - 1; i >= 0; i--)
65
+ for (var i = propertyTable.Rows.Count - 1; i >= 0; i--)
66
{
67
- Row row = propertyTable.Rows[i];
67
+ var row = propertyTable.Rows[i];
68
+ var id = row.FieldAsString(0);
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)
71
{
72
propertyTable.Rows.RemoveAt(i);
72
-
73
- if ("UpgradeCode" == (string)row[0])
74
- {
75
- updatedUpgradeCode = (string)row[1];
76
- }
73
+ }
74
+ else if ("UpgradeCode" == id)
75
+ {
76
+ updatedUpgradeCode = id;
77
+ propertyTable.Rows.RemoveAt(i);
78
}
79
}
80
}
@@ -383,11 +384,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
384
}
385
}
386
386
- //foreach (BinderExtension extension in this.Extensions)
387
- //{
388
- // extension.PostBind(this.Context);
389
- //}
390
-
387
// Any errors encountered up to this point can cause errors during generation.
388
if (this.Messaging.EncounteredError)
389
{
src/wix/WixToolset.Core.WindowsInstaller/Bind/LoadTableDefinitionsCommand.cs
+2
-4
@@ -2,9 +2,7 @@
2
3
namespace WixToolset.Core.WindowsInstaller.Bind
4
{
5
- using System;
5
using System.Collections.Generic;
7
- using System.Globalization;
6
using System.Linq;
7
using WixToolset.Data;
8
using WixToolset.Data.Symbols;
@@ -32,9 +30,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind
30
public TableDefinitionCollection Execute()
31
{
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);
34
37
- if (customColumnsById.Any())
35
+ if (customColumnsById?.Any() == true)
36
{
37
foreach (var symbol in this.Section.Symbols.OfType<WixCustomTableSymbol>())
38
{
src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs
new
+352
@@ -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
+}
src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerCommand.cs
+5
@@ -49,6 +49,10 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
49
this.Subcommand = new InscribeSubcommand(this.ServiceProvider);
50
return true;
51
52
+ case "transform":
53
+ this.Subcommand = new TransformSubcommand(this.ServiceProvider);
54
+ return true;
55
+
56
case "validate":
57
this.Subcommand = new ValidateSubcommand(this.ServiceProvider);
58
return true;
@@ -72,6 +76,7 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
76
Console.WriteLine("Commands:");
77
Console.WriteLine();
78
Console.WriteLine(" inscribe Updates MSI database with cabinet signature information.");
79
+ Console.WriteLine(" transform Creates an MST transform file.");
80
Console.WriteLine(" validate Validates MSI database using standard or custom ICEs.");
81
}
82
}
src/wix/WixToolset.Core.WindowsInstaller/Differ.cs
+78
-100
@@ -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.
2
3
-#if DELETE
4
-
3
namespace WixToolset.Core.WindowsInstaller
4
{
5
using System;
6
using System.Collections;
7
using System.Collections.Generic;
8
using System.Globalization;
11
- using WixToolset.Core.WindowsInstaller.Msi;
9
+ using WixToolset.Core.Native.Msi;
10
using WixToolset.Data;
11
using WixToolset.Data.Symbols;
12
using WixToolset.Data.WindowsInstaller;
15
- using WixToolset.Data.WindowsInstaller.Rows;
13
using WixToolset.Extensibility;
14
using WixToolset.Extensibility.Services;
15
@@ -22,9 +19,6 @@ namespace WixToolset.Core.WindowsInstaller
19
public sealed class Differ
20
{
21
private readonly List<IInspectorExtension> inspectorExtensions;
25
- private bool showPedanticMessages;
26
- private bool suppressKeepingSpecialRows;
27
- private bool preserveUnchangedRows;
22
private const char sectionDelimiter = '/';
23
private readonly IMessaging messaging;
24
private SummaryInformationStreams transformSummaryInfo;
@@ -42,31 +36,19 @@ namespace WixToolset.Core.WindowsInstaller
36
/// Gets or sets the option to show pedantic messages.
37
/// </summary>
38
/// <value>The option to show pedantic messages.</value>
45
- public bool ShowPedanticMessages
46
- {
47
- get { return this.showPedanticMessages; }
48
- set { this.showPedanticMessages = value; }
49
- }
39
+ public bool ShowPedanticMessages { get; set; }
40
41
/// <summary>
42
/// Gets or sets the option to suppress keeping special rows.
43
/// </summary>
44
/// <value>The option to suppress keeping special rows.</value>
55
- public bool SuppressKeepingSpecialRows
56
- {
57
- get { return this.suppressKeepingSpecialRows; }
58
- set { this.suppressKeepingSpecialRows = value; }
59
- }
45
+ public bool SuppressKeepingSpecialRows { get; set; }
46
47
/// <summary>
48
/// Gets or sets the flag to determine if all rows, even unchanged ones will be persisted in the output.
49
/// </summary>
50
/// <value>The option to keep all rows including unchanged rows.</value>
65
- public bool PreserveUnchangedRows
66
- {
67
- get { return this.preserveUnchangedRows; }
68
- set { this.preserveUnchangedRows = value; }
69
- }
51
+ public bool PreserveUnchangedRows { get; set; }
52
53
/// <summary>
54
/// Adds an extension.
@@ -97,7 +79,7 @@ namespace WixToolset.Core.WindowsInstaller
79
/// <returns>The transform.</returns>
80
public WindowsInstallerData Diff(WindowsInstallerData targetOutput, WindowsInstallerData updatedOutput, TransformFlags validationFlags)
81
{
100
- WindowsInstallerData transform = new WindowsInstallerData(null);
82
+ var transform = new WindowsInstallerData(null);
83
transform.Type = OutputType.Transform;
84
transform.Codepage = updatedOutput.Codepage;
85
this.transformSummaryInfo = new SummaryInformationStreams();
@@ -119,34 +101,34 @@ namespace WixToolset.Core.WindowsInstaller
101
}
102
103
// compare the contents of the tables
122
- foreach (Table targetTable in targetOutput.Tables)
104
+ foreach (var targetTable in targetOutput.Tables)
105
{
124
- Table updatedTable = updatedOutput.Tables[targetTable.Name];
125
- TableOperation operation = TableOperation.None;
106
+ var updatedTable = updatedOutput.Tables[targetTable.Name];
107
+ var operation = TableOperation.None;
108
127
- List<Row> rows = this.CompareTables(targetOutput, targetTable, updatedTable, out operation);
109
+ var rows = this.CompareTables(targetOutput, targetTable, updatedTable, out operation);
110
111
if (TableOperation.Drop == operation)
112
{
131
- Table droppedTable = transform.EnsureTable(targetTable.Definition);
113
+ var droppedTable = transform.EnsureTable(targetTable.Definition);
114
droppedTable.Operation = TableOperation.Drop;
115
}
116
else if (TableOperation.None == operation)
117
{
136
- Table modified = transform.EnsureTable(updatedTable.Definition);
118
+ var modified = transform.EnsureTable(updatedTable.Definition);
119
rows.ForEach(r => modified.Rows.Add(r));
120
}
121
}
122
123
// added tables
142
- foreach (Table updatedTable in updatedOutput.Tables)
124
+ foreach (var updatedTable in updatedOutput.Tables)
125
{
126
if (null == targetOutput.Tables[updatedTable.Name])
127
{
146
- Table addedTable = transform.EnsureTable(updatedTable.Definition);
128
+ var addedTable = transform.EnsureTable(updatedTable.Definition);
129
addedTable.Operation = TableOperation.Add;
130
149
- foreach (Row updatedRow in updatedTable.Rows)
131
+ foreach (var updatedRow in updatedTable.Rows)
132
{
133
updatedRow.Operation = RowOperation.Add;
134
updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId;
@@ -156,9 +138,9 @@ namespace WixToolset.Core.WindowsInstaller
138
}
139
140
// set summary information properties
159
- if (!this.suppressKeepingSpecialRows)
141
+ if (!this.SuppressKeepingSpecialRows)
142
{
161
- Table summaryInfoTable = transform.Tables["_SummaryInformation"];
143
+ var summaryInfoTable = transform.Tables["_SummaryInformation"];
144
this.UpdateTransformSummaryInformationTable(summaryInfoTable, validationFlags);
145
}
146
@@ -170,53 +152,56 @@ namespace WixToolset.Core.WindowsInstaller
152
/// </summary>
153
/// <param name="index">The indexed rows.</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)
156
{
175
- string primaryKey = row.GetPrimaryKey('/');
176
- if (null != primaryKey)
157
+ var primaryKey = row.GetPrimaryKey('/');
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))
162
{
178
- // Overriding WixActionRows have a primary key defined and take precedence in the index.
179
- if (row is WixActionRow)
163
+ // This is provided for compatibility with unreal tables with no primary key
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
175
{
181
- WixActionRow currentRow = (WixActionRow)row;
182
- if (index.Contains(primaryKey))
176
+#if TODO
177
+ // Overriding WixActionRows have a primary key defined and take precedence in the index.
178
+ if (row is WixActionRow currentActionRow)
179
{
180
// If the current row is not overridable, see if the indexed row is.
185
- if (!currentRow.Overridable)
181
+ if (!currentActionRow.Overridable)
182
{
187
- WixActionRow indexedRow = index[primaryKey] as WixActionRow;
188
- if (null != indexedRow && indexedRow.Overridable)
183
+ if (existingRow is WixActionRow existingActionRow && existingActionRow.Overridable)
184
{
185
// The indexed key is overridable and should be replaced
186
// (not removed and re-added which results in two Array.Copy
187
// operations for SortedList, or may be re-hashing in other
188
// implementations of IDictionary).
194
- index[primaryKey] = currentRow;
189
+ index[primaryKey] = currentActionRow;
190
}
191
}
192
193
// If we got this far, the row does not need to be indexed.
194
return;
195
}
201
- }
196
+#endif
197
203
- // Nothing else should be added more than once.
204
- if (!index.Contains(primaryKey))
205
- {
206
- index.Add(primaryKey, row);
207
- }
208
- else if (this.showPedanticMessages)
209
- {
210
- this.messaging.Write(ErrorMessages.DuplicatePrimaryKey(row.SourceLineNumbers, primaryKey, row.Table.Name));
198
+ // Nothing else should be added more than once.
199
+ if (this.ShowPedanticMessages)
200
+ {
201
+ this.messaging.Write(ErrorMessages.DuplicatePrimaryKey(row.SourceLineNumbers, primaryKey, row.Table.Name));
202
+ }
203
}
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
- }
205
}
206
207
private Row CompareRows(Table targetTable, Row targetRow, Row updatedRow, out RowOperation operation, out bool keepRow)
@@ -235,7 +220,7 @@ namespace WixToolset.Core.WindowsInstaller
220
else if (null == updatedRow)
221
{
222
operation = targetRow.Operation = RowOperation.Delete;
238
- targetRow.SectionId = targetRow.SectionId + sectionDelimiter;
223
+ targetRow.SectionId += sectionDelimiter;
224
comparedRow = targetRow;
225
keepRow = true;
226
}
@@ -243,7 +228,7 @@ namespace WixToolset.Core.WindowsInstaller
228
else // possibly modified
229
{
230
updatedRow.Operation = RowOperation.None;
246
- if (!this.suppressKeepingSpecialRows && "_SummaryInformation" == targetTable.Name)
231
+ if (!this.SuppressKeepingSpecialRows && "_SummaryInformation" == targetTable.Name)
232
{
233
// ignore rows that shouldn't be in a transform
234
if (Enum.IsDefined(typeof(SummaryInformation.Transform), (int)updatedRow[0]))
@@ -256,18 +241,18 @@ namespace WixToolset.Core.WindowsInstaller
241
}
242
else
243
{
259
- if (this.preserveUnchangedRows)
244
+ if (this.PreserveUnchangedRows)
245
{
246
keepRow = true;
247
}
248
264
- for (int i = 0; i < updatedRow.Fields.Length; i++)
249
+ for (var i = 0; i < updatedRow.Fields.Length; i++)
250
{
266
- ColumnDefinition columnDefinition = updatedRow.Fields[i].Column;
251
+ var columnDefinition = updatedRow.Fields[i].Column;
252
253
if (!columnDefinition.PrimaryKey)
254
{
270
- bool modified = false;
255
+ var modified = false;
256
257
if (i >= targetRow.Fields.Length)
258
{
@@ -290,12 +275,12 @@ namespace WixToolset.Core.WindowsInstaller
275
updatedRow.Fields[i].PreviousData = (string)targetRow.Fields[i].Data;
276
277
// keep rows containing preserved fields so the historical data is available to the binder
293
- keepRow = !this.suppressKeepingSpecialRows;
278
+ keepRow = !this.SuppressKeepingSpecialRows;
279
}
280
else if (ColumnType.Object == columnDefinition.Type)
281
{
297
- ObjectField targetObjectField = (ObjectField)targetRow.Fields[i];
298
- ObjectField updatedObjectField = (ObjectField)updatedRow.Fields[i];
282
+ var targetObjectField = (ObjectField)targetRow.Fields[i];
283
+ var updatedObjectField = (ObjectField)updatedRow.Fields[i];
284
285
updatedObjectField.PreviousEmbeddedFileIndex = targetObjectField.EmbeddedFileIndex;
286
updatedObjectField.PreviousBaseUri = targetObjectField.BaseUri;
@@ -308,7 +293,7 @@ namespace WixToolset.Core.WindowsInstaller
293
updatedObjectField.UnresolvedPreviousData = (string)targetObjectField.UnresolvedData;
294
295
// keep rows containing object fields so the files can be compared in the binder
311
- keepRow = !this.suppressKeepingSpecialRows;
296
+ keepRow = !this.SuppressKeepingSpecialRows;
297
}
298
else
299
{
@@ -342,7 +327,7 @@ namespace WixToolset.Core.WindowsInstaller
327
328
private List<Row> CompareTables(WindowsInstallerData targetOutput, Table targetTable, Table updatedTable, out TableOperation operation)
329
{
345
- List<Row> rows = new List<Row>();
330
+ var rows = new List<Row>();
331
operation = TableOperation.None;
332
333
// dropped tables
@@ -360,8 +345,8 @@ namespace WixToolset.Core.WindowsInstaller
345
}
346
else // possibly modified tables
347
{
363
- SortedList updatedPrimaryKeys = new SortedList();
364
- SortedList targetPrimaryKeys = new SortedList();
348
+ var updatedPrimaryKeys = new SortedDictionary<string, Row>();
349
+ var targetPrimaryKeys = new SortedDictionary<string, Row>();
350
351
// compare the table definitions
352
if (0 != targetTable.Definition.CompareTo(updatedTable.Definition))
@@ -374,13 +359,10 @@ namespace WixToolset.Core.WindowsInstaller
359
this.IndexPrimaryKeys(targetTable, targetPrimaryKeys, updatedTable, updatedPrimaryKeys);
360
361
// diff the target and updated rows
377
- foreach (DictionaryEntry targetPrimaryKeyEntry in targetPrimaryKeys)
362
+ foreach (var targetPrimaryKeyEntry in targetPrimaryKeys)
363
{
379
- string targetPrimaryKey = (string)targetPrimaryKeyEntry.Key;
380
- bool keepRow = false;
381
- RowOperation rowOperation = RowOperation.None;
382
-
383
- Row compared = this.CompareRows(targetTable, targetPrimaryKeyEntry.Value as Row, updatedPrimaryKeys[targetPrimaryKey] as Row, out rowOperation, out keepRow);
364
+ var targetPrimaryKey = targetPrimaryKeyEntry.Key;
365
+ var compared = this.CompareRows(targetTable, targetPrimaryKeyEntry.Value, updatedPrimaryKeys[targetPrimaryKey], out var _, out var keepRow);
366
367
if (keepRow)
368
{
@@ -389,13 +371,13 @@ namespace WixToolset.Core.WindowsInstaller
371
}
372
373
// find the inserted rows
392
- foreach (DictionaryEntry updatedPrimaryKeyEntry in updatedPrimaryKeys)
374
+ foreach (var updatedPrimaryKeyEntry in updatedPrimaryKeys)
375
{
394
- string updatedPrimaryKey = (string)updatedPrimaryKeyEntry.Key;
376
+ var updatedPrimaryKey = (string)updatedPrimaryKeyEntry.Key;
377
396
- if (!targetPrimaryKeys.Contains(updatedPrimaryKey))
378
+ if (!targetPrimaryKeys.ContainsKey(updatedPrimaryKey))
379
{
398
- Row updatedRow = (Row)updatedPrimaryKeyEntry.Value;
380
+ var updatedRow = (Row)updatedPrimaryKeyEntry.Value;
381
382
updatedRow.Operation = RowOperation.Add;
383
updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId;
@@ -408,10 +390,10 @@ namespace WixToolset.Core.WindowsInstaller
390
return rows;
391
}
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)
394
{
395
// index the target rows
414
- foreach (Row row in targetTable.Rows)
396
+ foreach (var row in targetTable.Rows)
397
{
398
this.AddIndexedRow(targetPrimaryKeys, row);
399
@@ -452,7 +434,7 @@ namespace WixToolset.Core.WindowsInstaller
434
}
435
436
// index the updated rows
455
- foreach (Row row in updatedTable.Rows)
437
+ foreach (var row in updatedTable.Rows)
438
{
439
this.AddIndexedRow(updatedPrimaryKeys, row);
440
@@ -492,17 +474,15 @@ namespace WixToolset.Core.WindowsInstaller
474
private void UpdateTransformSummaryInformationTable(Table summaryInfoTable, TransformFlags validationFlags)
475
{
476
// calculate the minimum version of MSI required to process the transform
495
- int targetMin;
496
- int updatedMin;
497
- int minimumVersion = 100;
477
+ var minimumVersion = 100;
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))
480
{
481
minimumVersion = Math.Max(targetMin, updatedMin);
482
}
483
504
- Hashtable summaryRows = new Hashtable(summaryInfoTable.Rows.Count);
505
- foreach (Row row in summaryInfoTable.Rows)
484
+ var summaryRows = new Hashtable(summaryInfoTable.Rows.Count);
485
+ foreach (var row in summaryInfoTable.Rows)
486
{
487
summaryRows[row[0]] = row;
488
@@ -535,35 +515,35 @@ namespace WixToolset.Core.WindowsInstaller
515
516
if (!summaryRows.Contains((int)SummaryInformation.Transform.TargetPlatformAndLanguage))
517
{
538
- Row summaryRow = summaryInfoTable.CreateRow(null);
518
+ var summaryRow = summaryInfoTable.CreateRow(null);
519
summaryRow[0] = (int)SummaryInformation.Transform.TargetPlatformAndLanguage;
520
summaryRow[1] = this.transformSummaryInfo.TargetPlatformAndLanguage;
521
}
522
523
if (!summaryRows.Contains((int)SummaryInformation.Transform.UpdatedPlatformAndLanguage))
524
{
545
- Row summaryRow = summaryInfoTable.CreateRow(null);
525
+ var summaryRow = summaryInfoTable.CreateRow(null);
526
summaryRow[0] = (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage;
527
summaryRow[1] = this.transformSummaryInfo.UpdatedPlatformAndLanguage;
528
}
529
530
if (!summaryRows.Contains((int)SummaryInformation.Transform.ValidationFlags))
531
{
552
- Row summaryRow = summaryInfoTable.CreateRow(null);
532
+ var summaryRow = summaryInfoTable.CreateRow(null);
533
summaryRow[0] = (int)SummaryInformation.Transform.ValidationFlags;
534
summaryRow[1] = ((int)validationFlags).ToString(CultureInfo.InvariantCulture);
535
}
536
537
if (!summaryRows.Contains((int)SummaryInformation.Transform.InstallerRequirement))
538
{
559
- Row summaryRow = summaryInfoTable.CreateRow(null);
539
+ var summaryRow = summaryInfoTable.CreateRow(null);
540
summaryRow[0] = (int)SummaryInformation.Transform.InstallerRequirement;
541
summaryRow[1] = minimumVersion.ToString(CultureInfo.InvariantCulture);
542
}
543
544
if (!summaryRows.Contains((int)SummaryInformation.Transform.Security))
545
{
566
- Row summaryRow = summaryInfoTable.CreateRow(null);
546
+ var summaryRow = summaryInfoTable.CreateRow(null);
547
summaryRow[0] = (int)SummaryInformation.Transform.Security;
548
summaryRow[1] = "4";
549
}
@@ -606,5 +586,3 @@ namespace WixToolset.Core.WindowsInstaller
586
}
587
}
588
}
609
-
610
-#endif
src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs
+2
-2
@@ -2,6 +2,7 @@
2
3
namespace WixToolset.Core.WindowsInstaller
4
{
5
+ using System;
6
using WixToolset.Core.WindowsInstaller.Bind;
7
using WixToolset.Core.WindowsInstaller.Decompile;
8
using WixToolset.Core.WindowsInstaller.Unbind;
@@ -71,8 +72,7 @@ namespace WixToolset.Core.WindowsInstaller
72
73
public Intermediate Unbind(IUnbindContext context)
74
{
74
- var command = new UnbindMsiOrMsmCommand(context);
75
- return command.Execute();
75
+ throw new NotImplementedException();
76
}
77
}
78
}
src/wix/WixToolset.Core.WindowsInstaller/MsmBackend.cs
+2
-2
@@ -2,6 +2,7 @@
2
3
namespace WixToolset.Core.WindowsInstaller
4
{
5
+ using System;
6
using WixToolset.Core.WindowsInstaller.Bind;
7
using WixToolset.Core.WindowsInstaller.Decompile;
8
using WixToolset.Core.WindowsInstaller.Unbind;
@@ -67,8 +68,7 @@ namespace WixToolset.Core.WindowsInstaller
68
69
public Intermediate Unbind(IUnbindContext context)
70
{
70
- var command = new UnbindMsiOrMsmCommand(context);
71
- return command.Execute();
71
+ throw new NotImplementedException();
72
}
73
}
74
}
src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs
+2
-9
@@ -4,13 +4,7 @@ namespace WixToolset.Core.WindowsInstaller
4
{
5
using System;
6
using System.Collections.Generic;
7
- using System.IO;
8
- using System.Linq;
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;
8
using WixToolset.Data.WindowsInstaller;
9
using WixToolset.Extensibility;
10
using WixToolset.Extensibility.Data;
@@ -74,9 +68,9 @@ namespace WixToolset.Core.WindowsInstaller
68
throw new NotImplementedException();
69
}
70
71
+#if TODO_PATCHING
72
public Intermediate Unbind(IUnbindContext context)
73
{
79
-#if TODO_PATCHING
74
Output patch;
75
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
150
}
151
152
return patch;
159
-#endif
160
- throw new NotImplementedException();
153
}
154
+#endif
155
}
156
}
src/wix/WixToolset.Core.WindowsInstaller/MstBackend.cs
deleted
-39
@@ -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
-}
src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs
+45
-17
@@ -4,52 +4,80 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
4
{
5
using System;
6
using System.ComponentModel;
7
+ using WixToolset.Core.Native.Msi;
8
using WixToolset.Data;
9
+ using WixToolset.Data.WindowsInstaller;
10
using WixToolset.Extensibility.Data;
9
- using WixToolset.Core.Native.Msi;
11
+ using WixToolset.Extensibility.Services;
12
13
internal class UnbindMsiOrMsmCommand
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
+
27
public UnbindMsiOrMsmCommand(IUnbindContext context)
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;
35
}
36
18
- public IUnbindContext Context { get; }
37
+ private IMessaging Messaging { get; }
38
20
- public Intermediate Execute()
21
- {
22
-#if TODO_PATCHING
23
- Output output;
39
+ private IBackendHelper BackendHelper { get; }
40
+
41
+ private string DatabasePath { get; }
42
+
43
+ private string ExportBasePath { get; }
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
+ {
55
try
56
{
27
- using (Database database = new Database(this.Context.InputFilePath, OpenDatabase.ReadOnly))
57
+ using (var database = new Database(this.DatabasePath, OpenDatabase.ReadOnly))
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);
30
- output = unbindCommand.Execute();
59
+ var unbindCommand = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, database, this.DatabasePath, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, this.IsAdminImage, this.SuppressDemodularization, skipSummaryInfo: false);
60
+ var data = unbindCommand.Execute();
61
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)
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);
66
extractCommand.Execute();
67
}
68
+
69
+ return data;
70
}
71
}
72
catch (Win32Exception e)
73
{
74
if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED
75
{
44
- throw new WixException(WixErrors.OpenDatabaseFailed(this.Context.InputFilePath));
76
+ //throw new WixException(WixErrors.OpenDatabaseFailed(this.DatabasePath));
77
}
78
79
throw;
80
}
49
-
50
- return output;
51
-#endif
52
- throw new NotImplementedException();
81
}
82
}
83
}
src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs
renamed
+1
-1
@@ -41,7 +41,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
41
private TableDefinitionCollection TableDefinitions { get; }
42
43
private string EmptyFile { get; set; }
44
-
44
+
45
public WindowsInstallerData Execute()
46
{
47
var transform = new WindowsInstallerData(new SourceLineNumber(this.TransformFile));
src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendErrors.cs
+8
-5
@@ -2,14 +2,17 @@
2
3
namespace WixToolset.Core.WindowsInstaller
4
{
5
+ using System;
6
using WixToolset.Data;
7
8
internal static class WindowsInstallerBackendErrors
9
{
9
- //public static Message ReplaceThisWithTheFirstError(SourceLineNumber sourceLineNumbers)
10
- //{
11
- // return Message(sourceLineNumbers, Ids.ReplaceThisWithTheFirstError, "format string", arg1, arg2);
12
- //}
10
+ public static Message CannotLoadWixoutAsTransform(SourceLineNumber sourceLineNumbers, Exception exception)
11
+ {
12
+ var additionalDetail = exception == null ? String.Empty : ", detail: " + exception.Message;
13
+
14
+ return Message(sourceLineNumbers, Ids.CannotLoadWixoutAsTransform, "Could not load wixout file as a transform{1}", additionalDetail);
15
+ }
16
17
private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args)
18
{
@@ -18,7 +21,7 @@ namespace WixToolset.Core.WindowsInstaller
21
22
public enum Ids
23
{
21
- // ReplaceThisWithTheFirstError = 7500,
24
+ CannotLoadWixoutAsTransform = 7500,
25
} // last available is 7999. 8000 is BurnBackendErrors.
26
}
27
}
src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendFactory.cs
-5
@@ -37,11 +37,6 @@ namespace WixToolset.Core.WindowsInstaller
37
//case "patchcreation":
38
//case ".pcp":
39
// return new PatchCreationBackend();
40
-
41
- case "transform":
42
- case ".mst":
43
- backend = new MstBackend();
44
- return true;
40
}
41
42
backend = null;
src/wix/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.en-us.wxl
+2
-2
@@ -1,7 +1,7 @@
1
<?xml version="1.0" encoding="utf-8"?>
2
<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US">
3
4
- <String Id="DowngradeError">A newer version of [ProductName] is already installed.</String>
5
- <String Id="FeatureTitle">MsiPackage</String>
4
+ <String Id="DowngradeError">A newer version en-us of [ProductName] is already installed.</String>
5
+ <String Id="FeatureTitle">MsiPackage en-us</String>
6
7
</WixLocalization>
src/wix/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.ja-jp.wxl
+2
-2
@@ -1,7 +1,7 @@
1
<?xml version="1.0" encoding="utf-8"?>
2
<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="ja-JP">
3
4
- <String Id="DowngradeError">A newer version of [ProductName] is already installed.</String>
5
- <String Id="FeatureTitle">MsiPackage</String>
4
+ <String Id="DowngradeError">A newer version ja-jp of [ProductName] is already installed.</String>
5
+ <String Id="FeatureTitle">MsiPackage ja-jp</String>
6
7
</WixLocalization>
src/wix/test/WixToolsetTest.CoreIntegration/TransformFixture.cs
new
+143
@@ -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
+}