Introduction

2022/06/11 更新
https://taktak.jp/2022/06/11/4230/ にて最新版の gRPC のビルドについて説明しています。1.28.0-pre1 以降を使用する場合は新しい記事を参照してください。

gRPCのv1.26.0をC++でビルドしてHello Worldを動かします。

今更な記事ですが、C#、Java、Pythonと違いC++のgRPCは動かすまでもが面倒です。
しかも公式のサンプルですら。
こういうエラーに苦しめられます。

でネットをさまよっていると解決策を提示してくれている方の記事が。
正確には上の問題を解決する意図の記事ではないのですが。

上の説明を整えて、シェルスクリプトにまとめました。
この方の記事ではHello Worldを実行しているわけではないですが。

How to?

ソースは下記にまとめてあります。

Dockerfile

Ubuntu 18.04のdockerイメージをベースにしたビルド用イメージを作成します。

1
2
3
4
5
6
7
8
9
FROM ubuntu:18.04

RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
git \
cmake \
golang \
&& apt-get clean && rm -rf /var/lib/apt/lists/*

そしてコンテナのビルドと起動のシェル。

1
2
3
4
#!/bin/bash +x

docker build -t grpc-build-ubuntu-18.04 .
docker run --rm -v ${PWD}:/opt/data -w /opt/data -it grpc-build-ubuntu-18.04 /bin/bash

Build

gRPCのビルドです。
上のコンテナ内で実行します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#!/bin/bash +x

VERSION=v1.26.0

GRPC_SOURCE_DIR=${PWD}/grpc-${VERSION}
THIRD_PARTY_SOURCE_DIR=${GRPC_SOURCE_DIR}/third_party
CMAKE_DIR=${GRPC_SOURCE_DIR}/cmake
CMAKE_BUILD_DIR=${CMAKE_DIR}/build
GRPC_BUILD_DIR=${CMAKE_BUILD_DIR}/grpc
BORINGSSL_BUILD_DIR=${CMAKE_BUILD_DIR}/boringssl
CARES_BUILD_DIR=${CMAKE_BUILD_DIR}/cares
PROTOBUF_BUILD_DIR=${CMAKE_BUILD_DIR}/protobuf

INSTALL_DIR=${CMAKE_BUILD_DIR}/install
GRPC_DIR=${INSTALL_DIR}/grpc
BORINGSSL_DIR=${INSTALL_DIR}/boringssl
ZLIB_DIR=${INSTALL_DIR}/zlib
CARES_DIR=${INSTALL_DIR}/cares
PROTOBUF_DIR=${INSTALL_DIR}/protobuf

BUILD_TYPE=Release
BUILD_PARALLEL=16

# clone
mkdir -p ${GRPC_SOURCE_DIR}
pushd ${GRPC_SOURCE_DIR}
git clone -b ${VERSION} https://github.com/grpc/grpc .
git submodule update --init --recursive
popd

# boringssl
mkdir -p ${BORINGSSL_BUILD_DIR}
pushd ${BORINGSSL_BUILD_DIR}
cmake -D CMAKE_BUILD_TYPE=${BUILD_TYPE} \
-D CMAKE_INSTALL_PREFIX=${BORINGSSL_DIR} \
${THIRD_PARTY_SOURCE_DIR}/boringssl
make -j${BUILD_PARALLEL}
mkdir -p ${BORINGSSL_DIR}/lib
cp ssl/libssl.a crypto/libcrypto.a ${BORINGSSL_DIR}/lib
mkdir -p ${BORINGSSL_DIR}/include
rm -rf ${BORINGSSL_DIR}/include/openssl
cp -r ${GRPC_SOURCE_DIR}/third_party/boringssl/include/openssl ${BORINGSSL_DIR}/include/openssl
popd

# zlib
pushd ${THIRD_PARTY_SOURCE_DIR}/zlib
./configure --prefix=${ZLIB_DIR} --static
make -j${BUILD_PARALLEL}
make install
make clean
popd

# cares
mkdir -p ${CARES_BUILD_DIR}
pushd ${CARES_BUILD_DIR}
cmake -D CMAKE_BUILD_TYPE=${BUILD_TYPE} \
-D CMAKE_INSTALL_PREFIX=${CARES_DIR} \
-D CARES_STATIC=ON \
-D CARES_SHARED=OFF \
${THIRD_PARTY_SOURCE_DIR}/cares/cares
make -j${BUILD_PARALLEL}
make install
popd

# protobuf
mkdir -p ${PROTOBUF_BUILD_DIR}
pushd ${PROTOBUF_BUILD_DIR}
cmake -D CMAKE_BUILD_TYPE=${BUILD_TYPE} \
-D CMAKE_INSTALL_PREFIX=${PROTOBUF_DIR} \
-D CMAKE_PREFIX_PATH="${ZLIB_DIR}" \
-D protobuf_BUILD_TESTS=OFF \
${THIRD_PARTY_SOURCE_DIR}/protobuf/cmake
make -j${BUILD_PARALLEL}
make install
popd

# grpc
mkdir -p ${GRPC_BUILD_DIR}
pushd ${GRPC_BUILD_DIR}
cmake -D CMAKE_BUILD_TYPE=${BUILD_TYPE} \
-D CMAKE_INSTALL_PREFIX=${GRPC_DIR} \
-D gRPC_ZLIB_PROVIDER=package \
-D gRPC_CARES_PROVIDER=package \
-D gRPC_PROTOBUF_PROVIDER=package \
-D gRPC_SSL_PROVIDER=package \
-D gRPC_BUILD_CSHARP_EXT=OFF \
-D OPENSSL_ROOT_DIR=${BORINGSSL_DIR} \
-D CMAKE_PREFIX_PATH="${CARES_DIR};${PROTOBUF_DIR};${ZLIB_DIR}" \
${GRPC_SOURCE_DIR}
make -j${BUILD_PARALLEL}
make install
popd

Hello World

これも上のコンテナ内で実行します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/bin/bash +x

VERSION=v1.26.0

GRPC_SOURCE_DIR=${PWD}/grpc-${VERSION}
CMAKE_DIR=${GRPC_SOURCE_DIR}/cmake
CMAKE_BUILD_DIR=${CMAKE_DIR}/build

INSTALL_DIR=${CMAKE_BUILD_DIR}/install
GRPC_DIR=${INSTALL_DIR}/grpc
BORINGSSL_DIR=${INSTALL_DIR}/boringssl
ZLIB_DIR=${INSTALL_DIR}/zlib
CARES_DIR=${INSTALL_DIR}/cares
PROTOBUF_DIR=${INSTALL_DIR}/protobuf

BUILD_TYPE=Release
BUILD_PARALLEL=16

HELLOWORLD_DIR=${GRPC_SOURCE_DIR}/examples/cpp/helloworld
BUILD_DIR=${HELLOWORLD_DIR}/build
mkdir -p ${BUILD_DIR}
pushd ${BUILD_DIR}
cmake -D CMAKE_BUILD_TYPE=${BUILD_TYPE} \
-D OPENSSL_ROOT_DIR="${BORINGSSL_DIR}" \
-D CMAKE_PREFIX_PATH="${GRPC_DIR};${CARES_DIR};${PROTOBUF_DIR};${ZLIB_DIR}" \
..
cmake --build .

# run server
./greeter_server &
# run client
./greeter_client

popd

上のスクリプト群を順番に実行すると、最終的にサーバーのLISTENとクライアントからの呼び出しが実行されます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# ./run-helloworld.sh 
/opt/data/grpc-v1.26.0/examples/cpp/helloworld/build /opt/data
-- Using protobuf
-- Using gRPC 1.26.0
-- Configuring done
-- Generating done
-- Build files have been written to: /opt/data/grpc-v1.26.0/examples/cpp/helloworld/build
[ 25%] Built target greeter_async_server
[ 50%] Built target greeter_async_client
[ 75%] Built target greeter_client
[100%] Built target greeter_server
Server listening on 0.0.0.0:50051
Greeter received: Hello world
/opt/data