RSS

gRPCと.NET SDK および Visual Studio:ビルド時の自動コード生成

Microsoft がクロスプラットフォームの .NET オファリングに向けて進む中で、プロジェクトファイル形式を大幅に簡素化し、サードパーティのコードジェネレーターを .NET プロジェクトと緊密に統合できるようにしました。私たちはこの声に耳を傾け、現在 Nuget.org から入手可能な Grpc.Tools NuGet パッケージのバージョン 1.17 以降、.NET C# プロジェクトでの Protocol Buffer および gRPC サービス .proto ファイルのコンパイル統合を発表できることを誇りに思います。

.proto ファイルからコードを生成するために、手書きのスクリプトを使用する必要はもうありません。.NET のビルドマジックがこれを処理します。統合ツールは、プロトコンパイラと gRPC プラグイン、標準 Protocol Buffer インポートを見つけ、コードジェネレーターを呼び出す前に依存関係を追跡するため、生成された C# ソースファイルは常に最新の状態に保たれ、同時に再生成は必要最低限に抑えられます。本質的に、.proto ファイルは .NET C# プロジェクト内でファーストクラスのソースとして扱われます。

ウォークスルー

このブログ投稿では、クロスプラットフォームの dotnet コマンドを使用して .proto ファイルからライブラリを作成する、最もシンプルで、おそらく最も一般的なシナリオを段階的に説明します。 essentially、C# Helloworld の例のディレクトリ でクライアントとサーバーのプロジェクト間で共有される Greeter ライブラリのクローンを実装します。

新しいプロジェクトを作成する

まず、新しいライブラリプロジェクトを作成しましょう。

~/work$ dotnet new classlib -o MyGreeter
The template "Class library" was created successfully.

~/work$ cd MyGreeter
~/work/MyGreeter$ ls -lF
total 12
-rw-rw-r-- 1 kkm kkm   86 Nov  9 16:10 Class1.cs
-rw-rw-r-- 1 kkm kkm  145 Nov  9 16:10 MyGreeter.csproj
drwxrwxr-x 2 kkm kkm 4096 Nov  9 16:10 obj/

dotnet new コマンドが Class1.cs ファイルを作成したことに注意してください。このファイルは必要ないので削除します。また、コンパイルするには .proto ファイルが必要です。この演習のために、gRPC ディストリビューションから例のファイル examples/protos/helloworld.proto をコピーします。

~/work/MyGreeter$ rm Class1.cs
~/work/MyGreeter$ wget -q https://raw.githubusercontent.com/grpc/grpc/master/examples/protos/helloworld.proto

(Windows では、del Class1.cs を使用し、wget コマンドがない場合は、上記の URL を開く だけで、Web ブラウザから 名前を付けて保存… コマンドを使用してください)。

次に、プロジェクトに必要な NuGet パッケージを追加します。

~/work/MyGreeter$ dotnet add package Grpc
info : PackageReference for package 'Grpc' version '1.17.0' added to file '/home/kkm/work/MyGreeter/MyGreeter.csproj'.
~/work/MyGreeter$ dotnet add package Grpc.Tools
info : PackageReference for package 'Grpc.Tools' version '1.17.0' added to file '/home/kkm/work/MyGreeter/MyGreeter.csproj'.
~/work/MyGreeter$ dotnet add package Google.Protobuf
info : PackageReference for package 'Google.Protobuf' version '3.6.1' added to file '/home/kkm/work/MyGreeter/MyGreeter.csproj'.

.proto ファイルをプロジェクトに追加する

次は重要な部分です。 まず、デフォルトでは、.csproj プロジェクトファイルは、そのディレクトリ内のすべての .cs ファイルを自動的に検出しますが、Microsoft は現在、このグロビング動作を抑制することを推奨しています。そのため、私たちも .proto ファイルのグロビングは行わないことにしました。したがって、.proto ファイルは明示的にプロジェクトに追加する必要があります。

第二に、Grpc.Tools パッケージ参照に PrivateAssets="All" プロパティを追加することが重要です。これにより、新しいライブラリのコンシューマーが不要に取得することを防ぎます。このパッケージには、コンパイラ、コードジェネレーター、およびインポートファイルのみが含まれており、.proto ファイルがコンパイルされたプロジェクトの外部では必要ないため、これは理にかなっています。この簡単なウォークスルーでは必須ではありませんが、常に標準的なプラクティスにする必要があります。

