@joebigelow / wix-1 / commits / 615bc202

Include the preprocessed include files with the processed document

This change also cleans up the internal state handling of the preprocesor to pass the processing state around rather than depend on "global state" in member variables. This removes the need to "reset" the member variables before preprocessing which is much cleaner.

Rob Mensching committed Mar 1, 2019 at 16:45 UTC 615bc202834ac45a9a107e5fccd900081a4abf74
8 files changed +266 -177
src/WixToolset.Core/CommandLine/BuildCommand.cs
+3 -3
@@ -377,18 +377,18 @@ namespace WixToolset.Core.CommandLine
377 context.SourcePath = sourcePath;
378 context.Variables = preprocessorVariables;
379
380 - XDocument document = null;
380 + IPreprocessResult result = null;
381 try
382 {
383 var preprocessor = this.ServiceProvider.GetService<IPreprocessor>();
384 - document = preprocessor.Preprocess(context);
384 + result = preprocessor.Preprocess(context);
385 }
386 catch (WixException e)
387 {
388 this.Messaging.Write(e.Error);
389 }
390
391 - return document;
391 + return result?.Document;
392 }
393
394 private class CommandLine
src/WixToolset.Core/CommandLine/CompileCommand.cs
+3 -3
@@ -63,11 +63,11 @@ namespace WixToolset.Core.CommandLine
63 context.SourcePath = sourceFile.SourcePath;
64 context.Variables = this.PreprocessorVariables;
65
66 - XDocument document = null;
66 + IPreprocessResult result = null;
67 try
68 {
69 var preprocessor = this.ServiceProvider.GetService<IPreprocessor>();
70 - document = preprocessor.Preprocess(context);
70 + result = preprocessor.Preprocess(context);
71 }
72 catch (WixException e)
73 {
@@ -83,7 +83,7 @@ namespace WixToolset.Core.CommandLine
83 compileContext.Extensions = this.ExtensionManager.Create<ICompilerExtension>();
84 compileContext.OutputPath = sourceFile.OutputPath;
85 compileContext.Platform = this.Platform;
86 - compileContext.Source = document;
86 + compileContext.Source = result?.Document;
87
88 var compiler = this.ServiceProvider.GetService<ICompiler>();
89 var intermediate = compiler.Compile(compileContext);
src/WixToolset.Core/IPreprocessor.cs
+2 -3
@@ -3,13 +3,12 @@
3 namespace WixToolset.Core
4 {
5 using System.Xml;
6 - using System.Xml.Linq;
6 using WixToolset.Extensibility.Data;
7
8 public interface IPreprocessor
9 {
11 - XDocument Preprocess(IPreprocessContext context);
10 + IPreprocessResult Preprocess(IPreprocessContext context);
11
13 - XDocument Preprocess(IPreprocessContext context, XmlReader reader);
12 + IPreprocessResult Preprocess(IPreprocessContext context, XmlReader reader);
13 }
14 }
src/WixToolset.Core/IncludedFile.cs new
+14
@@ -0,0 +1,14 @@
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 WixToolset.Data;
6 + using WixToolset.Extensibility.Data;
7 +
8 + internal class IncludedFile : IIncludedFile
9 + {
10 + public string Path { get; set; }
11 +
12 + public SourceLineNumber SourceLineNumbers { get; set; }
13 + }
14 +}
src/WixToolset.Core/PreprocessResult.cs new
+15
@@ -0,0 +1,15 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.Core
4 +{
5 + using System.Collections.Generic;
6 + using System.Xml.Linq;
7 + using WixToolset.Extensibility.Data;
8 +
9 + public class PreprocessResult : IPreprocessResult
10 + {
11 + public XDocument Document { get; set; }
12 +
13 + public IEnumerable<IIncludedFile> IncludedFiles { get; set; }
14 + }
15 +}
src/WixToolset.Core/Preprocessor.cs
+198 -164
@@ -48,18 +48,6 @@ namespace WixToolset.Core
48
49 private IMessaging Messaging { get; }
50
51 - private IPreprocessContext Context { get; set; }
52 -
53 - private Stack<string> CurrentFileStack { get; } = new Stack<string>();
54 -
55 - private Dictionary<string, IPreprocessorExtension> ExtensionsByPrefix { get; } = new Dictionary<string, IPreprocessorExtension>();
56 -
57 - private Stack<bool> IncludeNextStack { get; } = new Stack<bool>();
58 -
59 - private Stack<SourceLineNumber> SourceStack { get; } = new Stack<SourceLineNumber>();
60 -
61 - private IPreprocessHelper Helper { get; set; }
62 -
51 /// <summary>
52 /// Event for ifdef/ifndef directives.
53 /// </summary>
@@ -100,21 +88,21 @@ namespace WixToolset.Core
88 /// </summary>
89 /// <param name="context">The preprocessing context.</param>
90 /// <returns>XDocument with the postprocessed data.</returns>
103 - public XDocument Preprocess(IPreprocessContext context)
91 + public IPreprocessResult Preprocess(IPreprocessContext context)
92 {
105 - this.Context = context;
106 - this.Context.CurrentSourceLineNumber = new SourceLineNumber(context.SourcePath);
107 - this.Context.Variables = this.Context.Variables == null ? new Dictionary<string, string>() : new Dictionary<string, string>(this.Context.Variables);
93 + var state = new ProcessingState(this.ServiceProvider, context);
94
109 - this.PreProcess();
95 + this.PreProcess(state);
96
111 - XDocument document;
112 - using (var reader = XmlReader.Create(this.Context.SourcePath, DocumentXmlReaderSettings))
97 + IPreprocessResult result;
98 + using (var reader = XmlReader.Create(state.Context.SourcePath, DocumentXmlReaderSettings))
99 {
114 - document = this.Process(reader);
100 + result = this.Process(state, reader);
101 }
102
117 - return this.PostProcess(document);
103 + this.PostProcess(state, result);
104 +
105 + return result;
106 }
107
108 /// <summary>
@@ -123,7 +111,7 @@ namespace WixToolset.Core
111 /// <param name="context">The preprocessing context.</param>
112 /// <param name="reader">XmlReader to processing the context.</param>
113 /// <returns>XDocument with the postprocessed data.</returns>
126 - public XDocument Preprocess(IPreprocessContext context, XmlReader reader)
114 + public IPreprocessResult Preprocess(IPreprocessContext context, XmlReader reader)
115 {
116 if (String.IsNullOrEmpty(context.SourcePath) && !String.IsNullOrEmpty(reader.BaseURI))
117 {
@@ -131,15 +119,15 @@ namespace WixToolset.Core
119 context.SourcePath = uri.AbsolutePath;
120 }
121
134 - this.Context = context;
135 - this.Context.CurrentSourceLineNumber = new SourceLineNumber(context.SourcePath);
136 - this.Context.Variables = (this.Context.Variables == null) ? new Dictionary<string, string>() : new Dictionary<string, string>(this.Context.Variables);
122 + var state = new ProcessingState(this.ServiceProvider, context);
123
138 - this.PreProcess();
124 + this.PreProcess(state);
125
140 - var document = this.Process(reader);
126 + var result = this.Process(state, reader);
127
142 - return this.PostProcess(document);
128 + this.PostProcess(state, result);
129 +
130 + return result;
131 }
132
133 /// <summary>
@@ -148,29 +136,33 @@ namespace WixToolset.Core
136 /// <param name="context">The preprocessing context.</param>
137 /// <param name="reader">XmlReader to processing the context.</param>
138 /// <returns>XDocument with the postprocessed data.</returns>
151 - private XDocument Process(XmlReader reader)
139 + private IPreprocessResult Process(ProcessingState state, XmlReader reader)
140 {
153 - this.Helper = this.ServiceProvider.GetService<IPreprocessHelper>();
154 -
155 - this.CurrentFileStack.Clear();
156 - this.CurrentFileStack.Push(this.Helper.GetVariableValue(this.Context, "sys", "SOURCEFILEDIR"));
141 + state.CurrentFileStack.Push(state.Helper.GetVariableValue(state.Context, "sys", "SOURCEFILEDIR"));
142
143 // Process the reader into the output.
159 - var output = new XDocument();
144 + IPreprocessResult result = null;
145 try
146 {
162 - this.PreprocessReader(false, reader, output, 0);
147 + this.PreprocessReader(state, false, reader, state.Output, 0);
148
149 // Fire event with post-processed document.
165 - this.ProcessedStream?.Invoke(this, new ProcessedStreamEventArgs(this.Context.SourcePath, output));
150 + this.ProcessedStream?.Invoke(this, new ProcessedStreamEventArgs(state.Context.SourcePath, state.Output));
151 +
152 + if (!this.Messaging.EncounteredError)
153 + {
154 + result = this.ServiceProvider.GetService<IPreprocessResult>();
155 + result.Document = state.Output;
156 + result.IncludedFiles = state.IncludedFiles;
157 + }
158 }
159 catch (XmlException e)
160 {
169 - this.UpdateCurrentLineNumber(reader, 0);
170 - throw new WixException(ErrorMessages.InvalidXml(this.Context.CurrentSourceLineNumber, "source", e.Message));
161 + this.UpdateCurrentLineNumber(state, reader, 0);
162 + throw new WixException(ErrorMessages.InvalidXml(state.Context.CurrentSourceLineNumber, "source", e.Message));
163 }
164
173 - return this.Messaging.EncounteredError ? null : output;
165 + return result;
166 }
167
168 /// <summary>
@@ -277,7 +269,7 @@ namespace WixToolset.Core
269 /// <param name="reader">Reader for the source document.</param>
270 /// <param name="container">Node where content should be added.</param>
271 /// <param name="offset">Original offset for the line numbers being processed.</param>
280 - private void PreprocessReader(bool include, XmlReader reader, XContainer container, int offset)
272 + private void PreprocessReader(ProcessingState state, bool include, XmlReader reader, XContainer container, int offset)
273 {
274 var currentContainer = container;
275 var containerStack = new Stack<XContainer>();
@@ -289,9 +281,9 @@ namespace WixToolset.Core
281 while (reader.Read())
282 {
283 // update information here in case an error occurs before the next read
292 - this.UpdateCurrentLineNumber(reader, offset);
284 + this.UpdateCurrentLineNumber(state, reader, offset);
285
294 - var sourceLineNumbers = this.Context.CurrentSourceLineNumber;
286 + var sourceLineNumbers = state.Context.CurrentSourceLineNumber;
287
288 // check for changes in conditional processing
289 if (XmlNodeType.ProcessingInstruction == reader.NodeType)
@@ -305,7 +297,7 @@ namespace WixToolset.Core
297 ifStack.Push(ifContext);
298 if (ifContext.IsTrue)
299 {
308 - ifContext = new IfContext(ifContext.IsTrue & ifContext.Active, this.EvaluateExpression(reader.Value), IfState.If);
300 + ifContext = new IfContext(ifContext.IsTrue & ifContext.Active, this.EvaluateExpression(state, reader.Value), IfState.If);
301 }
302 else // Use a default IfContext object so we don't try to evaluate the expression if the context isn't true
303 {
@@ -319,7 +311,7 @@ namespace WixToolset.Core
311 name = reader.Value.Trim();
312 if (ifContext.IsTrue)
313 {
322 - ifContext = new IfContext(ifContext.IsTrue & ifContext.Active, (null != this.Helper.GetVariableValue(this.Context, name, true)), IfState.If);
314 + ifContext = new IfContext(ifContext.IsTrue & ifContext.Active, (null != state.Helper.GetVariableValue(state.Context, name, true)), IfState.If);
315 }
316 else // Use a default IfContext object so we don't try to evaluate the expression if the context isn't true
317 {
@@ -334,7 +326,7 @@ namespace WixToolset.Core
326 name = reader.Value.Trim();
327 if (ifContext.IsTrue)
328 {
337 - ifContext = new IfContext(ifContext.IsTrue & ifContext.Active, (null == this.Helper.GetVariableValue(this.Context, name, true)), IfState.If);
329 + ifContext = new IfContext(ifContext.IsTrue & ifContext.Active, (null == state.Helper.GetVariableValue(state.Context, name, true)), IfState.If);
330 }
331 else // Use a default IfContext object so we don't try to evaluate the expression if the context isn't true
332 {
@@ -358,7 +350,7 @@ namespace WixToolset.Core
350 ifContext.IfState = IfState.ElseIf; // we're now in an elseif
351 if (!ifContext.WasEverTrue) // if we've never evaluated the if context to true, then we can try this test
352 {
361 - ifContext.IsTrue = this.EvaluateExpression(reader.Value);
353 + ifContext.IsTrue = this.EvaluateExpression(state, reader.Value);
354 }
355 else if (ifContext.IsTrue)
356 {
@@ -441,35 +433,35 @@ namespace WixToolset.Core
433 switch (reader.LocalName)
434 {
435 case "define":
444 - this.PreprocessDefine(reader.Value);
436 + this.PreprocessDefine(state, reader.Value);
437 break;
438
439 case "error":
448 - this.PreprocessError(reader.Value);
440 + this.PreprocessError(state, reader.Value);
441 break;
442
443 case "warning":
452 - this.PreprocessWarning(reader.Value);
444 + this.PreprocessWarning(state, reader.Value);
445 break;
446
447 case "undef":
456 - this.PreprocessUndef(reader.Value);
448 + this.PreprocessUndef(state, reader.Value);
449 break;
450
451 case "include":
460 - this.UpdateCurrentLineNumber(reader, offset);
461 - this.PreprocessInclude(reader.Value, currentContainer);
452 + this.UpdateCurrentLineNumber(state, reader, offset);
453 + this.PreprocessInclude(state, reader.Value, currentContainer);
454 break;
455
456 case "foreach":
465 - this.PreprocessForeach(reader, currentContainer, offset);
457 + this.PreprocessForeach(state, reader, currentContainer, offset);
458 break;
459
460 case "endforeach": // endforeach is handled in PreprocessForeach, so seeing it here is an error
461 throw new WixException(ErrorMessages.UnmatchedPreprocessorInstruction(sourceLineNumbers, "foreach", "endforeach"));
462
463 case "pragma":
472 - this.PreprocessPragma(reader.Value, currentContainer);
464 + this.PreprocessPragma(state, reader.Value, currentContainer);
465 break;
466
467 default:
@@ -479,15 +471,15 @@ namespace WixToolset.Core
471 break;
472
473 case XmlNodeType.Element:
482 - if (0 < this.IncludeNextStack.Count && this.IncludeNextStack.Peek())
474 + if (0 < state.IncludeNextStack.Count && state.IncludeNextStack.Peek())
475 {
476 if ("Include" != reader.LocalName)
477 {
478 this.Messaging.Write(ErrorMessages.InvalidDocumentElement(sourceLineNumbers, reader.Name, "include", "Include"));
479 }
480
489 - this.IncludeNextStack.Pop();
490 - this.IncludeNextStack.Push(false);
481 + state.IncludeNextStack.Pop();
482 + state.IncludeNextStack.Push(false);
483 break;
484 }
485
@@ -496,12 +488,12 @@ namespace WixToolset.Core
488 var element = new XElement(ns + reader.LocalName);
489 currentContainer.Add(element);
490
499 - this.UpdateCurrentLineNumber(reader, offset);
491 + this.UpdateCurrentLineNumber(state, reader, offset);
492 element.AddAnnotation(sourceLineNumbers);
493
494 while (reader.MoveToNextAttribute())
495 {
504 - var value = this.Helper.PreprocessString(this.Context, reader.Value);
496 + var value = state.Helper.PreprocessString(state.Context, reader.Value);
497
498 var attribNamespace = XNamespace.Get(reader.NamespaceURI);
499 attribNamespace = XNamespace.Xmlns == attribNamespace && reader.LocalName.Equals("xmlns") ? XNamespace.None : attribNamespace;
@@ -524,12 +516,12 @@ namespace WixToolset.Core
516 break;
517
518 case XmlNodeType.Text:
527 - var postprocessedText = this.Helper.PreprocessString(this.Context, reader.Value);
519 + var postprocessedText = state.Helper.PreprocessString(state.Context, reader.Value);
520 currentContainer.Add(postprocessedText);
521 break;
522
523 case XmlNodeType.CDATA:
532 - var postprocessedValue = this.Helper.PreprocessString(this.Context, reader.Value);
524 + var postprocessedValue = state.Helper.PreprocessString(state.Context, reader.Value);
525 currentContainer.Add(new XCData(postprocessedValue));
526 break;
527
@@ -540,13 +532,13 @@ namespace WixToolset.Core
532
533 if (0 != ifStack.Count)
534 {
543 - throw new WixException(ErrorMessages.NonterminatedPreprocessorInstruction(this.Context.CurrentSourceLineNumber, "if", "endif"));
535 + throw new WixException(ErrorMessages.NonterminatedPreprocessorInstruction(state.Context.CurrentSourceLineNumber, "if", "endif"));
536 }
537
538 // TODO: can this actually happen?
539 if (0 != containerStack.Count)
540 {
549 - throw new WixException(ErrorMessages.NonterminatedPreprocessorInstruction(this.Context.CurrentSourceLineNumber, "nodes", "nodes"));
541 + throw new WixException(ErrorMessages.NonterminatedPreprocessorInstruction(state.Context.CurrentSourceLineNumber, "nodes", "nodes"));
542 }
543 }
544
@@ -554,37 +546,37 @@ namespace WixToolset.Core
546 /// Processes an error processing instruction.
547 /// </summary>
548 /// <param name="errorMessage">Text from source.</param>
557 - private void PreprocessError(string errorMessage)
549 + private void PreprocessError(ProcessingState state, string errorMessage)
550 {
551 // Resolve other variables in the error message.
560 - errorMessage = this.Helper.PreprocessString(this.Context, errorMessage);
552 + errorMessage = state.Helper.PreprocessString(state.Context, errorMessage);
553
562 - throw new WixException(ErrorMessages.PreprocessorError(this.Context.CurrentSourceLineNumber, errorMessage));
554 + throw new WixException(ErrorMessages.PreprocessorError(state.Context.CurrentSourceLineNumber, errorMessage));
555 }
556
557 /// <summary>
558 /// Processes a warning processing instruction.
559 /// </summary>
560 /// <param name="warningMessage">Text from source.</param>
569 - private void PreprocessWarning(string warningMessage)
561 + private void PreprocessWarning(ProcessingState state, string warningMessage)
562 {
563 // Resolve other variables in the warning message.
572 - warningMessage = this.Helper.PreprocessString(this.Context, warningMessage);
564 + warningMessage = state.Helper.PreprocessString(state.Context, warningMessage);
565
574 - this.Messaging.Write(WarningMessages.PreprocessorWarning(this.Context.CurrentSourceLineNumber, warningMessage));
566 + this.Messaging.Write(WarningMessages.PreprocessorWarning(state.Context.CurrentSourceLineNumber, warningMessage));
567 }
568
569 /// <summary>
570 /// Processes a define processing instruction and creates the appropriate parameter.
571 /// </summary>
572 /// <param name="originalDefine">Text from source.</param>
581 - private void PreprocessDefine(string originalDefine)
573 + private void PreprocessDefine(ProcessingState state, string originalDefine)
574 {
575 var match = DefineRegex.Match(originalDefine);
576
577 if (!match.Success)
578 {
587 - throw new WixException(ErrorMessages.IllegalDefineStatement(this.Context.CurrentSourceLineNumber, originalDefine));
579 + throw new WixException(ErrorMessages.IllegalDefineStatement(state.Context.CurrentSourceLineNumber, originalDefine));
580 }
581
582 var defineName = match.Groups["varName"].Value;
@@ -599,15 +591,15 @@ namespace WixToolset.Core
591 }
592
593 // resolve other variables in the variable value
602 - defineValue = this.Helper.PreprocessString(this.Context, defineValue);
594 + defineValue = state.Helper.PreprocessString(state.Context, defineValue);
595
596 if (defineName.StartsWith("var.", StringComparison.Ordinal))
597 {
606 - this.Helper.AddVariable(this.Context, defineName.Substring(4), defineValue);
598 + state.Helper.AddVariable(state.Context, defineName.Substring(4), defineValue);
599 }
600 else
601 {
610 - this.Helper.AddVariable(this.Context, defineName, defineValue);
602 + state.Helper.AddVariable(state.Context, defineName, defineValue);
603 }
604 }
605
@@ -615,17 +607,17 @@ namespace WixToolset.Core
607 /// Processes an undef processing instruction and creates the appropriate parameter.
608 /// </summary>
609 /// <param name="originalDefine">Text from source.</param>
618 - private void PreprocessUndef(string originalDefine)
610 + private void PreprocessUndef(ProcessingState state, string originalDefine)
611 {
620 - var name = this.Helper.PreprocessString(this.Context, originalDefine.Trim());
612 + var name = state.Helper.PreprocessString(state.Context, originalDefine.Trim());
613
614 if (name.StartsWith("var.", StringComparison.Ordinal))
615 {
624 - this.Helper.RemoveVariable(this.Context, name.Substring(4));
616 + state.Helper.RemoveVariable(state.Context, name.Substring(4));
617 }
618 else
619 {
628 - this.Helper.RemoveVariable(this.Context, name);
620 + state.Helper.RemoveVariable(state.Context, name);
621 }
622 }
623
@@ -634,14 +626,14 @@ namespace WixToolset.Core
626 /// </summary>
627 /// <param name="includePath">Path to included file.</param>
628 /// <param name="parent">Parent container for included content.</param>
637 - private void PreprocessInclude(string includePath, XContainer parent)
629 + private void PreprocessInclude(ProcessingState state, string includePath, XContainer parent)
630 {
639 - var sourceLineNumbers = this.Context.CurrentSourceLineNumber;
631 + var sourceLineNumbers = state.Context.CurrentSourceLineNumber;
632
633 // Preprocess variables in the path.
642 - includePath = this.Helper.PreprocessString(this.Context, includePath);
634 + includePath = state.Helper.PreprocessString(state.Context, includePath);
635
644 - var includeFile = this.GetIncludeFile(includePath);
636 + var includeFile = this.GetIncludeFile(state, includePath);
637
638 if (null == includeFile)
639 {
@@ -650,22 +642,28 @@ namespace WixToolset.Core
642
643 using (var reader = XmlReader.Create(includeFile, DocumentXmlReaderSettings))
644 {
653 - this.PushInclude(includeFile);
645 + this.PushInclude(state, includeFile);
646
647 // process the included reader into the writer
648 try
649 {
658 - this.PreprocessReader(true, reader, parent, 0);
650 + this.PreprocessReader(state, true, reader, parent, 0);
651 }
652 catch (XmlException e)
653 {
662 - this.UpdateCurrentLineNumber(reader, 0);
654 + this.UpdateCurrentLineNumber(state, reader, 0);
655 throw new WixException(ErrorMessages.InvalidXml(sourceLineNumbers, "source", e.Message));
656 }
657
658 this.IncludedFile?.Invoke(this, new IncludedFileEventArgs(sourceLineNumbers, includeFile));
659
668 - this.PopInclude();
660 + var includedFile = this.ServiceProvider.GetService<IIncludedFile>();
661 + includedFile.Path = includeFile;
662 + includedFile.SourceLineNumbers = sourceLineNumbers;
663 +
664 + state.IncludedFiles.Add(includedFile);
665 +
666 + this.PopInclude(state);
667 }
668 }
669
@@ -675,13 +673,13 @@ namespace WixToolset.Core
673 /// <param name="reader">The xml reader.</param>
674 /// <param name="container">The container where to output processed data.</param>
675 /// <param name="offset">Offset for the line numbers.</param>
678 - private void PreprocessForeach(XmlReader reader, XContainer container, int offset)
676 + private void PreprocessForeach(ProcessingState state, XmlReader reader, XContainer container, int offset)
677 {
678 // Find the "in" token.
679 var indexOfInToken = reader.Value.IndexOf(" in ", StringComparison.Ordinal);
680 if (0 > indexOfInToken)
681 {
684 - throw new WixException(ErrorMessages.IllegalForeach(this.Context.CurrentSourceLineNumber, reader.Value));
682 + throw new WixException(ErrorMessages.IllegalForeach(state.Context.CurrentSourceLineNumber, reader.Value));
683 }
684
685 // parse out the variable name
@@ -689,7 +687,7 @@ namespace WixToolset.Core
687 var varValuesString = reader.Value.Substring(indexOfInToken + 4).Trim();
688
689 // preprocess the variable values string because it might be a variable itself
692 - varValuesString = this.Helper.PreprocessString(this.Context, varValuesString);
690 + varValuesString = state.Helper.PreprocessString(state.Context, varValuesString);
691
692 var varValues = varValuesString.Split(';');
693
@@ -751,7 +749,7 @@ namespace WixToolset.Core
749 }
750 else if (reader.NodeType == XmlNodeType.None)
751 {
754 - throw new WixException(ErrorMessages.ExpectedEndforeach(this.Context.CurrentSourceLineNumber));
752 + throw new WixException(ErrorMessages.ExpectedEndforeach(state.Context.CurrentSourceLineNumber));
753 }
754
755 reader.Read();
@@ -765,16 +763,16 @@ namespace WixToolset.Core
763 using (var loopReader = XmlReader.Create(fragmentStream, FragmentXmlReaderSettings))
764 {
765 // Always overwrite foreach variables.
768 - this.Helper.AddVariable(this.Context, varName, varValue, false);
766 + state.Helper.AddVariable(state.Context, varName, varValue, false);
767
768 try
769 {
772 - this.PreprocessReader(false, loopReader, container, offset);
770 + this.PreprocessReader(state, false, loopReader, container, offset);
771 }
772 catch (XmlException e)
773 {
776 - this.UpdateCurrentLineNumber(loopReader, offset);
777 - throw new WixException(ErrorMessages.InvalidXml(this.Context.CurrentSourceLineNumber, "source", e.Message));
774 + this.UpdateCurrentLineNumber(state, loopReader, offset);
775 + throw new WixException(ErrorMessages.InvalidXml(state.Context.CurrentSourceLineNumber, "source", e.Message));
776 }
777
778 fragmentStream.Position = 0; // seek back to the beginning for the next loop.
@@ -787,25 +785,25 @@ namespace WixToolset.Core
785 /// Processes a pragma processing instruction
786 /// </summary>
787 /// <param name="pragmaText">Text from source.</param>
790 - private void PreprocessPragma(string pragmaText, XContainer parent)
788 + private void PreprocessPragma(ProcessingState state, string pragmaText, XContainer parent)
789 {
790 var match = PragmaRegex.Match(pragmaText);
791
792 if (!match.Success)
793 {
796 - throw new WixException(ErrorMessages.InvalidPreprocessorPragma(this.Context.CurrentSourceLineNumber, pragmaText));
794 + throw new WixException(ErrorMessages.InvalidPreprocessorPragma(state.Context.CurrentSourceLineNumber, pragmaText));
795 }
796
797 // resolve other variables in the pragma argument(s)
800 - var pragmaArgs = this.Helper.PreprocessString(this.Context, match.Groups["pragmaValue"].Value).Trim();
798 + var pragmaArgs = state.Helper.PreprocessString(state.Context, match.Groups["pragmaValue"].Value).Trim();
799
800 try
801 {
804 - this.Helper.PreprocessPragma(this.Context, match.Groups["pragmaName"].Value.Trim(), pragmaArgs, parent);
802 + state.Helper.PreprocessPragma(state.Context, match.Groups["pragmaName"].Value.Trim(), pragmaArgs, parent);
803 }
804 catch (Exception e)
805 {
808 - throw new WixException(ErrorMessages.PreprocessorExtensionPragmaFailed(this.Context.CurrentSourceLineNumber, pragmaText, e.Message));
806 + throw new WixException(ErrorMessages.PreprocessorExtensionPragmaFailed(state.Context.CurrentSourceLineNumber, pragmaText, e.Message));
807 }
808 }
809
@@ -816,7 +814,7 @@ namespace WixToolset.Core
814 /// <param name="expression">Expression with token removed.</param>
815 /// <param name="stringLiteral">Flag if token is a string literal instead of a variable.</param>
816 /// <returns>Next token.</returns>
819 - private string GetNextToken(string originalExpression, ref string expression, out bool stringLiteral)
817 + private string GetNextToken(ProcessingState state, string originalExpression, ref string expression, out bool stringLiteral)
818 {
819 stringLiteral = false;
820 var token = String.Empty;
@@ -832,11 +830,11 @@ namespace WixToolset.Core
830 var endingQuotes = expression.IndexOf('\"', 1);
831 if (-1 == endingQuotes)
832 {
835 - throw new WixException(ErrorMessages.UnmatchedQuotesInExpression(this.Context.CurrentSourceLineNumber, originalExpression));
833 + throw new WixException(ErrorMessages.UnmatchedQuotesInExpression(state.Context.CurrentSourceLineNumber, originalExpression));
834 }
835
836 // cut the quotes off the string
839 - token = this.Helper.PreprocessString(this.Context, expression.Substring(1, endingQuotes - 1));
837 + token = state.Helper.PreprocessString(state.Context, expression.Substring(1, endingQuotes - 1));
838
839 // advance past this string
840 expression = expression.Substring(endingQuotes + 1).Trim();
@@ -866,7 +864,7 @@ namespace WixToolset.Core
864
865 if (-1 == endingParen)
866 {
869 - throw new WixException(ErrorMessages.UnmatchedParenthesisInExpression(this.Context.CurrentSourceLineNumber, originalExpression));
867 + throw new WixException(ErrorMessages.UnmatchedParenthesisInExpression(state.Context.CurrentSourceLineNumber, originalExpression));
868 }
869 token = expression.Substring(0, endingParen + 1);
870
@@ -962,7 +960,7 @@ namespace WixToolset.Core
960 /// <param name="originalExpression">Original expression for error message.</param>
961 /// <param name="variable">Variable to evaluate.</param>
962 /// <returns>Value of variable.</returns>
965 - private string EvaluateVariable(string originalExpression, string variable)
963 + private string EvaluateVariable(ProcessingState state, string originalExpression, string variable)
964 {
965 // By default it's a literal and will only be evaluated if it
966 // matches the variable format
@@ -972,7 +970,7 @@ namespace WixToolset.Core
970 {
971 try
972 {
975 - varValue = this.Helper.PreprocessString(this.Context, variable);
973 + varValue = state.Helper.PreprocessString(state.Context, variable);
974 }
975 catch (ArgumentNullException)
976 {
@@ -983,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
986 - throw new WixException(ErrorMessages.UnmatchedParenthesisInExpression(this.Context.CurrentSourceLineNumber, originalExpression));
984 + throw new WixException(ErrorMessages.UnmatchedParenthesisInExpression(state.Context.CurrentSourceLineNumber, originalExpression));
985 }
986 else if (variable.IndexOf("\"", StringComparison.Ordinal) != -1)
987 {
988 // shouldn't contain quotes
991 - throw new WixException(ErrorMessages.UnmatchedQuotesInExpression(this.Context.CurrentSourceLineNumber, originalExpression));
989 + throw new WixException(ErrorMessages.UnmatchedQuotesInExpression(state.Context.CurrentSourceLineNumber, originalExpression));
990 }
991
992 return varValue;
@@ -1002,31 +1000,31 @@ namespace WixToolset.Core
1000 /// <param name="leftValue">Left side value from expression.</param>
1001 /// <param name="operation">Operation in expression.</param>
1002 /// <param name="rightValue">Right side value from expression.</param>
1005 - private void GetNameValuePair(string originalExpression, ref string expression, out string leftValue, out string operation, out string rightValue)
1003 + private void GetNameValuePair(ProcessingState state, string originalExpression, ref string expression, out string leftValue, out string operation, out string rightValue)
1004 {
1007 - leftValue = this.GetNextToken(originalExpression, ref expression, out var stringLiteral);
1005 + leftValue = this.GetNextToken(state, originalExpression, ref expression, out var stringLiteral);
1006
1007 // If it wasn't a string literal, evaluate it
1008 if (!stringLiteral)
1009 {
1012 - leftValue = this.EvaluateVariable(originalExpression, leftValue);
1010 + leftValue = this.EvaluateVariable(state, originalExpression, leftValue);
1011 }
1012
1013 // Get the operation
1016 - operation = this.GetNextToken(originalExpression, ref expression, out stringLiteral);
1014 + operation = this.GetNextToken(state, originalExpression, ref expression, out stringLiteral);
1015 if (IsOperator(operation))
1016 {
1017 if (stringLiteral)
1018 {
1021 - throw new WixException(ErrorMessages.UnmatchedQuotesInExpression(this.Context.CurrentSourceLineNumber, originalExpression));
1019 + throw new WixException(ErrorMessages.UnmatchedQuotesInExpression(state.Context.CurrentSourceLineNumber, originalExpression));
1020 }
1021
1024 - rightValue = this.GetNextToken(originalExpression, ref expression, out stringLiteral);
1022 + rightValue = this.GetNextToken(state, originalExpression, ref expression, out stringLiteral);
1023
1024 // If it wasn't a string literal, evaluate it
1025 if (!stringLiteral)
1026 {
1029 - rightValue = this.EvaluateVariable(originalExpression, rightValue);
1027 + rightValue = this.EvaluateVariable(state, originalExpression, rightValue);
1028 }
1029 }
1030 else
@@ -1052,11 +1050,11 @@ namespace WixToolset.Core
1050 /// <param name="originalExpression">Original expression to evaluate.</param>
1051 /// <param name="expression">Expression modified while processing.</param>
1052 /// <returns>true if expression evaluates to true.</returns>
1055 - private bool EvaluateAtomicExpression(string originalExpression, ref string expression)
1053 + private bool EvaluateAtomicExpression(ProcessingState state, string originalExpression, ref string expression)
1054 {
1055 // Quick test to see if the first token is a variable
1056 var startsWithVariable = expression.StartsWith("$(", StringComparison.Ordinal);
1059 - this.GetNameValuePair(originalExpression, ref expression, out var leftValue, out var operation, out var rightValue);
1057 + this.GetNameValuePair(state, originalExpression, ref expression, out var leftValue, out var operation, out var rightValue);
1058
1059 var expressionValue = false;
1060
@@ -1065,7 +1063,7 @@ namespace WixToolset.Core
1063 {
1064 if (operation.Length > 0)
1065 {
1068 - throw new WixException(ErrorMessages.ExpectedVariable(this.Context.CurrentSourceLineNumber, originalExpression));
1066 + throw new WixException(ErrorMessages.ExpectedVariable(state.Context.CurrentSourceLineNumber, originalExpression));
1067 }
1068
1069 // false expression
@@ -1080,7 +1078,7 @@ namespace WixToolset.Core
1078 }
1079 else
1080 {
1083 - throw new WixException(ErrorMessages.UnexpectedLiteral(this.Context.CurrentSourceLineNumber, originalExpression));
1081 + throw new WixException(ErrorMessages.UnexpectedLiteral(state.Context.CurrentSourceLineNumber, originalExpression));
1082 }
1083 }
1084 else
@@ -1120,11 +1118,11 @@ namespace WixToolset.Core
1118 }
1119 catch (FormatException)
1120 {
1123 - throw new WixException(ErrorMessages.IllegalIntegerInExpression(this.Context.CurrentSourceLineNumber, originalExpression));
1121 + throw new WixException(ErrorMessages.IllegalIntegerInExpression(state.Context.CurrentSourceLineNumber, originalExpression));
1122 }
1123 catch (OverflowException)
1124 {
1127 - throw new WixException(ErrorMessages.IllegalIntegerInExpression(this.Context.CurrentSourceLineNumber, originalExpression));
1125 + throw new WixException(ErrorMessages.IllegalIntegerInExpression(state.Context.CurrentSourceLineNumber, originalExpression));
1126 }
1127
1128 // Compare the numbers
@@ -1148,7 +1146,7 @@ namespace WixToolset.Core
1146 /// <param name="expression">Expression modified while processing.</param>
1147 /// <param name="endSubExpression">Index of end of sub-expression.</param>
1148 /// <returns>Sub-expression in parenthesis.</returns>
1151 - private string GetParenthesisExpression(string originalExpression, string expression, out int endSubExpression)
1149 + private string GetParenthesisExpression(ProcessingState state, string originalExpression, string expression, out int endSubExpression)
1150 {
1151 endSubExpression = 0;
1152
@@ -1166,7 +1164,7 @@ namespace WixToolset.Core
1164 closeParenIndex = expression.IndexOf(')', closeParenIndex);
1165 if (closeParenIndex == -1)
1166 {
1169 - throw new WixException(ErrorMessages.UnmatchedParenthesisInExpression(this.Context.CurrentSourceLineNumber, originalExpression));
1167 + throw new WixException(ErrorMessages.UnmatchedParenthesisInExpression(state.Context.CurrentSourceLineNumber, originalExpression));
1168 }
1169
1170 if (InsideQuotes(expression, closeParenIndex))
@@ -1201,7 +1199,7 @@ namespace WixToolset.Core
1199 /// <param name="currentValue">State to update.</param>
1200 /// <param name="operation">Operation to apply to current value.</param>
1201 /// <param name="prevResult">Previous result.</param>
1204 - private void UpdateExpressionValue(ref bool currentValue, PreprocessorOperation operation, bool prevResult)
1202 + private void UpdateExpressionValue(ProcessingState state, ref bool currentValue, PreprocessorOperation operation, bool prevResult)
1203 {
1204 switch (operation)
1205 {
@@ -1215,7 +1213,7 @@ namespace WixToolset.Core
1213 currentValue = !currentValue;
1214 break;
1215 default:
1218 - throw new WixException(ErrorMessages.UnexpectedPreprocessorOperator(this.Context.CurrentSourceLineNumber, operation.ToString()));
1216 + throw new WixException(ErrorMessages.UnexpectedPreprocessorOperator(state.Context.CurrentSourceLineNumber, operation.ToString()));
1217 }
1218 }
1219
@@ -1224,10 +1222,10 @@ namespace WixToolset.Core
1222 /// </summary>
1223 /// <param name="expression">Expression to evaluate.</param>
1224 /// <returns>Boolean result of expression.</returns>
1227 - private bool EvaluateExpression(string expression)
1225 + private bool EvaluateExpression(ProcessingState state, string expression)
1226 {
1227 var tmpExpression = expression;
1230 - return this.EvaluateExpressionRecurse(expression, ref tmpExpression, PreprocessorOperation.And, true);
1228 + return this.EvaluateExpressionRecurse(state, expression, ref tmpExpression, PreprocessorOperation.And, true);
1229 }
1230
1231 /// <summary>
@@ -1258,20 +1256,20 @@ namespace WixToolset.Core
1256 /// <param name="prevResultOperation">The operation to apply to this result</param>
1257 /// <param name="prevResult">The previous result to apply to this result</param>
1258 /// <returns>Boolean to indicate if the expression is true or false</returns>
1261 - private bool EvaluateExpressionRecurse(string originalExpression, ref string expression, PreprocessorOperation prevResultOperation, bool prevResult)
1259 + private bool EvaluateExpressionRecurse(ProcessingState state, string originalExpression, ref string expression, PreprocessorOperation prevResultOperation, bool prevResult)
1260 {
1261 var expressionValue = false;
1262 expression = expression.Trim();
1263 if (expression.Length == 0)
1264 {
1267 - throw new WixException(ErrorMessages.UnexpectedEmptySubexpression(this.Context.CurrentSourceLineNumber, originalExpression));
1265 + throw new WixException(ErrorMessages.UnexpectedEmptySubexpression(state.Context.CurrentSourceLineNumber, originalExpression));
1266 }
1267
1268 // If the expression starts with parenthesis, evaluate it
1269 if (expression.IndexOf('(') == 0)
1270 {
1273 - var subExpression = this.GetParenthesisExpression(originalExpression, expression, out var endSubExpressionIndex);
1274 - expressionValue = this.EvaluateExpressionRecurse(originalExpression, ref subExpression, PreprocessorOperation.And, true);
1271 + var subExpression = this.GetParenthesisExpression(state, originalExpression, expression, out var endSubExpressionIndex);
1272 + expressionValue = this.EvaluateExpressionRecurse(state, originalExpression, ref subExpression, PreprocessorOperation.And, true);
1273
1274 // Now get the rest of the expression that hasn't been evaluated
1275 expression = expression.Substring(endSubExpressionIndex).Trim();
@@ -1284,19 +1282,19 @@ namespace WixToolset.Core
1282 expression = expression.Substring(3).Trim();
1283 if (expression.Length == 0)
1284 {
1287 - throw new WixException(ErrorMessages.ExpectedExpressionAfterNot(this.Context.CurrentSourceLineNumber, originalExpression));
1285 + throw new WixException(ErrorMessages.ExpectedExpressionAfterNot(state.Context.CurrentSourceLineNumber, originalExpression));
1286 }
1287
1290 - expressionValue = this.EvaluateExpressionRecurse(originalExpression, ref expression, PreprocessorOperation.Not, true);
1288 + expressionValue = this.EvaluateExpressionRecurse(state, originalExpression, ref expression, PreprocessorOperation.Not, true);
1289 }
1290 else // Expect a literal
1291 {
1294 - expressionValue = this.EvaluateAtomicExpression(originalExpression, ref expression);
1292 + expressionValue = this.EvaluateAtomicExpression(state, originalExpression, ref expression);
1293
1294 // Expect the literal that was just evaluated to already be cut off
1295 }
1296 }
1299 - this.UpdateExpressionValue(ref expressionValue, prevResultOperation, prevResult);
1297 + this.UpdateExpressionValue(state, ref expressionValue, prevResultOperation, prevResult);
1298
1299 // If there's still an expression left, it must start with AND or OR.
1300 if (expression.Trim().Length > 0)
@@ -1304,16 +1302,16 @@ namespace WixToolset.Core
1302 if (StartsWithKeyword(expression, PreprocessorOperation.And))
1303 {
1304 expression = expression.Substring(3);
1307 - return this.EvaluateExpressionRecurse(originalExpression, ref expression, PreprocessorOperation.And, expressionValue);
1305 + return this.EvaluateExpressionRecurse(state, originalExpression, ref expression, PreprocessorOperation.And, expressionValue);
1306 }
1307 else if (StartsWithKeyword(expression, PreprocessorOperation.Or))
1308 {
1309 expression = expression.Substring(2);
1312 - return this.EvaluateExpressionRecurse(originalExpression, ref expression, PreprocessorOperation.Or, expressionValue);
1310 + return this.EvaluateExpressionRecurse(state, originalExpression, ref expression, PreprocessorOperation.Or, expressionValue);
1311 }
1312 else
1313 {
1316 - throw new WixException(ErrorMessages.InvalidSubExpression(this.Context.CurrentSourceLineNumber, expression, originalExpression));
1314 + throw new WixException(ErrorMessages.InvalidSubExpression(state.Context.CurrentSourceLineNumber, expression, originalExpression));
1315 }
1316 }
1317
@@ -1325,16 +1323,16 @@ namespace WixToolset.Core
1323 /// </summary>
1324 /// <param name="reader">The xml reader for the preprocessor.</param>
1325 /// <param name="offset">This is the artificial offset of the line numbers from the reader. Used for the foreach processing.</param>
1328 - private void UpdateCurrentLineNumber(XmlReader reader, int offset)
1326 + private void UpdateCurrentLineNumber(ProcessingState state, XmlReader reader, int offset)
1327 {
1328 var lineInfoReader = reader as IXmlLineInfo;
1329 if (null != lineInfoReader)
1330 {
1331 var newLine = lineInfoReader.LineNumber + offset;
1332
1335 - if (this.Context.CurrentSourceLineNumber.LineNumber != newLine)
1333 + if (state.Context.CurrentSourceLineNumber.LineNumber != newLine)
1334 {
1337 - this.Context.CurrentSourceLineNumber = new SourceLineNumber(this.Context.CurrentSourceLineNumber.FileName, newLine);
1335 + state.Context.CurrentSourceLineNumber = new SourceLineNumber(state.Context.CurrentSourceLineNumber.FileName, newLine);
1336 }
1337 }
1338 }
@@ -1343,28 +1341,28 @@ namespace WixToolset.Core
1341 /// Pushes a file name on the stack of included files.
1342 /// </summary>
1343 /// <param name="fileName">Name to push on to the stack of included files.</param>
1346 - private void PushInclude(string fileName)
1344 + private void PushInclude(ProcessingState state, string fileName)
1345 {
1348 - if (1023 < this.CurrentFileStack.Count)
1346 + if (1023 < state.CurrentFileStack.Count)
1347 {
1350 - throw new WixException(ErrorMessages.TooDeeplyIncluded(this.Context.CurrentSourceLineNumber, this.CurrentFileStack.Count));
1348 + throw new WixException(ErrorMessages.TooDeeplyIncluded(state.Context.CurrentSourceLineNumber, state.CurrentFileStack.Count));
1349 }
1350
1353 - this.CurrentFileStack.Push(fileName);
1354 - this.SourceStack.Push(this.Context.CurrentSourceLineNumber);
1355 - this.Context.CurrentSourceLineNumber = new SourceLineNumber(fileName);
1356 - this.IncludeNextStack.Push(true);
1351 + state.CurrentFileStack.Push(fileName);
1352 + state.SourceStack.Push(state.Context.CurrentSourceLineNumber);
1353 + state.Context.CurrentSourceLineNumber = new SourceLineNumber(fileName);
1354 + state.IncludeNextStack.Push(true);
1355 }
1356
1357 /// <summary>
1358 /// Pops a file name from the stack of included files.
1359 /// </summary>
1362 - private void PopInclude()
1360 + private void PopInclude(ProcessingState state)
1361 {
1364 - this.Context.CurrentSourceLineNumber = this.SourceStack.Pop();
1362 + state.Context.CurrentSourceLineNumber = state.SourceStack.Pop();
1363
1366 - this.CurrentFileStack.Pop();
1367 - this.IncludeNextStack.Pop();
1364 + state.CurrentFileStack.Pop();
1365 + state.IncludeNextStack.Pop();
1366 }
1367
1368 /// <summary>
@@ -1375,7 +1373,7 @@ namespace WixToolset.Core
1373 /// </summary>
1374 /// <param name="includePath">User-specified path to the included file (usually just the file name).</param>
1375 /// <returns>Returns a FileInfo for the found include file, or null if the file cannot be found.</returns>
1378 - private string GetIncludeFile(string includePath)
1376 + private string GetIncludeFile(ProcessingState state, string includePath)
1377 {
1378 string finalIncludePath = null;
1379
@@ -1399,7 +1397,7 @@ namespace WixToolset.Core
1397 else // relative path
1398 {
1399 // build a string to test the directory containing the source file first
1402 - var currentFolder = this.CurrentFileStack.Peek();
1400 + var currentFolder = state.CurrentFileStack.Peek();
1401 var includeTestPath = Path.Combine(Path.GetDirectoryName(currentFolder), includePath);
1402
1403 // test the source file directory
@@ -1407,9 +1405,9 @@ namespace WixToolset.Core
1405 {
1406 finalIncludePath = includeTestPath;
1407 }
1410 - else // test all search paths in the order specified on the command line
1408 + else if (state.Context.IncludeSearchPaths != null) // test all search paths in the order specified on the command line
1409 {
1412 - foreach (var includeSearchPath in this.Context.IncludeSearchPaths)
1410 + foreach (var includeSearchPath in state.Context.IncludeSearchPaths)
1411 {
1412 // if the path exists, we have found the final string
1413 includeTestPath = Path.Combine(includeSearchPath, includePath);
@@ -1425,17 +1423,22 @@ namespace WixToolset.Core
1423 return finalIncludePath;
1424 }
1425
1428 - private void PreProcess()
1426 + private void PreProcess(ProcessingState state)
1427 {
1430 - foreach (var extension in this.Context.Extensions)
1428 + if (state.Context.Extensions == null)
1429 + {
1430 + return;
1431 + }
1432 +
1433 + foreach (var extension in state.Context.Extensions)
1434 {
1435 if (extension.Prefixes != null)
1436 {
1437 foreach (var prefix in extension.Prefixes)
1438 {
1436 - if (!this.ExtensionsByPrefix.TryGetValue(prefix, out var collidingExtension))
1439 + if (!state.ExtensionsByPrefix.TryGetValue(prefix, out var collidingExtension))
1440 {
1438 - this.ExtensionsByPrefix.Add(prefix, extension);
1441 + state.ExtensionsByPrefix.Add(prefix, extension);
1442 }
1443 else
1444 {
@@ -1444,18 +1447,49 @@ namespace WixToolset.Core
1447 }
1448 }
1449
1447 - extension.PrePreprocess(this.Context);
1450 + extension.PrePreprocess(state.Context);
1451 }
1452 }
1453
1451 - private XDocument PostProcess(XDocument document)
1454 + private void PostProcess(ProcessingState state, IPreprocessResult result)
1455 {
1453 - foreach (var extension in this.Context.Extensions)
1456 + if (state.Context.Extensions == null)
1457 {
1455 - extension.PostPreprocess(document);
1458 + return;
1459 }
1460
1458 - return document;
1461 + foreach (var extension in state.Context.Extensions)
1462 + {
1463 + extension.PostPreprocess(result);
1464 + }
1465 + }
1466 +
1467 + private class ProcessingState
1468 + {
1469 + public ProcessingState(IServiceProvider serviceProvider, IPreprocessContext context)
1470 + {
1471 + this.Context = context;
1472 + this.Context.CurrentSourceLineNumber = new SourceLineNumber(context.SourcePath);
1473 + this.Context.Variables = this.Context.Variables == null ? new Dictionary<string, string>() : new Dictionary<string, string>(this.Context.Variables);
1474 +
1475 + this.Helper = serviceProvider.GetService<IPreprocessHelper>();
1476 + }
1477 +
1478 + public IPreprocessContext Context { get; }
1479 +
1480 + public IPreprocessHelper Helper { get; }
1481 +
1482 + public List<IIncludedFile> IncludedFiles { get; } = new List<IIncludedFile>();
1483 +
1484 + public XDocument Output { get; } = new XDocument();
1485 +
1486 + public Stack<string> CurrentFileStack { get; } = new Stack<string>();
1487 +
1488 + public Dictionary<string, IPreprocessorExtension> ExtensionsByPrefix { get; } = new Dictionary<string, IPreprocessorExtension>();
1489 +
1490 + public Stack<bool> IncludeNextStack { get; } = new Stack<bool>();
1491 +
1492 + public Stack<SourceLineNumber> SourceStack { get; } = new Stack<SourceLineNumber>();
1493 }
1494 }
1495 }
src/WixToolset.Core/WixToolsetServiceProvider.cs
+2
@@ -45,6 +45,8 @@ namespace WixToolset.Core
45 this.AddService<IBindResult>((provider, singletons) => new BindResult());
46 this.AddService<IComponentKeyPath>((provider, singletons) => new ComponentKeyPath());
47 this.AddService<IDecompileResult>((provider, singletons) => new DecompileResult());
48 + this.AddService<IIncludedFile>((provider, singletons) => new IncludedFile());
49 + this.AddService<IPreprocessResult>((provider, singletons) => new PreprocessResult());
50 this.AddService<IResolveFileResult>((provider, singletons) => new ResolveFileResult());
51 this.AddService<IResolveResult>((provider, singletons) => new ResolveResult());
52 this.AddService<IResolvedCabinet>((provider, singletons) => new ResolvedCabinet());
src/test/WixToolsetTest.CoreIntegration/PreprocessorFixture.cs
+29 -4
@@ -1,18 +1,43 @@
1 -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 namespace WixToolsetTest.CoreIntegration
4 {
5 using System.IO;
6 using System.Linq;
7 using WixBuildTools.TestSupport;
8 + using WixToolset.Core;
9 using WixToolset.Core.TestPackage;
9 - using WixToolset.Data;
10 - using WixToolset.Data.Tuples;
11 - using WixToolset.Data.WindowsInstaller;
10 + using WixToolset.Extensibility.Data;
11 using Xunit;
12
13 public class PreprocessorFixture
14 {
15 + [Fact]
16 + public void PreprocessDirectly()
17 + {
18 + var folder = TestData.Get(@"TestData\IncludePath");
19 + var sourcePath = Path.Combine(folder, "Package.wxs");
20 + var includeFolder = Path.Combine(folder, "data");
21 + var includeFile = Path.Combine(includeFolder, "Package.wxi");
22 +
23 + var serviceProvider = new WixToolsetServiceProvider();
24 +
25 + var context = (IPreprocessContext)serviceProvider.GetService(typeof(IPreprocessContext));
26 + context.SourcePath = sourcePath;
27 + context.IncludeSearchPaths = new[] { includeFolder };
28 +
29 + var preprocessor = (IPreprocessor)serviceProvider.GetService(typeof(IPreprocessor));
30 + var result = preprocessor.Preprocess(context);
31 +
32 + var includedFile = result.IncludedFiles.Single();
33 + Assert.NotNull(result.Document);
34 + Assert.Equal(includeFile, includedFile.Path);
35 + Assert.Equal(sourcePath, includedFile.SourceLineNumbers.FileName);
36 + Assert.Equal(2, includedFile.SourceLineNumbers.LineNumber.Value);
37 + Assert.Equal($"{sourcePath}*2", includedFile.SourceLineNumbers.QualifiedFileName);
38 + Assert.Null(includedFile.SourceLineNumbers.Parent);
39 + }
40 +
41 [Fact]
42 public void VariableRedefinitionIsAWarning()
43 {