aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2017-10-14 16:12:07 -0700
committerRob Mensching <rob@firegiant.com>2017-10-14 16:12:07 -0700
commitdbde9e7104b907bbbaea17e21247d8cafc8b3a4c (patch)
tree0f5fbbb6fe12c6b2e5e622a0e18ce4c5b4eb2b96 /src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
parentfbf986eb97f68396797a89fc7d40dec07b775440 (diff)
downloadwix-dbde9e7104b907bbbaea17e21247d8cafc8b3a4c.tar.gz
wix-dbde9e7104b907bbbaea17e21247d8cafc8b3a4c.tar.bz2
wix-dbde9e7104b907bbbaea17e21247d8cafc8b3a4c.zip
Massive refactoring to introduce the concept of IBackend
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs')
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs351
1 files changed, 351 insertions, 0 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
new file mode 100644
index 00000000..624cbb43
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
@@ -0,0 +1,351 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Core.WindowsInstaller.Databases
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Collections.Specialized;
8 using System.ComponentModel;
9 using System.Diagnostics;
10 using System.Globalization;
11 using System.IO;
12 using System.Linq;
13 using System.Runtime.InteropServices;
14 using System.Text;
15 using System.Xml;
16 using System.Xml.XPath;
17 using WixToolset.Clr.Interop;
18 using WixToolset.Data;
19 using WixToolset.Data.Rows;
20 using WixToolset.MergeMod;
21 using WixToolset.Msi;
22 using WixToolset.Core.Native;
23 using WixToolset.Core.Bind;
24
25 /// <summary>
26 /// Update file information.
27 /// </summary>
28 internal class MergeModulesCommand
29 {
30 public IEnumerable<FileFacade> FileFacades { private get; set; }
31
32 public Output Output { private get; set; }
33
34 public string OutputPath { private get; set; }
35
36 public IEnumerable<string> SuppressedTableNames { private get; set; }
37
38 public string TempFilesLocation { private get; set; }
39
40 public void Execute()
41 {
42 Debug.Assert(OutputType.Product == this.Output.Type);
43
44 Table wixMergeTable = this.Output.Tables["WixMerge"];
45 Table wixFeatureModulesTable = this.Output.Tables["WixFeatureModules"];
46
47 // check for merge rows to see if there is any work to do
48 if (null == wixMergeTable || 0 == wixMergeTable.Rows.Count)
49 {
50 return;
51 }
52
53 IMsmMerge2 merge = null;
54 bool commit = true;
55 bool logOpen = false;
56 bool databaseOpen = false;
57 string logPath = null;
58 try
59 {
60 merge = MsmInterop.GetMsmMerge();
61
62 logPath = Path.Combine(this.TempFilesLocation, "merge.log");
63 merge.OpenLog(logPath);
64 logOpen = true;
65
66 merge.OpenDatabase(this.OutputPath);
67 databaseOpen = true;
68
69 // process all the merge rows
70 foreach (WixMergeRow wixMergeRow in wixMergeTable.Rows)
71 {
72 bool moduleOpen = false;
73
74 try
75 {
76 short mergeLanguage;
77
78 try
79 {
80 mergeLanguage = Convert.ToInt16(wixMergeRow.Language, CultureInfo.InvariantCulture);
81 }
82 catch (System.FormatException)
83 {
84 Messaging.Instance.OnMessage(WixErrors.InvalidMergeLanguage(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.Language));
85 continue;
86 }
87
88 Messaging.Instance.OnMessage(WixVerboses.OpeningMergeModule(wixMergeRow.SourceFile, mergeLanguage));
89 merge.OpenModule(wixMergeRow.SourceFile, mergeLanguage);
90 moduleOpen = true;
91
92 // If there is merge configuration data, create a callback object to contain it all.
93 ConfigurationCallback callback = null;
94 if (!String.IsNullOrEmpty(wixMergeRow.ConfigurationData))
95 {
96 callback = new ConfigurationCallback(wixMergeRow.ConfigurationData);
97 }
98
99 // merge the module into the database that's being built
100 Messaging.Instance.OnMessage(WixVerboses.MergingMergeModule(wixMergeRow.SourceFile));
101 merge.MergeEx(wixMergeRow.Feature, wixMergeRow.Directory, callback);
102
103 // connect any non-primary features
104 if (null != wixFeatureModulesTable)
105 {
106 foreach (Row row in wixFeatureModulesTable.Rows)
107 {
108 if (wixMergeRow.Id == (string)row[1])
109 {
110 Messaging.Instance.OnMessage(WixVerboses.ConnectingMergeModule(wixMergeRow.SourceFile, (string)row[0]));
111 merge.Connect((string)row[0]);
112 }
113 }
114 }
115 }
116 catch (COMException)
117 {
118 commit = false;
119 }
120 finally
121 {
122 IMsmErrors mergeErrors = merge.Errors;
123
124 // display all the errors encountered during the merge operations for this module
125 for (int i = 1; i <= mergeErrors.Count; i++)
126 {
127 IMsmError mergeError = mergeErrors[i];
128 StringBuilder databaseKeys = new StringBuilder();
129 StringBuilder moduleKeys = new StringBuilder();
130
131 // build a string of the database keys
132 for (int j = 1; j <= mergeError.DatabaseKeys.Count; j++)
133 {
134 if (1 != j)
135 {
136 databaseKeys.Append(';');
137 }
138 databaseKeys.Append(mergeError.DatabaseKeys[j]);
139 }
140
141 // build a string of the module keys
142 for (int j = 1; j <= mergeError.ModuleKeys.Count; j++)
143 {
144 if (1 != j)
145 {
146 moduleKeys.Append(';');
147 }
148 moduleKeys.Append(mergeError.ModuleKeys[j]);
149 }
150
151 // display the merge error based on the msm error type
152 switch (mergeError.Type)
153 {
154 case MsmErrorType.msmErrorExclusion:
155 Messaging.Instance.OnMessage(WixErrors.MergeExcludedModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, moduleKeys.ToString()));
156 break;
157 case MsmErrorType.msmErrorFeatureRequired:
158 Messaging.Instance.OnMessage(WixErrors.MergeFeatureRequired(wixMergeRow.SourceLineNumbers, mergeError.ModuleTable, moduleKeys.ToString(), wixMergeRow.SourceFile, wixMergeRow.Id));
159 break;
160 case MsmErrorType.msmErrorLanguageFailed:
161 Messaging.Instance.OnMessage(WixErrors.MergeLanguageFailed(wixMergeRow.SourceLineNumbers, mergeError.Language, wixMergeRow.SourceFile));
162 break;
163 case MsmErrorType.msmErrorLanguageUnsupported:
164 Messaging.Instance.OnMessage(WixErrors.MergeLanguageUnsupported(wixMergeRow.SourceLineNumbers, mergeError.Language, wixMergeRow.SourceFile));
165 break;
166 case MsmErrorType.msmErrorResequenceMerge:
167 Messaging.Instance.OnMessage(WixWarnings.MergeRescheduledAction(wixMergeRow.SourceLineNumbers, mergeError.DatabaseTable, databaseKeys.ToString(), wixMergeRow.SourceFile));
168 break;
169 case MsmErrorType.msmErrorTableMerge:
170 if ("_Validation" != mergeError.DatabaseTable) // ignore merge errors in the _Validation table
171 {
172 Messaging.Instance.OnMessage(WixWarnings.MergeTableFailed(wixMergeRow.SourceLineNumbers, mergeError.DatabaseTable, databaseKeys.ToString(), wixMergeRow.SourceFile));
173 }
174 break;
175 case MsmErrorType.msmErrorPlatformMismatch:
176 Messaging.Instance.OnMessage(WixErrors.MergePlatformMismatch(wixMergeRow.SourceLineNumbers, wixMergeRow.SourceFile));
177 break;
178 default:
179 Messaging.Instance.OnMessage(WixErrors.UnexpectedException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_UnexpectedMergerErrorWithType, Enum.GetName(typeof(MsmErrorType), mergeError.Type), logPath), "InvalidOperationException", Environment.StackTrace));
180 break;
181 }
182 }
183
184 if (0 >= mergeErrors.Count && !commit)
185 {
186 Messaging.Instance.OnMessage(WixErrors.UnexpectedException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_UnexpectedMergerErrorInSourceFile, wixMergeRow.SourceFile, logPath), "InvalidOperationException", Environment.StackTrace));
187 }
188
189 if (moduleOpen)
190 {
191 merge.CloseModule();
192 }
193 }
194 }
195 }
196 finally
197 {
198 if (databaseOpen)
199 {
200 merge.CloseDatabase(commit);
201 }
202
203 if (logOpen)
204 {
205 merge.CloseLog();
206 }
207 }
208
209 // stop processing if an error previously occurred
210 if (Messaging.Instance.EncounteredError)
211 {
212 return;
213 }
214
215 using (Database db = new Database(this.OutputPath, OpenDatabase.Direct))
216 {
217 Table suppressActionTable = this.Output.Tables["WixSuppressAction"];
218
219 // suppress individual actions
220 if (null != suppressActionTable)
221 {
222 foreach (Row row in suppressActionTable.Rows)
223 {
224 if (db.TableExists((string)row[0]))
225 {
226 string query = String.Format(CultureInfo.InvariantCulture, "SELECT * FROM {0} WHERE `Action` = '{1}'", row[0].ToString(), (string)row[1]);
227
228 using (View view = db.OpenExecuteView(query))
229 {
230 using (Record record = view.Fetch())
231 {
232 if (null != record)
233 {
234 Messaging.Instance.OnMessage(WixWarnings.SuppressMergedAction((string)row[1], row[0].ToString()));
235 view.Modify(ModifyView.Delete, record);
236 }
237 }
238 }
239 }
240 }
241 }
242
243 // query for merge module actions in suppressed sequences and drop them
244 foreach (string tableName in this.SuppressedTableNames)
245 {
246 if (!db.TableExists(tableName))
247 {
248 continue;
249 }
250
251 using (View view = db.OpenExecuteView(String.Concat("SELECT `Action` FROM ", tableName)))
252 {
253 while (true)
254 {
255 using (Record resultRecord = view.Fetch())
256 {
257 if (null == resultRecord)
258 {
259 break;
260 }
261
262 Messaging.Instance.OnMessage(WixWarnings.SuppressMergedAction(resultRecord.GetString(1), tableName));
263 }
264 }
265 }
266
267 // drop suppressed sequences
268 using (View view = db.OpenExecuteView(String.Concat("DROP TABLE ", tableName)))
269 {
270 }
271
272 // delete the validation rows
273 using (View view = db.OpenView(String.Concat("DELETE FROM _Validation WHERE `Table` = ?")))
274 {
275 using (Record record = new Record(1))
276 {
277 record.SetString(1, tableName);
278 view.Execute(record);
279 }
280 }
281 }
282
283 // now update the Attributes column for the files from the Merge Modules
284 Messaging.Instance.OnMessage(WixVerboses.ResequencingMergeModuleFiles());
285 using (View view = db.OpenView("SELECT `Sequence`, `Attributes` FROM `File` WHERE `File`=?"))
286 {
287 foreach (FileFacade file in this.FileFacades)
288 {
289 if (!file.FromModule)
290 {
291 continue;
292 }
293
294 using (Record record = new Record(1))
295 {
296 record.SetString(1, file.File.File);
297 view.Execute(record);
298 }
299
300 using (Record recordUpdate = view.Fetch())
301 {
302 if (null == recordUpdate)
303 {
304 throw new InvalidOperationException("Failed to fetch a File row from the database that was merged in from a module.");
305 }
306
307 recordUpdate.SetInteger(1, file.File.Sequence);
308
309 // update the file attributes to match the compression specified
310 // on the Merge element or on the Package element
311 int attributes = 0;
312
313 // get the current value if its not null
314 if (!recordUpdate.IsNull(2))
315 {
316 attributes = recordUpdate.GetInteger(2);
317 }
318
319 if (YesNoType.Yes == file.File.Compressed)
320 {
321 // these are mutually exclusive
322 attributes |= MsiInterop.MsidbFileAttributesCompressed;
323 attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed;
324 }
325 else if (YesNoType.No == file.File.Compressed)
326 {
327 // these are mutually exclusive
328 attributes |= MsiInterop.MsidbFileAttributesNoncompressed;
329 attributes &= ~MsiInterop.MsidbFileAttributesCompressed;
330 }
331 else // not specified
332 {
333 Debug.Assert(YesNoType.NotSet == file.File.Compressed);
334
335 // clear any compression bits
336 attributes &= ~MsiInterop.MsidbFileAttributesCompressed;
337 attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed;
338 }
339
340 recordUpdate.SetInteger(2, attributes);
341
342 view.Modify(ModifyView.Update, recordUpdate);
343 }
344 }
345 }
346
347 db.Commit();
348 }
349 }
350 }
351}