aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2017-08-15 01:31:51 -0700
committerRob Mensching <rob@firegiant.com>2017-08-15 01:31:51 -0700
commit4418bd52f1fca52a0d8d5f5b60abd2fdfc7146bd (patch)
tree02338abc93fb2e065c8717a84520d5ea8c7ea06c /src
parent0358780c77469974dd8bc87840364e790ccecc29 (diff)
downloadwix-4418bd52f1fca52a0d8d5f5b60abd2fdfc7146bd.tar.gz
wix-4418bd52f1fca52a0d8d5f5b60abd2fdfc7146bd.tar.bz2
wix-4418bd52f1fca52a0d8d5f5b60abd2fdfc7146bd.zip
Migrate MsgGen
Diffstat (limited to 'src')
-rw-r--r--src/WixBuildTools.MsgGen/AssemblyInfo.cs8
-rw-r--r--src/WixBuildTools.MsgGen/GenerateMessageFiles.cs250
-rw-r--r--src/WixBuildTools.MsgGen/MsgGen.cs261
-rw-r--r--src/WixBuildTools.MsgGen/WixBuildTools.MsgGen.csproj22
-rw-r--r--src/WixBuildTools.MsgGen/Xsd/messages.xsd101
-rw-r--r--src/WixBuildTools.MsgGen/build/WixBuildTools.MsgGen.targets102
-rw-r--r--src/WixBuildTools.MsgGen/buildCrossTargeting/WixBuildTools.MsgGen.targets6
7 files changed, 750 insertions, 0 deletions
diff --git a/src/WixBuildTools.MsgGen/AssemblyInfo.cs b/src/WixBuildTools.MsgGen/AssemblyInfo.cs
new file mode 100644
index 00000000..378adbf0
--- /dev/null
+++ b/src/WixBuildTools.MsgGen/AssemblyInfo.cs
@@ -0,0 +1,8 @@
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
3using System.Reflection;
4
5// General Information about an assembly is controlled through the following
6// set of attributes. Change these attribute values to modify the information
7// associated with an assembly.
8[assembly: AssemblyCulture("")]
diff --git a/src/WixBuildTools.MsgGen/GenerateMessageFiles.cs b/src/WixBuildTools.MsgGen/GenerateMessageFiles.cs
new file mode 100644
index 00000000..6f51dbf9
--- /dev/null
+++ b/src/WixBuildTools.MsgGen/GenerateMessageFiles.cs
@@ -0,0 +1,250 @@
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
3namespace WixBuildTools.MsgGen
4{
5 using System;
6 using System.CodeDom;
7 using System.Collections;
8 using System.Globalization;
9 using System.Reflection;
10 using System.Resources;
11 using System.Xml;
12
13 /// <summary>
14 /// Message files generation class.
15 /// </summary>
16 public class GenerateMessageFiles
17 {
18 /// <summary>
19 /// Generate the message files.
20 /// </summary>
21 /// <param name="messagesDoc">Input Xml document containing message definitions.</param>
22 /// <param name="codeCompileUnit">CodeDom container.</param>
23 /// <param name="resourceWriter">Writer for default resource file.</param>
24 public static void Generate(XmlDocument messagesDoc, CodeCompileUnit codeCompileUnit, ResourceWriter resourceWriter)
25 {
26 Hashtable usedNumbers = new Hashtable();
27
28 if (null == messagesDoc)
29 {
30 throw new ArgumentNullException("messagesDoc");
31 }
32
33 if (null == codeCompileUnit)
34 {
35 throw new ArgumentNullException("codeCompileUnit");
36 }
37
38 if (null == resourceWriter)
39 {
40 throw new ArgumentNullException("resourceWriter");
41 }
42
43 string namespaceAttr = messagesDoc.DocumentElement.GetAttribute("Namespace");
44 string resourcesAttr = messagesDoc.DocumentElement.GetAttribute("Resources");
45
46 // namespace
47 CodeNamespace messagesNamespace = new CodeNamespace(namespaceAttr);
48 codeCompileUnit.Namespaces.Add(messagesNamespace);
49
50 // imports
51 messagesNamespace.Imports.Add(new CodeNamespaceImport("System"));
52 messagesNamespace.Imports.Add(new CodeNamespaceImport("System.Reflection"));
53 messagesNamespace.Imports.Add(new CodeNamespaceImport("System.Resources"));
54 if (namespaceAttr != "WixToolset.Data")
55 {
56 messagesNamespace.Imports.Add(new CodeNamespaceImport("WixToolset.Data"));
57 }
58
59 foreach (XmlElement classElement in messagesDoc.DocumentElement.ChildNodes)
60 {
61 string className = classElement.GetAttribute("Name");
62 string baseContainerName = classElement.GetAttribute("BaseContainerName");
63 string containerName = classElement.GetAttribute("ContainerName");
64 string messageLevel = classElement.GetAttribute("Level");
65
66 // message container class
67 messagesNamespace.Types.Add(CreateContainer(namespaceAttr, baseContainerName, containerName, messageLevel, resourcesAttr));
68
69 // class
70 CodeTypeDeclaration messagesClass = new CodeTypeDeclaration(className);
71 messagesClass.TypeAttributes = TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed;
72 messagesNamespace.Types.Add(messagesClass);
73
74 // private constructor (needed since all methods in this class are static)
75 CodeConstructor constructor = new CodeConstructor();
76 constructor.Attributes = MemberAttributes.Private;
77 constructor.ReturnType = null;
78 messagesClass.Members.Add(constructor);
79
80 // messages
81 foreach (XmlElement messageElement in classElement.ChildNodes)
82 {
83 int number;
84 string id = messageElement.GetAttribute("Id");
85 string numberString = messageElement.GetAttribute("Number");
86 bool sourceLineNumbers = true;
87
88 // determine the message number (and ensure it was set properly)
89 if (0 < numberString.Length)
90 {
91 number = Convert.ToInt32(numberString, CultureInfo.InvariantCulture);
92 }
93 else
94 {
95 throw new ApplicationException(String.Format("Message number must be assigned for {0} '{1}'.", containerName, id));
96 }
97
98 // check for message number collisions
99 if (usedNumbers.Contains(number))
100 {
101 throw new ApplicationException(String.Format("Collision detected between two or more messages with number '{0}'.", number));
102 }
103
104 usedNumbers.Add(number, null);
105
106 if ("no" == messageElement.GetAttribute("SourceLineNumbers"))
107 {
108 sourceLineNumbers = false;
109 }
110
111 int instanceCount = 0;
112 foreach (XmlElement instanceElement in messageElement.ChildNodes)
113 {
114 string formatString = instanceElement.InnerText.Trim();
115 string resourceName = String.Concat(className, "_", id, "_", (++instanceCount).ToString());
116
117 // create a resource
118 resourceWriter.AddResource(resourceName, formatString);
119
120 // create method
121 CodeMemberMethod method = new CodeMemberMethod();
122 method.ReturnType = new CodeTypeReference(baseContainerName);
123 method.Attributes = MemberAttributes.Public | MemberAttributes.Static;
124 messagesClass.Members.Add(method);
125
126 // method name
127 method.Name = id;
128
129 // return statement
130 CodeMethodReturnStatement stmt = new CodeMethodReturnStatement();
131 method.Statements.Add(stmt);
132
133 // return statement expression
134 CodeObjectCreateExpression expr = new CodeObjectCreateExpression();
135 stmt.Expression = expr;
136
137 // new struct
138 expr.CreateType = new CodeTypeReference(containerName);
139
140 // optionally have sourceLineNumbers as the first parameter
141 if (sourceLineNumbers)
142 {
143 // sourceLineNumbers parameter
144 expr.Parameters.Add(new CodeArgumentReferenceExpression("sourceLineNumbers"));
145 }
146 else
147 {
148 expr.Parameters.Add(new CodePrimitiveExpression(null));
149 }
150
151 // message number parameter
152 expr.Parameters.Add(new CodePrimitiveExpression(number));
153
154 // resource name parameter
155 expr.Parameters.Add(new CodePrimitiveExpression(resourceName));
156
157 // optionally have sourceLineNumbers as the first parameter
158 if (sourceLineNumbers)
159 {
160 method.Parameters.Add(new CodeParameterDeclarationExpression("SourceLineNumber", "sourceLineNumbers"));
161 }
162
163 foreach (XmlNode parameterNode in instanceElement.ChildNodes)
164 {
165 XmlElement parameterElement;
166
167 if (null != (parameterElement = parameterNode as XmlElement))
168 {
169 string type = parameterElement.GetAttribute("Type");
170 string name = parameterElement.GetAttribute("Name");
171
172 // method parameter
173 method.Parameters.Add(new CodeParameterDeclarationExpression(type, name));
174
175 // String.Format parameter
176 expr.Parameters.Add(new CodeArgumentReferenceExpression(name));
177 }
178 }
179 }
180 }
181 }
182 }
183
184 /// <summary>
185 /// Create message container class.
186 /// </summary>
187 /// <param name="namespaceName">Namespace to use for resources stream.</param>
188 /// <param name="baseContainerName">Name of the base message container class.</param>
189 /// <param name="containerName">Name of the message container class.</param>
190 /// <param name="messageLevel">Message level of for the message.</param>
191 /// <param name="resourcesName">Name of the resources stream (will get namespace prepended).</param>
192 /// <returns>Message container class CodeDom object.</returns>
193 private static CodeTypeDeclaration CreateContainer(string namespaceName, string baseContainerName, string containerName, string messageLevel, string resourcesName)
194 {
195 CodeTypeDeclaration messageContainer = new CodeTypeDeclaration();
196
197 messageContainer.Name = containerName;
198 messageContainer.BaseTypes.Add(new CodeTypeReference(baseContainerName));
199
200 // constructor
201 CodeConstructor constructor = new CodeConstructor();
202 constructor.Attributes = MemberAttributes.Public;
203 constructor.ReturnType = null;
204 messageContainer.Members.Add(constructor);
205
206 CodeMemberField resourceManager = new CodeMemberField();
207 resourceManager.Attributes = MemberAttributes.Private | MemberAttributes.Static;
208 resourceManager.Name = "resourceManager";
209 resourceManager.Type = new CodeTypeReference("ResourceManager");
210 resourceManager.InitExpression = new CodeObjectCreateExpression("ResourceManager", new CodeSnippetExpression(String.Format("\"{0}.{1}\"", namespaceName, resourcesName)), new CodeSnippetExpression("Assembly.GetExecutingAssembly()"));
211 messageContainer.Members.Add(resourceManager);
212
213 // constructor parameters
214 constructor.Parameters.Add(new CodeParameterDeclarationExpression("SourceLineNumber", "sourceLineNumbers"));
215 constructor.Parameters.Add(new CodeParameterDeclarationExpression(typeof(int), "id"));
216 constructor.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "resourceName"));
217 CodeParameterDeclarationExpression messageArgsParam = new CodeParameterDeclarationExpression("params object[]", "messageArgs");
218 constructor.Parameters.Add(messageArgsParam);
219
220 constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("sourceLineNumbers"));
221 constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("id"));
222 constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("resourceName"));
223 constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("messageArgs"));
224
225 // assign base.Level if messageLevel is specified
226 if (!String.IsNullOrEmpty(messageLevel))
227 {
228 CodePropertyReferenceExpression levelReference = new CodePropertyReferenceExpression(new CodeBaseReferenceExpression(), "Level");
229 CodeFieldReferenceExpression messageLevelField = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression("MessageLevel"), messageLevel);
230 constructor.Statements.Add(new CodeAssignStatement(levelReference, messageLevelField));
231 }
232
233 // Assign base.ResourceManager property
234 CodePropertyReferenceExpression baseResourceManagerReference = new CodePropertyReferenceExpression(new CodeBaseReferenceExpression(), "ResourceManager");
235 CodeFieldReferenceExpression resourceManagerField = new CodeFieldReferenceExpression(null, "resourceManager");
236 constructor.Statements.Add(new CodeAssignStatement(baseResourceManagerReference, resourceManagerField));
237
238 //CodeMemberProperty resourceManagerProperty = new CodeMemberProperty();
239 //resourceManagerProperty.Attributes = MemberAttributes.Public | MemberAttributes.Override;
240 //resourceManagerProperty.Name = "ResourceManager";
241 //resourceManagerProperty.Type = new CodeTypeReference("ResourceManager");
242 //CodeFieldReferenceExpression resourceManagerReference = new CodeFieldReferenceExpression();
243 //resourceManagerReference.FieldName = "resourceManager";
244 //resourceManagerProperty.GetStatements.Add(new CodeMethodReturnStatement(resourceManagerReference));
245 //messageContainer.Members.Add(resourceManagerProperty);
246
247 return messageContainer;
248 }
249 }
250}
diff --git a/src/WixBuildTools.MsgGen/MsgGen.cs b/src/WixBuildTools.MsgGen/MsgGen.cs
new file mode 100644
index 00000000..ff4a4a90
--- /dev/null
+++ b/src/WixBuildTools.MsgGen/MsgGen.cs
@@ -0,0 +1,261 @@
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
3namespace WixBuildTools.MsgGen
4{
5 using Microsoft.CSharp;
6 using System;
7 using System.CodeDom;
8 using System.CodeDom.Compiler;
9 using System.Collections;
10 using System.IO;
11 using System.Reflection;
12 using System.Resources;
13 using System.Runtime.InteropServices;
14 using System.Xml;
15 using System.Xml.Schema;
16
17 /// <summary>
18 /// The main entry point for MsgGen.
19 /// </summary>
20 public class MsgGen
21 {
22 /// <summary>
23 /// The main entry point for MsgGen.
24 /// </summary>
25 /// <param name="args">Commandline arguments for the application.</param>
26 /// <returns>Returns the application error code.</returns>
27 [STAThread]
28 public static int Main(string[] args)
29 {
30 try
31 {
32 MsgGenMain msgGen = new MsgGenMain(args);
33 }
34 catch (Exception e)
35 {
36 Console.WriteLine("MsgGen.exe : fatal error MSGG0000: {0}\r\n\r\nStack Trace:\r\n{1}", e.Message, e.StackTrace);
37 if (e is NullReferenceException || e is SEHException)
38 {
39 throw;
40 }
41 return 2;
42 }
43
44 return 0;
45 }
46
47 /// <summary>
48 /// Main class for MsgGen.
49 /// </summary>
50 private class MsgGenMain
51 {
52 private bool showLogo;
53 private bool showHelp;
54
55 private string sourceFile;
56 private string destClassFile;
57 private string destResourcesFile;
58
59 /// <summary>
60 /// Main method for the MsgGen application within the MsgGenMain class.
61 /// </summary>
62 /// <param name="args">Commandline arguments to the application.</param>
63 public MsgGenMain(string[] args)
64 {
65 this.showLogo = true;
66 this.showHelp = false;
67
68 this.sourceFile = null;
69 this.destClassFile = null;
70 this.destResourcesFile = null;
71
72 // parse the command line
73 this.ParseCommandLine(args);
74
75 if (null == this.sourceFile || null == this.destClassFile)
76 {
77 this.showHelp = true;
78 }
79 if (null == this.destResourcesFile)
80 {
81 this.destResourcesFile = Path.ChangeExtension(this.destClassFile, ".resources");
82 }
83
84 // get the assemblies
85 Assembly msgGenAssembly = Assembly.GetExecutingAssembly();
86
87 if (this.showLogo)
88 {
89 Console.WriteLine("Microsoft (R) Message Generation Tool version {0}", msgGenAssembly.GetName().Version.ToString());
90 Console.WriteLine("Copyright (C) Microsoft Corporation 2004. All rights reserved.");
91 Console.WriteLine();
92 }
93 if (this.showHelp)
94 {
95 Console.WriteLine(" usage: MsgGen.exe [-?] [-nologo] sourceFile destClassFile [destResourcesFile]");
96 Console.WriteLine();
97 Console.WriteLine(" -? this help information");
98 Console.WriteLine();
99 Console.WriteLine("For more information see: http://wix.sourceforge.net");
100 return; // exit
101 }
102
103 // load the schema
104 XmlReader reader = null;
105 XmlSchemaCollection schemaCollection = null;
106 try
107 {
108 reader = new XmlTextReader(msgGenAssembly.GetManifestResourceStream("WixBuildTools.MsgGen.Xsd.messages.xsd"));
109 schemaCollection = new XmlSchemaCollection();
110 schemaCollection.Add("http://schemas.microsoft.com/genmsgs/2004/07/messages", reader);
111 }
112 finally
113 {
114 reader.Close();
115 }
116
117 // load the source file and process it
118 using (StreamReader sr = new StreamReader(this.sourceFile))
119 {
120 XmlParserContext context = new XmlParserContext(null, null, null, XmlSpace.None);
121 XmlValidatingReader validatingReader = new XmlValidatingReader(sr.BaseStream, XmlNodeType.Document, context);
122 validatingReader.Schemas.Add(schemaCollection);
123
124 XmlDocument errorsDoc = new XmlDocument();
125 errorsDoc.Load(validatingReader);
126
127 CodeCompileUnit codeCompileUnit = new CodeCompileUnit();
128
129 using (ResourceWriter resourceWriter = new ResourceWriter(this.destResourcesFile))
130 {
131 GenerateMessageFiles.Generate(errorsDoc, codeCompileUnit, resourceWriter);
132
133 GenerateCSharpCode(codeCompileUnit, this.destClassFile);
134 }
135 }
136 }
137
138 /// <summary>
139 /// Generate the actual C# code.
140 /// </summary>
141 /// <param name="codeCompileUnit">The code DOM.</param>
142 /// <param name="destClassFile">Destination C# source file.</param>
143 public static void GenerateCSharpCode(CodeCompileUnit codeCompileUnit, string destClassFile)
144 {
145 // generate the code with the C# code provider
146 CSharpCodeProvider provider = new CSharpCodeProvider();
147
148 // obtain an ICodeGenerator from the CodeDomProvider class
149 ICodeGenerator gen = provider.CreateGenerator();
150
151 // create a TextWriter to a StreamWriter to the output file
152 using (StreamWriter sw = new StreamWriter(destClassFile))
153 {
154 using (IndentedTextWriter tw = new IndentedTextWriter(sw, " "))
155 {
156 CodeGeneratorOptions options = new CodeGeneratorOptions();
157
158 // code generation options
159 options.BlankLinesBetweenMembers = true;
160 options.BracingStyle = "C";
161
162 // generate source code using the code generator
163 gen.GenerateCodeFromCompileUnit(codeCompileUnit, tw, options);
164 }
165 }
166 }
167
168 /// <summary>
169 /// Parse the commandline arguments.
170 /// </summary>
171 /// <param name="args">Commandline arguments.</param>
172 private void ParseCommandLine(string[] args)
173 {
174 for (int i = 0; i < args.Length; ++i)
175 {
176 string arg = args[i];
177 if (null == arg || "" == arg) // skip blank arguments
178 {
179 continue;
180 }
181
182 if ('-' == arg[0] || '/' == arg[0])
183 {
184 string parameter = arg.Substring(1);
185 if ("nologo" == parameter)
186 {
187 this.showLogo = false;
188 }
189 else if ("?" == parameter || "help" == parameter)
190 {
191 this.showHelp = true;
192 }
193 }
194 else if ('@' == arg[0])
195 {
196 using (StreamReader reader = new StreamReader(arg.Substring(1)))
197 {
198 string line;
199 ArrayList newArgs = new ArrayList();
200
201 while (null != (line = reader.ReadLine()))
202 {
203 string newArg = "";
204 bool betweenQuotes = false;
205 for (int j = 0; j < line.Length; ++j)
206 {
207 // skip whitespace
208 if (!betweenQuotes && (' ' == line[j] || '\t' == line[j]))
209 {
210 if ("" != newArg)
211 {
212 newArgs.Add(newArg);
213 newArg = null;
214 }
215
216 continue;
217 }
218
219 // if we're escaping a quote
220 if ('\\' == line[j] && '"' == line[j])
221 {
222 ++j;
223 }
224 else if ('"' == line[j]) // if we've hit a new quote
225 {
226 betweenQuotes = !betweenQuotes;
227 continue;
228 }
229
230 newArg = String.Concat(newArg, line[j]);
231 }
232 if ("" != newArg)
233 {
234 newArgs.Add(newArg);
235 }
236 }
237 string[] ar = (string[])newArgs.ToArray(typeof(string));
238 this.ParseCommandLine(ar);
239 }
240 }
241 else if (null == this.sourceFile)
242 {
243 this.sourceFile = arg;
244 }
245 else if (null == this.destClassFile)
246 {
247 this.destClassFile = arg;
248 }
249 else if (null == this.destResourcesFile)
250 {
251 this.destResourcesFile = arg;
252 }
253 else
254 {
255 throw new ArgumentException(String.Format("Unknown argument '{0}'.", arg));
256 }
257 }
258 }
259 }
260 }
261}
diff --git a/src/WixBuildTools.MsgGen/WixBuildTools.MsgGen.csproj b/src/WixBuildTools.MsgGen/WixBuildTools.MsgGen.csproj
new file mode 100644
index 00000000..7d7da64c
--- /dev/null
+++ b/src/WixBuildTools.MsgGen/WixBuildTools.MsgGen.csproj
@@ -0,0 +1,22 @@
1<Project Sdk="Microsoft.NET.Sdk">
2
3 <PropertyGroup>
4 <OutputType>Exe</OutputType>
5 <TargetFramework>net461</TargetFramework>
6 <IsTool>true</IsTool>
7 </PropertyGroup>
8
9 <ItemGroup>
10 <Content Include="build\WixBuildTools.MsgGen.targets" PackagePath="build\" />
11 <Content Include="buildCrossTargeting\WixBuildTools.MsgGen.targets" PackagePath="buildCrossTargeting\" />
12 </ItemGroup>
13
14 <ItemGroup>
15 <EmbeddedResource Include="Xsd\messages.xsd" />
16 </ItemGroup>
17
18 <ItemGroup>
19 <PackageReference Include="Nerdbank.GitVersioning" Version="2.0.37-beta" PrivateAssets="all" />
20 </ItemGroup>
21
22</Project>
diff --git a/src/WixBuildTools.MsgGen/Xsd/messages.xsd b/src/WixBuildTools.MsgGen/Xsd/messages.xsd
new file mode 100644
index 00000000..fd086502
--- /dev/null
+++ b/src/WixBuildTools.MsgGen/Xsd/messages.xsd
@@ -0,0 +1,101 @@
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
5<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
6 targetNamespace="http://schemas.microsoft.com/genmsgs/2004/07/messages"
7 xmlns="http://schemas.microsoft.com/genmsgs/2004/07/messages">
8 <xs:annotation>
9 <xs:documentation>
10 Schema for describing any kind of messages.
11 </xs:documentation>
12 </xs:annotation>
13
14 <xs:element name="Messages">
15 <xs:complexType>
16 <xs:sequence maxOccurs="unbounded">
17 <xs:element ref="Class"/>
18 </xs:sequence>
19 <xs:attribute name="Namespace" type="xs:string" use="required">
20 <xs:annotation><xs:documentation>Namespace of the generated class.</xs:documentation></xs:annotation>
21 </xs:attribute>
22 <xs:attribute name="Resources" type="xs:string" use="required">
23 <xs:annotation><xs:documentation>Resources stream for messages. Will get namespace prepended to it.</xs:documentation></xs:annotation>
24 </xs:attribute>
25 </xs:complexType>
26 </xs:element>
27
28 <xs:element name="Class">
29 <xs:complexType>
30 <xs:sequence minOccurs="0" maxOccurs="unbounded">
31 <xs:element ref="Message"/>
32 </xs:sequence>
33 <xs:attribute name="Name" type="xs:string" use="required">
34 <xs:annotation><xs:documentation>Name of the generated class.</xs:documentation></xs:annotation>
35 </xs:attribute>
36 <xs:attribute name="ContainerName" type="xs:string" use="required">
37 <xs:annotation><xs:documentation>Name of the generated container class.</xs:documentation></xs:annotation>
38 </xs:attribute>
39 <xs:attribute name="BaseContainerName" type="xs:string" use="required">
40 <xs:annotation><xs:documentation>Name of the base container class.</xs:documentation></xs:annotation>
41 </xs:attribute>
42 <xs:attribute name="Level" type="MessageLevelType">
43 <xs:annotation><xs:documentation>Optional message level for this container class and derivative classes.</xs:documentation></xs:annotation>
44 </xs:attribute>
45 </xs:complexType>
46 </xs:element>
47
48 <xs:element name="Message">
49 <xs:complexType>
50 <xs:sequence maxOccurs="unbounded">
51 <xs:element ref="Instance"/>
52 </xs:sequence>
53 <xs:attribute name="Id" type="xs:string" use="required">
54 <xs:annotation><xs:documentation>Name of the message type.</xs:documentation></xs:annotation>
55 </xs:attribute>
56 <xs:attribute name="Number" type="xs:integer" use="required">
57 <xs:annotation><xs:documentation>Override the number for this message type.</xs:documentation></xs:annotation>
58 </xs:attribute>
59 <xs:attribute name="SourceLineNumbers" type="YesNoType">
60 <xs:annotation><xs:documentation>Associate SourceLineNumbers with this message. The default value is "yes".</xs:documentation></xs:annotation>
61 </xs:attribute>
62 </xs:complexType>
63 </xs:element>
64
65 <xs:element name="Instance">
66 <xs:complexType mixed="true">
67 <xs:sequence minOccurs="0" maxOccurs="unbounded">
68 <xs:element ref="Parameter"/>
69 </xs:sequence>
70 </xs:complexType>
71 </xs:element>
72
73 <xs:element name="Parameter">
74 <xs:complexType>
75 <xs:attribute name="Type" type="xs:string" use="required">
76 <xs:annotation><xs:documentation>Type of the parameter.</xs:documentation></xs:annotation>
77 </xs:attribute>
78 <xs:attribute name="Name" type="xs:string" use="required">
79 <xs:annotation><xs:documentation>Name of the parameter.</xs:documentation></xs:annotation>
80 </xs:attribute>
81 </xs:complexType>
82 </xs:element>
83
84 <xs:simpleType name="YesNoType">
85 <xs:annotation><xs:documentation>Values of this type will either be "yes" or "no".</xs:documentation></xs:annotation>
86 <xs:restriction base="xs:NMTOKEN">
87 <xs:enumeration value="no"/>
88 <xs:enumeration value="yes"/>
89 </xs:restriction>
90 </xs:simpleType>
91
92 <xs:simpleType name="MessageLevelType">
93 <xs:annotation><xs:documentation>The message level for this message which corresponds to the WixToolset.MessageLevel enumeration.</xs:documentation></xs:annotation>
94 <xs:restriction base="xs:NMTOKEN">
95 <xs:enumeration value="Verbose"/>
96 <xs:enumeration value="Information"/>
97 <xs:enumeration value="Warning"/>
98 <xs:enumeration value="Error"/>
99 </xs:restriction>
100 </xs:simpleType>
101</xs:schema>
diff --git a/src/WixBuildTools.MsgGen/build/WixBuildTools.MsgGen.targets b/src/WixBuildTools.MsgGen/build/WixBuildTools.MsgGen.targets
new file mode 100644
index 00000000..dfa7bcbe
--- /dev/null
+++ b/src/WixBuildTools.MsgGen/build/WixBuildTools.MsgGen.targets
@@ -0,0 +1,102 @@
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 xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
5 <PropertyGroup>
6 <MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
7 </PropertyGroup>
8
9 <ItemGroup>
10 <!--Provide support for setting type (BuildAction) from VS-->
11 <AvailableItemName Include="MsgGenSource" />
12 </ItemGroup>
13
14 <PropertyGroup>
15 <MsgGenPath Condition=" '$(MsgGenPath)'=='' ">$(MSBuildThisFileDirectory)..\tools\</MsgGenPath>
16 </PropertyGroup>
17
18 <!--
19 ================================================================================================
20 MsgGen
21
22 Generates a .cs class file and a .resx file from an XML file.
23
24 [IN]
25 @(MsgGenSource) - The items to run through the MsgGen tool.
26
27 [OUT]
28 $(IntermediateOutputPath)%(Filename).cs - The generated .cs files to include in the compilation.
29 $(IntermediateOutputPath)%(MsgGenSource.ResourcesLogicalName) - The generated .resources file to embed in the assembly.
30 ================================================================================================
31 -->
32 <PropertyGroup>
33 <MsgGenDependsOn>
34 PrepareMsgGen
35 </MsgGenDependsOn>
36 <PrepareResourcesDependsOn>
37 MsgGen;
38 $(PrepareResourcesDependsOn)
39 </PrepareResourcesDependsOn>
40 </PropertyGroup>
41 <Target
42 Name="MsgGen"
43 BeforeTargets="PrepareResources"
44 DependsOnTargets="$(MsgGenDependsOn)"
45 Condition=" '@(MsgGenSource)' != '' "
46 Inputs="@(MsgGenSource)"
47 Outputs="$(IntermediateOutputPath)%(MsgGenSource.Filename).cs;
48 $(IntermediateOutputPath)%(MsgGenSource.ResourcesLogicalName)">
49
50 <Exec Command="&quot;$(MsgGenPath)WixBuildTools.MsgGen.exe&quot; -nologo &quot;%(MsgGenSource.FullPath)&quot; &quot;$(MsgGenCsFile)&quot; &quot;$(MsgGenResourcesFile)&quot;"
51 Outputs="$(MsgGenCsFile);$(MsgGenResourcesFile)" />
52
53 <ItemGroup>
54 <!-- This will tell MSBuild to clean up the .cs and .resources file during a Clean build -->
55 <FileWrites Include="$(MsgGenCsFile);$(MsgGenResourcesFile)" />
56 </ItemGroup>
57 </Target>
58
59 <!--
60 ================================================================================================
61 PrepareMsgGen
62
63 Creates properties and Include items for MsgGen. This must be separate from the MsgGen target
64 to workaround an MSBuild bug: AdditionalMetadata is ignored when the target is up-to-date.
65
66 ================================================================================================
67 -->
68 <Target
69 Name="PrepareMsgGen"
70 Condition=" '@(MsgGenSource)' != '' ">
71
72 <CreateProperty Value="$(IntermediateOutputPath)%(MsgGenSource.Filename).cs">
73 <Output TaskParameter="Value" PropertyName="MsgGenCsFile" />
74 </CreateProperty>
75
76 <CreateProperty
77 Value="$(IntermediateOutputPath)%(MsgGenSource.ResourcesLogicalName)"
78 Condition=" '%(MsgGenSource.ResourcesLogicalName)' != '' ">
79
80 <Output TaskParameter="Value" PropertyName="MsgGenResourcesFile" />
81 </CreateProperty>
82
83 <!-- Add the generated .cs file to the list of source files to compile -->
84 <CreateItem
85 Include="$(MsgGenCsFile)"
86 AdditionalMetadata="Link=%(MsgGenCsFile.Filename)%(MsgGenCsFile.Extension)">
87
88 <Output TaskParameter="Include" ItemName="Compile" />
89 </CreateItem>
90
91 <!-- Add the generated .resources file to the list of resources to embed -->
92 <CreateItem
93 Include="$(MsgGenResourcesFile)"
94 AdditionalMetadata="Link=%(MsgGenResourcesFile.Filename)%(MsgGenResourcesFile.Extension);
95 LogicalName=%(MsgGenSource.ResourcesLogicalName)"
96 Condition=" '$(MsgGenResourcesFile)' != '' ">
97
98 <Output TaskParameter="Include" ItemName="EmbeddedResource" />
99 </CreateItem>
100 </Target>
101
102</Project>
diff --git a/src/WixBuildTools.MsgGen/buildCrossTargeting/WixBuildTools.MsgGen.targets b/src/WixBuildTools.MsgGen/buildCrossTargeting/WixBuildTools.MsgGen.targets
new file mode 100644
index 00000000..a3985af5
--- /dev/null
+++ b/src/WixBuildTools.MsgGen/buildCrossTargeting/WixBuildTools.MsgGen.targets
@@ -0,0 +1,6 @@
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 xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
5 <Import Project="..\build\WixBuildTools.MsgGen.targets" />
6</Project>