From fda3363cddf8bf61f1cb8c4ec0074d4288db9c69 Mon Sep 17 00:00:00 2001 From: George Barnett Date: Thu, 23 Jan 2025 17:44:11 +0000 Subject: [PATCH 1/4] Update tutorials to use the build plugin Motivation: The examples have been updated to use the build plugin; so too should the tutorials. Modifications: Update tutorials to use the build plugin. Result: Better tutorials. --- .../Hello-World/Hello-World.tutorial | 39 +++++---- .../route-guide-sec01-step09-plugin.swift | 25 ++++++ ...oute-guide-sec01-step10-plugin-config.json | 7 ++ .../route-guide-sec05-step00-package.swift | 4 + .../route-guide-sec05-step01-package.swift | 3 + .../route-guide-sec05-step02-package.swift | 3 + .../Route-Guide/Route-Guide.tutorial | 83 +++++++++++-------- 7 files changed, 112 insertions(+), 52 deletions(-) create mode 100644 Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Resources/route-guide-sec01-step09-plugin.swift create mode 100644 Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Resources/route-guide-sec01-step10-plugin-config.json diff --git a/Sources/GRPCCore/Documentation.docc/Tutorials/Hello-World/Hello-World.tutorial b/Sources/GRPCCore/Documentation.docc/Tutorials/Hello-World/Hello-World.tutorial index 4818c18ae..cb0ee4d1a 100644 --- a/Sources/GRPCCore/Documentation.docc/Tutorials/Hello-World/Hello-World.tutorial +++ b/Sources/GRPCCore/Documentation.docc/Tutorials/Hello-World/Hello-World.tutorial @@ -28,17 +28,23 @@ @Section(title: "Run a gRPC application") { Let's start by running the existing Greeter application. + As a prerequisite you must have the Protocol Buffers compiler (`protoc`) installed. You can + find the instructions for doing this in the [Protocol Buffers GitHub + repository](https://github.com/protocolbuffers/protobuf?tab=readme-ov-file#protobuf-compiler-installation). + The remainder of this tutorial assumes you installed `protoc` and it's available in + your `$PATH`. + @Steps { @Step { - In a terminal run `swift run hello-world serve` to start the server. By default it'll start - listening on port 31415. + In a terminal run `PROTOC_PATH=$(which protoc) swift run hello-world serve` to start the + server. By default it'll start listening on port 31415. @Code(name: "Console.txt", file: "hello-world-sec02-step01.txt") } @Step { - In another terminal run `swift run hello-world greet` to create a client, connect - to the server you started and send it a request and print the response. + In another terminal run `PROTOC_PATH=$(which protoc) swift run hello-world greet` to create + a client, connect to the server you started and send it a request and print the response. @Code(name: "Console.txt", file: "hello-world-sec02-step02.txt") } @@ -75,19 +81,19 @@ } @Section(title: "Update and run the application") { - You need to regenerate the stubs as the service definition has changed. To do this run the - following command from the _root of the checked out repository_: - - ```console - dev/protos/generate.sh - ``` + You need to regenerate the stubs as the service definition has changed. As you're using + the Swift Package Manager Build Plugin for gRPC Swift the gRPC code is automatically + generated if necessary when you build the example. You can learn more about generating stubs in + the article. - To learn how to generate stubs check out the article. - - Now that the stubs have been updated you need to implement and call the new method in the - human-written parts of your application. @Steps { + @Step { + Run `PROTOC_PATH=$(which protoc) swift build` to build the example. This will fail + because the service no longer implements all of the methods declared in the `.proto` file. + Let's fix that! + } + @Step { Open `Serve.swift` in the `Subcommands` directory. @@ -114,13 +120,14 @@ @Step { Just like we did before, open a terminal and start the server by - running `swift run hello-world serve` + running `PROTOC_PATH=$(which protoc) swift run hello-world serve` @Code(name: "Console.txt", file: "hello-world-sec04-step05.txt") } @Step { - In a separate terminal run `swift run hello-world greet` to call the server. + In a separate terminal run `PROTOC_PATH=$(which protoc) swift run hello-world greet` to + call the server. @Code(name: "Console.txt", file: "hello-world-sec04-step06.txt") } diff --git a/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Resources/route-guide-sec01-step09-plugin.swift b/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Resources/route-guide-sec01-step09-plugin.swift new file mode 100644 index 000000000..6db99da7f --- /dev/null +++ b/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Resources/route-guide-sec01-step09-plugin.swift @@ -0,0 +1,25 @@ +// swift-tools-version: 6.0 +import PackageDescription + +let package = Package( + name: "RouteGuide", + platforms: [.macOS(.v15)], + dependencies: [ + .package(url: "https://github.com/grpc/grpc-swift.git", from: "2.0.0-beta.3"), + .package(url: "https://github.com/grpc/grpc-swift-protobuf.git", from: "1.0.0-beta.3"), + .package(url: "https://github.com/grpc/grpc-swift-nio-transport.git", from: "1.0.0-beta.3"), + ], + targets: [ + .executableTarget( + name: "RouteGuide", + dependencies: [ + .product(name: "GRPCCore", package: "grpc-swift"), + .product(name: "GRPCNIOTransportHTTP2", package: "grpc-swift-nio-transport"), + .product(name: "GRPCProtobuf", package: "grpc-swift-protobuf"), + ], + plugins: [ + .plugin(name: "GRPCProtobufGenerator", package: "grpc-swift-protobuf") + ] + ) + ] +) diff --git a/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Resources/route-guide-sec01-step10-plugin-config.json b/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Resources/route-guide-sec01-step10-plugin-config.json new file mode 100644 index 000000000..e6dda31fb --- /dev/null +++ b/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Resources/route-guide-sec01-step10-plugin-config.json @@ -0,0 +1,7 @@ +{ + "generate": { + "clients": true, + "servers": true, + "messages": true + } +} diff --git a/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Resources/route-guide-sec05-step00-package.swift b/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Resources/route-guide-sec05-step00-package.swift index 6aaa5f7a7..6db99da7f 100644 --- a/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Resources/route-guide-sec05-step00-package.swift +++ b/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Resources/route-guide-sec05-step00-package.swift @@ -7,6 +7,7 @@ let package = Package( dependencies: [ .package(url: "https://github.com/grpc/grpc-swift.git", from: "2.0.0-beta.3"), .package(url: "https://github.com/grpc/grpc-swift-protobuf.git", from: "1.0.0-beta.3"), + .package(url: "https://github.com/grpc/grpc-swift-nio-transport.git", from: "1.0.0-beta.3"), ], targets: [ .executableTarget( @@ -15,6 +16,9 @@ let package = Package( .product(name: "GRPCCore", package: "grpc-swift"), .product(name: "GRPCNIOTransportHTTP2", package: "grpc-swift-nio-transport"), .product(name: "GRPCProtobuf", package: "grpc-swift-protobuf"), + ], + plugins: [ + .plugin(name: "GRPCProtobufGenerator", package: "grpc-swift-protobuf") ] ) ] diff --git a/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Resources/route-guide-sec05-step01-package.swift b/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Resources/route-guide-sec05-step01-package.swift index a450a82e4..27ee4f7f3 100644 --- a/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Resources/route-guide-sec05-step01-package.swift +++ b/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Resources/route-guide-sec05-step01-package.swift @@ -19,6 +19,9 @@ let package = Package( ], resources: [ .copy("route_guide_db.json") + ], + plugins: [ + .plugin(name: "GRPCProtobufGenerator", package: "grpc-swift-protobuf") ] ) ] diff --git a/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Resources/route-guide-sec05-step02-package.swift b/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Resources/route-guide-sec05-step02-package.swift index b250e8d82..49f69af0c 100644 --- a/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Resources/route-guide-sec05-step02-package.swift +++ b/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Resources/route-guide-sec05-step02-package.swift @@ -21,6 +21,9 @@ let package = Package( ], resources: [ .copy("route_guide_db.json") + ], + plugins: [ + .plugin(name: "GRPCProtobufGenerator", package: "grpc-swift-protobuf") ] ) ] diff --git a/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Route-Guide.tutorial b/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Route-Guide.tutorial index d5746c1b2..cecca8293 100644 --- a/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Route-Guide.tutorial +++ b/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Route-Guide.tutorial @@ -20,6 +20,12 @@ Before we can write any code we need to create a new Swift Package and configure it to depend on gRPC Swift. + As a prerequisite you must have the Protocol Buffers compiler (`protoc`) installed. You can + find the instructions for doing this in the [Protocol Buffers GitHub + repository](https://github.com/protocolbuffers/protobuf?tab=readme-ov-file#protobuf-compiler-installation). + The remainder of this tutorial assumes you installed `protoc` and it's available in + your `$PATH`. + @Steps { @Step { Create a new directory called for the package called `RouteGuide`. @@ -84,6 +90,24 @@ @Code(name: "Package.swift", file: "route-guide-sec01-step08-description.swift") } + + @Step { + We'll also add a build plugin. This allows the build system to generate gRPC code at build + time rather than having to generate it with separate tooling. + + @Code(name: "Package.swift", file: "route-guide-sec01-step09-plugin.swift") + } + + @Step { + A configuration file is required so that the plugin knows what to generate. Create + a JSON file in the `Sources/Protos` directory called `grpc-swift-proto-generator-config.json` + with this content. + + The name of the file (`grpc-swift-proto-generator-config.json`) is important: the plugin + looks for files matching this name in the source directory of your target. + + @Code(name: "Sources/Protos/grpc-swift-proto-generator-config.json", file: "route-guide-sec01-step10-plugin-config.json") + } } } @@ -93,16 +117,26 @@ @Steps { @Step { - Create a new empty file in the `Protos` directory called `route_guide.proto`. We'll use - the "proto3" syntax and our service will be part of the "routeguide" package. + Create a new directory in the `Sources/Protos` directory called `routeguide` + using `mkdir Sources/Protos/routeguide`. + } + + @Step { + Create a new empty file in the `Sources/Protos/routeguide` directory + called `route_guide.proto`. We'll use the "proto3" syntax and our service will be part of + the "routeguide" package. - @Code(name: "Protos/route_guide.proto", file: "route-guide-sec02-step01-import.proto") + It's good practice to organise your `.proto` files according to the package they are + declared in, that's why we created the `routeguide` directory to match the "routeguide" + package name. + + @Code(name: "Sources/Protos/routeguide/route_guide.proto", file: "route-guide-sec02-step01-import.proto") } @Step { To define a service we create a named `service` in the `.proto` file. - @Code(name: "Protos/route_guide.proto", file: "route-guide-sec02-step02-service.proto") + @Code(name: "Sources/Protos/routeguide/route_guide.proto", file: "route-guide-sec02-step02-service.proto") } @Step { @@ -113,7 +147,7 @@ A *unary RPC* where the client sends a request to the server using the stub and waits for a response to come back, just like a normal function call. - @Code(name: "Protos/route_guide.proto", file: "route-guide-sec02-step03-unary.proto") + @Code(name: "Sources/Protos/routeguide/route_guide.proto", file: "route-guide-sec02-step03-unary.proto") } @Step { @@ -123,7 +157,7 @@ example, you specify a server-side streaming method by placing the `stream` keyword before the *response* type. - @Code(name: "Protos/route_guide.proto", file: "route-guide-sec02-step04-server-streaming.proto") + @Code(name: "Sources/Protos/routeguide/route_guide.proto", file: "route-guide-sec02-step04-server-streaming.proto") } @Step { @@ -133,7 +167,7 @@ and return its response. You specify a client-side streaming method by placing the `stream` keyword before the *request* type. - @Code(name: "Protos/route_guide.proto", file: "route-guide-sec02-step05-client-streaming.proto") + @Code(name: "Sources/Protos/routeguide/route_guide.proto", file: "route-guide-sec02-step05-client-streaming.proto") } @Step { @@ -146,53 +180,30 @@ stream is preserved. You specify this type of method by placing the `stream` keyword before both the request and the response. - @Code(name: "Protos/route_guide.proto", file: "route-guide-sec02-step06-bidi-streaming.proto") + @Code(name: "Sources/Protos/routeguide/route_guide.proto", file: "route-guide-sec02-step06-bidi-streaming.proto") } @Step { The `.proto` file also contains the Protocol Buffers message type definitions for all request and response messages used by the service. - @Code(name: "Protos/route_guide.proto", file: "route-guide-sec02-step07-messages.proto") + @Code(name: "Sources/Protos/routeguide/route_guide.proto", file: "route-guide-sec02-step07-messages.proto") } } } @Section(title: "Generating client and server code") { Next we need to generate the gRPC client and server interfaces from our `.proto` - service definition. We do this using the Protocol Buffer compiler, `protoc`, with - two plugins: one with support for Swift (via [Swift Protobuf](https://github.com/apple/swift-protobuf)) - and the other for gRPC. This section assumes you already have `protoc` installed. + service definition. As we're using the build plugin we just need to build our project. To learn more about generating code check out the article. @Steps { @Step { - First we need to build the two plugins for `protoc`, `protoc-gen-swift` and - `protoc-gen-grpc-swift`. - - @Code(name: "Console", file: "route-guide-sec03-step01-protoc-plugins.txt") - } - - @Step { - We'll generate the code into a separate directory within `Sources` called `Generated` which - we need to create first. - - @Code(name: "Console", file: "route-guide-sec03-step02-mkdir.txt") - } - - @Step { - Now run `protoc` to generate the messages. This will create - `Sources/Generated/route_guide.pb.swift`. - - @Code(name: "Console", file: "route-guide-sec03-step03-gen-messages.txt") - } - - @Step { - Run `protoc` again to generate the service code. This will create - `Sources/Generated/route_guide.grpc.swift`. + Build the project using `PROTOC_PATH=$(which protoc) swift build`. - @Code(name: "Console", file: "route-guide-sec03-step04-gen-grpc.txt") + If you are using Xcode or another IDE then you'll need to set the environment variable + appropriately. } } } From 3dc41a0e4cf186837e19c864a97d2178594c103b Mon Sep 17 00:00:00 2001 From: George Barnett Date: Fri, 24 Jan 2025 10:00:50 +0000 Subject: [PATCH 2/4] Refer to docs --- .../Tutorials/Hello-World/Hello-World.tutorial | 4 ++-- .../Tutorials/Route-Guide/Route-Guide.tutorial | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/GRPCCore/Documentation.docc/Tutorials/Hello-World/Hello-World.tutorial b/Sources/GRPCCore/Documentation.docc/Tutorials/Hello-World/Hello-World.tutorial index cb0ee4d1a..1641c88fc 100644 --- a/Sources/GRPCCore/Documentation.docc/Tutorials/Hello-World/Hello-World.tutorial +++ b/Sources/GRPCCore/Documentation.docc/Tutorials/Hello-World/Hello-World.tutorial @@ -29,8 +29,8 @@ Let's start by running the existing Greeter application. As a prerequisite you must have the Protocol Buffers compiler (`protoc`) installed. You can - find the instructions for doing this in the [Protocol Buffers GitHub - repository](https://github.com/protocolbuffers/protobuf?tab=readme-ov-file#protobuf-compiler-installation). + find the instructions for doing this in the [gRPC Swift Protobuf + documentation](https://swiftpackageindex.com/grpc/grpc-swift-protobuf/documentation/grpcprotobuf/installing-protoc). The remainder of this tutorial assumes you installed `protoc` and it's available in your `$PATH`. diff --git a/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Route-Guide.tutorial b/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Route-Guide.tutorial index cecca8293..d5db96233 100644 --- a/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Route-Guide.tutorial +++ b/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Route-Guide.tutorial @@ -21,8 +21,8 @@ to depend on gRPC Swift. As a prerequisite you must have the Protocol Buffers compiler (`protoc`) installed. You can - find the instructions for doing this in the [Protocol Buffers GitHub - repository](https://github.com/protocolbuffers/protobuf?tab=readme-ov-file#protobuf-compiler-installation). + find the instructions for doing this in the [gRPC Swift Protobuf + documentation](https://swiftpackageindex.com/grpc/grpc-swift-protobuf/documentation/grpcprotobuf/installing-protoc). The remainder of this tutorial assumes you installed `protoc` and it's available in your `$PATH`. From e1b669dc444c128a0f27f42ee9d6e483142930ad Mon Sep 17 00:00:00 2001 From: George Barnett Date: Fri, 24 Jan 2025 10:38:11 +0000 Subject: [PATCH 3/4] Add note about PROTOC_PATH --- .../Tutorials/Hello-World/Hello-World.tutorial | 7 ++++++- .../Tutorials/Route-Guide/Route-Guide.tutorial | 13 +++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Sources/GRPCCore/Documentation.docc/Tutorials/Hello-World/Hello-World.tutorial b/Sources/GRPCCore/Documentation.docc/Tutorials/Hello-World/Hello-World.tutorial index 1641c88fc..ae335e85b 100644 --- a/Sources/GRPCCore/Documentation.docc/Tutorials/Hello-World/Hello-World.tutorial +++ b/Sources/GRPCCore/Documentation.docc/Tutorials/Hello-World/Hello-World.tutorial @@ -29,11 +29,16 @@ Let's start by running the existing Greeter application. As a prerequisite you must have the Protocol Buffers compiler (`protoc`) installed. You can - find the instructions for doing this in the [gRPC Swift Protobuf + find the instructions for doing this in the [gRPC Swift Protobuf documentation](https://swiftpackageindex.com/grpc/grpc-swift-protobuf/documentation/grpcprotobuf/installing-protoc). The remainder of this tutorial assumes you installed `protoc` and it's available in your `$PATH`. + You may notices that the `swift` commands are all prefixed with `PROTOC_PATH=$(which protoc)`, + this is to let the build system know where `protoc` is located so that it can generate stubs + for you. You can read more about it in the [gRPC Swift Protobuf + documentation](https://swiftpackageindex.com/grpc/grpc-swift-protobuf/documentation/grpcprotobuf/generating-stubs). + @Steps { @Step { In a terminal run `PROTOC_PATH=$(which protoc) swift run hello-world serve` to start the diff --git a/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Route-Guide.tutorial b/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Route-Guide.tutorial index d5db96233..3472dbe62 100644 --- a/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Route-Guide.tutorial +++ b/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Route-Guide.tutorial @@ -21,11 +21,16 @@ to depend on gRPC Swift. As a prerequisite you must have the Protocol Buffers compiler (`protoc`) installed. You can - find the instructions for doing this in the [gRPC Swift Protobuf + find the instructions for doing this in the [gRPC Swift Protobuf documentation](https://swiftpackageindex.com/grpc/grpc-swift-protobuf/documentation/grpcprotobuf/installing-protoc). The remainder of this tutorial assumes you installed `protoc` and it's available in your `$PATH`. + You may notices that the `swift` commands are all prefixed with `PROTOC_PATH=$(which protoc)`, + this is to let the build system know where `protoc` is located so that it can generate stubs + for you. You can read more about it in the [gRPC Swift Protobuf + documentation](https://swiftpackageindex.com/grpc/grpc-swift-protobuf/documentation/grpcprotobuf/generating-stubs). + @Steps { @Step { Create a new directory called for the package called `RouteGuide`. @@ -126,7 +131,7 @@ called `route_guide.proto`. We'll use the "proto3" syntax and our service will be part of the "routeguide" package. - It's good practice to organise your `.proto` files according to the package they are + It's good practice to organize your `.proto` files according to the package they are declared in, that's why we created the `routeguide` directory to match the "routeguide" package name. @@ -475,13 +480,13 @@ @Steps { @Step { - In one terminal run `swift run RouteGuide --server` to start the server. + In one terminal run `PROTOC_PATH=$(which protoc) swift run RouteGuide --server` to start the server. @Code(name: "Console", file: "route-guide-sec07-step01-server.txt") } @Step { - In another terminal run `swift run RouteGuide` to run the client program. + In another terminal run `PROTOC_PATH=$(which protoc) swift run RouteGuide` to run the client program. @Code(name: "Console", file: "route-guide-sec07-step02-client.txt") } From 3b6945c6afb8bac078c158eede43b6841d5f427c Mon Sep 17 00:00:00 2001 From: George Barnett Date: Fri, 24 Jan 2025 10:43:57 +0000 Subject: [PATCH 4/4] Apply suggestions from code review --- .../Tutorials/Hello-World/Hello-World.tutorial | 2 +- .../Tutorials/Route-Guide/Route-Guide.tutorial | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/GRPCCore/Documentation.docc/Tutorials/Hello-World/Hello-World.tutorial b/Sources/GRPCCore/Documentation.docc/Tutorials/Hello-World/Hello-World.tutorial index ae335e85b..3a2c60697 100644 --- a/Sources/GRPCCore/Documentation.docc/Tutorials/Hello-World/Hello-World.tutorial +++ b/Sources/GRPCCore/Documentation.docc/Tutorials/Hello-World/Hello-World.tutorial @@ -34,7 +34,7 @@ The remainder of this tutorial assumes you installed `protoc` and it's available in your `$PATH`. - You may notices that the `swift` commands are all prefixed with `PROTOC_PATH=$(which protoc)`, + You may notice that the `swift` commands are all prefixed with `PROTOC_PATH=$(which protoc)`, this is to let the build system know where `protoc` is located so that it can generate stubs for you. You can read more about it in the [gRPC Swift Protobuf documentation](https://swiftpackageindex.com/grpc/grpc-swift-protobuf/documentation/grpcprotobuf/generating-stubs). diff --git a/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Route-Guide.tutorial b/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Route-Guide.tutorial index 3472dbe62..1e62f5120 100644 --- a/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Route-Guide.tutorial +++ b/Sources/GRPCCore/Documentation.docc/Tutorials/Route-Guide/Route-Guide.tutorial @@ -26,7 +26,7 @@ The remainder of this tutorial assumes you installed `protoc` and it's available in your `$PATH`. - You may notices that the `swift` commands are all prefixed with `PROTOC_PATH=$(which protoc)`, + You may notice that the `swift` commands are all prefixed with `PROTOC_PATH=$(which protoc)`, this is to let the build system know where `protoc` is located so that it can generate stubs for you. You can read more about it in the [gRPC Swift Protobuf documentation](https://swiftpackageindex.com/grpc/grpc-swift-protobuf/documentation/grpcprotobuf/generating-stubs).