protobuf-netを使用したC#プロジェクトのプロトコルバッファー-コード生成のベストプラクティス
-
19-08-2019 - |
質問
protobuf-netを使用して、C#プロジェクトでprotobufを使用しようとしていますが、これをVisual Studioプロジェクト構造に整理する最良の方法は何ですか?
protogenツールを使用して手動でC#にコードを生成する場合、作業は簡単に思えますが、気分が悪くなります。
.protoファイルをプライマリソースコードファイルと見なし、C#ファイルを副産物として生成したいのですが、C#コンパイラが関与する前に
オプションは次のようです:
- プロトツール用のカスタムツール(どこから始めればよいかわかりませんが)
- ビルド前の手順(protogenまたはそれを行うバッチファイルの呼び出し)
上記2)に苦労しました<!> quot;指定されたファイルが見つかりません<!> quot;絶対パスを使用しない限り(そして、プロジェクトを明示的に配置するのが嫌いです)。
これには慣例はありますか(まだ)?
編集: @jonのコメントに基づいて、Googleのアドレス帳の例を使用して、ビルド前のステップメソッドを再試行し、これを使用しました(現在のところ、プロトタイプの場所はハードコードされています)。
c:\bin\protobuf\protogen "-i:$(ProjectDir)AddressBook.proto"
"-o:$(ProjectDir)AddressBook.cs" -t:c:\bin\protobuf\csharp.xslt
Edit2: .protoファイルが変更されていない場合、.protoファイルを処理しないことでビルド時間を最小限に抑えるという@jonの推奨事項に基づいて、チェック用の基本ツールをまとめました(これはおそらく完全なカスタムビルドツールに拡張できます)。
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);
}
}
}
私のビルド前コマンドは現在(すべて1行で)です:
$(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 で。
他のヒント
ビルド前のステップを呼び出すが、プロジェクト変数(例:$(ProjectPath)
)を使用して、実際にソリューションに含めずに絶対ファイル名を作成することは、合理的な賭けのように思えます。
コードジェネレーターの私の過去の経験に基づいて、考慮したいことの1つ:別の場所にコードを生成するprotogenのラッパーを作成し、新しく生成されたコードが古いコードと同じかどうかを確認するコードを作成し、上書きしても上書きしません。そうすれば、Visual Studioは何も変わっていないことを認識し、そのプロジェクトの再構築を強制しません。これにより、過去のビルド時間は劇的に 短縮されました。
あるいは、protogenが最後に実行されたときに.protoファイルのmd5ハッシュを保持し、.protoファイルが変更された場合にのみprotogenを実行することもできます-ビルドごとに行うことはさらに少なくなります!
質問としてこれを提起してくれてありがとう-これは、これを自分のポートの簡単なビルド前のステップにする方法を考えるべきだということを明確に示唆しています。
次のビルド前イベントをプロジェクト設定に追加して、.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 }
これを関連するプロジェクトファイルに追加します。
利点、増分ビルド。
欠点、ファイルを追加するときに手動で編集する必要があります。
<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=""@(_protoc)" "--csharp_out=$(ProjectDir.TrimEnd('\'))" @(Proto->'%(Identity)',' ')" WorkingDirectory="$(ProjectDir)" />
</Target>
まあ、それは私にアイデアを与えました(車輪の再発明に関する何か)...
- 単純なMakefile.makのようなものを作成します
.SUFFIXES : .cs .proto .proto.cs: protogen\protogen.exe -i:$? -o:$@ -t:protogen\csharp.xlst
(明らかに、protogenとcsharp.xlstへのパスを置き換えることを忘れないでください)。 重要-protogen \ protogen.exeコマンドは、8文字のスペースではなく、TAB文字から始まります
- 常にビルドする必要があるファイルを指定したくない場合は、次のようなものを使用できます
.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
この問題のために、ProtoGen.exeの周りにすばやく汚れたVisual StudioカスタムツールラッパーをGoogle Codeページに添付しました( http://code.google.com/p/protobuf-net/issues/detail?id=39 )。これにより、C#プロジェクトへの.protoファイルの追加が非常に簡単になります。
詳細については、添付ファイルのreadmeを参照してください。