From 720c4a0db1a2fb2aa3e08e5c99d5198873e448ba Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Fri, 1 Dec 2017 01:09:42 -0800 Subject: Introduce ExampleExtension for testing --- .../ExtensibilityServices/ParseHelper.cs | 5 ++ .../TupleDefinitionCreator.cs | 15 ++++ .../Example.Extension/Example.Extension.csproj | 18 +++++ .../Example.Extension/ExampleCompilerExtension.cs | 84 ++++++++++++++++++++++ src/test/Example.Extension/ExampleExtensionData.cs | 33 +++++++++ .../Example.Extension/ExampleExtensionFactory.cs | 32 +++++++++ .../ExamplePreprocessorExtension.cs | 55 ++++++++++++++ src/test/Example.Extension/ExampleTuple.cs | 31 ++++++++ src/test/Example.Extension/TupleDefinitions.cs | 18 +++++ .../ExtensionFixture.cs | 60 ++++++++++++++++ .../TestData/ExampleExtension/Package.en-us.wxl | 11 +++ .../TestData/ExampleExtension/Package.wxs | 21 ++++++ .../ExampleExtension/PackageComponents.wxs | 12 ++++ .../TestData/ExampleExtension/data/example.txt | 1 + .../WixToolsetTest.CoreIntegration.csproj | 5 ++ 15 files changed, 401 insertions(+) create mode 100644 src/test/Example.Extension/Example.Extension.csproj create mode 100644 src/test/Example.Extension/ExampleCompilerExtension.cs create mode 100644 src/test/Example.Extension/ExampleExtensionData.cs create mode 100644 src/test/Example.Extension/ExampleExtensionFactory.cs create mode 100644 src/test/Example.Extension/ExamplePreprocessorExtension.cs create mode 100644 src/test/Example.Extension/ExampleTuple.cs create mode 100644 src/test/Example.Extension/TupleDefinitions.cs create mode 100644 src/test/WixToolsetTest.CoreIntegration/ExtensionFixture.cs create mode 100644 src/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/Package.en-us.wxl create mode 100644 src/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/Package.wxs create mode 100644 src/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/PackageComponents.wxs create mode 100644 src/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/data/example.txt diff --git a/src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs b/src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs index 87ad0da8..8a67efe9 100644 --- a/src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs +++ b/src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs @@ -633,6 +633,11 @@ namespace WixToolset.Core.ExtensibilityServices } } + public SourceLineNumber GetSourceLineNumbers(XElement element) + { + return Preprocessor.GetSourceLineNumbers(element); + } + public string GetConditionInnerText(XElement element) { var value = Common.GetInnerText(element)?.Trim().Replace('\t', ' ').Replace('\r', ' ').Replace('\n', ' '); diff --git a/src/WixToolset.Core/ExtensibilityServices/TupleDefinitionCreator.cs b/src/WixToolset.Core/ExtensibilityServices/TupleDefinitionCreator.cs index 4075def8..b442da2b 100644 --- a/src/WixToolset.Core/ExtensibilityServices/TupleDefinitionCreator.cs +++ b/src/WixToolset.Core/ExtensibilityServices/TupleDefinitionCreator.cs @@ -19,8 +19,16 @@ namespace WixToolset.Core.ExtensibilityServices private IEnumerable ExtensionData { get; set; } + private Dictionary CustomDefinitionByName { get; } = new Dictionary(); + + public void AddCustomTupleDefinition(IntermediateTupleDefinition definition) + { + this.CustomDefinitionByName.Add(definition.Name, definition); + } + public bool TryGetTupleDefinitionByName(string name, out IntermediateTupleDefinition tupleDefinition) { + // First, look in the built-ins. tupleDefinition = TupleDefinitions.ByName(name); if (tupleDefinition == null) @@ -30,6 +38,7 @@ namespace WixToolset.Core.ExtensibilityServices this.LoadExtensionData(); } + // Second, look in the extensions. foreach (var data in this.ExtensionData) { if (data.TryGetTupleDefinitionByName(name, out tupleDefinition)) @@ -37,6 +46,12 @@ namespace WixToolset.Core.ExtensibilityServices break; } } + + // Finally, look in the custom tuple definitions provided during an intermediate load. + if (tupleDefinition == null) + { + this.CustomDefinitionByName.TryGetValue(name, out tupleDefinition); + } } return tupleDefinition != null; diff --git a/src/test/Example.Extension/Example.Extension.csproj b/src/test/Example.Extension/Example.Extension.csproj new file mode 100644 index 00000000..80c64b25 --- /dev/null +++ b/src/test/Example.Extension/Example.Extension.csproj @@ -0,0 +1,18 @@ + + + + + + netstandard2.0 + false + + + + + + + + + + + diff --git a/src/test/Example.Extension/ExampleCompilerExtension.cs b/src/test/Example.Extension/ExampleCompilerExtension.cs new file mode 100644 index 00000000..5b20e48f --- /dev/null +++ b/src/test/Example.Extension/ExampleCompilerExtension.cs @@ -0,0 +1,84 @@ +// 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. + +namespace Example.Extension +{ + using System; + using System.Collections.Generic; + using System.Xml.Linq; + using WixToolset.Data; + using WixToolset.Extensibility; + + internal class ExampleCompilerExtension : BaseCompilerExtension + { + public ExampleCompilerExtension() + { + this.Namespace = "http://www.example.com/scheams/v1/wxs"; + } + + public override void ParseElement(Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary context) + { + var processed = false; + + switch (parentElement.Name.LocalName) + { + case "Component": + switch (element.Name.LocalName) + { + case "Example": + this.ParseExampleElement(intermediate, section, element); + processed = true; + break; + } + break; + } + + if (!processed) + { + base.ParseElement(intermediate, section, parentElement, element, context); + } + } + + private void ParseExampleElement(Intermediate intermediate, IntermediateSection section, XElement element) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + string value = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + + case "Value": + value = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseAttribute(intermediate, section, element, attrib, null); + } + } + + if (null == id) + { + //this.Messaging(WixErrors.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Id")); + } + + if (!this.Messaging.EncounteredError) + { + var tuple = this.ParseHelper.CreateRow(section, sourceLineNumbers, "Example", id); + tuple.Set(1, value); + } + } + } +} diff --git a/src/test/Example.Extension/ExampleExtensionData.cs b/src/test/Example.Extension/ExampleExtensionData.cs new file mode 100644 index 00000000..c3cb0473 --- /dev/null +++ b/src/test/Example.Extension/ExampleExtensionData.cs @@ -0,0 +1,33 @@ +// 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. + +namespace Example.Extension +{ + using WixToolset.Data; + using WixToolset.Extensibility; + + internal class ExampleExtensionData : IExtensionData + { + public string DefaultCulture => null; + + public Intermediate GetLibrary(ITupleDefinitionCreator tupleDefinitions) + { + return null; + } + + public bool TryGetTupleDefinitionByName(string name, out IntermediateTupleDefinition tupleDefinition) + { + switch (name) + { + case "Example": + tupleDefinition = TupleDefinitions.Example; + break; + + default: + tupleDefinition = null; + break; + } + + return tupleDefinition != null; + } + } +} \ No newline at end of file diff --git a/src/test/Example.Extension/ExampleExtensionFactory.cs b/src/test/Example.Extension/ExampleExtensionFactory.cs new file mode 100644 index 00000000..9539ee85 --- /dev/null +++ b/src/test/Example.Extension/ExampleExtensionFactory.cs @@ -0,0 +1,32 @@ +// 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. + +namespace Example.Extension +{ + using System; + using WixToolset.Extensibility; + + public class ExampleExtensionFactory : IExtensionFactory + { + public bool TryCreateExtension(Type extensionType, out object extension) + { + if (extensionType == typeof(IPreprocessorExtension)) + { + extension = new ExamplePreprocessorExtension(); + } + else if (extensionType == typeof(ICompilerExtension)) + { + extension = new ExampleCompilerExtension(); + } + else if (extensionType == typeof(IExtensionData)) + { + extension = new ExampleExtensionData(); + } + else + { + extension = null; + } + + return extension != null; + } + } +} diff --git a/src/test/Example.Extension/ExamplePreprocessorExtension.cs b/src/test/Example.Extension/ExamplePreprocessorExtension.cs new file mode 100644 index 00000000..c16c8b5a --- /dev/null +++ b/src/test/Example.Extension/ExamplePreprocessorExtension.cs @@ -0,0 +1,55 @@ +// 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. + +namespace Example.Extension +{ + using System; + using System.Xml.Linq; + using WixToolset.Data; + using WixToolset.Extensibility; + + internal class ExamplePreprocessorExtension : IPreprocessorExtension + { + public ExamplePreprocessorExtension() + { + } + + public IPreprocessorCore Core { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + public string[] Prefixes => throw new NotImplementedException(); + + public string EvaluateFunction(string prefix, string function, string[] args) + { + throw new NotImplementedException(); + } + + public void Finish() + { + throw new NotImplementedException(); + } + + public string GetVariableValue(string prefix, string name) + { + throw new NotImplementedException(); + } + + public void Initialize() + { + throw new NotImplementedException(); + } + + public void PreprocessDocument(XDocument document) + { + throw new NotImplementedException(); + } + + public string PreprocessParameter(string name) + { + throw new NotImplementedException(); + } + + public bool ProcessPragma(SourceLineNumber sourceLineNumbers, string prefix, string pragma, string args, XContainer parent) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/test/Example.Extension/ExampleTuple.cs b/src/test/Example.Extension/ExampleTuple.cs new file mode 100644 index 00000000..f280a5c8 --- /dev/null +++ b/src/test/Example.Extension/ExampleTuple.cs @@ -0,0 +1,31 @@ +// 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. + +namespace Example.Extension +{ + using WixToolset.Data; + + public enum ExampleTupleFields + { + Example, + Value, + } + + public class ExampleTuple : IntermediateTuple + { + public ExampleTuple() : base(TupleDefinitions.Example, null, null) + { + } + + public ExampleTuple(SourceLineNumber sourceLineNumber, Identifier id = null) : base(TupleDefinitions.Example, sourceLineNumber, id) + { + } + + public IntermediateField this[ExampleTupleFields index] => this.Fields[(int)index]; + + public string Value + { + get => this.Fields[(int)ExampleTupleFields.Value]?.AsString(); + set => this.Set((int)ExampleTupleFields.Value, value); + } + } +} diff --git a/src/test/Example.Extension/TupleDefinitions.cs b/src/test/Example.Extension/TupleDefinitions.cs new file mode 100644 index 00000000..2c320fbc --- /dev/null +++ b/src/test/Example.Extension/TupleDefinitions.cs @@ -0,0 +1,18 @@ +// 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. + +namespace Example.Extension +{ + using WixToolset.Data; + + public static class TupleDefinitions + { + public static readonly IntermediateTupleDefinition Example = new IntermediateTupleDefinition( + "Example", + new[] + { + new IntermediateFieldDefinition(nameof(ExampleTupleFields.Example), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(ExampleTupleFields.Value), IntermediateFieldType.String), + }, + typeof(ExampleTuple)); + } +} diff --git a/src/test/WixToolsetTest.CoreIntegration/ExtensionFixture.cs b/src/test/WixToolsetTest.CoreIntegration/ExtensionFixture.cs new file mode 100644 index 00000000..5181c748 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/ExtensionFixture.cs @@ -0,0 +1,60 @@ +// 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. + +namespace WixToolsetTest.CoreIntegration +{ + using System; + using System.IO; + using System.Linq; + using Example.Extension; + using WixToolset.Core; + using WixToolset.Data; + using WixToolset.Data.Tuples; + using WixToolsetTest.CoreIntegration.Utility; + using Xunit; + + public class ExtensionFixture + { + [Fact] + public void CanBuildWithExampleExtension() + { + var folder = TestData.Get(@"TestData\ExampleExtension"); + var extensionPath = Path.GetFullPath(new Uri(typeof(ExampleExtensionFactory).Assembly.CodeBase).LocalPath); + + using (var fs = new DisposableFileSystem()) + { + var intermediateFolder = fs.GetFolder(); + + var program = new Program(); + var result = program.Run(new WixToolsetServiceProvider(), new[] + { + "build", + Path.Combine(folder, "Package.wxs"), + Path.Combine(folder, "PackageComponents.wxs"), + "-loc", Path.Combine(folder, "Package.en-us.wxl"), + "-ext", extensionPath, + "-bindpath", Path.Combine(folder, "data"), + "-intermediateFolder", intermediateFolder, + "-o", Path.Combine(intermediateFolder, @"bin\extest.msi") + }); + + Assert.Equal(0, result); + + Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\extest.msi"))); + Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\extest.wixpdb"))); + Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\MsiPackage\example.txt"))); + + var intermediate = Intermediate.Load(Path.Combine(intermediateFolder, @"bin\extest.wir")); + Assert.Single(intermediate.Sections); + + var wixFile = intermediate.Sections.SelectMany(s => s.Tuples).OfType().Single(); + Assert.Equal(Path.Combine(folder, @"data\example.txt"), wixFile[WixFileTupleFields.Source].AsPath().Path); + Assert.Equal(@"example.txt", wixFile[WixFileTupleFields.Source].PreviousValue.AsPath().Path); + + var example = intermediate.Sections.SelectMany(s => s.Tuples).Where(t => t.Definition.Type == TupleDefinitionType.MustBeFromAnExtension).Single(); + Assert.Equal("Foo", example.Id.Id); + Assert.Equal("Foo", example[0].AsString()); + Assert.Equal("Bar", example[1].AsString()); + } + } + } +} diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/Package.en-us.wxl b/src/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/Package.en-us.wxl new file mode 100644 index 00000000..38c12ac1 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/Package.en-us.wxl @@ -0,0 +1,11 @@ + + + + + + A newer version of [ProductName] is already installed. + MsiPackage + + diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/Package.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/Package.wxs new file mode 100644 index 00000000..cdc323ec --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/Package.wxs @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/PackageComponents.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/PackageComponents.wxs new file mode 100644 index 00000000..7f17b538 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/PackageComponents.wxs @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/data/example.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/data/example.txt new file mode 100644 index 00000000..1b4ffe8a --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/data/example.txt @@ -0,0 +1 @@ +This is example.txt. \ No newline at end of file diff --git a/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj b/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj index f9042cda..ede5967f 100644 --- a/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj +++ b/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj @@ -15,6 +15,10 @@ + + + + @@ -23,6 +27,7 @@ + -- cgit v1.2.3-55-g6feb