From 4418bd52f1fca52a0d8d5f5b60abd2fdfc7146bd Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Tue, 15 Aug 2017 01:31:51 -0700 Subject: Migrate MsgGen --- WixBuildTools.sln | 15 ++ src/WixBuildTools.MsgGen/AssemblyInfo.cs | 8 + src/WixBuildTools.MsgGen/GenerateMessageFiles.cs | 250 ++++++++++++++++++++ src/WixBuildTools.MsgGen/MsgGen.cs | 261 +++++++++++++++++++++ .../WixBuildTools.MsgGen.csproj | 22 ++ src/WixBuildTools.MsgGen/Xsd/messages.xsd | 101 ++++++++ .../build/WixBuildTools.MsgGen.targets | 102 ++++++++ .../WixBuildTools.MsgGen.targets | 6 + 8 files changed, 765 insertions(+) create mode 100644 src/WixBuildTools.MsgGen/AssemblyInfo.cs create mode 100644 src/WixBuildTools.MsgGen/GenerateMessageFiles.cs create mode 100644 src/WixBuildTools.MsgGen/MsgGen.cs create mode 100644 src/WixBuildTools.MsgGen/WixBuildTools.MsgGen.csproj create mode 100644 src/WixBuildTools.MsgGen/Xsd/messages.xsd create mode 100644 src/WixBuildTools.MsgGen/build/WixBuildTools.MsgGen.targets create mode 100644 src/WixBuildTools.MsgGen/buildCrossTargeting/WixBuildTools.MsgGen.targets diff --git a/WixBuildTools.sln b/WixBuildTools.sln index 251fed6d..d7f21621 100644 --- a/WixBuildTools.sln +++ b/WixBuildTools.sln @@ -6,6 +6,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{00E50075-E89 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixBuildTools.XsdGen", "src\WixBuildTools.XsdGen\WixBuildTools.XsdGen.csproj", "{E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixBuildTools.MsgGen", "src\WixBuildTools.MsgGen\WixBuildTools.MsgGen.csproj", "{DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -28,12 +30,25 @@ Global {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|x64.Build.0 = Release|Any CPU {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|x86.ActiveCfg = Release|Any CPU {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6}.Release|x86.Build.0 = Release|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|x64.ActiveCfg = Debug|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|x64.Build.0 = Debug|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|x86.ActiveCfg = Debug|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Debug|x86.Build.0 = Debug|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|Any CPU.Build.0 = Release|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|x64.ActiveCfg = Release|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|x64.Build.0 = Release|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|x86.ActiveCfg = Release|Any CPU + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {E89E52C9-A4A1-4174-A1B1-3B72975E6ED6} = {00E50075-E896-42D1-AC30-2D2E7D129FB9} + {DB6EF6F3-51B1-4214-9A14-D501C23F6FA4} = {00E50075-E896-42D1-AC30-2D2E7D129FB9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {83E9E075-B440-471A-9C37-9D84BA0AE3E0} 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 @@ +// 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. + +using System.Reflection; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[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 @@ +// 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 WixBuildTools.MsgGen +{ + using System; + using System.CodeDom; + using System.Collections; + using System.Globalization; + using System.Reflection; + using System.Resources; + using System.Xml; + + /// + /// Message files generation class. + /// + public class GenerateMessageFiles + { + /// + /// Generate the message files. + /// + /// Input Xml document containing message definitions. + /// CodeDom container. + /// Writer for default resource file. + public static void Generate(XmlDocument messagesDoc, CodeCompileUnit codeCompileUnit, ResourceWriter resourceWriter) + { + Hashtable usedNumbers = new Hashtable(); + + if (null == messagesDoc) + { + throw new ArgumentNullException("messagesDoc"); + } + + if (null == codeCompileUnit) + { + throw new ArgumentNullException("codeCompileUnit"); + } + + if (null == resourceWriter) + { + throw new ArgumentNullException("resourceWriter"); + } + + string namespaceAttr = messagesDoc.DocumentElement.GetAttribute("Namespace"); + string resourcesAttr = messagesDoc.DocumentElement.GetAttribute("Resources"); + + // namespace + CodeNamespace messagesNamespace = new CodeNamespace(namespaceAttr); + codeCompileUnit.Namespaces.Add(messagesNamespace); + + // imports + messagesNamespace.Imports.Add(new CodeNamespaceImport("System")); + messagesNamespace.Imports.Add(new CodeNamespaceImport("System.Reflection")); + messagesNamespace.Imports.Add(new CodeNamespaceImport("System.Resources")); + if (namespaceAttr != "WixToolset.Data") + { + messagesNamespace.Imports.Add(new CodeNamespaceImport("WixToolset.Data")); + } + + foreach (XmlElement classElement in messagesDoc.DocumentElement.ChildNodes) + { + string className = classElement.GetAttribute("Name"); + string baseContainerName = classElement.GetAttribute("BaseContainerName"); + string containerName = classElement.GetAttribute("ContainerName"); + string messageLevel = classElement.GetAttribute("Level"); + + // message container class + messagesNamespace.Types.Add(CreateContainer(namespaceAttr, baseContainerName, containerName, messageLevel, resourcesAttr)); + + // class + CodeTypeDeclaration messagesClass = new CodeTypeDeclaration(className); + messagesClass.TypeAttributes = TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed; + messagesNamespace.Types.Add(messagesClass); + + // private constructor (needed since all methods in this class are static) + CodeConstructor constructor = new CodeConstructor(); + constructor.Attributes = MemberAttributes.Private; + constructor.ReturnType = null; + messagesClass.Members.Add(constructor); + + // messages + foreach (XmlElement messageElement in classElement.ChildNodes) + { + int number; + string id = messageElement.GetAttribute("Id"); + string numberString = messageElement.GetAttribute("Number"); + bool sourceLineNumbers = true; + + // determine the message number (and ensure it was set properly) + if (0 < numberString.Length) + { + number = Convert.ToInt32(numberString, CultureInfo.InvariantCulture); + } + else + { + throw new ApplicationException(String.Format("Message number must be assigned for {0} '{1}'.", containerName, id)); + } + + // check for message number collisions + if (usedNumbers.Contains(number)) + { + throw new ApplicationException(String.Format("Collision detected between two or more messages with number '{0}'.", number)); + } + + usedNumbers.Add(number, null); + + if ("no" == messageElement.GetAttribute("SourceLineNumbers")) + { + sourceLineNumbers = false; + } + + int instanceCount = 0; + foreach (XmlElement instanceElement in messageElement.ChildNodes) + { + string formatString = instanceElement.InnerText.Trim(); + string resourceName = String.Concat(className, "_", id, "_", (++instanceCount).ToString()); + + // create a resource + resourceWriter.AddResource(resourceName, formatString); + + // create method + CodeMemberMethod method = new CodeMemberMethod(); + method.ReturnType = new CodeTypeReference(baseContainerName); + method.Attributes = MemberAttributes.Public | MemberAttributes.Static; + messagesClass.Members.Add(method); + + // method name + method.Name = id; + + // return statement + CodeMethodReturnStatement stmt = new CodeMethodReturnStatement(); + method.Statements.Add(stmt); + + // return statement expression + CodeObjectCreateExpression expr = new CodeObjectCreateExpression(); + stmt.Expression = expr; + + // new struct + expr.CreateType = new CodeTypeReference(containerName); + + // optionally have sourceLineNumbers as the first parameter + if (sourceLineNumbers) + { + // sourceLineNumbers parameter + expr.Parameters.Add(new CodeArgumentReferenceExpression("sourceLineNumbers")); + } + else + { + expr.Parameters.Add(new CodePrimitiveExpression(null)); + } + + // message number parameter + expr.Parameters.Add(new CodePrimitiveExpression(number)); + + // resource name parameter + expr.Parameters.Add(new CodePrimitiveExpression(resourceName)); + + // optionally have sourceLineNumbers as the first parameter + if (sourceLineNumbers) + { + method.Parameters.Add(new CodeParameterDeclarationExpression("SourceLineNumber", "sourceLineNumbers")); + } + + foreach (XmlNode parameterNode in instanceElement.ChildNodes) + { + XmlElement parameterElement; + + if (null != (parameterElement = parameterNode as XmlElement)) + { + string type = parameterElement.GetAttribute("Type"); + string name = parameterElement.GetAttribute("Name"); + + // method parameter + method.Parameters.Add(new CodeParameterDeclarationExpression(type, name)); + + // String.Format parameter + expr.Parameters.Add(new CodeArgumentReferenceExpression(name)); + } + } + } + } + } + } + + /// + /// Create message container class. + /// + /// Namespace to use for resources stream. + /// Name of the base message container class. + /// Name of the message container class. + /// Message level of for the message. + /// Name of the resources stream (will get namespace prepended). + /// Message container class CodeDom object. + private static CodeTypeDeclaration CreateContainer(string namespaceName, string baseContainerName, string containerName, string messageLevel, string resourcesName) + { + CodeTypeDeclaration messageContainer = new CodeTypeDeclaration(); + + messageContainer.Name = containerName; + messageContainer.BaseTypes.Add(new CodeTypeReference(baseContainerName)); + + // constructor + CodeConstructor constructor = new CodeConstructor(); + constructor.Attributes = MemberAttributes.Public; + constructor.ReturnType = null; + messageContainer.Members.Add(constructor); + + CodeMemberField resourceManager = new CodeMemberField(); + resourceManager.Attributes = MemberAttributes.Private | MemberAttributes.Static; + resourceManager.Name = "resourceManager"; + resourceManager.Type = new CodeTypeReference("ResourceManager"); + resourceManager.InitExpression = new CodeObjectCreateExpression("ResourceManager", new CodeSnippetExpression(String.Format("\"{0}.{1}\"", namespaceName, resourcesName)), new CodeSnippetExpression("Assembly.GetExecutingAssembly()")); + messageContainer.Members.Add(resourceManager); + + // constructor parameters + constructor.Parameters.Add(new CodeParameterDeclarationExpression("SourceLineNumber", "sourceLineNumbers")); + constructor.Parameters.Add(new CodeParameterDeclarationExpression(typeof(int), "id")); + constructor.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "resourceName")); + CodeParameterDeclarationExpression messageArgsParam = new CodeParameterDeclarationExpression("params object[]", "messageArgs"); + constructor.Parameters.Add(messageArgsParam); + + constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("sourceLineNumbers")); + constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("id")); + constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("resourceName")); + constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("messageArgs")); + + // assign base.Level if messageLevel is specified + if (!String.IsNullOrEmpty(messageLevel)) + { + CodePropertyReferenceExpression levelReference = new CodePropertyReferenceExpression(new CodeBaseReferenceExpression(), "Level"); + CodeFieldReferenceExpression messageLevelField = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression("MessageLevel"), messageLevel); + constructor.Statements.Add(new CodeAssignStatement(levelReference, messageLevelField)); + } + + // Assign base.ResourceManager property + CodePropertyReferenceExpression baseResourceManagerReference = new CodePropertyReferenceExpression(new CodeBaseReferenceExpression(), "ResourceManager"); + CodeFieldReferenceExpression resourceManagerField = new CodeFieldReferenceExpression(null, "resourceManager"); + constructor.Statements.Add(new CodeAssignStatement(baseResourceManagerReference, resourceManagerField)); + + //CodeMemberProperty resourceManagerProperty = new CodeMemberProperty(); + //resourceManagerProperty.Attributes = MemberAttributes.Public | MemberAttributes.Override; + //resourceManagerProperty.Name = "ResourceManager"; + //resourceManagerProperty.Type = new CodeTypeReference("ResourceManager"); + //CodeFieldReferenceExpression resourceManagerReference = new CodeFieldReferenceExpression(); + //resourceManagerReference.FieldName = "resourceManager"; + //resourceManagerProperty.GetStatements.Add(new CodeMethodReturnStatement(resourceManagerReference)); + //messageContainer.Members.Add(resourceManagerProperty); + + return messageContainer; + } + } +} 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 @@ +// 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 WixBuildTools.MsgGen +{ + using Microsoft.CSharp; + using System; + using System.CodeDom; + using System.CodeDom.Compiler; + using System.Collections; + using System.IO; + using System.Reflection; + using System.Resources; + using System.Runtime.InteropServices; + using System.Xml; + using System.Xml.Schema; + + /// + /// The main entry point for MsgGen. + /// + public class MsgGen + { + /// + /// The main entry point for MsgGen. + /// + /// Commandline arguments for the application. + /// Returns the application error code. + [STAThread] + public static int Main(string[] args) + { + try + { + MsgGenMain msgGen = new MsgGenMain(args); + } + catch (Exception e) + { + Console.WriteLine("MsgGen.exe : fatal error MSGG0000: {0}\r\n\r\nStack Trace:\r\n{1}", e.Message, e.StackTrace); + if (e is NullReferenceException || e is SEHException) + { + throw; + } + return 2; + } + + return 0; + } + + /// + /// Main class for MsgGen. + /// + private class MsgGenMain + { + private bool showLogo; + private bool showHelp; + + private string sourceFile; + private string destClassFile; + private string destResourcesFile; + + /// + /// Main method for the MsgGen application within the MsgGenMain class. + /// + /// Commandline arguments to the application. + public MsgGenMain(string[] args) + { + this.showLogo = true; + this.showHelp = false; + + this.sourceFile = null; + this.destClassFile = null; + this.destResourcesFile = null; + + // parse the command line + this.ParseCommandLine(args); + + if (null == this.sourceFile || null == this.destClassFile) + { + this.showHelp = true; + } + if (null == this.destResourcesFile) + { + this.destResourcesFile = Path.ChangeExtension(this.destClassFile, ".resources"); + } + + // get the assemblies + Assembly msgGenAssembly = Assembly.GetExecutingAssembly(); + + if (this.showLogo) + { + Console.WriteLine("Microsoft (R) Message Generation Tool version {0}", msgGenAssembly.GetName().Version.ToString()); + Console.WriteLine("Copyright (C) Microsoft Corporation 2004. All rights reserved."); + Console.WriteLine(); + } + if (this.showHelp) + { + Console.WriteLine(" usage: MsgGen.exe [-?] [-nologo] sourceFile destClassFile [destResourcesFile]"); + Console.WriteLine(); + Console.WriteLine(" -? this help information"); + Console.WriteLine(); + Console.WriteLine("For more information see: http://wix.sourceforge.net"); + return; // exit + } + + // load the schema + XmlReader reader = null; + XmlSchemaCollection schemaCollection = null; + try + { + reader = new XmlTextReader(msgGenAssembly.GetManifestResourceStream("WixBuildTools.MsgGen.Xsd.messages.xsd")); + schemaCollection = new XmlSchemaCollection(); + schemaCollection.Add("http://schemas.microsoft.com/genmsgs/2004/07/messages", reader); + } + finally + { + reader.Close(); + } + + // load the source file and process it + using (StreamReader sr = new StreamReader(this.sourceFile)) + { + XmlParserContext context = new XmlParserContext(null, null, null, XmlSpace.None); + XmlValidatingReader validatingReader = new XmlValidatingReader(sr.BaseStream, XmlNodeType.Document, context); + validatingReader.Schemas.Add(schemaCollection); + + XmlDocument errorsDoc = new XmlDocument(); + errorsDoc.Load(validatingReader); + + CodeCompileUnit codeCompileUnit = new CodeCompileUnit(); + + using (ResourceWriter resourceWriter = new ResourceWriter(this.destResourcesFile)) + { + GenerateMessageFiles.Generate(errorsDoc, codeCompileUnit, resourceWriter); + + GenerateCSharpCode(codeCompileUnit, this.destClassFile); + } + } + } + + /// + /// Generate the actual C# code. + /// + /// The code DOM. + /// Destination C# source file. + public static void GenerateCSharpCode(CodeCompileUnit codeCompileUnit, string destClassFile) + { + // generate the code with the C# code provider + CSharpCodeProvider provider = new CSharpCodeProvider(); + + // obtain an ICodeGenerator from the CodeDomProvider class + ICodeGenerator gen = provider.CreateGenerator(); + + // create a TextWriter to a StreamWriter to the output file + using (StreamWriter sw = new StreamWriter(destClassFile)) + { + using (IndentedTextWriter tw = new IndentedTextWriter(sw, " ")) + { + CodeGeneratorOptions options = new CodeGeneratorOptions(); + + // code generation options + options.BlankLinesBetweenMembers = true; + options.BracingStyle = "C"; + + // generate source code using the code generator + gen.GenerateCodeFromCompileUnit(codeCompileUnit, tw, options); + } + } + } + + /// + /// Parse the commandline arguments. + /// + /// Commandline arguments. + private void ParseCommandLine(string[] args) + { + for (int i = 0; i < args.Length; ++i) + { + string arg = args[i]; + if (null == arg || "" == arg) // skip blank arguments + { + continue; + } + + if ('-' == arg[0] || '/' == arg[0]) + { + string parameter = arg.Substring(1); + if ("nologo" == parameter) + { + this.showLogo = false; + } + else if ("?" == parameter || "help" == parameter) + { + this.showHelp = true; + } + } + else if ('@' == arg[0]) + { + using (StreamReader reader = new StreamReader(arg.Substring(1))) + { + string line; + ArrayList newArgs = new ArrayList(); + + while (null != (line = reader.ReadLine())) + { + string newArg = ""; + bool betweenQuotes = false; + for (int j = 0; j < line.Length; ++j) + { + // skip whitespace + if (!betweenQuotes && (' ' == line[j] || '\t' == line[j])) + { + if ("" != newArg) + { + newArgs.Add(newArg); + newArg = null; + } + + continue; + } + + // if we're escaping a quote + if ('\\' == line[j] && '"' == line[j]) + { + ++j; + } + else if ('"' == line[j]) // if we've hit a new quote + { + betweenQuotes = !betweenQuotes; + continue; + } + + newArg = String.Concat(newArg, line[j]); + } + if ("" != newArg) + { + newArgs.Add(newArg); + } + } + string[] ar = (string[])newArgs.ToArray(typeof(string)); + this.ParseCommandLine(ar); + } + } + else if (null == this.sourceFile) + { + this.sourceFile = arg; + } + else if (null == this.destClassFile) + { + this.destClassFile = arg; + } + else if (null == this.destResourcesFile) + { + this.destResourcesFile = arg; + } + else + { + throw new ArgumentException(String.Format("Unknown argument '{0}'.", arg)); + } + } + } + } + } +} 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 @@ + + + + Exe + net461 + true + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + Schema for describing any kind of messages. + + + + + + + + + + Namespace of the generated class. + + + Resources stream for messages. Will get namespace prepended to it. + + + + + + + + + + + Name of the generated class. + + + Name of the generated container class. + + + Name of the base container class. + + + Optional message level for this container class and derivative classes. + + + + + + + + + + + Name of the message type. + + + Override the number for this message type. + + + Associate SourceLineNumbers with this message. The default value is "yes". + + + + + + + + + + + + + + + + Type of the parameter. + + + Name of the parameter. + + + + + + Values of this type will either be "yes" or "no". + + + + + + + + The message level for this message which corresponds to the WixToolset.MessageLevel enumeration. + + + + + + + + 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 @@ + + + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + + + + + + + $(MSBuildThisFileDirectory)..\tools\ + + + + + + PrepareMsgGen + + + MsgGen; + $(PrepareResourcesDependsOn) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + -- cgit v1.2.3-55-g6feb