@joebigelow / wix / commits / 720c4a0d

Introduce ExampleExtension for testing

Rob Mensching committed Dec 1, 2017 at 01:09 UTC 720c4a0db1a2fb2aa3e08e5c99d5198873e448ba
15 files changed +401
src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs
+5
@@ -633,6 +633,11 @@ namespace WixToolset.Core.ExtensibilityServices
633 }
634 }
635
636 + public SourceLineNumber GetSourceLineNumbers(XElement element)
637 + {
638 + return Preprocessor.GetSourceLineNumbers(element);
639 + }
640 +
641 public string GetConditionInnerText(XElement element)
642 {
643 var value = Common.GetInnerText(element)?.Trim().Replace('\t', ' ').Replace('\r', ' ').Replace('\n', ' ');
src/WixToolset.Core/ExtensibilityServices/TupleDefinitionCreator.cs
+15
@@ -19,8 +19,16 @@ namespace WixToolset.Core.ExtensibilityServices
19
20 private IEnumerable<IExtensionData> ExtensionData { get; set; }
21
22 + private Dictionary<string, IntermediateTupleDefinition> CustomDefinitionByName { get; } = new Dictionary<string, IntermediateTupleDefinition>();
23 +
24 + public void AddCustomTupleDefinition(IntermediateTupleDefinition definition)
25 + {
26 + this.CustomDefinitionByName.Add(definition.Name, definition);
27 + }
28 +
29 public bool TryGetTupleDefinitionByName(string name, out IntermediateTupleDefinition tupleDefinition)
30 {
31 + // First, look in the built-ins.
32 tupleDefinition = TupleDefinitions.ByName(name);
33
34 if (tupleDefinition == null)
@@ -30,6 +38,7 @@ namespace WixToolset.Core.ExtensibilityServices
38 this.LoadExtensionData();
39 }
40
41 + // Second, look in the extensions.
42 foreach (var data in this.ExtensionData)
43 {
44 if (data.TryGetTupleDefinitionByName(name, out tupleDefinition))
@@ -37,6 +46,12 @@ namespace WixToolset.Core.ExtensibilityServices
46 break;
47 }
48 }
49 +
50 + // Finally, look in the custom tuple definitions provided during an intermediate load.
51 + if (tupleDefinition == null)
52 + {
53 + this.CustomDefinitionByName.TryGetValue(name, out tupleDefinition);
54 + }
55 }
56
57 return tupleDefinition != null;
src/test/Example.Extension/Example.Extension.csproj new
+18
@@ -0,0 +1,18 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<!-- 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. -->
3 +
4 +<Project Sdk="Microsoft.NET.Sdk">
5 + <PropertyGroup>
6 + <TargetFramework>netstandard2.0</TargetFramework>
7 + <IsPackable>false</IsPackable>
8 + </PropertyGroup>
9 +
10 + <ItemGroup>
11 + <ProjectReference Include="$(WixToolsetRootFolder)\Data\src\WixToolset.Data\WixToolset.Data.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Data\src\WixToolset.Data\WixToolset.Data.csproj') " />
12 + <PackageReference Include="WixToolset.Data" Version="4.0.*" Condition=" '$(Configuration)' == 'Release' Or !Exists('$(WixToolsetRootFolder)\Data\src\WixToolset.Data\WixToolset.Data.csproj') " />
13 +
14 + <ProjectReference Include="$(WixToolsetRootFolder)\Extensibility\src\WixToolset.Extensibility\WixToolset.Extensibility.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Extensibility\src\WixToolset.Extensibility\WixToolset.Extensibility.csproj') " />
15 + <PackageReference Include="WixToolset.Extensibility" Version="4.0.*" Condition=" '$(Configuration)' == 'Release' Or !Exists('$(WixToolsetRootFolder)\Extensibility\src\WixToolset.Extensibility\WixToolset.Extensibility.csproj') " />
16 + </ItemGroup>
17 +
18 +</Project>
src/test/Example.Extension/ExampleCompilerExtension.cs new
+84
@@ -0,0 +1,84 @@
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 System.Xml.Linq;
8 + using WixToolset.Data;
9 + using WixToolset.Extensibility;
10 +
11 + internal class ExampleCompilerExtension : BaseCompilerExtension
12 + {
13 + public ExampleCompilerExtension()
14 + {
15 + this.Namespace = "http://www.example.com/scheams/v1/wxs";
16 + }
17 +
18 + public override void ParseElement(Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary<string, string> context)
19 + {
20 + var processed = false;
21 +
22 + switch (parentElement.Name.LocalName)
23 + {
24 + case "Component":
25 + switch (element.Name.LocalName)
26 + {
27 + case "Example":
28 + this.ParseExampleElement(intermediate, section, element);
29 + processed = true;
30 + break;
31 + }
32 + break;
33 + }
34 +
35 + if (!processed)
36 + {
37 + base.ParseElement(intermediate, section, parentElement, element, context);
38 + }
39 + }
40 +
41 + private void ParseExampleElement(Intermediate intermediate, IntermediateSection section, XElement element)
42 + {
43 + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
44 + Identifier id = null;
45 + string value = null;
46 +
47 + foreach (var attrib in element.Attributes())
48 + {
49 + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
50 + {
51 + switch (attrib.Name.LocalName)
52 + {
53 + case "Id":
54 + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib);
55 + break;
56 +
57 + case "Value":
58 + value = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
59 + break;
60 +
61 + default:
62 + this.ParseHelper.UnexpectedAttribute(element, attrib);
63 + break;
64 + }
65 + }
66 + else
67 + {
68 + this.ParseAttribute(intermediate, section, element, attrib, null);
69 + }
70 + }
71 +
72 + if (null == id)
73 + {
74 + //this.Messaging(WixErrors.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Id"));
75 + }
76 +
77 + if (!this.Messaging.EncounteredError)
78 + {
79 + var tuple = this.ParseHelper.CreateRow(section, sourceLineNumbers, "Example", id);
80 + tuple.Set(1, value);
81 + }
82 + }
83 + }
84 +}
src/test/Example.Extension/ExampleExtensionData.cs new
+33
@@ -0,0 +1,33 @@
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 WixToolset.Data;
6 + using WixToolset.Extensibility;
7 +
8 + internal class ExampleExtensionData : IExtensionData
9 + {
10 + public string DefaultCulture => null;
11 +
12 + public Intermediate GetLibrary(ITupleDefinitionCreator tupleDefinitions)
13 + {
14 + return null;
15 + }
16 +
17 + public bool TryGetTupleDefinitionByName(string name, out IntermediateTupleDefinition tupleDefinition)
18 + {
19 + switch (name)
20 + {
21 + case "Example":
22 + tupleDefinition = TupleDefinitions.Example;
23 + break;
24 +
25 + default:
26 + tupleDefinition = null;
27 + break;
28 + }
29 +
30 + return tupleDefinition != null;
31 + }
32 + }
33 +}
\ No newline at end of file
src/test/Example.Extension/ExampleExtensionFactory.cs new
+32
@@ -0,0 +1,32 @@
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 WixToolset.Extensibility;
7 +
8 + public class ExampleExtensionFactory : IExtensionFactory
9 + {
10 + public bool TryCreateExtension(Type extensionType, out object extension)
11 + {
12 + if (extensionType == typeof(IPreprocessorExtension))
13 + {
14 + extension = new ExamplePreprocessorExtension();
15 + }
16 + else if (extensionType == typeof(ICompilerExtension))
17 + {
18 + extension = new ExampleCompilerExtension();
19 + }
20 + else if (extensionType == typeof(IExtensionData))
21 + {
22 + extension = new ExampleExtensionData();
23 + }
24 + else
25 + {
26 + extension = null;
27 + }
28 +
29 + return extension != null;
30 + }
31 + }
32 +}
src/test/Example.Extension/ExamplePreprocessorExtension.cs new
+55
@@ -0,0 +1,55 @@
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/ExampleTuple.cs new
+31
@@ -0,0 +1,31 @@
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 WixToolset.Data;
6 +
7 + public enum ExampleTupleFields
8 + {
9 + Example,
10 + Value,
11 + }
12 +
13 + public class ExampleTuple : IntermediateTuple
14 + {
15 + public ExampleTuple() : base(TupleDefinitions.Example, null, null)
16 + {
17 + }
18 +
19 + public ExampleTuple(SourceLineNumber sourceLineNumber, Identifier id = null) : base(TupleDefinitions.Example, sourceLineNumber, id)
20 + {
21 + }
22 +
23 + public IntermediateField this[ExampleTupleFields index] => this.Fields[(int)index];
24 +
25 + public string Value
26 + {
27 + get => this.Fields[(int)ExampleTupleFields.Value]?.AsString();
28 + set => this.Set((int)ExampleTupleFields.Value, value);
29 + }
30 + }
31 +}
src/test/Example.Extension/TupleDefinitions.cs new
+18
@@ -0,0 +1,18 @@
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 WixToolset.Data;
6 +
7 + public static class TupleDefinitions
8 + {
9 + public static readonly IntermediateTupleDefinition Example = new IntermediateTupleDefinition(
10 + "Example",
11 + new[]
12 + {
13 + new IntermediateFieldDefinition(nameof(ExampleTupleFields.Example), IntermediateFieldType.String),
14 + new IntermediateFieldDefinition(nameof(ExampleTupleFields.Value), IntermediateFieldType.String),
15 + },
16 + typeof(ExampleTuple));
17 + }
18 +}
src/test/WixToolsetTest.CoreIntegration/ExtensionFixture.cs new
+60
@@ -0,0 +1,60 @@
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;
6 + using System.IO;
7 + using System.Linq;
8 + using Example.Extension;
9 + using WixToolset.Core;
10 + using WixToolset.Data;
11 + using WixToolset.Data.Tuples;
12 + using WixToolsetTest.CoreIntegration.Utility;
13 + using Xunit;
14 +
15 + public class ExtensionFixture
16 + {
17 + [Fact]
18 + public void CanBuildWithExampleExtension()
19 + {
20 + var folder = TestData.Get(@"TestData\ExampleExtension");
21 + var extensionPath = Path.GetFullPath(new Uri(typeof(ExampleExtensionFactory).Assembly.CodeBase).LocalPath);
22 +
23 + using (var fs = new DisposableFileSystem())
24 + {
25 + var intermediateFolder = fs.GetFolder();
26 +
27 + var program = new Program();
28 + var result = program.Run(new WixToolsetServiceProvider(), new[]
29 + {
30 + "build",
31 + Path.Combine(folder, "Package.wxs"),
32 + Path.Combine(folder, "PackageComponents.wxs"),
33 + "-loc", Path.Combine(folder, "Package.en-us.wxl"),
34 + "-ext", extensionPath,
35 + "-bindpath", Path.Combine(folder, "data"),
36 + "-intermediateFolder", intermediateFolder,
37 + "-o", Path.Combine(intermediateFolder, @"bin\extest.msi")
38 + });
39 +
40 + Assert.Equal(0, result);
41 +
42 + Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\extest.msi")));
43 + Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\extest.wixpdb")));
44 + Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\MsiPackage\example.txt")));
45 +
46 + var intermediate = Intermediate.Load(Path.Combine(intermediateFolder, @"bin\extest.wir"));
47 + Assert.Single(intermediate.Sections);
48 +
49 + var wixFile = intermediate.Sections.SelectMany(s => s.Tuples).OfType<WixFileTuple>().Single();
50 + Assert.Equal(Path.Combine(folder, @"data\example.txt"), wixFile[WixFileTupleFields.Source].AsPath().Path);
51 + Assert.Equal(@"example.txt", wixFile[WixFileTupleFields.Source].PreviousValue.AsPath().Path);
52 +
53 + var example = intermediate.Sections.SelectMany(s => s.Tuples).Where(t => t.Definition.Type == TupleDefinitionType.MustBeFromAnExtension).Single();
54 + Assert.Equal("Foo", example.Id.Id);
55 + Assert.Equal("Foo", example[0].AsString());
56 + Assert.Equal("Bar", example[1].AsString());
57 + }
58 + }
59 + }
60 +}
src/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/Package.en-us.wxl new
+11
@@ -0,0 +1,11 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +
3 +<!--
4 +This file contains the declaration of all the localizable strings.
5 +-->
6 +<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US">
7 +
8 + <String Id="DowngradeError">A newer version of [ProductName] is already installed.</String>
9 + <String Id="FeatureTitle">MsiPackage</String>
10 +
11 +</WixLocalization>
src/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/Package.wxs new
+21
@@ -0,0 +1,21 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
3 + <Product Id="*" Name="MsiPackage" Language="1033" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
4 + <Package InstallerVersion="200" Compressed="no" InstallScope="perMachine" />
5 +
6 + <MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" />
7 + <MediaTemplate />
8 +
9 + <Feature Id="ProductFeature" Title="!(loc.FeatureTitle)">
10 + <ComponentGroupRef Id="ProductComponents" />
11 + </Feature>
12 + </Product>
13 +
14 + <Fragment>
15 + <Directory Id="TARGETDIR" Name="SourceDir">
16 + <Directory Id="ProgramFilesFolder">
17 + <Directory Id="INSTALLFOLDER" Name="MsiPackage" />
18 + </Directory>
19 + </Directory>
20 + </Fragment>
21 +</Wix>
src/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/PackageComponents.wxs new
+12
@@ -0,0 +1,12 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
3 + xmlns:ex="http://www.example.com/scheams/v1/wxs">
4 + <Fragment>
5 + <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
6 + <Component>
7 + <File Source="example.txt" />
8 + <ex:Example Id="Foo" Value="Bar" />
9 + </Component>
10 + </ComponentGroup>
11 + </Fragment>
12 +</Wix>
src/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/data/example.txt new
+1
@@ -0,0 +1 @@
1 +This is example.txt.
\ No newline at end of file
src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj
+5
@@ -15,6 +15,10 @@
15 <Content Include="TestData\InstanceTransform\Package.en-us.wxl" CopyToOutputDirectory="PreserveNewest" />
16 <Content Include="TestData\InstanceTransform\Package.wxs" CopyToOutputDirectory="PreserveNewest" />
17 <Content Include="TestData\InstanceTransform\PackageComponents.wxs" CopyToOutputDirectory="PreserveNewest" />
18 + <Content Include="TestData\ExampleExtension\data\example.txt" CopyToOutputDirectory="PreserveNewest" />
19 + <Content Include="TestData\ExampleExtension\Package.en-us.wxl" CopyToOutputDirectory="PreserveNewest" />
20 + <Content Include="TestData\ExampleExtension\Package.wxs" CopyToOutputDirectory="PreserveNewest" />
21 + <Content Include="TestData\ExampleExtension\PackageComponents.wxs" CopyToOutputDirectory="PreserveNewest" />
22 <Content Include="TestData\SingleFile\data\test.txt" CopyToOutputDirectory="PreserveNewest" />
23 <Content Include="TestData\SingleFile\Package.en-us.wxl" CopyToOutputDirectory="PreserveNewest" />
24 <Content Include="TestData\SingleFile\Package.wxs" CopyToOutputDirectory="PreserveNewest" />
@@ -23,6 +27,7 @@
27
28 <ItemGroup>
29 <ProjectReference Include="..\..\wix\wix.csproj" />
30 + <ProjectReference Include="..\Example.Extension\Example.Extension.csproj" />
31 </ItemGroup>
32
33 <ItemGroup>