使用protobuf-net的C#项目中的协议缓冲区 - 代码生成的最佳实践

时间:2009-01-17 18:57:26

标签: c# visual-studio protocol-buffers protobuf-net

我正在尝试使用protobuf-net在C#项目中使用protobuf,并且想知道将这个组织到Visual Studio项目结构中的最佳方法是什么。

当手动使用protogen工具生成C#代码时,生活似乎很容易,但感觉不对。

我希望.proto文件被认为是主要的源代码文件,生成C#文件作为副产品,但在C#编译器参与之前。

选项似乎是:

  1. 原型工具的自定义工具(虽然我看不到从哪里开始)
  2. 预构建步骤(调用protogen或批处理文件)
  3. 我一直在努力解决2)因为它一直给我“系统找不到指定的文件”,除非我使用绝对路径(我不喜欢强制项目明确定位)。

    是否还有一个约定呢?


    编辑: 基于@ jon的评论,我重新使用了预构建步骤方法,并使用Google的地址簿示例使用了这个(protogen的位置现在硬编码):

    c:\bin\protobuf\protogen "-i:$(ProjectDir)AddressBook.proto" 
           "-o:$(ProjectDir)AddressBook.cs" -t:c:\bin\protobuf\csharp.xslt
    

    EDIT2: 考虑到@ jon的建议,如果它们没有改变就不通过处理.proto文件来最小化构建时间,我已经敲了一个基本工具来检查我(这可能会扩展到一个完整的Custom-Build工具):

    using System;
    using System.Diagnostics;
    using System.IO;
    
    namespace PreBuildChecker
    {
        public class Checker
        {
            static int Main(string[] args)
            {
                try
                {
                    Check(args);
                    return 0;
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                    return 1;
                }
            }
    
            public static void Check(string[] args)
            {
                if (args.Length < 3)
                {
                    throw new ArgumentException(
                        "Command line must be supplied with source, target and command-line [plus options]");
                }
    
                string source = args[0];
                string target = args[1];
                string executable = args[2];
                string arguments = args.Length > 3 ? GetCommandLine(args) : null;
    
                FileInfo targetFileInfo = new FileInfo(target);
                FileInfo sourceFileInfo = new FileInfo(source);
                if (!sourceFileInfo.Exists) 
                {
                    throw new ArgumentException(string.Format(
                        "Source file {0} not found", source));
                }
    
                if (!targetFileInfo.Exists || 
                    sourceFileInfo.LastWriteTimeUtc > targetFileInfo.LastAccessTimeUtc)
                {
                    Process process = new Process();
                    process.StartInfo.FileName = executable;
                    process.StartInfo.Arguments = arguments;
                    process.StartInfo.ErrorDialog = true;
    
                    Console.WriteLine(string.Format(
                         "Source newer than target, launching tool: {0} {1}",
                         executable,
                         arguments));
                    process.Start();
                }
            }
    
            private static string GetCommandLine(string[] args)
            {
                string[] arguments = new string[args.Length - 3];
                Array.Copy(args, 3, arguments, 0, arguments.Length);
                return String.Join(" ", arguments);
            }
        }
    }
    

    我的预构建命令现在(全部在一行):

    $(SolutionDir)PreBuildChecker\$(OutDir)PreBuildChecker 
        $(ProjectDir)AddressBook.proto 
        $(ProjectDir)AddressBook.cs 
        c:\bin\protobuf\protogen 
          "-i:$(ProjectDir)AddressBook.proto" 
          "-o:$(ProjectDir)AddressBook.cs" 
          -t:c:\bin\protobuf\csharp.xslt
    

6 个答案:

答案 0 :(得分:8)

调用预构建步骤但使用项目变量(例如$(ProjectPath))来创建绝对文件名而不将它们实际存在于您的解决方案中似乎是合理的选择。

根据我过去的代码生成器经验,你可能想要考虑一件事:你可能想要为protogen编写一个包装器,它将代码生成到不同的位置,然后检查新生成的代码是否与旧代码相同代码,如果是这样,不会覆盖它。这样,Visual Studio就会意识到没有任何改变,也没有强制重建项目 - 这在过去已经为我缩短了构建时间

或者,您可以在上次执行protogen时保留.proto文件的md5哈希值,并且只有在.proto文件发生更改时才执行protogen - 更不用说每次构建都要执行!

感谢您将此作为一个问题提出来 - 它清楚地表明我应该找到一种方法来为我自己的端口做一个简单的预构建步骤。

答案 1 :(得分:8)

作为Shaun代码的扩展,我很高兴地宣布protobuf-net现在通过自定义工具集成了Visual Studio。 msi安装程序可从project page获得。这里有更完整的信息:protobuf-net; now with added Orcas

Visual Studio with protobuf-net as a Custom Tool

答案 2 :(得分:6)

将以下预构建事件添加到项目设置,以便仅在.proto文件更改时生成C#文件。只需将YourFile替换为.proto文件的基本名称即可。

cd $(ProjectDir) && powershell -Command if (!(Test-Path YourFile.proto.cs) -or (Get-Item YourFile.proto).LastWriteTimeUtc -gt (Get-Item YourFile.proto.cs).LastWriteTimeUtc) { PathToProtoGen\protogen -i:YourFile.proto -o:YourFile.proto.cs }

这适用于任何最新版本的Visual Studio,与protobuf-net自定义构建工具不同,后者根据问题338413不支持Visual Studio 2012或Visual Studio 2013。

答案 3 :(得分:6)

将其添加到相关的项目文件中。

优势,增量构建。

缺点,您需要在添加文件时手动编辑。

string a = "hello";
string b = "world";

Console.WriteLine(a);
Console.WriteLine(b);

答案 4 :(得分:5)

嗯,这给了我一个想法(关于重新发明轮子的事情)......

  • 创建简单的Makefile.mak,类似
.SUFFIXES : .cs .proto

.proto.cs:
    protogen\protogen.exe -i:$? -o:$@ -t:protogen\csharp.xlst

(显然,不要忘记替换protogen和csharp.xlst的路径)。 重要信息 - 从TAB字符开始的protogen \ protogen.exe命令,而不是8个空格

  • 如果您不想指定需要一直构建的文件,可以使用类似
  • 的内容
.SUFFIXES : .cs .proto

all: mycs1.cs myotherfile.cs

.proto.cs:
    protogen\protogen.exe -i:$? -o:$@ -t:protogen\csharp.xlst
  • 在预构建步骤中添加
cd $(ProjectDir) && "$(DevEnvDir)..\..\vc\bin\nmake" /NOLOGO -c -f Makefile.mak mycs1.cs myotherfile.cs

或者,如果您的路径中有nmake,可以使用  

cd $(ProjectDir) && nmake /NOLOGO -c -f Makefile.mak mycs1.cs myotherfile.cs

答案 5 :(得分:2)

我已将针对ProtoGen.exe的快速且脏的Visual Studio自定义工具包装器附加到此问题的Google代码页(http://code.google.com/p/protobuf-net/issues/detail?id=39)。这使得将.proto文件添加到C#项目非常容易。

有关详细信息,请参阅附件中的自述文件。