代码之家  ›  专栏  ›  技术社区  ›  Ray Hayes

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

  •  16
  • Ray Hayes  · 技术社区  · 16 年前

    我试图在C#项目中使用protobuf,使用protobuf net,我想知道将其组织到Visual Studio项目结构中的最佳方法是什么。

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

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

    选择似乎是:

    1. proto工具的自定义工具(尽管我不知道从哪里开始)
    2. 预构建步骤(调用protogen或执行此操作的批处理文件)

    我一直在与上面的2)进行斗争,因为它一直给我“系统无法找到指定的文件”,除非我使用绝对路径(而且我不喜欢强制项目显式定位)。


    编辑:

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

    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 回复  |  直到 16 年前
        1
  •  8
  •   Jon Skeet    16 年前

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

    根据我过去的代码生成器的经验,您可能需要考虑的一件事是:您可能要为Pr原生境编写一个包装器,它将代码生成到不同的位置,然后检查新生成的代码是否与旧代码相同,如果不这样,则不重写。这样VisualStudio就不会意识到什么都没有改变,也不会强制重新构建该项目——这缩短了构建时间 戏剧性地 过去对我来说。

    或者,您可以在最后一次执行protogen时保留.proto文件的md5哈希,并且仅在.proto文件发生更改时执行protogen—在每次构建时更不用说了!

    不过,感谢您将此作为一个问题提出—它清楚地建议我应该找到一种方法,使其成为我自己的端口的一个简单的预构建步骤。

        2
  •  8
  •   Community CDub    8 年前

    作为Shaun代码的扩展,我很高兴地宣布protobuf net现在通过一个定制工具与Visual Studio集成。msi安装程序可从 project page . 更多完整信息请点击此处: protobuf-net; now with added Orcas .

    Visual Studio with protobuf-net as a Custom Tool

        3
  •  6
  •   Edward Brey    11 年前

    将以下预构建事件添加到项目设置中,以仅在.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自定义构建工具不支持Visual Studio 2012或Visual Studio 2013 338 413 .

        4
  •  6
  •   Tamir Daniely    9 年前

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

    优势,增量构建。

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

    <ItemGroup>
        <Proto Include="Person.proto" />
        <Compile Include="Person.cs">
            <DependentUpon>Person.proto</DependentUpon>
        </Compile>
    </ItemGroup>
    <PropertyGroup>
        <CompileDependsOn>ProtobufGenerate;$(CompileDependsOn)</CompileDependsOn>
    </PropertyGroup>
    <Target Name="ProtobufGenerate" Inputs="@(Proto)" Outputs="@(Proto->'$(ProjectDir)%(Filename).cs')">
        <ItemGroup>
            <_protoc Include="..\packages\Google.Protobuf.*\tools\protoc.exe" />
        </ItemGroup>
        <Error Condition="!Exists(@(_protoc))" Text="Could not find protoc.exe" />
        <Exec Command="&quot;@(_protoc)&quot; &quot;--csharp_out=$(ProjectDir.TrimEnd('\'))&quot; @(Proto->'%(Identity)',' ')" WorkingDirectory="$(ProjectDir)" />
    </Target>
    
        5
  •  5
  •   WrightsCS    13 年前

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

    • 创建简单的Makefile.mak,类似于
    .SUFFIXES : .cs .proto
    
    .proto.cs:
        protogen\protogen.exe -i:$? -o:$@ -t:protogen\csharp.xlst
    

    • 如果您不想一直指定需要生成的文件,您可以使用
    .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
        6
  •  2
  •   Shaun Shaun    15 年前

    我已经在ProtoGen.exe的Google代码页上附加了一个快速而肮脏的Visual Studio自定义工具包装器( http://code.google.com/p/protobuf-net/issues/detail?id=39