@joebigelow / wix / commits / 0d4df529

Use validation now implemented in Core.Native

Fixes wixtoolset/issues#5946

Rob Mensching committed Mar 16, 2021 at 10:52 UTC 0d4df529a7e34f033112a1c6f16f749880334e7d
6 files changed +227 -401
src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
+24 -26
@@ -21,11 +21,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
21 // As outlined in RFC 4122, this is our namespace for generating name-based (version 3) UUIDs.
22 internal static readonly Guid WixComponentGuidNamespace = new Guid("{3064E5C6-FB63-4FE9-AC49-E446A792EFA5}");
23
24 - public BindDatabaseCommand(IBindContext context, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtension, Validator validator) : this(context, backendExtension, null, validator)
24 + public BindDatabaseCommand(IBindContext context, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtension, string cubeFile) : this(context, backendExtension, null, cubeFile)
25 {
26 }
27
28 - public BindDatabaseCommand(IBindContext context, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtension, IEnumerable<SubStorage> subStorages, Validator validator)
28 + public BindDatabaseCommand(IBindContext context, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtension, IEnumerable<SubStorage> subStorages, string cubeFile)
29 {
30 this.ServiceProvider = context.ServiceProvider;
31
@@ -50,7 +50,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
50 this.SuppressLayout = context.SuppressLayout;
51
52 this.SubStorages = subStorages;
53 - this.Validator = validator;
53 +
54 + this.SuppressValidation = context.SuppressValidation;
55 + this.Ices = context.Ices;
56 + this.SuppressedIces = context.SuppressIces;
57 + this.CubeFiles = String.IsNullOrEmpty(cubeFile) ? null : new[] { cubeFile };
58
59 this.BackendExtensions = backendExtension;
60 }
@@ -97,7 +101,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind
101
102 private string IntermediateFolder { get; }
103
100 - private Validator Validator { get; }
104 + private bool SuppressValidation { get; }
105 +
106 + private IEnumerable<string> Ices { get; }
107 +
108 + private IEnumerable<string> SuppressedIces { get; }
109 +
110 + private IEnumerable<string> CubeFiles { get; }
111
112 public IBindResult Execute()
113 {
@@ -522,34 +532,22 @@ namespace WixToolset.Core.WindowsInstaller.Bind
532 if (this.Messaging.EncounteredError)
533 {
534 return null;
525 - }
535 + }
536
527 -#if TODO_FINISH_VALIDATION
528 - // Validate the output if there is an MSI validator.
529 - if (null != this.Validator)
537 + // Validate the output if there are CUBe files and we're not explicitly suppressing validation.
538 + if (this.CubeFiles != null && !this.SuppressValidation)
539 {
531 - Stopwatch stopwatch = Stopwatch.StartNew();
532 -
533 - // set the output file for source line information
534 - this.Validator.Output = this.Output;
535 -
536 - Messaging.Instance.Write(WixVerboses.ValidatingDatabase());
537 -
538 - this.Validator.Validate(this.OutputPath);
539 -
540 - stopwatch.Stop();
541 - Messaging.Instance.Write(WixVerboses.ValidatedDatabase(stopwatch.ElapsedMilliseconds));
540 + var command = new ValidateDatabaseCommand(this.Messaging, this.IntermediateFolder, data, this.OutputPath, this.CubeFiles, this.Ices, this.SuppressedIces);
541 + command.Execute();
542 + }
543
543 - // Stop processing if an error occurred.
544 - if (Messaging.Instance.EncounteredError)
545 - {
546 - return;
547 - }
544 + if (this.Messaging.EncounteredError)
545 + {
546 + return null;
547 }
549 -#endif
548
549 // Process uncompressed files.
552 - if (!this.Messaging.EncounteredError && !this.SuppressLayout && uncompressedFiles.Any())
550 + if (!this.SuppressLayout && uncompressedFiles.Any())
551 {
552 var command = new ProcessUncompressedFilesCommand(section, this.WindowsInstallerBackendHelper, this.PathResolver);
553 command.Compressed = compressed;
src/WixToolset.Core.WindowsInstaller/Bind/ValidateDatabaseCommand.cs new
+177
@@ -0,0 +1,177 @@
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.Bind
4 +{
5 + using System;
6 + using System.Collections.Generic;
7 + using System.ComponentModel;
8 + using System.Diagnostics;
9 + using System.IO;
10 + using System.Linq;
11 + using System.Threading;
12 + using WixToolset.Core.Native;
13 + using WixToolset.Core.Native.Msi;
14 + using WixToolset.Data;
15 + using WixToolset.Data.WindowsInstaller;
16 + using WixToolset.Extensibility.Services;
17 +
18 + internal class ValidateDatabaseCommand : IWindowsInstallerValidatorCallback
19 + {
20 + // Set of ICEs that have equivalent-or-better checks in WiX.
21 + private static readonly string[] WellKnownSuppressedIces = new[] { "ICE08", "ICE33", "ICE47", "ICE66" };
22 +
23 + public ValidateDatabaseCommand(IMessaging messaging, string intermediateFolder, WindowsInstallerData data, string outputPath, IEnumerable<string> cubeFiles, IEnumerable<string> ices, IEnumerable<string> suppressedIces)
24 + {
25 + this.Messaging = messaging;
26 + this.Data = data;
27 + this.OutputPath = outputPath;
28 + this.CubeFiles = cubeFiles;
29 + this.Ices = ices;
30 + this.SuppressedIces = suppressedIces == null ? WellKnownSuppressedIces : suppressedIces.Union(WellKnownSuppressedIces);
31 +
32 + this.IntermediateFolder = intermediateFolder;
33 + this.OutputSourceLineNumber = new SourceLineNumber(outputPath);
34 + }
35 +
36 + /// <summary>
37 + /// Encountered error implementation for <see cref="IWindowsInstallerValidatorCallback"/>.
38 + /// </summary>
39 + public bool EncounteredError => this.Messaging.EncounteredError;
40 +
41 + private IMessaging Messaging { get; }
42 +
43 + private WindowsInstallerData Data { get; }
44 +
45 + private string OutputPath { get; }
46 +
47 + private IEnumerable<string> CubeFiles { get; }
48 +
49 + private IEnumerable<string> Ices { get; }
50 +
51 + private IEnumerable<string> SuppressedIces { get; }
52 +
53 + private string IntermediateFolder { get; }
54 +
55 + /// <summary>
56 + /// Fallback when an exact source line number cannot be calculated for a validation error.
57 + /// </summary>
58 + private SourceLineNumber OutputSourceLineNumber { get; set; }
59 +
60 + private Dictionary<string, SourceLineNumber> SourceLineNumbersByTablePrimaryKey { get; set; }
61 +
62 + public void Execute()
63 + {
64 + var stopwatch = Stopwatch.StartNew();
65 +
66 + this.Messaging.Write(VerboseMessages.ValidatingDatabase());
67 +
68 + // Ensure the temporary files can be created the working folder.
69 + var workingFolder = Path.Combine(this.IntermediateFolder, "_validate");
70 + Directory.CreateDirectory(workingFolder);
71 +
72 + // Copy the database to a temporary location so it can be manipulated.
73 + // Ensure it is not read-only.
74 + var workingDatabasePath = Path.Combine(workingFolder, Path.GetFileName(this.OutputPath));
75 + FileSystem.CopyFile(this.OutputPath, workingDatabasePath, allowHardlink: false);
76 +
77 + var attributes = File.GetAttributes(workingDatabasePath);
78 + File.SetAttributes(workingDatabasePath, attributes & ~FileAttributes.ReadOnly);
79 +
80 + var validator = new WindowsInstallerValidator(this, workingDatabasePath, this.CubeFiles, this.Ices, this.SuppressedIces);
81 + validator.Execute();
82 +
83 + stopwatch.Stop();
84 + this.Messaging.Write(VerboseMessages.ValidatedDatabase(stopwatch.ElapsedMilliseconds));
85 + }
86 +
87 + private void LogValidationMessage(ValidationMessage message)
88 + {
89 + var messageSourceLineNumbers = this.OutputSourceLineNumber;
90 + if (!String.IsNullOrEmpty(message.Table) && !String.IsNullOrEmpty(message.Column) && message.PrimaryKeys != null)
91 + {
92 + messageSourceLineNumbers = this.GetSourceLineNumbers(message.Table, message.PrimaryKeys);
93 + }
94 +
95 + switch (message.Type)
96 + {
97 + case ValidationMessageType.InternalFailure:
98 + case ValidationMessageType.Error:
99 + this.Messaging.Write(ErrorMessages.ValidationError(messageSourceLineNumbers, message.IceName, message.Description));
100 + break;
101 + case ValidationMessageType.Warning:
102 + this.Messaging.Write(WarningMessages.ValidationWarning(messageSourceLineNumbers, message.IceName, message.Description));
103 + break;
104 + case ValidationMessageType.Info:
105 + this.Messaging.Write(VerboseMessages.ValidationInfo(message.IceName, message.Description));
106 + break;
107 + default:
108 + throw new WixException(ErrorMessages.InvalidValidatorMessageType(message.Type.ToString()));
109 + }
110 + }
111 +
112 + /// <summary>
113 + /// Validation blocked by other installation operation for <see cref="IWindowsInstallerValidatorCallback"/>.
114 + /// </summary>
115 + public void ValidationBlocked()
116 + {
117 + this.Messaging.Write(VerboseMessages.ValidationSerialized());
118 + }
119 +
120 + /// <summary>
121 + /// Validation message implementation for <see cref="IWindowsInstallerValidatorCallback"/>.
122 + /// </summary>
123 + public bool ValidationMessage(ValidationMessage message)
124 + {
125 + this.LogValidationMessage(message);
126 + return true;
127 + }
128 +
129 + /// <summary>
130 + /// Gets the source line information (if available) for a row by its table name and primary key.
131 + /// </summary>
132 + /// <param name="tableName">The table name of the row.</param>
133 + /// <param name="primaryKeys">The primary keys of the row.</param>
134 + /// <returns>The source line number information if found; null otherwise.</returns>
135 + private SourceLineNumber GetSourceLineNumbers(string tableName, IEnumerable<string> primaryKeys)
136 + {
137 + // Source line information only exists if an output file was supplied
138 + if (this.Data == null)
139 + {
140 + // Use the file name as the source line information.
141 + return this.OutputSourceLineNumber;
142 + }
143 +
144 + // Index the source line information if it hasn't been indexed already.
145 + if (this.SourceLineNumbersByTablePrimaryKey == null)
146 + {
147 + this.SourceLineNumbersByTablePrimaryKey = new Dictionary<string, SourceLineNumber>();
148 +
149 + // Index each real table
150 + foreach (var table in this.Data.Tables.Where(t => !t.Definition.Unreal))
151 + {
152 + // Index each row that contain source line information
153 + foreach (var row in table.Rows.Where(r => r.SourceLineNumbers != null))
154 + {
155 + // Index the row using its table name and primary key
156 + var primaryKey = row.GetPrimaryKey(';');
157 +
158 + if (!String.IsNullOrEmpty(primaryKey))
159 + {
160 + try
161 + {
162 + var key = String.Concat(table.Name, ":", primaryKey);
163 + this.SourceLineNumbersByTablePrimaryKey.Add(key, row.SourceLineNumbers);
164 + }
165 + catch (ArgumentException)
166 + {
167 + this.Messaging.Write(WarningMessages.DuplicatePrimaryKey(row.SourceLineNumbers, primaryKey, table.Name));
168 + }
169 + }
170 + }
171 + }
172 + }
173 +
174 + return this.SourceLineNumbersByTablePrimaryKey.TryGetValue(String.Concat(tableName, ":", String.Join(";", primaryKeys)), out var sourceLineNumbers) ? sourceLineNumbers : null;
175 + }
176 + }
177 +}
src/WixToolset.Core.WindowsInstaller/MsiBackend.cs
+1 -3
@@ -24,13 +24,11 @@ namespace WixToolset.Core.WindowsInstaller
24 extension.PreBackendBind(context);
25 }
26
27 - var validator = Validator.CreateFromContext(context, "darice.cub");
28 -
27 IBindResult result = null;
28 var dispose = true;
29 try
30 {
33 - var command = new BindDatabaseCommand(context, backendExtensions, validator);
31 + var command = new BindDatabaseCommand(context, backendExtensions, "darice.cub");
32 result = command.Execute();
33
34 foreach (var extension in backendExtensions)
src/WixToolset.Core.WindowsInstaller/MsmBackend.cs
+1 -3
@@ -23,12 +23,10 @@ namespace WixToolset.Core.WindowsInstaller
23 extension.PreBackendBind(context);
24 }
25
26 - var validator = Validator.CreateFromContext(context, "mergemod.cub");
27 -
26 IBindResult result = null;
27 try
28 {
31 - var command = new BindDatabaseCommand(context, backendExtensions, validator);
29 + var command = new BindDatabaseCommand(context, backendExtensions, "mergemod.cub");
30 result = command.Execute();
31
32 foreach (var extension in backendExtensions)
src/WixToolset.Core.WindowsInstaller/Validator.cs deleted
-365
@@ -1,365 +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 System.Collections.Generic;
7 - using System.Collections.Specialized;
8 - using System.ComponentModel;
9 - using System.Diagnostics.CodeAnalysis;
10 - using System.Globalization;
11 - using System.IO;
12 - using System.Linq;
13 - using System.Reflection;
14 - using System.Threading;
15 - using WixToolset.Core.WindowsInstaller.Msi;
16 - using WixToolset.Data;
17 - using WixToolset.Data.WindowsInstaller;
18 - using WixToolset.Extensibility;
19 - using WixToolset.Extensibility.Data;
20 - using WixToolset.Extensibility.Services;
21 -
22 - /// <summary>
23 - /// Runs internal consistency evaluators (ICEs) from cub files against a database.
24 - /// </summary>
25 - internal sealed class Validator
26 - {
27 - private string actionName;
28 - private readonly StringCollection cubeFiles;
29 - private ValidatorExtension extension;
30 - private WindowsInstallerData output;
31 - private readonly InstallUIHandler validationUIHandler;
32 - private bool validationSessionComplete;
33 - private readonly IMessaging messaging;
34 -
35 - /// <summary>
36 - /// Instantiate a new Validator.
37 - /// </summary>
38 - public Validator(IMessaging messaging)
39 - {
40 - this.cubeFiles = new StringCollection();
41 - this.extension = new ValidatorExtension(messaging);
42 - this.validationUIHandler = new InstallUIHandler(this.ValidationUIHandler);
43 - this.messaging = messaging;
44 - }
45 -
46 - /// <summary>
47 - /// Gets or sets a <see cref="ValidatorExtension"/> that directs messages from the validator.
48 - /// </summary>
49 - /// <value>A <see cref="ValidatorExtension"/> that directs messages from the validator.</value>
50 - public ValidatorExtension Extension
51 - {
52 - get { return this.extension; }
53 - set { this.extension = value; }
54 - }
55 -
56 - /// <summary>
57 - /// Gets or sets the list of ICEs to run.
58 - /// </summary>
59 - /// <value>The list of ICEs.</value>
60 - public ISet<string> ICEs { get; set; }
61 -
62 - /// <summary>
63 - /// Gets or sets the output used for finding source line information.
64 - /// </summary>
65 - /// <value>The output used for finding source line information.</value>
66 - public WindowsInstallerData Output
67 - {
68 - // cache Output object until validation for changes in extension
69 - get { return this.output; }
70 - set { this.output = value; }
71 - }
72 -
73 - /// <summary>
74 - /// Gets or sets the suppressed ICEs.
75 - /// </summary>
76 - /// <value>The suppressed ICEs.</value>
77 - public ISet<string> SuppressedICEs { get; set; }
78 -
79 - /// <summary>
80 - /// Sets the temporary path for the Binder.
81 - /// </summary>
82 - public string IntermediateFolder { private get; set; }
83 -
84 - /// <summary>
85 - /// Add a cube file to the validation run.
86 - /// </summary>
87 - /// <param name="cubeFile">A cube file.</param>
88 - public void AddCubeFile(string cubeFile)
89 - {
90 - this.cubeFiles.Add(cubeFile);
91 - }
92 -
93 - /// <summary>
94 - /// Validate a database.
95 - /// </summary>
96 - /// <param name="databaseFile">The database to validate.</param>
97 - /// <returns>true if validation succeeded; false otherwise.</returns>
98 - public void Validate(string databaseFile)
99 - {
100 - int previousUILevel = (int)InstallUILevels.Basic;
101 - IntPtr previousHwnd = IntPtr.Zero;
102 - InstallUIHandler previousUIHandler = null;
103 -
104 - // initialize the validator extension
105 - this.extension.DatabaseFile = databaseFile ?? throw new ArgumentNullException(nameof(databaseFile));
106 - this.extension.Output = this.output;
107 - this.extension.InitializeValidator();
108 -
109 - // Ensure the temporary files can be created.
110 - Directory.CreateDirectory(this.IntermediateFolder);
111 -
112 - // copy the database to a temporary location so it can be manipulated
113 - string tempDatabaseFile = Path.Combine(this.IntermediateFolder, Path.GetFileName(databaseFile));
114 - File.Copy(databaseFile, tempDatabaseFile);
115 -
116 - // remove the read-only property from the temporary database
117 - FileAttributes attributes = File.GetAttributes(tempDatabaseFile);
118 - File.SetAttributes(tempDatabaseFile, attributes & ~FileAttributes.ReadOnly);
119 -
120 - Mutex mutex = new Mutex(false, "WixValidator");
121 - try
122 - {
123 - if (!mutex.WaitOne(0, false))
124 - {
125 - this.messaging.Write(VerboseMessages.ValidationSerialized());
126 - mutex.WaitOne();
127 - }
128 -
129 - using (Database database = new Database(tempDatabaseFile, OpenDatabase.Direct))
130 - {
131 - bool propertyTableExists = database.TableExists("Property");
132 - string productCode = null;
133 -
134 - // remove the product code from the database before opening a session to prevent opening an installed product
135 - if (propertyTableExists)
136 - {
137 - using (View view = database.OpenExecuteView("SELECT `Value` FROM `Property` WHERE Property = 'ProductCode'"))
138 - {
139 - using (Record record = view.Fetch())
140 - {
141 - if (null != record)
142 - {
143 - productCode = record.GetString(1);
144 -
145 - using (View dropProductCodeView = database.OpenExecuteView("DELETE FROM `Property` WHERE `Property` = 'ProductCode'"))
146 - {
147 - }
148 - }
149 - }
150 - }
151 - }
152 -
153 - // merge in the cube databases
154 - foreach (string cubeFile in this.cubeFiles)
155 - {
156 - try
157 - {
158 - using (Database cubeDatabase = new Database(cubeFile, OpenDatabase.ReadOnly))
159 - {
160 - try
161 - {
162 - database.Merge(cubeDatabase, "MergeConflicts");
163 - }
164 - catch
165 - {
166 - // ignore merge errors since they are expected in the _Validation table
167 - }
168 - }
169 - }
170 - catch (Win32Exception e)
171 - {
172 - if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED
173 - {
174 - throw new WixException(ErrorMessages.CubeFileNotFound(cubeFile));
175 - }
176 -
177 - throw;
178 - }
179 - }
180 -
181 - // commit the database before proceeding to ensure the streams don't get confused
182 - database.Commit();
183 -
184 - // the property table may have been added to the database
185 - // from a cub database without the proper validation rows
186 - if (!propertyTableExists)
187 - {
188 - using (View view = database.OpenExecuteView("DROP table `Property`"))
189 - {
190 - }
191 - }
192 -
193 - // get all the action names for ICEs which have not been suppressed
194 - List<string> actions = new List<string>();
195 - using (View view = database.OpenExecuteView("SELECT `Action` FROM `_ICESequence` ORDER BY `Sequence`"))
196 - {
197 - foreach (Record record in view.Records)
198 - {
199 - string action = record.GetString(1);
200 -
201 - if ((this.SuppressedICEs == null || !this.SuppressedICEs.Contains(action)) && (this.ICEs == null || this.ICEs.Contains(action)))
202 - {
203 - actions.Add(action);
204 - }
205 - }
206 - }
207 -
208 - // disable the internal UI handler and set an external UI handler
209 - previousUILevel = Installer.SetInternalUI((int)InstallUILevels.None, ref previousHwnd);
210 - previousUIHandler = Installer.SetExternalUI(this.validationUIHandler, (int)InstallLogModes.Error | (int)InstallLogModes.Warning | (int)InstallLogModes.User, IntPtr.Zero);
211 -
212 - // create a session for running the ICEs
213 - this.validationSessionComplete = false;
214 - using (Session session = new Session(database))
215 - {
216 - // add the product code back into the database
217 - if (null != productCode)
218 - {
219 - // some CUBs erroneously have a ProductCode property, so delete it if we just picked one up
220 - using (View dropProductCodeView = database.OpenExecuteView("DELETE FROM `Property` WHERE `Property` = 'ProductCode'"))
221 - {
222 - }
223 -
224 - using (View view = database.OpenExecuteView(String.Format(CultureInfo.InvariantCulture, "INSERT INTO `Property` (`Property`, `Value`) VALUES ('ProductCode', '{0}')", productCode)))
225 - {
226 - }
227 - }
228 -
229 - foreach (string action in actions)
230 - {
231 - this.actionName = action;
232 - try
233 - {
234 - session.DoAction(action);
235 - }
236 - catch (Win32Exception e)
237 - {
238 - if (!this.messaging.EncounteredError)
239 - {
240 - throw e;
241 - }
242 - // TODO: Review why this was clearing the error state when an exception had happened but an error was already encountered. That's weird.
243 - //else
244 - //{
245 - // this.encounteredError = false;
246 - //}
247 - }
248 - this.actionName = null;
249 - }
250 -
251 - // Mark the validation session complete so we ignore any messages that MSI may fire
252 - // during session clean-up.
253 - this.validationSessionComplete = true;
254 - }
255 - }
256 - }
257 - catch (Win32Exception e)
258 - {
259 - // avoid displaying errors twice since one may have already occurred in the UI handler
260 - if (!this.messaging.EncounteredError)
261 - {
262 - if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED
263 - {
264 - // databaseFile is not passed since during light
265 - // this would be the temporary copy and there would be
266 - // no final output since the error occured; during smoke
267 - // they should know the path passed into smoke
268 - this.messaging.Write(ErrorMessages.ValidationFailedToOpenDatabase());
269 - }
270 - else if (0x64D == e.NativeErrorCode)
271 - {
272 - this.messaging.Write(ErrorMessages.ValidationFailedDueToLowMsiEngine());
273 - }
274 - else if (0x654 == e.NativeErrorCode)
275 - {
276 - this.messaging.Write(ErrorMessages.ValidationFailedDueToInvalidPackage());
277 - }
278 - else if (0x658 == e.NativeErrorCode)
279 - {
280 - this.messaging.Write(ErrorMessages.ValidationFailedDueToMultilanguageMergeModule());
281 - }
282 - else if (0x659 == e.NativeErrorCode)
283 - {
284 - this.messaging.Write(WarningMessages.ValidationFailedDueToSystemPolicy());
285 - }
286 - else
287 - {
288 - string msgTemp = e.Message;
289 -
290 - if (null != this.actionName)
291 - {
292 - msgTemp = String.Concat("Action - '", this.actionName, "' ", e.Message);
293 - }
294 -
295 - this.messaging.Write(ErrorMessages.Win32Exception(e.NativeErrorCode, msgTemp));
296 - }
297 - }
298 - }
299 - finally
300 - {
301 - Installer.SetExternalUI(previousUIHandler, 0, IntPtr.Zero);
302 - Installer.SetInternalUI(previousUILevel, ref previousHwnd);
303 -
304 - this.validationSessionComplete = false; // no validation session at this point, so reset the completion flag.
305 -
306 - mutex.ReleaseMutex();
307 - this.cubeFiles.Clear();
308 - this.extension.FinalizeValidator();
309 - }
310 - }
311 -
312 - public static Validator CreateFromContext(IBindContext context, string cubeFilename)
313 - {
314 - Validator validator = null;
315 - var messaging = context.ServiceProvider.GetService<IMessaging>();
316 -
317 - // Tell the binder about the validator if validation isn't suppressed
318 - if (!context.SuppressValidation)
319 - {
320 - validator = new Validator(messaging);
321 - validator.IntermediateFolder = Path.Combine(context.IntermediateFolder, "validate");
322 -
323 - // set the default cube file
324 - string thisPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
325 - validator.AddCubeFile(Path.Combine(thisPath, cubeFilename));
326 -
327 - // Set the ICEs
328 - validator.ICEs = new SortedSet<string>(context.Ices);
329 -
330 - // Set the suppressed ICEs and disable ICEs that have equivalent-or-better checks in WiX.
331 - validator.SuppressedICEs = new SortedSet<string>(context.SuppressIces.Union(new[] { "ICE08", "ICE33", "ICE47", "ICE66" }));
332 - }
333 -
334 - return validator;
335 - }
336 -
337 - /// <summary>
338 - /// The validation external UI handler.
339 - /// </summary>
340 - /// <param name="context">Pointer to an application context.
341 - /// This parameter can be used for error checking.</param>
342 - /// <param name="messageType">Specifies a combination of one message box style,
343 - /// one message box icon type, one default button, and one installation message type.</param>
344 - /// <param name="message">Specifies the message text.</param>
345 - /// <returns>-1 for an error, 0 if no action was taken, 1 if OK, 3 to abort.</returns>
346 - private int ValidationUIHandler(IntPtr context, uint messageType, string message)
347 - {
348 - try
349 - {
350 - // If we're getting messges during the validation session, send them to
351 - // the extension. Otherwise, ignore the messages.
352 - if (!this.validationSessionComplete)
353 - {
354 - this.extension.Log(message, this.actionName);
355 - }
356 - }
357 - catch (WixException ex)
358 - {
359 - this.messaging.Write(ex.Error);
360 - }
361 -
362 - return 1;
363 - }
364 - }
365 -}
src/WixToolset.Core/CommandLine/BuildCommand.cs
+24 -4
@@ -344,14 +344,14 @@ namespace WixToolset.Core.CommandLine
344 context.ExpectedEmbeddedFiles = resolveResult.ExpectedEmbeddedFiles;
345 context.Extensions = this.ExtensionManager.GetServices<IBinderExtension>();
346 context.FileSystemExtensions = this.ExtensionManager.GetServices<IFileSystemExtension>();
347 - context.Ices = Array.Empty<string>(); // TODO: set this correctly
347 + context.Ices = this.commandLine.Ices;
348 context.IntermediateFolder = intermediateFolder;
349 context.IntermediateRepresentation = resolveResult.IntermediateRepresentation;
350 context.OutputPath = this.OutputFile;
351 context.PdbType = this.PdbType;
352 context.PdbPath = this.PdbType == PdbType.None ? null : this.PdbFile ?? Path.ChangeExtension(this.OutputFile, ".wixpdb");
353 - context.SuppressIces = Array.Empty<string>(); // TODO: set this correctly
354 - context.SuppressValidation = true; // TODO: set this correctly
353 + context.SuppressIces = this.commandLine.SuppressIces;
354 + context.SuppressValidation = this.commandLine.SuppressValidation;
355 context.CancellationToken = cancellationToken;
356
357 var binder = this.ServiceProvider.GetService<IBinder>();
@@ -541,6 +541,12 @@ namespace WixToolset.Core.CommandLine
541
542 public string BuiltOutputsFile { get; private set; }
543
544 + public List<string> Ices { get; } = new List<string>();
545 +
546 + public List<string> SuppressIces { get; } = new List<string>();
547 +
548 + public bool SuppressValidation { get; set; }
549 +
550 public CommandLine(IServiceProvider serviceProvider, IMessaging messaging)
551 {
552 this.ServiceProvider = serviceProvider;
@@ -634,6 +640,13 @@ namespace WixToolset.Core.CommandLine
640 parser.GetNextArgumentOrError(arg, this.IncludeSearchPaths);
641 return true;
642
643 + case "ice":
644 + {
645 + var value = parser.GetNextArgumentOrError(arg);
646 + this.Ices.Add(value);
647 + return true;
648 + }
649 +
650 case "intermediatefolder":
651 this.IntermediateFolder = parser.GetNextArgumentAsDirectoryOrError(arg);
652 return true;
@@ -670,6 +683,13 @@ namespace WixToolset.Core.CommandLine
683 return false;
684 }
685
686 + case "sice":
687 + {
688 + var value = parser.GetNextArgumentOrError(arg);
689 + this.SuppressIces.Add(value);
690 + return true;
691 + }
692 +
693 case "nologo":
694 this.ShowLogo = false;
695 return true;
@@ -680,7 +700,7 @@ namespace WixToolset.Core.CommandLine
700 return true;
701
702 case "sval":
683 - // todo: implement
703 + this.SuppressValidation = true;
704 return true;
705 }
706