Generate gRPC source files using CMake

Dennis Hezel
3YOURMIND-Tech
Published in
3 min readSep 14, 2022

--

When working with Protobuf schema files you will eventually get to a point where you need to turn them into source code. Protobuf provides a CMake module to do just that, it merely seems to lack all documentation (EDIT: My documentation has since been merged upstream). In this article I describe how to generate Protobuf and gRPC source code from schema files at build time. Here is the complete code:

Using protobuf_generate to create gRPC sources

Given the following directory structure:

We start by calling find_package(protobuf) which has to be done in the same directory that uses protobuf_generate or any parent directory. Next we add our proto schema files to the sources of a CMake target, here we create a new target but it could also be an existing one. Alternatively, we can also provide the path to our proto schema files through the PROTOS argument to protobuf_generate. Since the generated source files will be compiled when we build our target, we also need to link with the Protobuf and gRPC libraries using their imported target names protobuf::libprotobuf and gRPC::grpc++ respectively.

What follows is the actual call to protobuf_generate. It sets up add_custom_command for protoc (the Protobuf compiler) that causes automatic re-generation of the Protobuf and gRPC source files whenever we make changes to their proto schemas. protobuf_generate accepts the following arguments:

  • APPEND_PATH — A flag that causes the base path of all proto schema files to be added to IMPORT_DIRS.
  • LANGUAGE — A single value: cpp or python. Determines what kind of source files are being generated.
  • OUT_VAR — Name of a CMake variable that will be filled with the paths to the generated source files.
  • EXPORT_MACRO — Name of a macro that is applied to all generated Protobuf message classes and extern variables.
  • PROTOC_OUT_DIR —Output directory of the generated source files, defaults to CMAKE_CURRENT_BINARY_DIR.
  • PLUGIN — An optional plugin executable. This could e.g. be the path to grpc_cpp_plugin.
  • PLUGIN_OPTIONS — Additional options provided to the plugin, e.g. generate_mock_code=true for the gRPC cpp plugin.
  • TARGET — Generated files will be added as sources to the provided target.
  • PROTOS — List of proto schema files. If omitted, then every source file ending in proto of TARGET will be used.
  • IMPORT_DIRS — A common parent directory for the schema files. E.g., if the schema file is proto/helloworld/helloworld.proto and the import directory proto/ then the generated file is ${PROTOC_OUT_DIR}/helloworld/helloworld.pb.cc.
  • GENERATE_EXTENSIONS — If LANGUAGE is omitted then this must be set to the extensions that protoc generates.
  • PROTOC_OPTIONS — Additional arguments that are forwarded to the protoc invocation.

After invoking protobuf_generate we are left with adding PROTOC_OUT_DIR to the include directories of our target and disabling unity build. Within the generated .cc files, Protobuf creates static variables at namespace-scope whose names are not unique and are therefore unsuitable for unity builds.

Actually, there is one more thing. If you are using cmake-format (highly recommended) and want to get the same formatting as in the code above then add the following entry to the additional_commands section of the cmake-format.yaml in the root directory of your project:

protobuf_generate in cmake-format.yaml

If you also think these calls to protobuf_generate are too verbose and hard to remember then take a look at asio-grpc. It not only provides a convenient Asio/std::execution-based API for writing asynchronous gRPC clients and servers but also the CMake function asio_grpc_protobuf_generate that combines the generation of Protobuf and gRPC source files into a single call:

Using asio_grpc_protobuf_generate to create gRPC sources

Special thanks to Falko Axmann for contributing the PLUGIN support to protobuf_generate and his article on its usage.

--

--