Migrate MsgGen
Rob Mensching committed
Aug 15, 2017 at 01:31 UTC
4418bd52f1fca52a0d8d5f5b60abd2fdfc7146bd
8 files changed
+765
WixBuildTools.sln
+15
@@ -6,6 +6,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{00E50075-E89
6
EndProject
7
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixBuildTools.XsdGen", "src\WixBuildTools.XsdGen\WixBuildTools.XsdGen.csproj", "{E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}"
8
EndProject
9
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixBuildTools.MsgGen", "src\WixBuildTools.MsgGen\WixBuildTools.MsgGen.csproj", "{DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}"
10
+EndProject
11
Global
12
GlobalSection(SolutionConfigurationPlatforms) = preSolution
13
Debug|Any CPU = Debug|Any CPU
@@ -28,12 +30,25 @@ Global
30
{E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|x64.Build.0 = Release|Any CPU
31
{E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|x86.ActiveCfg = Release|Any CPU
32
{E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|x86.Build.0 = Release|Any CPU
33
+ {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
34
+ {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|Any CPU.Build.0 = Debug|Any CPU
35
+ {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|x64.ActiveCfg = Debug|Any CPU
36
+ {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|x64.Build.0 = Debug|Any CPU
37
+ {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|x86.ActiveCfg = Debug|Any CPU
38
+ {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|x86.Build.0 = Debug|Any CPU
39
+ {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|Any CPU.ActiveCfg = Release|Any CPU
40
+ {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|Any CPU.Build.0 = Release|Any CPU
41
+ {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|x64.ActiveCfg = Release|Any CPU
42
+ {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|x64.Build.0 = Release|Any CPU
43
+ {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|x86.ActiveCfg = Release|Any CPU
44
+ {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|x86.Build.0 = Release|Any CPU
45
EndGlobalSection
46
GlobalSection(SolutionProperties) = preSolution
47
HideSolutionNode = FALSE
48
EndGlobalSection
49
GlobalSection(NestedProjects) = preSolution
50
{E89E52C9-A4A1-4174-A1B1-3B72975E6ED6} = {00E50075-E896-42D1-AC30-2D2E7D129FB9}
51
+ {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4} = {00E50075-E896-42D1-AC30-2D2E7D129FB9}
52
EndGlobalSection
53
GlobalSection(ExtensibilityGlobals) = postSolution
54
SolutionGuid = {83E9E075-B440-471A-9C37-9D84BA0AE3E0}
src/WixBuildTools.MsgGen/AssemblyInfo.cs
new
+8
@@ -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
+
3
+using 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("")]
src/WixBuildTools.MsgGen/GenerateMessageFiles.cs
new
+250
@@ -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
+
3
+namespace 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
+}
src/WixBuildTools.MsgGen/MsgGen.cs
new
+261
@@ -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
+
3
+namespace 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
+}
src/WixBuildTools.MsgGen/WixBuildTools.MsgGen.csproj
new
+22
@@ -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>
src/WixBuildTools.MsgGen/Xsd/messages.xsd
new
+101
@@ -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>
src/WixBuildTools.MsgGen/build/WixBuildTools.MsgGen.targets
new
+102
@@ -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=""$(MsgGenPath)WixBuildTools.MsgGen.exe" -nologo "%(MsgGenSource.FullPath)" "$(MsgGenCsFile)" "$(MsgGenResourcesFile)""
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>
src/WixBuildTools.MsgGen/buildCrossTargeting/WixBuildTools.MsgGen.targets
new
+6
@@ -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>