public string GenerateHeaderFile(MessageParser.PostalDefinition parsed) { var constants = from def in parsed.PostalTypes where def is MessageParser.ConstantDefinition let constDef = def as MessageParser.ConstantDefinition select string.Format("#define {0} {1}", constDef.Name, constDef.Value); return(string.Format(HeaderFileTemplate, parsed.Namespace, string.Join("\n", constants))); }
public CodeCompileUnit GenerateCodeDOM(string filename, MessageParser.PostalDefinition parsed) { var className = Path.GetFileNameWithoutExtension(filename); var codeUnit = new CodeCompileUnit(); var ns = new CodeNamespace(parsed.Namespace); codeUnit.Namespaces.Add(ns); ns.Imports.Add(new CodeNamespaceImport("global::System")); ns.Imports.Add(new CodeNamespaceImport("global::System.Collections.Generic")); ns.Imports.Add(new CodeNamespaceImport("global::System.Collections.ObjectModel")); ns.Imports.Add(new CodeNamespaceImport("global::System.ComponentModel")); ns.Imports.Add(new CodeNamespaceImport("global::System.IO")); ns.Imports.Add(new CodeNamespaceImport("global::System.Linq")); ns.Imports.Add(new CodeNamespaceImport("global::System.Threading.Tasks")); ns.Imports.Add(new CodeNamespaceImport("global::ProtoBuf")); ns.Imports.Add(new CodeNamespaceImport("global::Postal.Core")); var messagesType = new CodeTypeDeclaration(className) { // Hack to make this class static so we can define extension methods // http://stackoverflow.com/a/6308395/802203 Attributes = MemberAttributes.Final, TypeAttributes = TypeAttributes.Public, StartDirectives = { new CodeRegionDirective(CodeRegionMode.Start, "\nstatic") }, EndDirectives = { new CodeRegionDirective(CodeRegionMode.End, string.Empty) }, IsPartial = true }; ns.Types.Add(messagesType); CodeTypeDeclaration messagesContainerType = null; messagesContainerType = new CodeTypeDeclaration(string.Format("{0}_MessagesContainer", messagesType.Name)) { TypeAttributes = TypeAttributes.Public, CustomAttributes = { new CodeAttributeDeclaration("ProtoContract") } }; messagesType.Members.Add(messagesContainerType); messagesType.Members.Add(new CodeSnippetTypeMember("public delegate TResponse ProcessRequestDelegate<TRequest, TResponse>(TRequest request) where TRequest : IRequest where TResponse : IResponse;")); messagesType.Members.Add(new CodeSnippetTypeMember(string.Format("private readonly static Dictionary<int, Type> _messageRequestTypes = new Dictionary<int, Type>(){{ {0} }};", string.Join(",\n", from message in parsed.PostalTypes where message is MessageParser.MessageDefinition select string.Format("{{ {0}.{0}RequestTag, typeof({0}.Request) }}", (message as MessageParser.MessageDefinition).Name))))); messagesType.Members.Add(new CodeSnippetTypeMember("public readonly static ReadOnlyDictionary<int, Type> MessageRequestTypes = new ReadOnlyDictionary<int, Type>(_messageRequestTypes);")); messagesType.Members.Add(new CodeSnippetTypeMember(string.Format("private readonly static Dictionary<int, Type> _messageResponseTypes = new Dictionary<int, Type>(){{ {0} }};", string.Join(",\n", from message in parsed.PostalTypes where message is MessageParser.MessageDefinition select string.Format("{{ {0}.{0}ResponseTag, typeof({0}.Response) }}", (message as MessageParser.MessageDefinition).Name))))); messagesType.Members.Add(new CodeSnippetTypeMember("public readonly static ReadOnlyDictionary<int, Type> MessageResponseTypes = new ReadOnlyDictionary<int, Type>(_messageResponseTypes);")); messagesType.Members.Add(new CodeSnippetTypeMember(@" private static void Serialize<T>(Stream stream, T request) where T: IRequest { Serializer.SerializeWithLengthPrefix(stream, request, PrefixStyle.Base128, request.Tag); }")); messagesType.Members.Add(new CodeSnippetTypeMember(@" private static T Deserialize<T>(Stream stream) where T : IResponse { object value; Serializer.NonGeneric.TryDeserializeWithLengthPrefix(stream, PrefixStyle.Base128, tag => { Type type; return _messageResponseTypes.TryGetValue(tag, out type) ? type : null; }, out value); return (T)value; }")); messagesType.Members.Add(new CodeSnippetTypeMember(@" public static void ProcessRequest(this Stream stream, Func<Stream, IRequest, bool> postDeserialize = null) { object value; Serializer.NonGeneric.TryDeserializeWithLengthPrefix(stream, PrefixStyle.Base128, tag => { Type type; return _messageRequestTypes.TryGetValue(tag, out type) ? type : null; }, out value); var request = (IRequest)value; if (request == null) return; if (postDeserialize != null) if (!postDeserialize(stream, request)) return; var response = (IResponse)request.InvokeReceived(); Serializer.NonGeneric.SerializeWithLengthPrefix(stream, response, PrefixStyle.Base128, response.Tag); }")); int dummyFieldsTag = 1; foreach (var type in parsed.PostalTypes) { var constDef = type as MessageParser.ConstantDefinition; if (constDef != null) { var qualifiedType = constDef.Type.ReplaceAll(builtInTypeReplacements); var constant = new CodeMemberField(qualifiedType, constDef.Name) { InitExpression = new CodeSnippetExpression(string.Format("({0}){1}", qualifiedType, constDef.Value)), Attributes = MemberAttributes.Const | MemberAttributes.Public }; messagesType.Members.Add(constant); continue; } var enumDef = type as MessageParser.EnumDefinition; if (enumDef != null) { var enumType = new CodeTypeDeclaration(enumDef.Name) { IsEnum = true }; foreach (var enumValue in enumDef.Values) { var field = new CodeMemberField(enumDef.Name, enumValue.Item1); if (enumValue.Item2.HasValue) { field.InitExpression = new CodePrimitiveExpression(enumValue.Item2.Value); } enumType.Members.Add(field); } messagesType.Members.Add(enumType); continue; } var structDef = type as MessageParser.StructDefinition; if (structDef != null) { var structType = new CodeTypeDeclaration(structDef.Name) { IsStruct = true, IsPartial = true, CustomAttributes = { new CodeAttributeDeclaration("ProtoContract") } }; int fieldTag = 1; foreach (var structField in structDef.Fields) { var field = new CodeMemberField(structField.Type.ReplaceAll(builtInTypeReplacements), structField.Name) { CustomAttributes = { new CodeAttributeDeclaration("ProtoMember", new CodeAttributeArgument(new CodeSnippetExpression(fieldTag.ToString()))) }, Attributes = MemberAttributes.Public }; if (structField.DefaultValue != null) { field.CustomAttributes.Add(new CodeAttributeDeclaration("DefaultValue", new CodeAttributeArgument(new CodeSnippetExpression(structField.DefaultValue)))); } structType.Members.Add(field); fieldTag++; } messagesType.Members.Add(structType); messagesContainerType.Members.Add(new CodeSnippetTypeMember(string.Format("[ProtoMember({0})] public {1} struct{1} {{ get; set; }}", dummyFieldsTag++, structDef.Name))); continue; } var messageDef = type as MessageParser.MessageDefinition; if (messageDef != null) { var messageType = new CodeTypeDeclaration(messageDef.Name) { Attributes = MemberAttributes.Final, TypeAttributes = TypeAttributes.Public }; messagesType.Members.Add(messageType); messageType.Members.Add(new CodeSnippetTypeMember(string.Format("public const int {0}RequestTag = {1};", messageType.Name, GetStableHash(string.Format("{0}.{1}.{2}.Request", ns.Name, messagesType.Name, messageType.Name))))); messageType.Members.Add(new CodeSnippetTypeMember(string.Format("public const int {0}ResponseTag = {1};", messageType.Name, GetStableHash(string.Format("{0}.{1}.{2}.Response", ns.Name, messagesType.Name, messageType.Name))))); messageType.Members.Add(new CodeSnippetTypeMember(@"public static event ProcessRequestDelegate<Request, Response> MessageReceived;")); messageType.Members.Add(new CodeSnippetTypeMember(string.Format( messageDef.Response == null ? @"public static Task SendAsync({1}) {{ return Task.Factory.StartNew(() => {{ Serialize(stream, new {0}.Request {{ {2} }}); }}); }}" : @"public static Task<{0}.Response> SendAsync({1}) {{ return Task.Factory.StartNew(() => {{ Serialize(stream, new {0}.Request {{ {2} }}); return Deserialize<{0}.Response>(stream); }}); }}", messageType.Name, string.Join(", ", new[] { "Stream stream" }.Concat(from field in messageDef.Request.Fields let paramName = getFormalParamName(field) select string.Format("{0} {1}", field.Type, paramName))), string.Join(", \n", from field in messageDef.Request.Fields let paramName = getFormalParamName(field) select string.Format("{0} = {1}", field.Name, paramName))))); messageType.Members.Add(new CodeSnippetTypeMember(string.Format( messageDef.Response == null ? @"public static void Send({1}) {{ Serialize(stream, new {0}.Request {{ {2} }}); }}" : @"public static {0}.Response Send({1}) {{ Serialize(stream, new {0}.Request {{ {2} }}); return Deserialize<{0}.Response>(stream); }}", messageType.Name, string.Join(", ", new[] { "Stream stream" }.Concat(from field in messageDef.Request.Fields let paramName = getFormalParamName(field) select string.Format("{0} {1}", field.Type, paramName))), string.Join(", \n", from field in messageDef.Request.Fields let paramName = getFormalParamName(field) select string.Format("{0} = {1}", field.Name, paramName))))); // Add extensions methods for true RPC messagesType.Members.Add(new CodeSnippetTypeMember(string.Format( messageDef.Response == null ? @"public static void {1}(this {2}) {{ {3}.Send({4}); }}" : @"public static {0}.Response {1}(this {2}) {{ return {3}.Send({4}); }}", messageType.Name, messagesType.Name + messageType.Name, string.Join(", ", new[] { "Stream stream" }.Concat(from field in messageDef.Request.Fields let paramName = getFormalParamName(field) select string.Format("{0} {1}", field.Type, paramName))), messageType.Name, string.Join(", ", new[] { "stream" }.Concat(from field in messageDef.Request.Fields let paramName = getFormalParamName(field) select paramName)) ))); messagesType.Members.Add(new CodeSnippetTypeMember(string.Format( messageDef.Response == null ? @"public static Task {1}Async(this {2}) {{ {3}.SendAsync({4}); }}" : @"public static Task<{0}.Response> {1}Async(this {2}) {{ return {3}.SendAsync({4}); }}", messageType.Name, messagesType.Name + messageType.Name, string.Join(", ", new[] { "Stream stream" }.Concat(from field in messageDef.Request.Fields let paramName = getFormalParamName(field) select string.Format("{0} {1}", field.Type, paramName))), messageType.Name, string.Join(", ", new[] { "stream" }.Concat(from field in messageDef.Request.Fields let paramName = getFormalParamName(field) select paramName)) ))); CodeTypeDeclaration messageRequestType = null; if (messageDef.Request != null) { messageRequestType = new CodeTypeDeclaration("Request") { Attributes = MemberAttributes.Final, TypeAttributes = TypeAttributes.Public, BaseTypes = { new CodeTypeReference("IRequest") }, CustomAttributes = { new CodeAttributeDeclaration("ProtoContract", new[] { new CodeAttributeArgument { Name = "Name", Value = new CodeSnippetExpression(string.Format("\"{0}{1}\"",messageType.Name, "Request")) } }) } }; messageRequestType.Members.Add(new CodeSnippetTypeMember(string.Format("int IRequest.Tag {{ get {{ return {0}RequestTag; }} }}", messageType.Name))); messageRequestType.Members.Add(new CodeSnippetTypeMember(string.Format("object IRequest.InvokeReceived() {{ return {0}.MessageReceived(this); }}", messageType.Name))); messageType.Members.Add(messageRequestType); int fieldTag = 1; foreach (var field in messageDef.Request.Fields) { var member = new CodeSnippetTypeMember(string.Format("[ProtoMember({2}, IsRequired = {3})] {4} public {0} {1} {{ get; set; }}", field.Type, field.Name, fieldTag++, field.Mandatory.ToString().ToLowerInvariant(), field.DefaultValue != null ? string.Format("[DefaultValue({0})]", field.DefaultValue) : "")); messageRequestType.Members.Add(member); } messagesContainerType.Members.Add(new CodeSnippetTypeMember(string.Format("[ProtoMember({0})] public {1}.{2}.{3} {1}_{2}_{3} {{ get; set; }}", dummyFieldsTag++, messagesType.Name, messageType.Name, "Request"))); } CodeTypeDeclaration messageResponseType = null; if (messageDef.Response != null) { messageResponseType = new CodeTypeDeclaration("Response") { Attributes = MemberAttributes.Final, TypeAttributes = TypeAttributes.Public, BaseTypes = { new CodeTypeReference("IResponse") }, CustomAttributes = { new CodeAttributeDeclaration("ProtoContract", new[] { new CodeAttributeArgument { Name = "Name", Value = new CodeSnippetExpression(string.Format("\"{0}{1}\"",messageType.Name, "Response")) } }) } }; messageResponseType.Members.Add(new CodeSnippetTypeMember(string.Format("int IResponse.Tag {{ get {{ return {0}ResponseTag; }} }}", messageType.Name))); messageType.Members.Add(messageResponseType); int fieldTag = 1; foreach (var field in messageDef.Response.Fields) { var member = new CodeSnippetTypeMember(string.Format("[ProtoMember({2}, IsRequired = {3})] {4} public {0} {1} {{ get; set; }}", field.Type, field.Name, fieldTag++, field.Mandatory.ToString().ToLowerInvariant(), field.DefaultValue != null ? string.Format("[DefaultValue({0})]", field.DefaultValue) : "")); messageResponseType.Members.Add(member); } messagesContainerType.Members.Add(new CodeSnippetTypeMember(string.Format("[ProtoMember({0})] public {1}.{2}.{3} {1}_{2}_{3} {{ get; set; }}", dummyFieldsTag++, messagesType.Name, messageType.Name, "Response"))); } } } // foreach type return(codeUnit); }
public string GenerateProtoFile(string sourceFilename, CodeCompileUnit compileUnit, MessageParser.PostalDefinition parsed) { var provider = new CSharpCodeProvider(); var sourceCode = File.ReadAllText(sourceFilename); var parameters = new CompilerParameters { GenerateExecutable = false, GenerateInMemory = true, ReferencedAssemblies = { "Microsoft.CSharp.dll", "System.dll", "System.Core.dll", "System.Xml.dll", Path.Combine(OutputDir.Replace("obj", "bin"), "protobuf-net.dll"), Path.Combine(OutputDir.Replace("obj", "bin"), "Postal.Core.dll"), Path.Combine(OutputDir.Replace("obj", "bin"), "Sprache.dll") }, OutputAssembly = string.Format("{0}\\{1}.dll", OutputDir.Replace("obj", "bin"), Guid.NewGuid().ToString()), }; var results = provider.CompileAssemblyFromSource(parameters, sourceCode); if (results.Errors.Count > 0) { for (int i = 0; i < results.Errors.Count; i++) { BuildEngine.LogErrorEvent(new BuildErrorEventArgs("Postal", results.Errors[i].ErrorNumber, results.Errors[i].FileName, results.Errors[i].Line, results.Errors[i].Column, results.Errors[i].Line, results.Errors[i].Column, results.Errors[i].ErrorText, "Postal", "Postal")); } } var assembly = results.CompiledAssembly; // Loads this assembly into our namespace var type = (from t in assembly.GetTypes() where t.IsDefined(typeof(ProtoContractAttribute), false) && t.Name.EndsWith("_MessagesContainer") select t).FirstOrDefault(); if (type == null) { return(string.Empty); } var method = GetProtoMethod.MakeGenericMethod(type); if (method == null) { return("method was null"); } var proto = (string)method.Invoke(null, null); proto = RemoveMessageContainer.Replace(proto, ""); var messagesTypeName = RemoveAllExtensions(Path.GetFileName(sourceFilename)); var sb = new StringBuilder(); foreach (var postalType in parsed.PostalTypes) { var message = postalType as MessageParser.MessageDefinition; if (message == null) { continue; } sb.AppendFormat("message {0}RequestWrapper\n", message.Name); sb.AppendLine("{"); sb.AppendFormat(" optional {0}Request {0}Request = {1};\n", message.Name, GetStableHash(string.Format("{0}.{1}.{2}.Request", parsed.Namespace, messagesTypeName, message.Name))); sb.AppendLine("}"); sb.AppendFormat("message {0}ResponseWrapper\n", message.Name); sb.AppendLine("{"); sb.AppendFormat(" optional {0}Response {0}Response = {1};\n", message.Name, GetStableHash(string.Format("{0}.{1}.{2}.Response", parsed.Namespace, messagesTypeName, message.Name))); sb.AppendLine("}"); } return(proto + "\n" + sb.ToString()); }