Skip to content
This repository was archived by the owner on Apr 9, 2021. It is now read-only.

Commit ae762c0

Browse files
authored
Merge pull request #775 from kkm000/kkm/grpc-dotnet-build
.NET SDK integration post, NuGet v1.17
2 parents 0c9e2e3 + 7b7441a commit ae762c0

1 file changed

Lines changed: 208 additions & 0 deletions

File tree

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
---
2+
layout: post
3+
title: gRPC Meets .NET SDK And Visual Studio: Automatic Codegen On Build
4+
published: true
5+
permalink: blog/grpc-dotnet-build
6+
author: Kirill 'kkm' Katsnelson
7+
author-link: https://github.com/kkm000
8+
company: SmartAction
9+
company-link: https://www.smartaction.ai
10+
---
11+
12+
As part of Microsoft's move towards its cross-platform .NET offering, they have
13+
greatly simplified the project file format, and allowed a tight integration of
14+
third-party code generators with .NET projects. We are listening, and now proud
15+
to introduce integrated compilation of Protocol Buffer and gRPC service
16+
`.proto` files in .NET C# projects starting with the version 1.17 of the
17+
Grpc.Tools NuGet package, now available from Nuget.org.
18+
19+
You no longer need to use hand-written scripts to generate code from `.proto`
20+
files: The .NET build magic handles this for you. The integrated tools locate
21+
the proto compiler and gRPC plugin, standard Protocol Buffer imports, and track
22+
dependencies before invoking the code generators, so that the generated C#
23+
source files are never out of date, at the same time keeping regeneration to
24+
the minimum required. In essence, `.proto` files are treated as first-class
25+
sources in a .NET C# project.
26+
27+
<!--more-->
28+
29+
## A Walkthrough
30+
31+
In this blog post, we'll walk through the simplest and probably the most common
32+
scenario of creating a library from `.proto` files using the cross-platform
33+
`dotnet` command. We will implement essentially a clone of the `Greeter`
34+
library, shared by client and server projects in the [C# `Helloworld` example
35+
directory
36+
](https://github.com/grpc/grpc/tree/master/examples/csharp/Helloworld/Greeter).
37+
38+
### Create a new project
39+
40+
Let's start by creating a new library project.
41+
42+
```
43+
~/work$ dotnet new classlib -o MyGreeter
44+
The template "Class library" was created successfully.
45+
46+
~/work$ cd MyGreeter
47+
~/work/MyGreeter$ ls -lF
48+
total 12
49+
-rw-rw-r-- 1 kkm kkm 86 Nov 9 16:10 Class1.cs
50+
-rw-rw-r-- 1 kkm kkm 145 Nov 9 16:10 MyGreeter.csproj
51+
drwxrwxr-x 2 kkm kkm 4096 Nov 9 16:10 obj/
52+
```
53+
54+
Observe that the `dotnet new` command has created the file `Class1.cs` that
55+
we won't need, so remove it. Also, we need some `.proto` files to compile. For
56+
this exercise, we'll copy an example file [`examples/protos/helloworld.proto`
57+
](https://github.com/grpc/grpc/blob/master/examples/protos/helloworld.proto)
58+
from the gRPC distribution.
59+
60+
```
61+
~/work/MyGreeter$ rm Class1.cs
62+
~/work/MyGreeter$ wget -q https://raw.githubusercontent.com/grpc/grpc/master/examples/protos/helloworld.proto
63+
```
64+
65+
(on Windows, use `del Class1.cs`, and, if you do not have the wget command,
66+
just [open the above URL
67+
](https://raw.githubusercontent.com/grpc/grpc/master/examples/protos/helloworld.proto)
68+
and use a *Save As...* command from your Web browser).
69+
70+
Next, add required NuGet packages to the project:
71+
72+
```
73+
~/work/MyGreeter$ dotnet add package Grpc
74+
info : PackageReference for package 'Grpc' version '1.17.0' added to file '/home/kkm/work/MyGreeter/MyGreeter.csproj'.
75+
~/work/MyGreeter$ dotnet add package Grpc.Tools
76+
info : PackageReference for package 'Grpc.Tools' version '1.17.0' added to file '/home/kkm/work/MyGreeter/MyGreeter.csproj'.
77+
~/work/MyGreeter$ dotnet add package Google.Protobuf
78+
info : PackageReference for package 'Google.Protobuf' version '3.6.1' added to file '/home/kkm/work/MyGreeter/MyGreeter.csproj'.
79+
```
80+
81+
### Add `.proto` files to the project
82+
83+
**Next comes an important part.** First of all, by default, a `.csproj` project
84+
file automatically finds all `.cs` files in its directory, although
85+
[Microsoft now recommends suppressing this globbing
86+
behavior](https://docs.microsoft.com/dotnet/core/tools/csproj#recommendation),
87+
so we too decided against globbing `.proto` files. Thus the `.proto`
88+
files must be added to the project explicitly.
89+
90+
Second of all, it is important to add a property `PrivateAssets="All"` to the
91+
Grpc.Tools package reference, so that it will not be needlessly fetched by the
92+
consumers of your new library. This makes sense, as the package only contains
93+
compilers, code generators and import files, which are not needed outside of
94+
the project where the `.proto` files have been compiled. While not strictly
95+
required in this simple walkthrough, it must be your standard practice to do
96+
that always.
97+
98+
So edit the file `MyGreeter.csproj` to add the `helloworld.proto` so that it
99+
will be compiled, and the `PrivateAssets` property to the Grpc.Tools package
100+
reference. Your resulting project file should now look like this:
101+
102+
```xml
103+
<Project Sdk="Microsoft.NET.Sdk">
104+
105+
<PropertyGroup>
106+
<TargetFramework>netstandard2.0</TargetFramework>
107+
</PropertyGroup>
108+
109+
<ItemGroup>
110+
<PackageReference Include="Google.Protobuf" Version="3.6.1" />
111+
<PackageReference Include="Grpc" Version="1.17.0" />
112+
113+
<!-- The Grpc.Tools package generates C# sources from .proto files during
114+
project build, but is not needed by projects using the built library.
115+
It's IMPORTANT to add the 'PrivateAssets="All"' to this reference: -->
116+
<PackageReference Include="Grpc.Tools" Version="1.17.0" PrivateAssets="All" />
117+
118+
<!-- Explicitly include our helloworld.proto file by adding this line: -->
119+
<Protobuf Include="helloworld.proto" />
120+
</ItemGroup>
121+
122+
</Project>
123+
```
124+
125+
### Build it!
126+
127+
At this point you can build the project with the `dotnet build` command to
128+
compile the `.proto` file and the library assembly. For this walkthrough, we'll
129+
add a logging switch `-v:n` to the command, so we can see that the command to
130+
compile the `helloworld.proto` file was in fact run. You may find it a good
131+
idea to always do that the very first time you compile a project!
132+
133+
Note that many output lines are omitted below, as the build output is quite
134+
verbose.
135+
136+
```
137+
~/work/MyGreeter$ dotnet build -v:n
138+
139+
Build started 11/9/18 5:33:44 PM.
140+
1:7>Project "/home/kkm/work/MyGreeter/MyGreeter.csproj" on node 1 (Build target(s)).
141+
1>_Protobuf_CoreCompile:
142+
/home/kkm/.nuget/packages/grpc.tools/1.17.0/tools/linux_x64/protoc
143+
--csharp_out=obj/Debug/netstandard2.0
144+
--plugin=protoc-gen-grpc=/home/kkm/.nuget/packages/grpc.tools/1.17.0/tools/linux_x64/grpc_csharp_plugin
145+
--grpc_out=obj/Debug/netstandard2.0 --proto_path=/home/kkm/.nuget/packages/grpc.tools/1.17.0/build/native/include
146+
--proto_path=. --dependency_out=obj/Debug/netstandard2.0/da39a3ee5e6b4b0d_helloworld.protodep helloworld.proto
147+
CoreCompile:
148+
149+
[ ... skipping long output ... ]
150+
151+
MyGreeter -> /home/kkm/work/MyGreeter/bin/Debug/netstandard2.0/MyGreeter.dll
152+
153+
Build succeeded.
154+
```
155+
156+
If at this point you invoke the `dotnet build -v:n` command again, `protoc`
157+
would not be invoked, and no C# sources would be compiled. But if you change
158+
the `helloworld.proto` source, then its outputs will be regenerated and then
159+
recompiled by the C# compiler during the build. This is a regular dependency
160+
tracking behavior that you expect from modifying any source file.
161+
162+
Of course, you can also add `.cs` files to the same project: It is a regular C#
163+
project building a .NET library, after all. This is done in our [RouteGuide
164+
](https://github.com/grpc/grpc/tree/master/examples/csharp/RouteGuide/RouteGuide)
165+
example.
166+
167+
### Where are the generated files?
168+
169+
You may wonder where the proto compiler and gRPC plugin output C# files are. By
170+
default, they are placed in the same directory as other generated files, such
171+
as objects (termed the "intermediate output" directory in the .NET build
172+
parlance), under the `obj/` directory. This is a regular practice of .NET
173+
builds, so that autogenerated files do not clutter the working directory or
174+
accidentally placed under source control. Otherwise, they are accessible to the
175+
tools like the debugger. You can see other autogenerated sources in that
176+
directory, too:
177+
178+
```
179+
~/work/MyGreeter$ find obj -name '*.cs'
180+
obj/Debug/netstandard2.0/MyGreeter.AssemblyInfo.cs
181+
obj/Debug/netstandard2.0/Helloworld.cs
182+
obj/Debug/netstandard2.0/HelloworldGrpc.cs
183+
```
184+
185+
(use `dir /s obj\*.cs` if you are following this walkthrough from a Windows
186+
command prompt).
187+
188+
## There Is More To It
189+
190+
While the simplest default behavior is adequate in many cases, there are many
191+
ways to fine-tune your `.proto` compilation process in a large project. We
192+
encourage you to read the [documentation file BUILD-INTEGRATION.md
193+
](https://github.com/grpc/grpc/blob/master/src/csharp/BUILD-INTEGRATION.md)
194+
for available options if you find that the default arrangement does not suit
195+
your workflow. The package also extends the Visual Studio's Properties window,
196+
so you may set some options per file in the Visual Studio interface.
197+
198+
"Classic" `.csproj` projects and Mono are also supported.
199+
200+
## Share Your Experience
201+
202+
As with any initial release of a complex feature, we are thrilled to receive
203+
your feedback. Did something not work as expected? Do you have a scenario that
204+
is not easy to cover with the new tools? Do you have an idea how to improve the
205+
workflow in general? Please read the documentation carefully, and then [open an
206+
issue](https://github.com/grpc/grpc/issues) in the gRPC code repository on
207+
GitHub. Your feedback is important to determine the future direction for our
208+
build integration work!

0 commit comments

Comments
 (0)