16

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

当手动使用 protogen 工具将代码生成到 C# 中时,生活似乎很轻松,但感觉并不正确。

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

选项似乎是:

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

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

是否有一个约定(还)?


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

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

Edit2: 采纳@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)
            {
                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
4

6 回答 6

8

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

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

或者,您可以保留上次执行 protogen 时 .proto 文件的 md5 散列,并且仅在 .proto 文件发生更改时才执行 protogen - 每次构建时要做的更少!

不过,感谢您提出这个问题 - 它清楚地表明我应该想出一种方法,使这成为我自己的端口的简单预构建步骤。

于 2009-01-17T19:06:34.320 回答
8

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

使用 protobuf-net 作为自定义工具的 Visual Studio

于 2009-07-16T18:06:36.723 回答
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 }

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

于 2012-12-13T08:52:12.963 回答
6

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

优势,增量构建。

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

<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>
于 2015-09-17T19:50:48.227 回答
5

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

  • 创建简单的 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

全部: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

于 2009-05-22T00:44:05.607 回答
2

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

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

于 2009-06-24T18:02:25.807 回答