17

If I want to recreate the following protoc command in cmake:

protoc -I ../proto/ --cpp_out=. service.proto

I use the following lines in cmake:

file(GLOB ProtoFiles "${CMAKE_CURRENT_SOURCE_DIR}/*.proto")
PROTOBUF_GENERATE_CPP(ProtoSources ProtoHeaders ${ProtoFiles})

If I instead want to recreate the protoc command below:

protoc -I ../proto/ --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` service.proto

In the case above I am not able to determine how to change the cmake file, please help!

The Question is how do I address the:

--plugin=EXECUTABLE           Specifies a plugin executable to use.
                              Normally, protoc searches the PATH for
                              plugins, but you may specify additional
                              executables not in the path using this flag.
                              Additionally, EXECUTABLE may be of the form
                              NAME=PATH, in which case the given plugin name
                              is mapped to the given executable even if
                              the executable's own name differs.

I have been reading the PROTOBUF_GENERATE_CPP documentation, but did not find an answer!

3 Answers3

20

Module findProtobuf.cmake defines functions-wrappers only for common protoc calls: PROTOBUF_GENERATE_CPP - for --cpp_out and PROTOBUF_GENERATE_PYTHON - for --py_out. But you can implement your own function-wrapper for needed plugin. Code below is based on PROTOBUF_GENERATE_CPP implementation.

find_program(GRPC_CPP_PLUGIN grpc_cpp_plugin) # Get full path to plugin

function(PROTOBUF_GENERATE_GRPC_CPP SRCS HDRS)
  if(NOT ARGN)
    message(SEND_ERROR "Error: PROTOBUF_GENERATE_GRPC_CPP() called without any proto files")
    return()
  endif()

  if(PROTOBUF_GENERATE_CPP_APPEND_PATH) # This variable is common for all types of output.
    # Create an include path for each file specified
    foreach(FIL ${ARGN})
      get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
      get_filename_component(ABS_PATH ${ABS_FIL} PATH)
      list(FIND _protobuf_include_path ${ABS_PATH} _contains_already)
      if(${_contains_already} EQUAL -1)
          list(APPEND _protobuf_include_path -I ${ABS_PATH})
      endif()
    endforeach()
  else()
    set(_protobuf_include_path -I ${CMAKE_CURRENT_SOURCE_DIR})
  endif()

  if(DEFINED PROTOBUF_IMPORT_DIRS)
    foreach(DIR ${Protobuf_IMPORT_DIRS})
      get_filename_component(ABS_PATH ${DIR} ABSOLUTE)
      list(FIND _protobuf_include_path ${ABS_PATH} _contains_already)
      if(${_contains_already} EQUAL -1)
          list(APPEND _protobuf_include_path -I ${ABS_PATH})
      endif()
    endforeach()
  endif()

  set(${SRCS})
  set(${HDRS})
  foreach(FIL ${ARGN})
    get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
    get_filename_component(FIL_WE ${FIL} NAME_WE)

    list(APPEND ${SRCS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.grpc.pb.cc")
    list(APPEND ${HDRS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.grpc.pb.h")

    add_custom_command(
      OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.grpc.pb.cc"
             "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.grpc.pb.h"
      COMMAND  ${Protobuf_PROTOC_EXECUTABLE}
      ARGS --grpc_out=${CMAKE_CURRENT_BINARY_DIR}
           --plugin=protoc-gen-grpc=${GRPC_CPP_PLUGIN}
           ${_protobuf_include_path} ${ABS_FIL}
      DEPENDS ${ABS_FIL} ${Protobuf_PROTOC_EXECUTABLE}
      COMMENT "Running gRPC C++ protocol buffer compiler on ${FIL}"
      VERBATIM)
  endforeach()

  set_source_files_properties(${${SRCS}} ${${HDRS}} PROPERTIES GENERATED TRUE)
  set(${SRCS} ${${SRCS}} PARENT_SCOPE)
  set(${HDRS} ${${HDRS}} PARENT_SCOPE)
endfunction()

Usage is same as for PROTOBUF_GENERATE_CPP:

file(GLOB ProtoFiles "${CMAKE_CURRENT_SOURCE_DIR}/*.proto")
PROTOBUF_GENERATE_GRPC_CPP(ProtoGRPCSources ProtoGRPCHeaders ${ProtoFiles})
mistapink
  • 1,926
  • 1
  • 26
  • 37
Tsyvarev
  • 60,011
  • 17
  • 110
  • 153
  • Would you still recommend this solution? What is a good alternative? Thanks. – arhuaco Apr 29 '16 at 03:48
  • This is just a code which do the things required. You may modify(or rewrite) it for your own purposes. – Tsyvarev Apr 29 '16 at 07:29
  • This code is quite perfect, but the only drawback of it that if user defines `PROTOBUF_GENERATE_CPP_APPEND_PATH` variable to off, the script won't be able to populate `SRCS` and `HDRS` lists correctly. – Andrei K. Jul 08 '17 at 06:10
  • To fix the issue I commented above, we should replace these lines: `list(APPEND ${SRCS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.grpc.pb.cc") list(APPEND ${HDRS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.grpc.pb.h")` with these: `get_filename_component(DIR_FIL ${FIL} DIRECTORY) list(APPEND ${SRCS} "${CMAKE_CURRENT_BINARY_DIR}/${DIR_FIL}/${FIL_WE}.grpc.pb.cc") list(APPEND ${HDRS} "${CMAKE_CURRENT_BINARY_DIR}/${DIR_FIL}/${FIL_WE}.grpc.pb.h")` – Andrei K. Jul 08 '17 at 06:19
  • Does it generate both the service.pb.cc + h and service.grpc.pb.cc + h files? When I use it only the service.grpc.pb.cc + h files are generated. – Damian Sep 07 '17 at 09:09
  • 1
    The code is intended to generate only `.grps.pb.cc` and `.grps.pb.hh` files. Corresponded arguments for `protoc` utility are specified with *ARGS* part of `add_custom_command()` call. – Tsyvarev Sep 07 '17 at 16:36
  • Adding --cpp_out=${CMAKE_CURRENT_BINARY_DIR} worked, i.e. add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.grpc.pb.cc" "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.grpc.pb.h" COMMAND ${Protobuf_PROTOC_EXECUTABLE} ARGS --cpp_out=${CMAKE_CURRENT_BINARY_DIR} --grpc_out=${CMAKE_CURRENT_BINARY_DIR} --plugin=protoc-gen-grpc=${GRPC_CPP_PLUGIN} ${_protobuf_include_path} ${ABS_FIL} DEPENDS ${ABS_FIL} ${Protobuf_PROTOC_EXECUTABLE} COMMENT "Running gRPC C++ protocol buffer compiler on ${FIL}" VERBATIM) – Damian Sep 07 '17 at 23:05
  • `list(APPEND proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.grpc.pb.h")` does not seem to be accounting for subdirectories and I am not enough of a cmake expert to know how to make it do so. If we had a proto file in "protos/messages", the generated file will be in the CMAKE_BINARY_DIR/messages, but the list only contains CMAKE_BINARY_DIR/filename – Christopher Pisz Nov 03 '20 at 01:20
  • Think I fixed that with `get_filename_component(DIR ${FIL} DIRECTORY) file(RELATIVE_PATH REL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/protos/" ${DIR}) list(APPEND proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/${REL_DIR}/${FIL_WE}.grpc.pb.cc") list(APPEND proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/${REL_DIR}/${FIL_WE}.grpc.pb.h")` – Christopher Pisz Nov 03 '20 at 02:15
4

For me, this blogpost led to success because it provided a full example (thanks @powerpete).

I'm putting the code here so it is available as an answer and not just as a comment:

project(my-service VERSION 1.0 LANGUAGES CXX C)

find_package(protobuf CONFIG REQUIRED)
find_package(gRPC CONFIG REQUIRED)
find_package(Threads)

set(PROTO_FILES
    MyService.proto
)

# protobuf source files go into the lib just like any other CPP source file
add_library(my-service ${PROTO_FILES})
target_link_libraries(my-service
    PUBLIC
        protobuf::libprotobuf
        gRPC::grpc
        gRPC::grpc++
)

target_include_directories(my-service
    PUBLIC
        ${CMAKE_CURRENT_BINARY_DIR}
)

get_target_property(grpc_cpp_plugin_location gRPC::grpc_cpp_plugin LOCATION)

# compile the message types
protobuf_generate(TARGET my-service LANGUAGE cpp)

# compile the GRPC services
protobuf_generate(
    TARGET
        my-service
    LANGUAGE
        grpc
    GENERATE_EXTENSIONS
        .grpc.pb.h
        .grpc.pb.cc
    PLUGIN
        "protoc-gen-grpc=${grpc_cpp_plugin_location}"
)
bselu
  • 194
  • 2
  • 14
  • 1
    Note, that not every Protobuf installation contains `protobuf-config.cmake` **config** script. E.g. none packet on Ubuntu contains that script. Without `protobuf-config.cmake` script the `protobuf_generate_*` functions are provided by [FindProtobuf.cmake](https://cmake.org/cmake/help/latest/module/FindProtobuf.html) **module**, shipped with CMake. This module seems to support PLUGIN option (but forget to document it). But for work with the given module one need to omit `CONFIG` option in the line `find_package(protobuf CONFIG REQUIRED)`. – Tsyvarev Mar 27 '23 at 13:51
3

Starting at version 3.12, protobuf_generate supports a PLUGIN argument

https://github.com/protocolbuffers/protobuf/blob/v3.12.0/cmake/protobuf-config.cmake.in#L46

so you could try something along the line: PROTOBUF_GENERATE_CPP(ProtoSources ProtoHeaders ${ProtoFiles} PLUGIN protoc-gen-grpc=${GRPC_CPP_PLUGIN_PATH})

Julien Pilet
  • 504
  • 4
  • 5
  • 2
    There is a blog post with an example project here: https://www.falkoaxmann.de/dev/2020/11/08/grpc-plugin-cmake-support.html – powerpete Nov 09 '20 at 13:46