したがって、MyGreeter.csproj ファイルを編集して helloworld.proto を追加し、コンパイルできるようにし、Grpc.Tools パッケージ参照に PrivateAssets プロパティを追加します。結果のプロジェクトファイルは次のようになります。

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Google.Protobuf" Version="3.6.1" />
    <PackageReference Include="Grpc" Version="1.17.0" />

    <!-- The Grpc.Tools package generates C# sources from .proto files during
         project build, but is not needed by projects using the built library.
         It's IMPORTANT to add the 'PrivateAssets="All"' to this reference: -->
    <PackageReference Include="Grpc.Tools" Version="1.17.0" PrivateAssets="All" />

    <!-- Explicitly include our helloworld.proto file by adding this line: -->
    <Protobuf Include="helloworld.proto" />
  </ItemGroup>

</Project>

ビルドする!

この時点で、dotnet build コマンドでプロジェクトをビルドして、.proto ファイルとライブラリのアセンブリをコンパイルできます。このウォークスルーでは、コマンドにログスイッチ -v:n を追加して、helloworld.proto ファイルをコンパイルするコマンドが実際に実行されたことを確認できるようにします。プロジェクトを初めてコンパイルするときは、常にこのスイッチを使用することをお勧めします!

以下の出力行の多くは省略されています。ビルド出力は非常に冗長であるためです。

~/work/MyGreeter$ dotnet build -v:n

Build started 11/9/18 5:33:44 PM.
  1:7>Project "/home/kkm/work/MyGreeter/MyGreeter.csproj" on node 1 (Build target(s)).
   1>_Protobuf_CoreCompile:
      /home/kkm/.nuget/packages/grpc.tools/1.17.0/tools/linux_x64/protoc
        --csharp_out=obj/Debug/netstandard2.0
        --plugin=protoc-gen-grpc=/home/kkm/.nuget/packages/grpc.tools/1.17.0/tools/linux_x64/grpc_csharp_plugin
        --grpc_out=obj/Debug/netstandard2.0 --proto_path=/home/kkm/.nuget/packages/grpc.tools/1.17.0/build/native/include
        --proto_path=. --dependency_out=obj/Debug/netstandard2.0/da39a3ee5e6b4b0d_helloworld.protodep helloworld.proto
     CoreCompile:

        [ ... skipping long output ... ]

       MyGreeter -> /home/kkm/work/MyGreeter/bin/Debug/netstandard2.0/MyGreeter.dll

Build succeeded.

この時点で dotnet build -v:n コマンドを再度呼び出しても、protoc は呼び出されず、C# ソースもコンパイルされません。ただし、helloworld.proto ソースを変更すると、その出力が再生成され、ビルド中に C# コンパイラによって再コンパイルされます。これは、ソースファイルを変更したときに期待される通常の依存関係追跡動作です。

もちろん、同じプロジェクトに .cs ファイルを追加することもできます。結局のところ、これは .NET ライブラリをビルドする通常の C# プロジェクトです。これは、RouteGuide の例で行われています。

生成されたファイルはどこにあるのか?

プロトコンパイラと gRPC プラグインの C# ファイルの出力がどこにあるのか不思議に思うかもしれません。デフォルトでは、それらは obj/ ディレクトリの下にある、他の生成されたファイル(.NET ビルドの専門用語では「中間出力」ディレクトリと呼ばれる)と同じディレクトリに配置されます。これは .NET ビルドの通常のプラクティスであり、自動生成されたファイルが作業ディレクトリを散らかしたり、誤ってソース管理下に配置されたりするのを防ぎます。そうでなければ、デバッガーのようなツールからアクセスできます。そのディレクトリには、他の自動生成されたソースも表示されます。

~/work/MyGreeter$ find obj -name '*.cs'
obj/Debug/netstandard2.0/MyGreeter.AssemblyInfo.cs
obj/Debug/netstandard2.0/Helloworld.cs
obj/Debug/netstandard2.0/HelloworldGrpc.cs

(Windows コマンドプロンプトからこのウォークスルーをフォローしている場合は、dir /s obj\*.cs を使用してください)。

さらに詳細へ

最もシンプルなデフォルトの動作は多くのケースで十分ですが、大規模なプロジェクトで .proto コンパイルプロセスを微調整するには多くの方法があります。デフォルトの構成がワークフローに適さない場合は、ドキュメントファイル BUILD-INTEGRATION.md を参照して、利用可能なオプションを確認することをお勧めします。このパッケージは Visual Studio のプロパティウィンドウも拡張しているため、Visual Studio インターフェイスでファイルごとに一部のオプションを設定できます。

「クラシック」な .csproj プロジェクトと Mono もサポートされています。

あなたの経験を共有する

複雑な機能の初期リリースと同様に、皆様からのフィードバックを心よりお待ちしております。期待どおりに動作しなかったことはありますか?新しいツールではカバーしにくいシナリオがありますか?ワークフロー全般を改善する方法についてのアイデアはありますか?ドキュメントを注意深くお読みになり、その後、GitHub の gRPC コードリポジトリで問題を報告してください。皆様のフィードバックは、ビルド統合作業の将来の方向性を決定するために重要です!