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

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

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

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

选项似乎是:

  1. 原型工具的自定义工具(虽然我看不到从哪里开始)
  2. 预构建步骤(调用protogen或批处理文件,这样做)

我一直在努力2)因为它一直给我“系统找不到指定的文件”,除非我使用绝对路径(我不喜欢强制项目明确定位)。

还有一个约定吗?


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

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

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

 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 ? 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 

作为Shaun代码的扩展,我很高兴地宣布protobuf-net现在通过自定义工具集成了Visual Studio。 msi安装程序可从项目页面获得 。 更完整的信息: protobuf-net; 现在添加了Orcas 。

Visual Studio与protobuf-net作为自定义工具

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

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

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

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

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

 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 } 

根据问题338和413 ,这适用于任何最新版本的Visual Studio,与protobuf-net Custom-Build工具不同,后者不支持Visual Studio 2012或Visual Studio 2013。

好吧,这给了我一个想法(关于重新发明轮子的东西)……

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

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

(显然,不要忘记替换protogen和csharp.xlst的路径)。 重要信息 – protogen \ protogen.exe命令从TAB字符开始,而不是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 

或者,如果你在你的道路上有人,你可以使用

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

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

优势,增量构建。

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

    Person.proto    ProtobufGenerate;$(CompileDependsOn)    <_protoc Include="..\packages\Google.Protobuf.*\tools\protoc.exe">     

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

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