Implement support for IExtensionCommandLine and IPreprocessorExtension
Rob Mensching committed
Dec 2, 2017 at 00:46 UTC
95f2f4425b900374c7d7b583ae810b096121b3c4
15 files changed
+842
-937
src/WixToolset.Core/CommandLine/BuildCommand.cs
+10
-5
@@ -40,6 +40,8 @@ namespace WixToolset.Core
40
41
public IExtensionManager ExtensionManager { get; }
42
43
+ public IEnumerable<string> IncludeSearchPaths { get; }
44
+
45
public IEnumerable<string> LocFiles { get; }
46
47
public IEnumerable<string> LibraryFiles { get; }
@@ -102,15 +104,18 @@ namespace WixToolset.Core
104
{
105
var intermediates = new List<Intermediate>();
106
105
-
107
foreach (var sourceFile in this.SourceFiles)
108
{
108
- //var preprocessContext = this.ServiceProvider.GetService<IPreprocessContext>();
109
- //preprocessContext.SourcePath = sourceFile.SourcePath;
110
- //preprocessContext.Variables = this.PreprocessorVariables;
109
+ var preprocessContext = this.ServiceProvider.GetService<IPreprocessContext>();
110
+ preprocessContext.Messaging = Messaging.Instance;
111
+ preprocessContext.Extensions = this.ExtensionManager.Create<IPreprocessorExtension>();
112
+ preprocessContext.Platform = Platform.X86; // TODO: set this correctly
113
+ preprocessContext.IncludeSearchPaths = this.IncludeSearchPaths?.ToList() ?? new List<string>();
114
+ preprocessContext.SourceFile = sourceFile.SourcePath;
115
+ preprocessContext.Variables = new Dictionary<string, string>(this.PreprocessorVariables);
116
117
var preprocessor = new Preprocessor();
113
- var document = preprocessor.Process(sourceFile.SourcePath, this.PreprocessorVariables);
118
+ var document = preprocessor.Process(preprocessContext);
119
120
var compileContext = this.ServiceProvider.GetService<ICompileContext>();
121
compileContext.Messaging = Messaging.Instance;
src/WixToolset.Core/CommandLine/CommandLine.cs
+17
-12
@@ -22,7 +22,7 @@ namespace WixToolset.Core
22
Bind,
23
}
24
25
- internal class CommandLine : ICommandLine
25
+ internal class CommandLine : ICommandLine, IParseCommandLine
26
{
27
public CommandLine()
28
{
@@ -57,10 +57,10 @@ namespace WixToolset.Core
57
args = CommandLine.ParseArgumentsToArray(context.Arguments).Union(args).ToArray();
58
}
59
60
- return this.ParseStandardCommandLine(args);
60
+ return this.ParseStandardCommandLine(context, args);
61
}
62
63
- private ICommandLineCommand ParseStandardCommandLine(string[] args)
63
+ private ICommandLineCommand ParseStandardCommandLine(ICommandLineContext context, string[] args)
64
{
65
var next = String.Empty;
66
@@ -90,7 +90,7 @@ namespace WixToolset.Core
90
var builtOutputsFile = String.Empty;
91
var wixProjectFile = String.Empty;
92
93
- this.Parse(args, (cmdline, arg) => Enum.TryParse(arg, true, out command), (cmdline, arg) =>
93
+ this.Parse(context, args, (cmdline, arg) => Enum.TryParse(arg, true, out command), (cmdline, arg) =>
94
{
95
if (cmdline.IsSwitch(arg))
96
{
@@ -279,13 +279,13 @@ namespace WixToolset.Core
279
}
280
#endif
281
282
- private ICommandLine Parse(string[] commandLineArguments, Func<CommandLine, string, bool> parseCommand, Func<CommandLine, string, bool> parseArgument)
282
+ private ICommandLine Parse(ICommandLineContext context, string[] commandLineArguments, Func<CommandLine, string, bool> parseCommand, Func<CommandLine, string, bool> parseArgument)
283
{
284
this.FlattenArgumentsWithResponseFilesIntoOriginalArguments(commandLineArguments);
285
286
this.QueueArgumentsAndLoadExtensions(this.OriginalArguments);
287
288
- this.ProcessRemainingArguments(parseArgument, parseCommand);
288
+ this.ProcessRemainingArguments(context, parseArgument, parseCommand);
289
290
return this;
291
}
@@ -413,7 +413,7 @@ namespace WixToolset.Core
413
/// <returns>True if a valid switch exists there, false if not.</returns>
414
public bool IsSwitch(string arg)
415
{
416
- return arg != null && ('/' == arg[0] || '-' == arg[0]);
416
+ return arg != null && arg.Length > 1 && ('/' == arg[0] || '-' == arg[0]);
417
}
418
419
/// <summary>
@@ -522,10 +522,15 @@ namespace WixToolset.Core
522
}
523
}
524
525
- private void ProcessRemainingArguments(Func<CommandLine, string, bool> parseArgument, Func<CommandLine, string, bool> parseCommand)
525
+ private void ProcessRemainingArguments(ICommandLineContext context, Func<CommandLine, string, bool> parseArgument, Func<CommandLine, string, bool> parseCommand)
526
{
527
var extensions = this.ExtensionManager.Create<IExtensionCommandLine>();
528
529
+ foreach (var extension in extensions)
530
+ {
531
+ extension.PreParse(context);
532
+ }
533
+
534
while (!this.ShowHelp &&
535
String.IsNullOrEmpty(this.ErrorArgument) &&
536
TryDequeue(this.RemainingArguments, out var arg))
@@ -566,10 +571,10 @@ namespace WixToolset.Core
571
{
572
foreach (var extension in extensions)
573
{
569
- //if (extension.ParseArgument(this, arg))
570
- //{
571
- // return true;
572
- //}
574
+ if (extension.TryParseArgument(this, arg))
575
+ {
576
+ return true;
577
+ }
578
}
579
580
return false;
src/WixToolset.Core/CommandLine/CompileCommand.cs
+12
-1
@@ -4,6 +4,7 @@ namespace WixToolset.Core
4
{
5
using System;
6
using System.Collections.Generic;
7
+ using System.Linq;
8
using WixToolset.Data;
9
using WixToolset.Extensibility;
10
using WixToolset.Extensibility.Services;
@@ -22,6 +23,8 @@ namespace WixToolset.Core
23
24
private IExtensionManager ExtensionManager { get; }
25
26
+ public IEnumerable<string> IncludeSearchPaths { get; }
27
+
28
private IEnumerable<SourceFile> SourceFiles { get; }
29
30
private IDictionary<string, string> PreprocessorVariables { get; }
@@ -30,8 +33,16 @@ namespace WixToolset.Core
33
{
34
foreach (var sourceFile in this.SourceFiles)
35
{
36
+ var preprocessContext = this.ServiceProvider.GetService<IPreprocessContext>();
37
+ preprocessContext.Messaging = Messaging.Instance;
38
+ preprocessContext.Extensions = this.ExtensionManager.Create<IPreprocessorExtension>();
39
+ preprocessContext.Platform = Platform.X86; // TODO: set this correctly
40
+ preprocessContext.IncludeSearchPaths = this.IncludeSearchPaths?.ToList() ?? new List<string>();
41
+ preprocessContext.SourceFile = sourceFile.SourcePath;
42
+ preprocessContext.Variables = new Dictionary<string, string>(this.PreprocessorVariables);
43
+
44
var preprocessor = new Preprocessor();
34
- var document = preprocessor.Process(sourceFile.SourcePath, this.PreprocessorVariables);
45
+ var document = preprocessor.Process(preprocessContext);
46
47
var compileContext = this.ServiceProvider.GetService<ICompileContext>();
48
compileContext.Messaging = Messaging.Instance;
src/WixToolset.Core/Compiler.cs
+4
-3
@@ -10825,9 +10825,10 @@ namespace WixToolset.Core
10825
switch (installScopeType)
10826
{
10827
case Wix.Package.InstallScopeType.perMachine:
10828
- row = this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.Property);
10829
- row.Set(0, "ALLUSERS");
10830
- row.Set(1, "1");
10828
+ {
10829
+ row = this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.Property, new Identifier("ALLUSERS", AccessModifier.Public));
10830
+ row.Set(1, "1");
10831
+ }
10832
break;
10833
case Wix.Package.InstallScopeType.perUser:
10834
sourceBits = sourceBits | 8;
src/WixToolset.Core/ExtensibilityServices/PreprocessHelper.cs
new
+479
@@ -0,0 +1,479 @@
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.ExtensibilityServices
4
+{
5
+ using System;
6
+ using System.Collections.Generic;
7
+ using System.IO;
8
+ using System.Text;
9
+ using System.Xml.Linq;
10
+ using WixToolset.Data;
11
+ using WixToolset.Extensibility;
12
+ using WixToolset.Extensibility.Services;
13
+
14
+ internal class PreprocessHelper : IPreprocessHelper
15
+ {
16
+ private static readonly char[] VariableSplitter = new char[] { '.' };
17
+ private static readonly char[] ArgumentSplitter = new char[] { ',' };
18
+
19
+ public PreprocessHelper(IServiceProvider serviceProvider)
20
+ {
21
+ this.ServiceProvider = serviceProvider;
22
+ }
23
+
24
+ private IServiceProvider ServiceProvider { get; }
25
+
26
+ private Dictionary<string, IPreprocessorExtension> ExtensionsByPrefix { get; set; }
27
+
28
+ public void AddVariable(IPreprocessContext context, string name, string value)
29
+ {
30
+ this.AddVariable(context, name, value, true);
31
+ }
32
+
33
+ public void AddVariable(IPreprocessContext context, string name, string value, bool showWarning)
34
+ {
35
+ var currentValue = this.GetVariableValue(context, "var", name);
36
+
37
+ if (null == currentValue)
38
+ {
39
+ context.Variables.Add(name, value);
40
+ }
41
+ else
42
+ {
43
+ if (showWarning)
44
+ {
45
+ context.Messaging.OnMessage(WixWarnings.VariableDeclarationCollision(context.CurrentSourceLineNumber, name, value, currentValue));
46
+ }
47
+
48
+ context.Variables[name] = value;
49
+ }
50
+ }
51
+
52
+ public string EvaluateFunction(IPreprocessContext context, string function)
53
+ {
54
+ var prefixParts = function.Split(VariableSplitter, 2);
55
+
56
+ // Check to make sure there are 2 parts and neither is an empty string.
57
+ if (2 != prefixParts.Length || 0 >= prefixParts[0].Length || 0 >= prefixParts[1].Length)
58
+ {
59
+ throw new WixException(WixErrors.InvalidPreprocessorFunction(context.CurrentSourceLineNumber, function));
60
+ }
61
+
62
+ var prefix = prefixParts[0];
63
+ var functionParts = prefixParts[1].Split(new char[] { '(' }, 2);
64
+
65
+ // Check to make sure there are 2 parts, neither is an empty string, and the second part ends with a closing paren.
66
+ if (2 != functionParts.Length || 0 >= functionParts[0].Length || 0 >= functionParts[1].Length || !functionParts[1].EndsWith(")", StringComparison.Ordinal))
67
+ {
68
+ throw new WixException(WixErrors.InvalidPreprocessorFunction(context.CurrentSourceLineNumber, function));
69
+ }
70
+
71
+ var functionName = functionParts[0];
72
+
73
+ // Remove the trailing closing paren.
74
+ var allArgs = functionParts[1].Substring(0, functionParts[1].Length - 1);
75
+
76
+ // Parse the arguments and preprocess them.
77
+ var args = allArgs.Split(ArgumentSplitter);
78
+ for (var i = 0; i < args.Length; i++)
79
+ {
80
+ args[i] = this.PreprocessString(context, args[i].Trim());
81
+ }
82
+
83
+ var result = this.EvaluateFunction(context, prefix, functionName, args);
84
+
85
+ // If the function didn't evaluate, try to evaluate the original value as a variable to support
86
+ // the use of open and closed parens inside variable names. Example: $(env.ProgramFiles(x86)) should resolve.
87
+ if (result == null)
88
+ {
89
+ result = this.GetVariableValue(context, function, false);
90
+ }
91
+
92
+ return result;
93
+ }
94
+
95
+ public string EvaluateFunction(IPreprocessContext context, string prefix, string function, string[] args)
96
+ {
97
+ if (String.IsNullOrEmpty(prefix))
98
+ {
99
+ throw new ArgumentNullException("prefix");
100
+ }
101
+
102
+ if (String.IsNullOrEmpty(function))
103
+ {
104
+ throw new ArgumentNullException("function");
105
+ }
106
+
107
+ switch (prefix)
108
+ {
109
+ case "fun":
110
+ switch (function)
111
+ {
112
+ case "AutoVersion":
113
+ // Make sure the base version is specified
114
+ if (args.Length == 0 || String.IsNullOrEmpty(args[0]))
115
+ {
116
+ throw new WixException(WixErrors.InvalidPreprocessorFunctionAutoVersion(context.CurrentSourceLineNumber));
117
+ }
118
+
119
+ // Build = days since 1/1/2000; Revision = seconds since midnight / 2
120
+ var now = DateTime.UtcNow;
121
+ var build = now - new DateTime(2000, 1, 1);
122
+ var revision = now - new DateTime(now.Year, now.Month, now.Day);
123
+
124
+ return String.Join(".", args[0], (int)build.TotalDays, (int)(revision.TotalSeconds / 2));
125
+
126
+ default:
127
+ return null;
128
+ }
129
+
130
+ default:
131
+ var extensionsByPrefix = this.GetExtensionsByPrefix(context);
132
+ if (extensionsByPrefix.TryGetValue(prefix, out var extension))
133
+ {
134
+ try
135
+ {
136
+ return extension.EvaluateFunction(prefix, function, args);
137
+ }
138
+ catch (Exception e)
139
+ {
140
+ throw new WixException(WixErrors.PreprocessorExtensionEvaluateFunctionFailed(context.CurrentSourceLineNumber, prefix, function, String.Join(",", args), e.Message));
141
+ }
142
+ }
143
+ else
144
+ {
145
+ return null;
146
+ }
147
+ }
148
+ }
149
+
150
+ public string GetVariableValue(IPreprocessContext context, string variable, bool allowMissingPrefix)
151
+ {
152
+ // Strip the "$(" off the front.
153
+ if (variable.StartsWith("$(", StringComparison.Ordinal))
154
+ {
155
+ variable = variable.Substring(2);
156
+ }
157
+
158
+ var parts = variable.Split(VariableSplitter, 2);
159
+
160
+ if (1 == parts.Length) // missing prefix
161
+ {
162
+ if (allowMissingPrefix)
163
+ {
164
+ return this.GetVariableValue(context, "var", parts[0]);
165
+ }
166
+ else
167
+ {
168
+ throw new WixException(WixErrors.InvalidPreprocessorVariable(context.CurrentSourceLineNumber, variable));
169
+ }
170
+ }
171
+ else
172
+ {
173
+ // check for empty variable name
174
+ if (0 < parts[1].Length)
175
+ {
176
+ string result = this.GetVariableValue(context, parts[0], parts[1]);
177
+
178
+ // If we didn't find it and we allow missing prefixes and the variable contains a dot, perhaps the dot isn't intended to indicate a prefix
179
+ if (null == result && allowMissingPrefix && variable.Contains("."))
180
+ {
181
+ result = this.GetVariableValue(context, "var", variable);
182
+ }
183
+
184
+ return result;
185
+ }
186
+ else
187
+ {
188
+ throw new WixException(WixErrors.InvalidPreprocessorVariable(context.CurrentSourceLineNumber, variable));
189
+ }
190
+ }
191
+ }
192
+
193
+ public string GetVariableValue(IPreprocessContext context, string prefix, string name)
194
+ {
195
+ if (String.IsNullOrEmpty(prefix))
196
+ {
197
+ throw new ArgumentNullException("prefix");
198
+ }
199
+
200
+ if (String.IsNullOrEmpty(name))
201
+ {
202
+ throw new ArgumentNullException("name");
203
+ }
204
+
205
+ switch (prefix)
206
+ {
207
+ case "env":
208
+ return Environment.GetEnvironmentVariable(name);
209
+
210
+ case "sys":
211
+ switch (name)
212
+ {
213
+ case "CURRENTDIR":
214
+ return String.Concat(Directory.GetCurrentDirectory(), Path.DirectorySeparatorChar);
215
+
216
+ case "SOURCEFILEDIR":
217
+ return String.Concat(Path.GetDirectoryName(context.CurrentSourceLineNumber.FileName), Path.DirectorySeparatorChar);
218
+
219
+ case "SOURCEFILEPATH":
220
+ return context.CurrentSourceLineNumber.FileName;
221
+
222
+ case "PLATFORM":
223
+ context.Messaging.OnMessage(WixWarnings.DeprecatedPreProcVariable(context.CurrentSourceLineNumber, "$(sys.PLATFORM)", "$(sys.BUILDARCH)"));
224
+
225
+ goto case "BUILDARCH";
226
+
227
+ case "BUILDARCH":
228
+ switch (context.Platform)
229
+ {
230
+ case Platform.X86:
231
+ return "x86";
232
+
233
+ case Platform.X64:
234
+ return "x64";
235
+
236
+ case Platform.IA64:
237
+ return "ia64";
238
+
239
+ case Platform.ARM:
240
+ return "arm";
241
+
242
+ default:
243
+ throw new ArgumentException(WixStrings.EXP_UnknownPlatformEnum, context.Platform.ToString());
244
+ }
245
+
246
+ default:
247
+ return null;
248
+ }
249
+
250
+ case "var":
251
+ return context.Variables.TryGetValue(name, out var result) ? result : null;
252
+
253
+ default:
254
+ var extensionsByPrefix = this.GetExtensionsByPrefix(context);
255
+ if (extensionsByPrefix.TryGetValue(prefix, out var extension))
256
+ {
257
+ try
258
+ {
259
+ return extension.GetVariableValue(prefix, name);
260
+ }
261
+ catch (Exception e)
262
+ {
263
+ throw new WixException(WixErrors.PreprocessorExtensionGetVariableValueFailed(context.CurrentSourceLineNumber, prefix, name, e.Message));
264
+ }
265
+ }
266
+ else
267
+ {
268
+ return null;
269
+ }
270
+ }
271
+ }
272
+
273
+ public void PreprocessPragma(IPreprocessContext context, string pragmaName, string args, XContainer parent)
274
+ {
275
+ var prefixParts = pragmaName.Split(VariableSplitter, 2);
276
+
277
+ // Check to make sure there are 2 parts and neither is an empty string.
278
+ if (2 != prefixParts.Length)
279
+ {
280
+ throw new WixException(WixErrors.InvalidPreprocessorPragma(context.CurrentSourceLineNumber, pragmaName));
281
+ }
282
+
283
+ var prefix = prefixParts[0];
284
+ var pragma = prefixParts[1];
285
+
286
+ if (String.IsNullOrEmpty(prefix) || String.IsNullOrEmpty(pragma))
287
+ {
288
+ throw new WixException(WixErrors.InvalidPreprocessorPragma(context.CurrentSourceLineNumber, pragmaName));
289
+ }
290
+
291
+ switch (prefix)
292
+ {
293
+ case "wix":
294
+ switch (pragma)
295
+ {
296
+ // Add any core defined pragmas here
297
+ default:
298
+ context.Messaging.OnMessage(WixWarnings.PreprocessorUnknownPragma(context.CurrentSourceLineNumber, pragmaName));
299
+ break;
300
+ }
301
+ break;
302
+
303
+ default:
304
+ var extensionsByPrefix = this.GetExtensionsByPrefix(context);
305
+ if (extensionsByPrefix.TryGetValue(prefix, out var extension))
306
+ {
307
+ if (!extension.ProcessPragma(prefix, pragma, args, parent))
308
+ {
309
+ context.Messaging.OnMessage(WixWarnings.PreprocessorUnknownPragma(context.CurrentSourceLineNumber, pragmaName));
310
+ }
311
+ }
312
+ break;
313
+ }
314
+ }
315
+
316
+ public string PreprocessString(IPreprocessContext context, string value)
317
+ {
318
+ var sb = new StringBuilder();
319
+ var currentPosition = 0;
320
+ var end = 0;
321
+
322
+ while (-1 != (currentPosition = value.IndexOf('$', end)))
323
+ {
324
+ if (end < currentPosition)
325
+ {
326
+ sb.Append(value, end, currentPosition - end);
327
+ }
328
+
329
+ end = currentPosition + 1;
330
+
331
+ var remainder = value.Substring(end);
332
+ if (remainder.StartsWith("$", StringComparison.Ordinal))
333
+ {
334
+ sb.Append("$");
335
+ end++;
336
+ }
337
+ else if (remainder.StartsWith("(loc.", StringComparison.Ordinal))
338
+ {
339
+ currentPosition = remainder.IndexOf(')');
340
+ if (-1 == currentPosition)
341
+ {
342
+ context.Messaging.OnMessage(WixErrors.InvalidPreprocessorVariable(context.CurrentSourceLineNumber, remainder));
343
+ break;
344
+ }
345
+
346
+ sb.Append("$"); // just put the resource reference back as was
347
+ sb.Append(remainder, 0, currentPosition + 1);
348
+
349
+ end += currentPosition + 1;
350
+ }
351
+ else if (remainder.StartsWith("(", StringComparison.Ordinal))
352
+ {
353
+ var openParenCount = 1;
354
+ var closingParenCount = 0;
355
+ var isFunction = false;
356
+ var foundClosingParen = false;
357
+
358
+ // find the closing paren
359
+ int closingParenPosition;
360
+ for (closingParenPosition = 1; closingParenPosition < remainder.Length; closingParenPosition++)
361
+ {
362
+ switch (remainder[closingParenPosition])
363
+ {
364
+ case '(':
365
+ openParenCount++;
366
+ isFunction = true;
367
+ break;
368
+
369
+ case ')':
370
+ closingParenCount++;
371
+ break;
372
+ }
373
+
374
+ if (openParenCount == closingParenCount)
375
+ {
376
+ foundClosingParen = true;
377
+ break;
378
+ }
379
+ }
380
+
381
+ // move the currentPosition to the closing paren
382
+ currentPosition += closingParenPosition;
383
+
384
+ if (!foundClosingParen)
385
+ {
386
+ if (isFunction)
387
+ {
388
+ context.Messaging.OnMessage(WixErrors.InvalidPreprocessorFunction(context.CurrentSourceLineNumber, remainder));
389
+ break;
390
+ }
391
+ else
392
+ {
393
+ context.Messaging.OnMessage(WixErrors.InvalidPreprocessorVariable(context.CurrentSourceLineNumber, remainder));
394
+ break;
395
+ }
396
+ }
397
+
398
+ var subString = remainder.Substring(1, closingParenPosition - 1);
399
+ string result = null;
400
+ if (isFunction)
401
+ {
402
+ result = this.EvaluateFunction(context, subString);
403
+ }
404
+ else
405
+ {
406
+ result = this.GetVariableValue(context, subString, false);
407
+ }
408
+
409
+ if (null == result)
410
+ {
411
+ if (isFunction)
412
+ {
413
+ context.Messaging.OnMessage(WixErrors.UndefinedPreprocessorFunction(context.CurrentSourceLineNumber, subString));
414
+ break;
415
+ }
416
+ else
417
+ {
418
+ context.Messaging.OnMessage(WixErrors.UndefinedPreprocessorVariable(context.CurrentSourceLineNumber, subString));
419
+ break;
420
+ }
421
+ }
422
+ else
423
+ {
424
+ if (!isFunction)
425
+ {
426
+ //this.OnResolvedVariable(new ResolvedVariableEventArgs(context.CurrentSourceLineNumber, subString, result));
427
+ }
428
+ }
429
+
430
+ sb.Append(result);
431
+ end += closingParenPosition + 1;
432
+ }
433
+ else // just a floating "$" so put it in the final string (i.e. leave it alone) and keep processing
434
+ {
435
+ sb.Append('$');
436
+ }
437
+ }
438
+
439
+ if (end < value.Length)
440
+ {
441
+ sb.Append(value.Substring(end));
442
+ }
443
+
444
+ return sb.ToString();
445
+ }
446
+
447
+ public void RemoveVariable(IPreprocessContext context, string name)
448
+ {
449
+ if (!context.Variables.Remove(name))
450
+ {
451
+ context.Messaging.OnMessage(WixErrors.CannotReundefineVariable(context.CurrentSourceLineNumber, name));
452
+ }
453
+ }
454
+
455
+ private Dictionary<string, IPreprocessorExtension> GetExtensionsByPrefix(IPreprocessContext context)
456
+ {
457
+ if (this.ExtensionsByPrefix == null)
458
+ {
459
+ this.ExtensionsByPrefix = new Dictionary<string, IPreprocessorExtension>();
460
+
461
+ foreach (var extension in context.Extensions)
462
+ {
463
+ if (null != extension.Prefixes)
464
+ {
465
+ foreach (string prefix in extension.Prefixes)
466
+ {
467
+ if (!this.ExtensionsByPrefix.ContainsKey(prefix))
468
+ {
469
+ this.ExtensionsByPrefix.Add(prefix, extension);
470
+ }
471
+ }
472
+ }
473
+ }
474
+ }
475
+
476
+ return this.ExtensionsByPrefix;
477
+ }
478
+ }
479
+}
src/WixToolset.Core/Preprocess/PreprocessorOperation.cs
new
+19
@@ -0,0 +1,19 @@
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.Preprocess
4
+{
5
+ /// <summary>
6
+ /// Enumeration for preprocessor operations in if statements.
7
+ /// </summary>
8
+ internal enum PreprocessorOperation
9
+ {
10
+ /// <summary>The and operator.</summary>
11
+ And,
12
+
13
+ /// <summary>The or operator.</summary>
14
+ Or,
15
+
16
+ /// <summary>The not operator.</summary>
17
+ Not
18
+ }
19
+}
src/WixToolset.Core/PreprocessContext.cs
new
+36
@@ -0,0 +1,36 @@
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
4
+{
5
+ using System;
6
+ using System.Collections.Generic;
7
+ using WixToolset.Data;
8
+ using WixToolset.Extensibility;
9
+
10
+ /// <summary>
11
+ /// The preprocessor core.
12
+ /// </summary>
13
+ internal class PreprocessContext : IPreprocessContext
14
+ {
15
+ internal PreprocessContext(IServiceProvider serviceProvider)
16
+ {
17
+ this.ServiceProvider = serviceProvider;
18
+ }
19
+
20
+ public IServiceProvider ServiceProvider { get; }
21
+
22
+ public Messaging Messaging { get; set; }
23
+
24
+ public IEnumerable<IPreprocessorExtension> Extensions { get; set; }
25
+
26
+ public Platform Platform { get; set; }
27
+
28
+ public IList<string> IncludeSearchPaths { get; set; }
29
+
30
+ public string SourceFile { get; set; }
31
+
32
+ public IDictionary<string, string> Variables { get; set; }
33
+
34
+ public SourceLineNumber CurrentSourceLineNumber { get; set; }
35
+ }
36
+}
src/WixToolset.Core/Preprocessor.cs
+154
-299
@@ -4,7 +4,6 @@ namespace WixToolset.Core
4
{
5
using System;
6
using System.Collections.Generic;
7
- using System.Diagnostics.CodeAnalysis;
7
using System.Globalization;
8
using System.IO;
9
using System.Text;
@@ -14,6 +13,7 @@ namespace WixToolset.Core
13
using WixToolset.Data;
14
using WixToolset.Extensibility;
15
using WixToolset.Core.Preprocess;
16
+ using WixToolset.Extensibility.Services;
17
18
/// <summary>
19
/// Preprocessor object
@@ -30,42 +30,22 @@ namespace WixToolset.Core
30
};
31
private readonly XmlReaderSettings FragmentXmlReaderSettings = new XmlReaderSettings()
32
{
33
- ConformanceLevel = System.Xml.ConformanceLevel.Fragment,
33
+ ConformanceLevel = ConformanceLevel.Fragment,
34
ValidationFlags = System.Xml.Schema.XmlSchemaValidationFlags.None,
35
XmlResolver = null,
36
};
37
38
- private List<IPreprocessorExtension> extensions;
39
- private Dictionary<string, IPreprocessorExtension> extensionsByPrefix;
38
+ private IPreprocessContext Context { get; set; }
39
41
- private SourceLineNumber currentLineNumber;
42
- private Stack<SourceLineNumber> sourceStack;
40
+ private Stack<string> CurrentFileStack { get; } = new Stack<string>();
41
44
- private PreprocessorCore core;
45
- private TextWriter preprocessOut;
42
+ private Dictionary<string, IPreprocessorExtension> ExtensionsByPrefix { get; } = new Dictionary<string, IPreprocessorExtension>();
43
47
- private Stack<bool> includeNextStack;
48
- private Stack<string> currentFileStack;
44
+ private Stack<bool> IncludeNextStack { get; } = new Stack<bool>();
45
50
- private Platform currentPlatform;
46
+ private Stack<SourceLineNumber> SourceStack { get; } = new Stack<SourceLineNumber>();
47
52
- /// <summary>
53
- /// Creates a new preprocesor.
54
- /// </summary>
55
- public Preprocessor()
56
- {
57
- this.IncludeSearchPaths = new List<string>();
58
-
59
- this.extensions = new List<IPreprocessorExtension>();
60
- this.extensionsByPrefix = new Dictionary<string, IPreprocessorExtension>();
61
-
62
- this.sourceStack = new Stack<SourceLineNumber>();
63
-
64
- this.includeNextStack = new Stack<bool>();
65
- this.currentFileStack = new Stack<string>();
66
-
67
- this.currentPlatform = Platform.X86;
68
- }
48
+ private IPreprocessHelper Helper { get; set; }
49
50
/// <summary>
51
/// Event for ifdef/ifndef directives.
@@ -87,62 +67,6 @@ namespace WixToolset.Core
67
/// </summary>
68
public event ResolvedVariableEventHandler ResolvedVariable;
69
90
- /// <summary>
91
- /// Enumeration for preprocessor operations in if statements.
92
- /// </summary>
93
- private enum PreprocessorOperation
94
- {
95
- /// <summary>The and operator.</summary>
96
- And,
97
-
98
- /// <summary>The or operator.</summary>
99
- Or,
100
-
101
- /// <summary>The not operator.</summary>
102
- Not
103
- }
104
-
105
- /// <summary>
106
- /// Gets or sets the platform which the compiler will use when defaulting 64-bit attributes and elements.
107
- /// </summary>
108
- /// <value>The platform which the compiler will use when defaulting 64-bit attributes and elements.</value>
109
- public Platform CurrentPlatform
110
- {
111
- get { return this.currentPlatform; }
112
- set { this.currentPlatform = value; }
113
- }
114
-
115
- /// <summary>
116
- /// Ordered list of search paths that the precompiler uses to find included files.
117
- /// </summary>
118
- /// <value>List of ordered search paths to use during precompiling.</value>
119
- public IList<string> IncludeSearchPaths { get; private set; }
120
-
121
- /// <summary>
122
- /// Specifies the text stream to display the postprocessed data to.
123
- /// </summary>
124
- /// <value>TextWriter to write preprocessed xml to.</value>
125
- public TextWriter PreprocessOut
126
- {
127
- get { return this.preprocessOut; }
128
- set { this.preprocessOut = value; }
129
- }
130
-
131
- /// <summary>
132
- /// Get the source line information for the current element. The precompiler will insert
133
- /// special source line number processing instructions before each element that it
134
- /// encounters. This is where those line numbers are read and processed. This function
135
- /// may return an array of source line numbers because the element may have come from
136
- /// an included file, in which case the chain of imports is expressed in the array.
137
- /// </summary>
138
- /// <param name="node">Element to get source line information for.</param>
139
- /// <returns>Returns the stack of imports used to author the element being processed.</returns>
140
- [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")]
141
- public static SourceLineNumber GetSourceLineNumbers(XmlNode node)
142
- {
143
- return null;
144
- }
145
-
70
/// <summary>
71
/// Get the source line information for the current element. The precompiler will insert
72
/// special source line number information for each element that it encounters.
@@ -159,122 +83,94 @@ namespace WixToolset.Core
83
}
84
85
/// <summary>
162
- /// Adds an extension.
86
+ /// Preprocesses a file.
87
/// </summary>
164
- /// <param name="extension">The extension to add.</param>
165
- public void AddExtension(IPreprocessorExtension extension)
88
+ /// <param name="context">The preprocessing context.</param>
89
+ /// <returns>XDocument with the postprocessed data.</returns>
90
+ public XDocument Process(IPreprocessContext context)
91
{
167
- this.extensions.Add(extension);
92
+ this.Context = context ?? throw new ArgumentNullException(nameof(context));
93
169
- if (null != extension.Prefixes)
94
+ using (XmlReader reader = XmlReader.Create(context.SourceFile, DocumentXmlReaderSettings))
95
{
171
- foreach (string prefix in extension.Prefixes)
172
- {
173
- IPreprocessorExtension collidingExtension;
174
- if (!this.extensionsByPrefix.TryGetValue(prefix, out collidingExtension))
175
- {
176
- this.extensionsByPrefix.Add(prefix, extension);
177
- }
178
- else
179
- {
180
- Messaging.Instance.OnMessage(WixErrors.DuplicateExtensionPreprocessorType(extension.GetType().ToString(), prefix, collidingExtension.GetType().ToString()));
181
- }
182
- }
96
+ return Process(context, reader);
97
}
184
-
185
- //if (null != extension.InspectorExtension)
186
- //{
187
- // this.inspectorExtensions.Add(extension.InspectorExtension);
188
- //}
98
}
99
100
/// <summary>
101
/// Preprocesses a file.
102
/// </summary>
194
- /// <param name="sourceFile">The file to preprocess.</param>
195
- /// <param name="variables">The variables defined prior to preprocessing.</param>
103
+ /// <param name="context">The preprocessing context.</param>
104
+ /// <param name="reader">XmlReader to processing the context.</param>
105
/// <returns>XDocument with the postprocessed data.</returns>
197
- [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")]
198
- public XDocument Process(string sourceFile, IDictionary<string, string> variables)
106
+ public XDocument Process(IPreprocessContext context, XmlReader reader)
107
{
200
- using (Stream sourceStream = new FileStream(sourceFile, FileMode.Open, FileAccess.Read, FileShare.Read))
201
- using (XmlReader reader = XmlReader.Create(sourceFile, DocumentXmlReaderSettings))
108
+ if (this.Context == null)
109
{
203
- return Process(reader, variables, sourceFile);
110
+ this.Context = context ?? throw new ArgumentNullException(nameof(context));
111
+ }
112
+ else if (this.Context != context)
113
+ {
114
+ throw new ArgumentException(nameof(context));
115
}
205
- }
116
207
- /// <summary>
208
- /// Preprocesses a file.
209
- /// </summary>
210
- /// <param name="sourceFile">The file to preprocess.</param>
211
- /// <param name="variables">The variables defined prior to preprocessing.</param>
212
- /// <returns>XDocument with the postprocessed data.</returns>
213
- [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")]
214
- public XDocument Process(XmlReader reader, IDictionary<string, string> variables, string sourceFile = null)
215
- {
216
- if (String.IsNullOrEmpty(sourceFile) && !String.IsNullOrEmpty(reader.BaseURI))
117
+ if (String.IsNullOrEmpty(this.Context.SourceFile) && !String.IsNullOrEmpty(reader.BaseURI))
118
{
218
- Uri uri = new Uri(reader.BaseURI);
219
- sourceFile = uri.AbsolutePath;
119
+ var uri = new Uri(reader.BaseURI);
120
+ this.Context.SourceFile = uri.AbsolutePath;
121
}
122
222
- this.core = new PreprocessorCore(this.extensionsByPrefix, sourceFile, variables);
223
- this.core.ResolvedVariableHandler = this.ResolvedVariable;
224
- this.core.CurrentPlatform = this.currentPlatform;
225
- this.currentLineNumber = new SourceLineNumber(sourceFile);
226
- this.currentFileStack.Clear();
227
- this.currentFileStack.Push(this.core.GetVariableValue(this.currentLineNumber, "sys", "SOURCEFILEDIR"));
123
+ this.Context.CurrentSourceLineNumber = new SourceLineNumber(this.Context.SourceFile);
124
229
- // Process the reader into the output.
230
- XDocument output = new XDocument();
231
- try
125
+ this.Helper = this.Context.ServiceProvider.GetService<IPreprocessHelper>();
126
+
127
+ foreach (var extension in this.Context.Extensions)
128
{
233
- foreach (PreprocessorExtension extension in this.extensions)
129
+ if (null != extension.Prefixes)
130
{
235
- extension.Core = this.core;
236
- extension.Initialize();
131
+ foreach (string prefix in extension.Prefixes)
132
+ {
133
+ if (!this.ExtensionsByPrefix.TryGetValue(prefix, out var collidingExtension))
134
+ {
135
+ this.ExtensionsByPrefix.Add(prefix, extension);
136
+ }
137
+ else
138
+ {
139
+ this.Context.Messaging.OnMessage(WixErrors.DuplicateExtensionPreprocessorType(extension.GetType().ToString(), prefix, collidingExtension.GetType().ToString()));
140
+ }
141
+ }
142
}
143
239
- this.PreprocessReader(false, reader, output, 0);
240
- }
241
- catch (XmlException e)
242
- {
243
- this.UpdateCurrentLineNumber(reader, 0);
244
- throw new WixException(WixErrors.InvalidXml(this.currentLineNumber, "source", e.Message));
144
+ extension.PrePreprocess(context);
145
}
146
247
- // Fire event with post-processed document.
248
- ProcessedStreamEventArgs args = new ProcessedStreamEventArgs(sourceFile, output);
249
- this.OnProcessedStream(args);
147
+ this.CurrentFileStack.Clear();
148
+ this.CurrentFileStack.Push(this.Helper.GetVariableValue(this.Context, "sys", "SOURCEFILEDIR"));
149
251
- // preprocess the generated XML Document
252
- foreach (PreprocessorExtension extension in this.extensions)
150
+ // Process the reader into the output.
151
+ XDocument output = new XDocument();
152
+ try
153
{
254
- extension.PreprocessDocument(output);
255
- }
154
+ this.PreprocessReader(false, reader, output, 0);
155
257
- // finalize the preprocessing
258
- foreach (PreprocessorExtension extension in this.extensions)
259
- {
260
- extension.Finish();
261
- extension.Core = null;
156
+ // Fire event with post-processed document.
157
+ this.ProcessedStream?.Invoke(this, new ProcessedStreamEventArgs(this.Context.SourceFile, output));
158
}
263
-
264
- if (this.core.EncounteredError)
159
+ catch (XmlException e)
160
{
266
- return null;
161
+ this.UpdateCurrentLineNumber(reader, 0);
162
+ throw new WixException(WixErrors.InvalidXml(this.Context.CurrentSourceLineNumber, "source", e.Message));
163
}
268
- else
164
+ finally
165
{
270
- if (null != this.preprocessOut)
166
+ // Finalize the preprocessing.
167
+ foreach (var extension in this.Context.Extensions)
168
{
272
- output.Save(this.preprocessOut);
273
- this.preprocessOut.Flush();
169
+ extension.PostPreprocess(output);
170
}
275
-
276
- return output;
171
}
172
+
173
+ return this.Context.Messaging.EncounteredError ? null : output;
174
}
175
176
/// <summary>
@@ -339,42 +235,6 @@ namespace WixToolset.Core
235
return true;
236
}
237
342
- /// <summary>
343
- /// Fires an event when an ifdef/ifndef directive is processed.
344
- /// </summary>
345
- /// <param name="ea">ifdef/ifndef event arguments.</param>
346
- private void OnIfDef(IfDefEventArgs ea)
347
- {
348
- if (null != this.IfDef)
349
- {
350
- this.IfDef(this, ea);
351
- }
352
- }
353
-
354
- /// <summary>
355
- /// Fires an event when an included file is processed.
356
- /// </summary>
357
- /// <param name="ea">Included file event arguments.</param>
358
- private void OnIncludedFile(IncludedFileEventArgs ea)
359
- {
360
- if (null != this.IncludedFile)
361
- {
362
- this.IncludedFile(this, ea);
363
- }
364
- }
365
-
366
- /// <summary>
367
- /// Fires an event after the file is preprocessed.
368
- /// </summary>
369
- /// <param name="ea">Included file event arguments.</param>
370
- private void OnProcessedStream(ProcessedStreamEventArgs ea)
371
- {
372
- if (null != this.ProcessedStream)
373
- {
374
- this.ProcessedStream(this, ea);
375
- }
376
- }
377
-
238
/// <summary>
239
/// Tests expression to see if it starts with a keyword.
240
/// </summary>
@@ -383,7 +243,7 @@ namespace WixToolset.Core
243
/// <returns>true if expression starts with a keyword.</returns>
244
private static bool StartsWithKeyword(string expression, PreprocessorOperation operation)
245
{
386
- expression = expression.ToUpper(CultureInfo.InvariantCulture);
246
+ expression = expression.ToUpperInvariant();
247
switch (operation)
248
{
249
case PreprocessorOperation.Not:
@@ -431,7 +291,7 @@ namespace WixToolset.Core
291
// update information here in case an error occurs before the next read
292
this.UpdateCurrentLineNumber(reader, offset);
293
434
- SourceLineNumber sourceLineNumbers = this.currentLineNumber;
294
+ var sourceLineNumbers = this.Context.CurrentSourceLineNumber;
295
296
// check for changes in conditional processing
297
if (XmlNodeType.ProcessingInstruction == reader.NodeType)
@@ -459,14 +319,14 @@ namespace WixToolset.Core
319
name = reader.Value.Trim();
320
if (ifContext.IsTrue)
321
{
462
- ifContext = new IfContext(ifContext.IsTrue & ifContext.Active, (null != this.core.GetVariableValue(sourceLineNumbers, name, true)), IfState.If);
322
+ ifContext = new IfContext(ifContext.IsTrue & ifContext.Active, (null != this.Helper.GetVariableValue(this.Context, name, true)), IfState.If);
323
}
324
else // Use a default IfContext object so we don't try to evaluate the expression if the context isn't true
325
{
326
ifContext = new IfContext();
327
}
328
ignore = true;
469
- OnIfDef(new IfDefEventArgs(sourceLineNumbers, true, ifContext.IsTrue, name));
329
+ this.IfDef?.Invoke(this, new IfDefEventArgs(sourceLineNumbers, true, ifContext.IsTrue, name));
330
break;
331
332
case "ifndef":
@@ -474,25 +334,25 @@ namespace WixToolset.Core
334
name = reader.Value.Trim();
335
if (ifContext.IsTrue)
336
{
477
- ifContext = new IfContext(ifContext.IsTrue & ifContext.Active, (null == this.core.GetVariableValue(sourceLineNumbers, name, true)), IfState.If);
337
+ ifContext = new IfContext(ifContext.IsTrue & ifContext.Active, (null == this.Helper.GetVariableValue(this.Context, name, true)), IfState.If);
338
}
339
else // Use a default IfContext object so we don't try to evaluate the expression if the context isn't true
340
{
341
ifContext = new IfContext();
342
}
343
ignore = true;
484
- OnIfDef(new IfDefEventArgs(sourceLineNumbers, false, !ifContext.IsTrue, name));
344
+ this.IfDef?.Invoke(this, new IfDefEventArgs(sourceLineNumbers, false, !ifContext.IsTrue, name));
345
break;
346
347
case "elseif":
348
if (0 == ifStack.Count)
349
{
490
- throw new WixException(WixErrors.UnmatchedPreprocessorInstruction(this.currentLineNumber, "if", "elseif"));
350
+ throw new WixException(WixErrors.UnmatchedPreprocessorInstruction(sourceLineNumbers, "if", "elseif"));
351
}
352
353
if (IfState.If != ifContext.IfState && IfState.ElseIf != ifContext.IfState)
354
{
495
- throw new WixException(WixErrors.UnmatchedPreprocessorInstruction(this.currentLineNumber, "if", "elseif"));
355
+ throw new WixException(WixErrors.UnmatchedPreprocessorInstruction(sourceLineNumbers, "if", "elseif"));
356
}
357
358
ifContext.IfState = IfState.ElseIf; // we're now in an elseif
@@ -510,12 +370,12 @@ namespace WixToolset.Core
370
case "else":
371
if (0 == ifStack.Count)
372
{
513
- throw new WixException(WixErrors.UnmatchedPreprocessorInstruction(this.currentLineNumber, "if", "else"));
373
+ throw new WixException(WixErrors.UnmatchedPreprocessorInstruction(sourceLineNumbers, "if", "else"));
374
}
375
376
if (IfState.If != ifContext.IfState && IfState.ElseIf != ifContext.IfState)
377
{
518
- throw new WixException(WixErrors.UnmatchedPreprocessorInstruction(this.currentLineNumber, "if", "else"));
378
+ throw new WixException(WixErrors.UnmatchedPreprocessorInstruction(sourceLineNumbers, "if", "else"));
379
}
380
381
ifContext.IfState = IfState.Else; // we're now in an else
@@ -526,7 +386,7 @@ namespace WixToolset.Core
386
case "endif":
387
if (0 == ifStack.Count)
388
{
529
- throw new WixException(WixErrors.UnmatchedPreprocessorInstruction(this.currentLineNumber, "if", "endif"));
389
+ throw new WixException(WixErrors.UnmatchedPreprocessorInstruction(sourceLineNumbers, "if", "endif"));
390
}
391
392
ifContext = (IfContext)ifStack.Pop();
@@ -606,7 +466,7 @@ namespace WixToolset.Core
466
break;
467
468
case "endforeach": // endforeach is handled in PreprocessForeach, so seeing it here is an error
609
- throw new WixException(WixErrors.UnmatchedPreprocessorInstruction(this.currentLineNumber, "foreach", "endforeach"));
469
+ throw new WixException(WixErrors.UnmatchedPreprocessorInstruction(sourceLineNumbers, "foreach", "endforeach"));
470
471
case "pragma":
472
this.PreprocessPragma(reader.Value, currentContainer);
@@ -619,31 +479,33 @@ namespace WixToolset.Core
479
break;
480
481
case XmlNodeType.Element:
622
- if (0 < this.includeNextStack.Count && this.includeNextStack.Peek())
482
+ if (0 < this.IncludeNextStack.Count && this.IncludeNextStack.Peek())
483
{
484
if ("Include" != reader.LocalName)
485
{
626
- this.core.OnMessage(WixErrors.InvalidDocumentElement(this.currentLineNumber, reader.Name, "include", "Include"));
486
+ this.Context.Messaging.OnMessage(WixErrors.InvalidDocumentElement(sourceLineNumbers, reader.Name, "include", "Include"));
487
}
488
629
- this.includeNextStack.Pop();
630
- this.includeNextStack.Push(false);
489
+ this.IncludeNextStack.Pop();
490
+ this.IncludeNextStack.Push(false);
491
break;
492
}
493
634
- bool empty = reader.IsEmptyElement;
635
- XNamespace ns = XNamespace.Get(reader.NamespaceURI);
636
- XElement element = new XElement(ns + reader.LocalName);
494
+ var empty = reader.IsEmptyElement;
495
+ var ns = XNamespace.Get(reader.NamespaceURI);
496
+ var element = new XElement(ns + reader.LocalName);
497
currentContainer.Add(element);
498
499
this.UpdateCurrentLineNumber(reader, offset);
640
- element.AddAnnotation(this.currentLineNumber);
500
+ element.AddAnnotation(sourceLineNumbers);
501
502
while (reader.MoveToNextAttribute())
503
{
644
- string value = this.core.PreprocessString(this.currentLineNumber, reader.Value);
645
- XNamespace attribNamespace = XNamespace.Get(reader.NamespaceURI);
504
+ var value = this.Helper.PreprocessString(this.Context, reader.Value);
505
+
506
+ var attribNamespace = XNamespace.Get(reader.NamespaceURI);
507
attribNamespace = XNamespace.Xmlns == attribNamespace && reader.LocalName.Equals("xmlns") ? XNamespace.None : attribNamespace;
508
+
509
element.Add(new XAttribute(attribNamespace + reader.LocalName, value));
510
}
511
@@ -662,12 +524,12 @@ namespace WixToolset.Core
524
break;
525
526
case XmlNodeType.Text:
665
- string postprocessedText = this.core.PreprocessString(this.currentLineNumber, reader.Value);
527
+ string postprocessedText = this.Helper.PreprocessString(this.Context, reader.Value);
528
currentContainer.Add(postprocessedText);
529
break;
530
531
case XmlNodeType.CDATA:
670
- string postprocessedValue = this.core.PreprocessString(this.currentLineNumber, reader.Value);
532
+ string postprocessedValue = this.Helper.PreprocessString(this.Context, reader.Value);
533
currentContainer.Add(new XCData(postprocessedValue));
534
break;
535
@@ -678,13 +540,13 @@ namespace WixToolset.Core
540
541
if (0 != ifStack.Count)
542
{
681
- throw new WixException(WixErrors.NonterminatedPreprocessorInstruction(this.currentLineNumber, "if", "endif"));
543
+ throw new WixException(WixErrors.NonterminatedPreprocessorInstruction(this.Context.CurrentSourceLineNumber, "if", "endif"));
544
}
545
546
// TODO: can this actually happen?
547
if (0 != containerStack.Count)
548
{
687
- throw new WixException(WixErrors.NonterminatedPreprocessorInstruction(this.currentLineNumber, "nodes", "nodes"));
549
+ throw new WixException(WixErrors.NonterminatedPreprocessorInstruction(this.Context.CurrentSourceLineNumber, "nodes", "nodes"));
550
}
551
}
552
@@ -694,12 +556,10 @@ namespace WixToolset.Core
556
/// <param name="errorMessage">Text from source.</param>
557
private void PreprocessError(string errorMessage)
558
{
697
- SourceLineNumber sourceLineNumbers = this.currentLineNumber;
698
-
699
- // resolve other variables in the error message
700
- errorMessage = this.core.PreprocessString(sourceLineNumbers, errorMessage);
559
+ // Resolve other variables in the error message.
560
+ errorMessage = this.Helper.PreprocessString(this.Context, errorMessage);
561
702
- throw new WixException(WixErrors.PreprocessorError(sourceLineNumbers, errorMessage));
562
+ throw new WixException(WixErrors.PreprocessorError(this.Context.CurrentSourceLineNumber, errorMessage));
563
}
564
565
/// <summary>
@@ -708,12 +568,10 @@ namespace WixToolset.Core
568
/// <param name="warningMessage">Text from source.</param>
569
private void PreprocessWarning(string warningMessage)
570
{
711
- SourceLineNumber sourceLineNumbers = this.currentLineNumber;
712
-
713
- // resolve other variables in the warning message
714
- warningMessage = this.core.PreprocessString(sourceLineNumbers, warningMessage);
571
+ // Resolve other variables in the warning message.
572
+ warningMessage = this.Helper.PreprocessString(this.Context, warningMessage);
573
716
- this.core.OnMessage(WixWarnings.PreprocessorWarning(sourceLineNumbers, warningMessage));
574
+ this.Context.Messaging.OnMessage(WixWarnings.PreprocessorWarning(this.Context.CurrentSourceLineNumber, warningMessage));
575
}
576
577
/// <summary>
@@ -722,16 +580,15 @@ namespace WixToolset.Core
580
/// <param name="originalDefine">Text from source.</param>
581
private void PreprocessDefine(string originalDefine)
582
{
725
- Match match = defineRegex.Match(originalDefine);
726
- SourceLineNumber sourceLineNumbers = this.currentLineNumber;
583
+ var match = defineRegex.Match(originalDefine);
584
585
if (!match.Success)
586
{
730
- throw new WixException(WixErrors.IllegalDefineStatement(sourceLineNumbers, originalDefine));
587
+ throw new WixException(WixErrors.IllegalDefineStatement(this.Context.CurrentSourceLineNumber, originalDefine));
588
}
589
733
- string defineName = match.Groups["varName"].Value;
734
- string defineValue = match.Groups["varValue"].Value;
590
+ var defineName = match.Groups["varName"].Value;
591
+ var defineValue = match.Groups["varValue"].Value;
592
593
// strip off the optional quotes
594
if (1 < defineValue.Length &&
@@ -742,15 +599,15 @@ namespace WixToolset.Core
599
}
600
601
// resolve other variables in the variable value
745
- defineValue = this.core.PreprocessString(sourceLineNumbers, defineValue);
602
+ defineValue = this.Helper.PreprocessString(this.Context, defineValue);
603
604
if (defineName.StartsWith("var.", StringComparison.Ordinal))
605
{
749
- this.core.AddVariable(sourceLineNumbers, defineName.Substring(4), defineValue);
606
+ this.Helper.AddVariable(this.Context, defineName.Substring(4), defineValue);
607
}
608
else
609
{
753
- this.core.AddVariable(sourceLineNumbers, defineName, defineValue);
610
+ this.Helper.AddVariable(this.Context, defineName, defineValue);
611
}
612
}
613
@@ -760,16 +617,15 @@ namespace WixToolset.Core
617
/// <param name="originalDefine">Text from source.</param>
618
private void PreprocessUndef(string originalDefine)
619
{
763
- SourceLineNumber sourceLineNumbers = this.currentLineNumber;
764
- string name = this.core.PreprocessString(sourceLineNumbers, originalDefine.Trim());
620
+ var name = this.Helper.PreprocessString(this.Context, originalDefine.Trim());
621
622
if (name.StartsWith("var.", StringComparison.Ordinal))
623
{
768
- this.core.RemoveVariable(sourceLineNumbers, name.Substring(4));
624
+ this.Helper.RemoveVariable(this.Context, name.Substring(4));
625
}
626
else
627
{
772
- this.core.RemoveVariable(sourceLineNumbers, name);
628
+ this.Helper.RemoveVariable(this.Context, name);
629
}
630
}
631
@@ -780,12 +636,12 @@ namespace WixToolset.Core
636
/// <param name="parent">Parent container for included content.</param>
637
private void PreprocessInclude(string includePath, XContainer parent)
638
{
783
- SourceLineNumber sourceLineNumbers = this.currentLineNumber;
639
+ var sourceLineNumbers = this.Context.CurrentSourceLineNumber;
640
785
- // preprocess variables in the path
786
- includePath = this.core.PreprocessString(sourceLineNumbers, includePath);
641
+ // Preprocess variables in the path.
642
+ includePath = this.Helper.PreprocessString(this.Context, includePath);
643
788
- string includeFile = this.GetIncludeFile(includePath);
644
+ var includeFile = this.GetIncludeFile(includePath);
645
646
if (null == includeFile)
647
{
@@ -807,7 +663,7 @@ namespace WixToolset.Core
663
throw new WixException(WixErrors.InvalidXml(sourceLineNumbers, "source", e.Message));
664
}
665
810
- this.OnIncludedFile(new IncludedFileEventArgs(sourceLineNumbers, includeFile));
666
+ this.IncludedFile?.Invoke(this, new IncludedFileEventArgs(sourceLineNumbers, includeFile));
667
668
this.PopInclude();
669
}
@@ -821,11 +677,11 @@ namespace WixToolset.Core
677
/// <param name="offset">Offset for the line numbers.</param>
678
private void PreprocessForeach(XmlReader reader, XContainer container, int offset)
679
{
824
- // find the "in" token
825
- int indexOfInToken = reader.Value.IndexOf(" in ", StringComparison.Ordinal);
680
+ // Find the "in" token.
681
+ var indexOfInToken = reader.Value.IndexOf(" in ", StringComparison.Ordinal);
682
if (0 > indexOfInToken)
683
{
828
- throw new WixException(WixErrors.IllegalForeach(this.currentLineNumber, reader.Value));
684
+ throw new WixException(WixErrors.IllegalForeach(this.Context.CurrentSourceLineNumber, reader.Value));
685
}
686
687
// parse out the variable name
@@ -833,7 +689,7 @@ namespace WixToolset.Core
689
string varValuesString = reader.Value.Substring(indexOfInToken + 4).Trim();
690
691
// preprocess the variable values string because it might be a variable itself
836
- varValuesString = this.core.PreprocessString(this.currentLineNumber, varValuesString);
692
+ varValuesString = this.Helper.PreprocessString(this.Context, varValuesString);
693
694
string[] varValues = varValuesString.Split(';');
695
@@ -895,20 +751,20 @@ namespace WixToolset.Core
751
}
752
else if (reader.NodeType == XmlNodeType.None)
753
{
898
- throw new WixException(WixErrors.ExpectedEndforeach(this.currentLineNumber));
754
+ throw new WixException(WixErrors.ExpectedEndforeach(this.Context.CurrentSourceLineNumber));
755
}
756
757
reader.Read();
758
}
759
904
- using (MemoryStream fragmentStream = new MemoryStream(Encoding.UTF8.GetBytes(fragmentBuilder.ToString())))
905
- using (XmlReader loopReader = XmlReader.Create(fragmentStream, FragmentXmlReaderSettings))
760
+ using (var fragmentStream = new MemoryStream(Encoding.UTF8.GetBytes(fragmentBuilder.ToString())))
761
+ using (var loopReader = XmlReader.Create(fragmentStream, FragmentXmlReaderSettings))
762
{
763
// process each iteration, updating the variable's value each time
764
foreach (string varValue in varValues)
765
{
766
// Always overwrite foreach variables.
911
- this.core.AddVariable(this.currentLineNumber, varName, varValue, false);
767
+ this.Helper.AddVariable(this.Context, varName, varValue, false);
768
769
try
770
{
@@ -917,7 +773,7 @@ namespace WixToolset.Core
773
catch (XmlException e)
774
{
775
this.UpdateCurrentLineNumber(loopReader, offset);
920
- throw new WixException(WixErrors.InvalidXml(this.currentLineNumber, "source", e.Message));
776
+ throw new WixException(WixErrors.InvalidXml(this.Context.CurrentSourceLineNumber, "source", e.Message));
777
}
778
779
fragmentStream.Position = 0; // seek back to the beginning for the next loop.
@@ -931,24 +787,23 @@ namespace WixToolset.Core
787
/// <param name="pragmaText">Text from source.</param>
788
private void PreprocessPragma(string pragmaText, XContainer parent)
789
{
934
- Match match = pragmaRegex.Match(pragmaText);
935
- SourceLineNumber sourceLineNumbers = this.currentLineNumber;
790
+ var match = pragmaRegex.Match(pragmaText);
791
792
if (!match.Success)
793
{
939
- throw new WixException(WixErrors.InvalidPreprocessorPragma(sourceLineNumbers, pragmaText));
794
+ throw new WixException(WixErrors.InvalidPreprocessorPragma(this.Context.CurrentSourceLineNumber, pragmaText));
795
}
796
797
// resolve other variables in the pragma argument(s)
943
- string pragmaArgs = this.core.PreprocessString(sourceLineNumbers, match.Groups["pragmaValue"].Value).Trim();
798
+ string pragmaArgs = this.Helper.PreprocessString(this.Context, match.Groups["pragmaValue"].Value).Trim();
799
800
try
801
{
947
- this.core.PreprocessPragma(sourceLineNumbers, match.Groups["pragmaName"].Value.Trim(), pragmaArgs, parent);
802
+ this.Helper.PreprocessPragma(this.Context, match.Groups["pragmaName"].Value.Trim(), pragmaArgs, parent);
803
}
804
catch (Exception e)
805
{
951
- throw new WixException(WixErrors.PreprocessorExtensionPragmaFailed(sourceLineNumbers, pragmaText, e.Message));
806
+ throw new WixException(WixErrors.PreprocessorExtensionPragmaFailed(this.Context.CurrentSourceLineNumber, pragmaText, e.Message));
807
}
808
}
809
@@ -975,11 +830,11 @@ namespace WixToolset.Core
830
int endingQuotes = expression.IndexOf('\"', 1);
831
if (-1 == endingQuotes)
832
{
978
- throw new WixException(WixErrors.UnmatchedQuotesInExpression(this.currentLineNumber, originalExpression));
833
+ throw new WixException(WixErrors.UnmatchedQuotesInExpression(this.Context.CurrentSourceLineNumber, originalExpression));
834
}
835
836
// cut the quotes off the string
982
- token = this.core.PreprocessString(this.currentLineNumber, expression.Substring(1, endingQuotes - 1));
837
+ token = this.Helper.PreprocessString(this.Context, expression.Substring(1, endingQuotes - 1));
838
839
// advance past this string
840
expression = expression.Substring(endingQuotes + 1).Trim();
@@ -1009,7 +864,7 @@ namespace WixToolset.Core
864
865
if (-1 == endingParen)
866
{
1012
- throw new WixException(WixErrors.UnmatchedParenthesisInExpression(this.currentLineNumber, originalExpression));
867
+ throw new WixException(WixErrors.UnmatchedParenthesisInExpression(this.Context.CurrentSourceLineNumber, originalExpression));
868
}
869
token = expression.Substring(0, endingParen + 1);
870
@@ -1115,7 +970,7 @@ namespace WixToolset.Core
970
{
971
try
972
{
1118
- varValue = this.core.PreprocessString(this.currentLineNumber, variable);
973
+ varValue = this.Helper.PreprocessString(this.Context, variable);
974
}
975
catch (ArgumentNullException)
976
{
@@ -1126,12 +981,12 @@ namespace WixToolset.Core
981
else if (variable.IndexOf("(", StringComparison.Ordinal) != -1 || variable.IndexOf(")", StringComparison.Ordinal) != -1)
982
{
983
// make sure it doesn't contain parenthesis
1129
- throw new WixException(WixErrors.UnmatchedParenthesisInExpression(this.currentLineNumber, originalExpression));
984
+ throw new WixException(WixErrors.UnmatchedParenthesisInExpression(this.Context.CurrentSourceLineNumber, originalExpression));
985
}
986
else if (variable.IndexOf("\"", StringComparison.Ordinal) != -1)
987
{
988
// shouldn't contain quotes
1134
- throw new WixException(WixErrors.UnmatchedQuotesInExpression(this.currentLineNumber, originalExpression));
989
+ throw new WixException(WixErrors.UnmatchedQuotesInExpression(this.Context.CurrentSourceLineNumber, originalExpression));
990
}
991
992
return varValue;
@@ -1162,7 +1017,7 @@ namespace WixToolset.Core
1017
{
1018
if (stringLiteral)
1019
{
1165
- throw new WixException(WixErrors.UnmatchedQuotesInExpression(this.currentLineNumber, originalExpression));
1020
+ throw new WixException(WixErrors.UnmatchedQuotesInExpression(this.Context.CurrentSourceLineNumber, originalExpression));
1021
}
1022
1023
rightValue = this.GetNextToken(originalExpression, ref expression, out stringLiteral);
@@ -1213,7 +1068,7 @@ namespace WixToolset.Core
1068
{
1069
if (operation.Length > 0)
1070
{
1216
- throw new WixException(WixErrors.ExpectedVariable(this.currentLineNumber, originalExpression));
1071
+ throw new WixException(WixErrors.ExpectedVariable(this.Context.CurrentSourceLineNumber, originalExpression));
1072
}
1073
1074
// false expression
@@ -1228,7 +1083,7 @@ namespace WixToolset.Core
1083
}
1084
else
1085
{
1231
- throw new WixException(WixErrors.UnexpectedLiteral(this.currentLineNumber, originalExpression));
1086
+ throw new WixException(WixErrors.UnexpectedLiteral(this.Context.CurrentSourceLineNumber, originalExpression));
1087
}
1088
}
1089
else
@@ -1268,11 +1123,11 @@ namespace WixToolset.Core
1123
}
1124
catch (FormatException)
1125
{
1271
- throw new WixException(WixErrors.IllegalIntegerInExpression(this.currentLineNumber, originalExpression));
1126
+ throw new WixException(WixErrors.IllegalIntegerInExpression(this.Context.CurrentSourceLineNumber, originalExpression));
1127
}
1128
catch (OverflowException)
1129
{
1275
- throw new WixException(WixErrors.IllegalIntegerInExpression(this.currentLineNumber, originalExpression));
1130
+ throw new WixException(WixErrors.IllegalIntegerInExpression(this.Context.CurrentSourceLineNumber, originalExpression));
1131
}
1132
1133
// Compare the numbers
@@ -1314,7 +1169,7 @@ namespace WixToolset.Core
1169
closeParenIndex = expression.IndexOf(')', closeParenIndex);
1170
if (closeParenIndex == -1)
1171
{
1317
- throw new WixException(WixErrors.UnmatchedParenthesisInExpression(this.currentLineNumber, originalExpression));
1172
+ throw new WixException(WixErrors.UnmatchedParenthesisInExpression(this.Context.CurrentSourceLineNumber, originalExpression));
1173
}
1174
1175
if (InsideQuotes(expression, closeParenIndex))
@@ -1363,7 +1218,7 @@ namespace WixToolset.Core
1218
currentValue = !currentValue;
1219
break;
1220
default:
1366
- throw new WixException(WixErrors.UnexpectedPreprocessorOperator(this.currentLineNumber, operation.ToString()));
1221
+ throw new WixException(WixErrors.UnexpectedPreprocessorOperator(this.Context.CurrentSourceLineNumber, operation.ToString()));
1222
}
1223
}
1224
@@ -1412,7 +1267,7 @@ namespace WixToolset.Core
1267
expression = expression.Trim();
1268
if (expression.Length == 0)
1269
{
1415
- throw new WixException(WixErrors.UnexpectedEmptySubexpression(this.currentLineNumber, originalExpression));
1270
+ throw new WixException(WixErrors.UnexpectedEmptySubexpression(this.Context.CurrentSourceLineNumber, originalExpression));
1271
}
1272
1273
// If the expression starts with parenthesis, evaluate it
@@ -1433,7 +1288,7 @@ namespace WixToolset.Core
1288
expression = expression.Substring(3).Trim();
1289
if (expression.Length == 0)
1290
{
1436
- throw new WixException(WixErrors.ExpectedExpressionAfterNot(this.currentLineNumber, originalExpression));
1291
+ throw new WixException(WixErrors.ExpectedExpressionAfterNot(this.Context.CurrentSourceLineNumber, originalExpression));
1292
}
1293
1294
expressionValue = this.EvaluateExpressionRecurse(originalExpression, ref expression, PreprocessorOperation.Not, true);
@@ -1462,7 +1317,7 @@ namespace WixToolset.Core
1317
}
1318
else
1319
{
1465
- throw new WixException(WixErrors.InvalidSubExpression(this.currentLineNumber, expression, originalExpression));
1320
+ throw new WixException(WixErrors.InvalidSubExpression(this.Context.CurrentSourceLineNumber, expression, originalExpression));
1321
}
1322
}
1323
@@ -1481,9 +1336,9 @@ namespace WixToolset.Core
1336
{
1337
int newLine = lineInfoReader.LineNumber + offset;
1338
1484
- if (this.currentLineNumber.LineNumber != newLine)
1339
+ if (this.Context.CurrentSourceLineNumber.LineNumber != newLine)
1340
{
1486
- this.currentLineNumber = new SourceLineNumber(this.currentLineNumber.FileName, newLine);
1341
+ this.Context.CurrentSourceLineNumber = new SourceLineNumber(this.Context.CurrentSourceLineNumber.FileName, newLine);
1342
}
1343
}
1344
}
@@ -1494,15 +1349,15 @@ namespace WixToolset.Core
1349
/// <param name="fileName">Name to push on to the stack of included files.</param>
1350
private void PushInclude(string fileName)
1351
{
1497
- if (1023 < this.currentFileStack.Count)
1352
+ if (1023 < this.CurrentFileStack.Count)
1353
{
1499
- throw new WixException(WixErrors.TooDeeplyIncluded(this.currentLineNumber, this.currentFileStack.Count));
1354
+ throw new WixException(WixErrors.TooDeeplyIncluded(this.Context.CurrentSourceLineNumber, this.CurrentFileStack.Count));
1355
}
1356
1502
- this.currentFileStack.Push(fileName);
1503
- this.sourceStack.Push(this.currentLineNumber);
1504
- this.currentLineNumber = new SourceLineNumber(fileName);
1505
- this.includeNextStack.Push(true);
1357
+ this.CurrentFileStack.Push(fileName);
1358
+ this.SourceStack.Push(this.Context.CurrentSourceLineNumber);
1359
+ this.Context.CurrentSourceLineNumber = new SourceLineNumber(fileName);
1360
+ this.IncludeNextStack.Push(true);
1361
}
1362
1363
/// <summary>
@@ -1510,10 +1365,10 @@ namespace WixToolset.Core
1365
/// </summary>
1366
private void PopInclude()
1367
{
1513
- this.currentLineNumber = this.sourceStack.Pop();
1368
+ this.Context.CurrentSourceLineNumber = this.SourceStack.Pop();
1369
1515
- this.currentFileStack.Pop();
1516
- this.includeNextStack.Pop();
1370
+ this.CurrentFileStack.Pop();
1371
+ this.IncludeNextStack.Pop();
1372
}
1373
1374
/// <summary>
@@ -1548,8 +1403,8 @@ namespace WixToolset.Core
1403
else // relative path
1404
{
1405
// build a string to test the directory containing the source file first
1551
- string currentFolder = this.currentFileStack.Peek();
1552
- string includeTestPath = Path.Combine(Path.GetDirectoryName(currentFolder) ?? String.Empty, includePath);
1406
+ var currentFolder = this.CurrentFileStack.Peek();
1407
+ var includeTestPath = Path.Combine(Path.GetDirectoryName(currentFolder) , includePath);
1408
1409
// test the source file directory
1410
if (File.Exists(includeTestPath))
@@ -1558,7 +1413,7 @@ namespace WixToolset.Core
1413
}
1414
else // test all search paths in the order specified on the command line
1415
{
1561
- foreach (string includeSearchPath in this.IncludeSearchPaths)
1416
+ foreach (var includeSearchPath in this.Context.IncludeSearchPaths)
1417
{
1418
// if the path exists, we have found the final string
1419
includeTestPath = Path.Combine(includeSearchPath, includePath);
src/WixToolset.Core/PreprocessorCore.cs
deleted
-560
@@ -1,560 +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
4
-{
5
- using System;
6
- using System.Collections.Generic;
7
- using System.IO;
8
- using System.Text;
9
- using System.Xml.Linq;
10
- using WixToolset.Data;
11
- using WixToolset.Extensibility;
12
-
13
- /// <summary>
14
- /// The preprocessor core.
15
- /// </summary>
16
- internal class PreprocessorCore : IPreprocessorCore
17
- {
18
- private static readonly char[] variableSplitter = new char[] { '.' };
19
- private static readonly char[] argumentSplitter = new char[] { ',' };
20
-
21
- private Platform currentPlatform;
22
- private Dictionary<string, IPreprocessorExtension> extensionsByPrefix;
23
- private string sourceFile;
24
- private IDictionary<string, string> variables;
25
-
26
- /// <summary>
27
- /// Instantiate a new PreprocessorCore.
28
- /// </summary>
29
- /// <param name="extensionsByPrefix">The extensions indexed by their prefixes.</param>
30
- /// <param name="messageHandler">The message handler.</param>
31
- /// <param name="sourceFile">The source file being preprocessed.</param>
32
- /// <param name="variables">The variables defined prior to preprocessing.</param>
33
- internal PreprocessorCore(Dictionary<string, IPreprocessorExtension> extensionsByPrefix, string sourceFile, IDictionary<string, string> variables)
34
- {
35
- this.extensionsByPrefix = extensionsByPrefix;
36
- this.sourceFile = String.IsNullOrEmpty(sourceFile) ? null : Path.GetFullPath(sourceFile);
37
-
38
- this.variables = new Dictionary<string, string>();
39
- foreach (var entry in variables)
40
- {
41
- this.AddVariable(null, entry.Key, entry.Value);
42
- }
43
- }
44
-
45
- /// <summary>
46
- /// Event for resolved variables.
47
- /// </summary>
48
- private event ResolvedVariableEventHandler ResolvedVariable;
49
-
50
- /// <summary>
51
- /// Sets event for ResolvedVariableEventHandler.
52
- /// </summary>
53
- public ResolvedVariableEventHandler ResolvedVariableHandler
54
- {
55
- set { this.ResolvedVariable = value; }
56
- }
57
-
58
- /// <summary>
59
- /// Gets or sets the platform which the compiler will use when defaulting 64-bit attributes and elements.
60
- /// </summary>
61
- /// <value>The platform which the compiler will use when defaulting 64-bit attributes and elements.</value>
62
- public Platform CurrentPlatform
63
- {
64
- get { return this.currentPlatform; }
65
- set { this.currentPlatform = value; }
66
- }
67
-
68
- /// <summary>
69
- /// Gets whether the core encountered an error while processing.
70
- /// </summary>
71
- /// <value>Flag if core encountered an error during processing.</value>
72
- public bool EncounteredError
73
- {
74
- get { return Messaging.Instance.EncounteredError; }
75
- }
76
-
77
- /// <summary>
78
- /// Replaces parameters in the source text.
79
- /// </summary>
80
- /// <param name="sourceLineNumbers">The source line information for the function.</param>
81
- /// <param name="value">Text that may contain parameters to replace.</param>
82
- /// <returns>Text after parameters have been replaced.</returns>
83
- public string PreprocessString(SourceLineNumber sourceLineNumbers, string value)
84
- {
85
- StringBuilder sb = new StringBuilder();
86
- int currentPosition = 0;
87
- int end = 0;
88
-
89
- while (-1 != (currentPosition = value.IndexOf('$', end)))
90
- {
91
- if (end < currentPosition)
92
- {
93
- sb.Append(value, end, currentPosition - end);
94
- }
95
-
96
- end = currentPosition + 1;
97
- string remainder = value.Substring(end);
98
- if (remainder.StartsWith("$", StringComparison.Ordinal))
99
- {
100
- sb.Append("$");
101
- end++;
102
- }
103
- else if (remainder.StartsWith("(loc.", StringComparison.Ordinal))
104
- {
105
- currentPosition = remainder.IndexOf(')');
106
- if (-1 == currentPosition)
107
- {
108
- this.OnMessage(WixErrors.InvalidPreprocessorVariable(sourceLineNumbers, remainder));
109
- break;
110
- }
111
-
112
- sb.Append("$"); // just put the resource reference back as was
113
- sb.Append(remainder, 0, currentPosition + 1);
114
-
115
- end += currentPosition + 1;
116
- }
117
- else if (remainder.StartsWith("(", StringComparison.Ordinal))
118
- {
119
- int openParenCount = 1;
120
- int closingParenCount = 0;
121
- bool isFunction = false;
122
- bool foundClosingParen = false;
123
-
124
- // find the closing paren
125
- int closingParenPosition;
126
- for (closingParenPosition = 1; closingParenPosition < remainder.Length; closingParenPosition++)
127
- {
128
- switch (remainder[closingParenPosition])
129
- {
130
- case '(':
131
- openParenCount++;
132
- isFunction = true;
133
- break;
134
- case ')':
135
- closingParenCount++;
136
- break;
137
- }
138
- if (openParenCount == closingParenCount)
139
- {
140
- foundClosingParen = true;
141
- break;
142
- }
143
- }
144
-
145
- // move the currentPosition to the closing paren
146
- currentPosition += closingParenPosition;
147
-
148
- if (!foundClosingParen)
149
- {
150
- if (isFunction)
151
- {
152
- this.OnMessage(WixErrors.InvalidPreprocessorFunction(sourceLineNumbers, remainder));
153
- break;
154
- }
155
- else
156
- {
157
- this.OnMessage(WixErrors.InvalidPreprocessorVariable(sourceLineNumbers, remainder));
158
- break;
159
- }
160
- }
161
-
162
- string subString = remainder.Substring(1, closingParenPosition - 1);
163
- string result = null;
164
- if (isFunction)
165
- {
166
- result = this.EvaluateFunction(sourceLineNumbers, subString);
167
- }
168
- else
169
- {
170
- result = this.GetVariableValue(sourceLineNumbers, subString, false);
171
- }
172
-
173
- if (null == result)
174
- {
175
- if (isFunction)
176
- {
177
- this.OnMessage(WixErrors.UndefinedPreprocessorFunction(sourceLineNumbers, subString));
178
- break;
179
- }
180
- else
181
- {
182
- this.OnMessage(WixErrors.UndefinedPreprocessorVariable(sourceLineNumbers, subString));
183
- break;
184
- }
185
- }
186
- else
187
- {
188
- if (!isFunction)
189
- {
190
- this.OnResolvedVariable(new ResolvedVariableEventArgs(sourceLineNumbers, subString, result));
191
- }
192
- }
193
- sb.Append(result);
194
- end += closingParenPosition + 1;
195
- }
196
- else // just a floating "$" so put it in the final string (i.e. leave it alone) and keep processing
197
- {
198
- sb.Append('$');
199
- }
200
- }
201
-
202
- if (end < value.Length)
203
- {
204
- sb.Append(value.Substring(end));
205
- }
206
-
207
- return sb.ToString();
208
- }
209
-
210
- /// <summary>
211
- /// Evaluate a Pragma.
212
- /// </summary>
213
- /// <param name="sourceLineNumbers">The source line information for the function.</param>
214
- /// <param name="pragmaName">The pragma's full name (<prefix>.<pragma>).</param>
215
- /// <param name="args">The arguments to the pragma.</param>
216
- /// <param name="parent">The parent element of the pragma.</param>
217
- public void PreprocessPragma(SourceLineNumber sourceLineNumbers, string pragmaName, string args, XContainer parent)
218
- {
219
- string[] prefixParts = pragmaName.Split(variableSplitter, 2);
220
- // Check to make sure there are 2 parts and neither is an empty string.
221
- if (2 != prefixParts.Length)
222
- {
223
- throw new WixException(WixErrors.InvalidPreprocessorPragma(sourceLineNumbers, pragmaName));
224
- }
225
- string prefix = prefixParts[0];
226
- string pragma = prefixParts[1];
227
-
228
- if (String.IsNullOrEmpty(prefix) || String.IsNullOrEmpty(pragma))
229
- {
230
- throw new WixException(WixErrors.InvalidPreprocessorPragma(sourceLineNumbers, pragmaName));
231
- }
232
-
233
- switch (prefix)
234
- {
235
- case "wix":
236
- switch (pragma)
237
- {
238
- // Add any core defined pragmas here
239
- default:
240
- this.OnMessage(WixWarnings.PreprocessorUnknownPragma(sourceLineNumbers, pragmaName));
241
- break;
242
- }
243
- break;
244
- default:
245
- PreprocessorExtension extension = (PreprocessorExtension)this.extensionsByPrefix[prefix];
246
- if (null == extension || !extension.ProcessPragma(sourceLineNumbers, prefix, pragma, args, parent))
247
- {
248
- this.OnMessage(WixWarnings.PreprocessorUnknownPragma(sourceLineNumbers, pragmaName));
249
- }
250
- break;
251
- }
252
- }
253
-
254
- /// <summary>
255
- /// Evaluate a function.
256
- /// </summary>
257
- /// <param name="sourceLineNumbers">The source line information for the function.</param>
258
- /// <param name="function">The function expression including the prefix and name.</param>
259
- /// <returns>The function value.</returns>
260
- public string EvaluateFunction(SourceLineNumber sourceLineNumbers, string function)
261
- {
262
- string[] prefixParts = function.Split(variableSplitter, 2);
263
- // Check to make sure there are 2 parts and neither is an empty string.
264
- if (2 != prefixParts.Length || 0 >= prefixParts[0].Length || 0 >= prefixParts[1].Length)
265
- {
266
- throw new WixException(WixErrors.InvalidPreprocessorFunction(sourceLineNumbers, function));
267
- }
268
- string prefix = prefixParts[0];
269
-
270
- string[] functionParts = prefixParts[1].Split(new char[] { '(' }, 2);
271
- // Check to make sure there are 2 parts, neither is an empty string, and the second part ends with a closing paren.
272
- if (2 != functionParts.Length || 0 >= functionParts[0].Length || 0 >= functionParts[1].Length || !functionParts[1].EndsWith(")", StringComparison.Ordinal))
273
- {
274
- throw new WixException(WixErrors.InvalidPreprocessorFunction(sourceLineNumbers, function));
275
- }
276
- string functionName = functionParts[0];
277
-
278
- // Remove the trailing closing paren.
279
- string allArgs = functionParts[1].Substring(0, functionParts[1].Length - 1);
280
-
281
- // Parse the arguments and preprocess them.
282
- string[] args = allArgs.Split(argumentSplitter);
283
- for (int i = 0; i < args.Length; i++)
284
- {
285
- args[i] = this.PreprocessString(sourceLineNumbers, args[i].Trim());
286
- }
287
-
288
- string result = this.EvaluateFunction(sourceLineNumbers, prefix, functionName, args);
289
-
290
- // If the function didn't evaluate, try to evaluate the original value as a variable to support
291
- // the use of open and closed parens inside variable names. Example: $(env.ProgramFiles(x86)) should resolve.
292
- if (null == result)
293
- {
294
- result = this.GetVariableValue(sourceLineNumbers, function, false);
295
- }
296
-
297
- return result;
298
- }
299
-
300
- /// <summary>
301
- /// Evaluate a function.
302
- /// </summary>
303
- /// <param name="sourceLineNumbers">The source line information for the function.</param>
304
- /// <param name="prefix">The function prefix.</param>
305
- /// <param name="function">The function name.</param>
306
- /// <param name="args">The arguments for the function.</param>
307
- /// <returns>The function value or null if the function is not defined.</returns>
308
- public string EvaluateFunction(SourceLineNumber sourceLineNumbers, string prefix, string function, string[] args)
309
- {
310
- if (String.IsNullOrEmpty(prefix))
311
- {
312
- throw new ArgumentNullException("prefix");
313
- }
314
-
315
- if (String.IsNullOrEmpty(function))
316
- {
317
- throw new ArgumentNullException("function");
318
- }
319
-
320
- switch (prefix)
321
- {
322
- case "fun":
323
- switch (function)
324
- {
325
- case "AutoVersion":
326
- // Make sure the base version is specified
327
- if (args.Length == 0 || String.IsNullOrEmpty(args[0]))
328
- {
329
- throw new WixException(WixErrors.InvalidPreprocessorFunctionAutoVersion(sourceLineNumbers));
330
- }
331
-
332
- // Build = days since 1/1/2000; Revision = seconds since midnight / 2
333
- DateTime now = DateTime.UtcNow;
334
- TimeSpan build = now - new DateTime(2000, 1, 1);
335
- TimeSpan revision = now - new DateTime(now.Year, now.Month, now.Day);
336
-
337
- return String.Join(".", args[0], (int)build.TotalDays, (int)(revision.TotalSeconds / 2));
338
-
339
- default:
340
- return null;
341
- }
342
- default:
343
- PreprocessorExtension extension = (PreprocessorExtension)this.extensionsByPrefix[prefix];
344
- if (null != extension)
345
- {
346
- try
347
- {
348
- return extension.EvaluateFunction(prefix, function, args);
349
- }
350
- catch (Exception e)
351
- {
352
- throw new WixException(WixErrors.PreprocessorExtensionEvaluateFunctionFailed(sourceLineNumbers, prefix, function, String.Join(",", args), e.Message));
353
- }
354
- }
355
- else
356
- {
357
- return null;
358
- }
359
- }
360
- }
361
-
362
- /// <summary>
363
- /// Get the value of a variable expression like var.name.
364
- /// </summary>
365
- /// <param name="sourceLineNumbers">The source line information for the variable.</param>
366
- /// <param name="variable">The variable expression including the optional prefix and name.</param>
367
- /// <param name="allowMissingPrefix">true to allow the variable prefix to be missing.</param>
368
- /// <returns>The variable value.</returns>
369
- public string GetVariableValue(SourceLineNumber sourceLineNumbers, string variable, bool allowMissingPrefix)
370
- {
371
- // Strip the "$(" off the front.
372
- if (variable.StartsWith("$(", StringComparison.Ordinal))
373
- {
374
- variable = variable.Substring(2);
375
- }
376
-
377
- string[] parts = variable.Split(variableSplitter, 2);
378
-
379
- if (1 == parts.Length) // missing prefix
380
- {
381
- if (allowMissingPrefix)
382
- {
383
- return this.GetVariableValue(sourceLineNumbers, "var", parts[0]);
384
- }
385
- else
386
- {
387
- throw new WixException(WixErrors.InvalidPreprocessorVariable(sourceLineNumbers, variable));
388
- }
389
- }
390
- else
391
- {
392
- // check for empty variable name
393
- if (0 < parts[1].Length)
394
- {
395
- string result = this.GetVariableValue(sourceLineNumbers, parts[0], parts[1]);
396
-
397
- // If we didn't find it and we allow missing prefixes and the variable contains a dot, perhaps the dot isn't intended to indicate a prefix
398
- if (null == result && allowMissingPrefix && variable.Contains("."))
399
- {
400
- result = this.GetVariableValue(sourceLineNumbers, "var", variable);
401
- }
402
-
403
- return result;
404
- }
405
- else
406
- {
407
- throw new WixException(WixErrors.InvalidPreprocessorVariable(sourceLineNumbers, variable));
408
- }
409
- }
410
- }
411
-
412
- /// <summary>
413
- /// Get the value of a variable.
414
- /// </summary>
415
- /// <param name="sourceLineNumbers">The source line information for the function.</param>
416
- /// <param name="prefix">The variable prefix.</param>
417
- /// <param name="name">The variable name.</param>
418
- /// <returns>The variable value or null if the variable is not set.</returns>
419
- public string GetVariableValue(SourceLineNumber sourceLineNumbers, string prefix, string name)
420
- {
421
- if (String.IsNullOrEmpty(prefix))
422
- {
423
- throw new ArgumentNullException("prefix");
424
- }
425
-
426
- if (String.IsNullOrEmpty(name))
427
- {
428
- throw new ArgumentNullException("name");
429
- }
430
-
431
- switch (prefix)
432
- {
433
- case "env":
434
- return Environment.GetEnvironmentVariable(name);
435
- case "sys":
436
- switch (name)
437
- {
438
- case "CURRENTDIR":
439
- return String.Concat(Directory.GetCurrentDirectory(), Path.DirectorySeparatorChar);
440
- case "SOURCEFILEDIR":
441
- return String.Concat(Path.GetDirectoryName(sourceLineNumbers.FileName), Path.DirectorySeparatorChar);
442
- case "SOURCEFILEPATH":
443
- return sourceLineNumbers.FileName;
444
- case "PLATFORM":
445
- this.OnMessage(WixWarnings.DeprecatedPreProcVariable(sourceLineNumbers, "$(sys.PLATFORM)", "$(sys.BUILDARCH)"));
446
-
447
- goto case "BUILDARCH";
448
-
449
- case "BUILDARCH":
450
- switch (this.currentPlatform)
451
- {
452
- case Platform.X86:
453
- return "x86";
454
- case Platform.X64:
455
- return "x64";
456
- case Platform.IA64:
457
- return "ia64";
458
- case Platform.ARM:
459
- return "arm";
460
- default:
461
- throw new ArgumentException(WixStrings.EXP_UnknownPlatformEnum, this.currentPlatform.ToString());
462
- }
463
- default:
464
- return null;
465
- }
466
- case "var":
467
- string result = null;
468
- return this.variables.TryGetValue(name, out result) ? result : null;
469
- default:
470
- PreprocessorExtension extension = (PreprocessorExtension)this.extensionsByPrefix[prefix];
471
- if (null != extension)
472
- {
473
- try
474
- {
475
- return extension.GetVariableValue(prefix, name);
476
- }
477
- catch (Exception e)
478
- {
479
- throw new WixException(WixErrors.PreprocessorExtensionGetVariableValueFailed(sourceLineNumbers, prefix, name, e.Message));
480
- }
481
- }
482
- else
483
- {
484
- return null;
485
- }
486
- }
487
- }
488
-
489
- /// <summary>
490
- /// Sends a message to the message delegate if there is one.
491
- /// </summary>
492
- /// <param name="mea">Message event arguments.</param>
493
- public void OnMessage(MessageEventArgs e)
494
- {
495
- Messaging.Instance.OnMessage(e);
496
- }
497
-
498
- /// <summary>
499
- /// Sends resolved variable to delegate if there is one.
500
- /// </summary>
501
- /// <param name="mea">Message event arguments.</param>
502
- public void OnResolvedVariable(ResolvedVariableEventArgs mea)
503
- {
504
- if (null != this.ResolvedVariable)
505
- {
506
- this.ResolvedVariable(this, mea);
507
- }
508
- }
509
-
510
- /// <summary>
511
- /// Add a variable.
512
- /// </summary>
513
- /// <param name="sourceLineNumbers">The source line information of the variable.</param>
514
- /// <param name="name">The variable name.</param>
515
- /// <param name="value">The variable value.</param>
516
- internal void AddVariable(SourceLineNumber sourceLineNumbers, string name, string value)
517
- {
518
- this.AddVariable(sourceLineNumbers, name, value, true);
519
- }
520
-
521
- /// <summary>
522
- /// Add a variable.
523
- /// </summary>
524
- /// <param name="sourceLineNumbers">The source line information of the variable.</param>
525
- /// <param name="name">The variable name.</param>
526
- /// <param name="value">The variable value.</param>
527
- /// <param name="overwrite">Set to true to show variable overwrite warning.</param>
528
- internal void AddVariable(SourceLineNumber sourceLineNumbers, string name, string value, bool showWarning)
529
- {
530
- string currentValue = this.GetVariableValue(sourceLineNumbers, "var", name);
531
-
532
- if (null == currentValue)
533
- {
534
- this.variables.Add(name, value);
535
- }
536
- else
537
- {
538
- if (showWarning)
539
- {
540
- this.OnMessage(WixWarnings.VariableDeclarationCollision(sourceLineNumbers, name, value, currentValue));
541
- }
542
-
543
- this.variables[name] = value;
544
- }
545
- }
546
-
547
- /// <summary>
548
- /// Remove a variable.
549
- /// </summary>
550
- /// <param name="sourceLineNumbers">The source line information of the variable.</param>
551
- /// <param name="name">The variable name.</param>
552
- internal void RemoveVariable(SourceLineNumber sourceLineNumbers, string name)
553
- {
554
- if (!this.variables.Remove(name))
555
- {
556
- this.OnMessage(WixErrors.CannotReundefineVariable(sourceLineNumbers, name));
557
- }
558
- }
559
- }
560
-}
src/WixToolset.Core/WixToolsetServiceProvider.cs
+11
@@ -12,6 +12,7 @@ namespace WixToolset.Core
12
{
13
private ExtensionManager extensionManager;
14
private ParseHelper parseHelper;
15
+ private PreprocessHelper preprocessHelper;
16
private TupleDefinitionCreator tupleDefinitionCreator;
17
18
public object GetService(Type serviceType)
@@ -19,6 +20,11 @@ namespace WixToolset.Core
20
if (serviceType == null) throw new ArgumentNullException(nameof(serviceType));
21
22
// Transients.
23
+ if (serviceType == typeof(IPreprocessContext))
24
+ {
25
+ return new PreprocessContext(this);
26
+ }
27
+
28
if (serviceType == typeof(ICompileContext))
29
{
30
return new CompileContext(this);
@@ -65,6 +71,11 @@ namespace WixToolset.Core
71
return this.parseHelper = this.parseHelper ?? new ParseHelper(this);
72
}
73
74
+ if (serviceType == typeof(IPreprocessHelper))
75
+ {
76
+ return this.preprocessHelper = this.preprocessHelper ?? new PreprocessHelper(this);
77
+ }
78
+
79
throw new ArgumentException($"Unknown service type: {serviceType.Name}", nameof(serviceType));
80
}
81
}
src/test/Example.Extension/ExampleExtensionFactory.cs
+9
-2
@@ -7,11 +7,18 @@ namespace Example.Extension
7
8
public class ExampleExtensionFactory : IExtensionFactory
9
{
10
+ private ExamplePreprocessorExtensionAndCommandLine preprocessorExtension;
11
+
12
public bool TryCreateExtension(Type extensionType, out object extension)
13
{
12
- if (extensionType == typeof(IPreprocessorExtension))
14
+ if (extensionType == typeof(IExtensionCommandLine) || extensionType == typeof(IPreprocessorExtension))
15
{
14
- extension = new ExamplePreprocessorExtension();
16
+ if (preprocessorExtension == null)
17
+ {
18
+ preprocessorExtension = new ExamplePreprocessorExtensionAndCommandLine();
19
+ }
20
+
21
+ extension = preprocessorExtension;
22
}
23
else if (extensionType == typeof(ICompilerExtension))
24
{
src/test/Example.Extension/ExamplePreprocessorExtension.cs
deleted
-55
@@ -1,55 +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 Example.Extension
4
-{
5
- using System;
6
- using System.Xml.Linq;
7
- using WixToolset.Data;
8
- using WixToolset.Extensibility;
9
-
10
- internal class ExamplePreprocessorExtension : IPreprocessorExtension
11
- {
12
- public ExamplePreprocessorExtension()
13
- {
14
- }
15
-
16
- public IPreprocessorCore Core { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
17
-
18
- public string[] Prefixes => throw new NotImplementedException();
19
-
20
- public string EvaluateFunction(string prefix, string function, string[] args)
21
- {
22
- throw new NotImplementedException();
23
- }
24
-
25
- public void Finish()
26
- {
27
- throw new NotImplementedException();
28
- }
29
-
30
- public string GetVariableValue(string prefix, string name)
31
- {
32
- throw new NotImplementedException();
33
- }
34
-
35
- public void Initialize()
36
- {
37
- throw new NotImplementedException();
38
- }
39
-
40
- public void PreprocessDocument(XDocument document)
41
- {
42
- throw new NotImplementedException();
43
- }
44
-
45
- public string PreprocessParameter(string name)
46
- {
47
- throw new NotImplementedException();
48
- }
49
-
50
- public bool ProcessPragma(SourceLineNumber sourceLineNumbers, string prefix, string pragma, string args, XContainer parent)
51
- {
52
- throw new NotImplementedException();
53
- }
54
- }
55
-}
\ No newline at end of file
src/test/Example.Extension/ExamplePreprocessorExtensionAndCommandLine.cs
new
+46
@@ -0,0 +1,46 @@
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 Example.Extension
4
+{
5
+ using System;
6
+ using System.Collections.Generic;
7
+ using WixToolset.Extensibility;
8
+ using WixToolset.Extensibility.Services;
9
+
10
+ internal class ExamplePreprocessorExtensionAndCommandLine : BasePreprocessorExtension, IExtensionCommandLine
11
+ {
12
+ private string exampleValueFromCommandLine;
13
+
14
+ public IEnumerable<ExtensionCommandLineSwitch> CommandLineSwitches => throw new NotImplementedException();
15
+
16
+ public ExamplePreprocessorExtensionAndCommandLine()
17
+ {
18
+ this.Prefixes = new[] { "ex" };
19
+ }
20
+
21
+ public void PreParse(ICommandLineContext context)
22
+ {
23
+ }
24
+
25
+ public bool TryParseArgument(IParseCommandLine parseCommandLine, string arg)
26
+ {
27
+ if (parseCommandLine.IsSwitch(arg) && arg.Substring(1).Equals("example", StringComparison.OrdinalIgnoreCase))
28
+ {
29
+ parseCommandLine.GetNextArgumentOrError(ref this.exampleValueFromCommandLine);
30
+ return true;
31
+ }
32
+
33
+ return false;
34
+ }
35
+
36
+ public override string GetVariableValue(string prefix, string name)
37
+ {
38
+ if (prefix == "ex" && "test".Equals(name, StringComparison.OrdinalIgnoreCase))
39
+ {
40
+ return String.IsNullOrWhiteSpace(this.exampleValueFromCommandLine) ? "(null)" : this.exampleValueFromCommandLine;
41
+ }
42
+
43
+ return null;
44
+ }
45
+ }
46
+}
\ No newline at end of file
src/test/WixToolsetTest.CoreIntegration/ExtensionFixture.cs
+43
@@ -56,5 +56,48 @@ namespace WixToolsetTest.CoreIntegration
56
Assert.Equal("Bar", example[1].AsString());
57
}
58
}
59
+
60
+ [Fact]
61
+ public void CanParseCommandLineWithExtension()
62
+ {
63
+ var folder = TestData.Get(@"TestData\ExampleExtension");
64
+ var extensionPath = Path.GetFullPath(new Uri(typeof(ExampleExtensionFactory).Assembly.CodeBase).LocalPath);
65
+
66
+ using (var fs = new DisposableFileSystem())
67
+ {
68
+ var intermediateFolder = fs.GetFolder();
69
+
70
+ var program = new Program();
71
+ var result = program.Run(new WixToolsetServiceProvider(), new[]
72
+ {
73
+ "build",
74
+ Path.Combine(folder, "Package.wxs"),
75
+ Path.Combine(folder, "PackageComponents.wxs"),
76
+ "-loc", Path.Combine(folder, "Package.en-us.wxl"),
77
+ "-ext", extensionPath,
78
+ "-bindpath", Path.Combine(folder, "data"),
79
+ "-intermediateFolder", intermediateFolder,
80
+ "-example", "test",
81
+ "-o", Path.Combine(intermediateFolder, @"bin\extest.msi")
82
+ });
83
+
84
+ Assert.Equal(0, result);
85
+
86
+ Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\extest.msi")));
87
+ Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\extest.wixpdb")));
88
+ Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\MsiPackage\example.txt")));
89
+
90
+ var intermediate = Intermediate.Load(Path.Combine(intermediateFolder, @"bin\extest.wir"));
91
+ var section = intermediate.Sections.Single();
92
+
93
+ var wixFile = section.Tuples.OfType<WixFileTuple>().Single();
94
+ Assert.Equal(Path.Combine(folder, @"data\example.txt"), wixFile[WixFileTupleFields.Source].AsPath().Path);
95
+ Assert.Equal(@"example.txt", wixFile[WixFileTupleFields.Source].PreviousValue.AsPath().Path);
96
+
97
+ var property = section.Tuples.OfType<PropertyTuple>().Where(p => p.Id.Id == "ExampleProperty").Single();
98
+ Assert.Equal("ExampleProperty", property.Property);
99
+ Assert.Equal("test", property.Value);
100
+ }
101
+ }
102
}
103
}
src/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/Package.wxs
+2
@@ -6,6 +6,8 @@
6
<MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" />
7
<MediaTemplate />
8
9
+ <Property Id="ExampleProperty" Value="$(ex.Test)" />
10
+
11
<Feature Id="ProductFeature" Title="!(loc.FeatureTitle)">
12
<ComponentGroupRef Id="ProductComponents" />
13
</Feature>