PROTOBUF_GENERATE_CPP not generating src and header files - cmake

My cmake configuration is not generating any protobuf src and header files.
I've already checked if the proto files can be found.
Cmakelists.txt
cmake_minimum_required(VERSION 3.0.2)
..
include(FindProtobuf REQUIRED)
file(GLOB PROTO_DEF "${CMAKE_CURRENT_SOURCE_DIR}/protobuf/*/*.proto")
foreach(file ${PROTO_DEF})
if(EXISTS ${file})
MESSAGE("YES")
else()
MESSAGE("NO")
endif()
endforeach()
SET(PROTOBUF_GENERATE_CPP_APPEND_PATH PROTOBUF)
SET(PROTOBUF_PROTOC_EXECUTABLE protoc.exe)
..
PROTOBUF_GENERATE_CPP(PROTO_SRC PROTO_INCL ${PROTO_DEF})
..
add_library(${PROJECT_NAME} STATIC ${INCLUDES} ${INTERNAL_INCLUDES} ${SRC} ${PROTO_SRC} ${PROTO_INCL})
target_link_libraries(${PROJECT_NAME} ${PROTOBUF_LIBRARIES})
I have checked the FindProtobuf.cmake and half way through:
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}.pb.cc")
list(APPEND ${HDRS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.h")
MESSAGE(1 ${CMAKE_CURRENT_BINARY_DIR})
MESSAGE(2 ${_protobuf_include_path})
MESSAGE(3 ${ABS_FIL})
MESSAGE(4 ${PROTOBUF_PROTOC_EXECUTABLE})
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.cc"
"${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.h"
COMMAND ${PROTOBUF_PROTOC_EXECUTABLE}
ARGS --cpp_out ${CMAKE_CURRENT_BINARY_DIR} ${_protobuf_include_path} ${ABS_FIL}
DEPENDS ${ABS_FIL}
COMMENT "Running C++ protocol buffer compiler on ${FIL}"
VERBATIM )
endforeach()
You can see i've added the 4 message commands, the script reaches this point and variables show good values.
The proto files do have a depencency to the library, thus the command should get executed !?
Any ideas on this problem?
update
replacing the add_custom_command with
EXEC_PROGRAM(${PROTOBUF_PROTOC_EXECUTABLE} ARGS --cpp_out ${CMAKE_CURRENT_BINARY_DIR} ${_protobuf_include_path} ${ABS_FIL}
does generate the source and header files, must i manually activate the custom_commmands?
Regards Auke

The add_custom_command is fired (for my project) during compilation time.
Adding
SET_SOURCE_FILES_PROPERTIES(${PROTO_SRC} ${PROTO_INCL} PROPERTIES GENERATED TRUE)
gives cmake info that the files will be generated.

Alternative if you have an external proto folder:
file(GLOB PROTOBUF_FILELIST ${PROTO_INCLUDE_DIR}/*.proto)
foreach( proto_file ${PROTOBUF_FILELIST} )
get_filename_component(proto_name ${proto_file} NAME_WE)
get_filename_component(proto_path ${PROTO_INCLUDE_DIR} ABSOLUTE)
set_source_files_properties("${proto_path}/${proto_name}.pb.cc"
"${proto_path}/${proto_name}.pb.h"
PROPERTIES GENERATED TRUE)
endforeach()

See that:
https://cmake.org/cmake/help/v3.20/module/FindProtobuf.html
Example:
find_package(Protobuf REQUIRED)
include_directories(${Protobuf_INCLUDE_DIRS})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS foo.proto)
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS EXPORT_MACRO DLL_EXPORT foo.proto)
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS DESCRIPTORS PROTO_DESCS foo.proto)
protobuf_generate_python(PROTO_PY foo.proto)
add_executable(bar bar.cc ${PROTO_SRCS} ${PROTO_HDRS})
target_link_libraries(bar ${Protobuf_LIBRARIES})
Note The protobuf_generate_cpp and protobuf_generate_python functions and add_executable() or add_library() calls only work properly within the same directory.

This problem only happens when the target is a library (using add_library, add_executable seems to be fine). The PROTOBUF_GENERATE_CPP will defer the generation of protobuf sources and headers when building the library. A workaround is to define a custom target and add it as a dependency to the lib target. e.g.
add_custom_target(proto_dep DEPENDS ${PROTO_SRCS} ${PROTO_HDRS})
add_library(${PROJECT_NAME} STATIC ${OTHER_SRCS})
add_dependencies(${PROJECT_NAME} proto_dep)
target_link_libraries(${PROJECT_NAME} PRIVATE gRPC::grpc++ gRPC::grpc++_reflection protobuf::libprotobuf)

Related

CMake question: Two same output obj generated by VS IDE and CMake command line cause LNK4006

I am new for CMake, and I'm adding NASM files into projects by CMakelists.
With this version, IDE and project have two output folders to generate same obj, so that cause warning lnk4006 already defined second definition ignored.
One output folder and it is like build\encoder\x86.
Another is like build\encoder\x86\xxxenc_x86_asm.dir\Debug\ or build\encoder\x86\xxxenc_x86_asm.dir\Release.
I have trid to update the second output path to be same with the first, but it can't work.
CMakelists like following:
add_library(${ENCODER_X86_ASM_LIB_NAME} OBJECT "")
if ((LINUX OR MACOSX OR WIN32) AND ${ENABLE_ASM})
set(ASM_EXECUTABLE "${NASM_EXE}")
if(CMAKE_GENERATOR STREQUAL "Xcode")
add_library(${ENCODER_NATIVE_ASM_LIB_NAME} INTERFACE)
endif()
set(X86_ASM_SRCS
asm/avx2/average_avx2.asm)
if (WIN32)
set(ASM_FLAGS ...)
elseif(MACOSX)
set(ASM_FLAGS -I${PROJECT_SOURCE_DIR}/ -f macho64 -O2 -DARCH_X86_64=1 -DPREFIX -DOS_MAC=1 -DPIC -Worphan-labels -DSTACK_ALIGNMENT=16)
elseif(LINUX)
set(ASM_FLAGS -I${PROJECT_SOURCE_DIR}/ -f elf64 -O2 -DARCH_X86_64=1 -DOS_MAC=0 -DPIC -Worphan-labels -DSTACK_ALIGNMENT=16)
endif()
#generate objects when building CMake
if(CMAKE_GENERATOR STREQUAL "Xcode")
asm_compile_to_target(${ENCODER_NATIVE_ASM_LIB_NAME} ${X86_ASM_SRCS})
else()
asm_compile_to_target(${ENCODER_X86_ASM_LIB_NAME} ${X86_ASM_SRCS})
endif()
if ((MACOSX AND (CMAKE_GENERATOR STREQUAL "Xcode")) OR WIN32)
foreach(asm_file ${X86_ASM_SRCS})
set(ASM_SRC ${CMAKE_CURRENT_LIST_DIR}/${asm_file})
get_filename_component(filename "${asm_file}" NAME_WE)
set(ASM_OBJ "${CMAKE_CURRENT_BINARY_DIR}/${filename}${CMAKE_C_OUTPUT_EXTENSION}")
list(APPEND ASM_SRCS ${ASM_SRC})
list(APPEND ASM_OBJS ${ASM_OBJ})
add_custom_command(
OUTPUT ${ASM_OBJ}
COMMAND ${ASM_EXECUTABLE}
ARGS ${ASM_FLAGS} ${ASM_SRC} -o ${ASM_OBJ}
DEPENDS ${ASM_SRC}
)
endforeach()
foreach(OBJ ${ASM_OBJS})
list(APPEND ASM_PRIMITIVES ${OBJ})
endforeach()
foreach(SRC ${X86_ASM_SRCS})
list(APPEND ASM_PRIMITIVES ${CMAKE_CURRENT_LIST_DIR}/${SRC})
endforeach()
source_group(Assembly FILES ${ASM_PRIMITIVES})
if(CMAKE_GENERATOR STREQUAL "Xcode")
target_sources(${ENCODER_NATIVE_ASM_LIB_NAME} INTERFACE ${ASM_PRIMITIVES})
else()
target_sources(${ENCODER_X86_ASM_LIB_NAME} PRIVATE ${ASM_PRIMITIVES})
endif()
if (MACOSX)
add_custom_target(${ENCODER_X86_ASM_LIB_NAME}-asm ALL DEPENDS ${ASM_PRIMITIVES})
target_sources(${ENCODER_X86_ASM_LIB_NAME} INTERFACE ${ASM_PRIMITIVES})
add_dependencies(${ENCODER_X86_ASM_LIB_NAME} ${ENCODER_X86_ASM_LIB_NAME}-asm)
else()
target_sources(${ENCODER_X86_ASM_LIB_NAME} PRIVATE ${ASM_PRIMITIVES})
endif()
endif() ## if ((MACOSX) AND (CMAKE_GENERATOR STREQUAL "Xcode") OR WIN32)
endif() ## if ((LINUX OR MACOSX OR WIN32) AND ${ENABLE_AVX2})
target_compile_definitions(${ENCODER_X86_ASM_LIB_NAME} PUBLIC)
Here, CMAKE_CURRENT_BINARY_DIR is one output folder and it is build\encoder\x86.
how to deal with this issue?
Background is this: we need to support incremental compiling in IDE, also need to support building by cmake command line.
I have tried to update output path(or directory) as:
set_target_properties(${ENCODER_X86_ASM_LIB_NAME} PROPERTIES LIBRARY_OUTPUT_PATH ${PATH} )
set_target_properties(${ENCODER_X86_ASM_LIB_NAME} PROPERTIES BUILD_RPATH ${PATH} )
set_target_properties(${ENCODER_X86_ASM_LIB_NAME} PROPERTIES INSTALL_RPATH ${PATH} )
set_target_properties(${ENCODER_X86_ASM_LIB_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PATH} )
It just can't work.
Thanks!

Unix make file fails with cmakejs when adding a dependency

I successfully created a native node addon with napi and cmakejs. But when adding a simple library the unix make file, generated by cmake-js explodes with
[ 50%] Linking CXX static library liblib_name.a
[ 50%] Built target lib_name
CMakeFiles/spielwiese.dir/flags.make:10: * missing separator. Stop.
make[1]: * [CMakeFiles/Makefile2:72: CMakeFiles/spielwiese.dir/all] Error 2
make: *** [Makefile:130: all] Error 2
ERR! OMG Process terminated: 2
Minimized example project: https://github.com/Superlokkus/spielwiese/tree/napi
The root CMakeLists should be close or intented to be close to cmakejs example boilerplate version, just with an additional function PARSE_CMAKEJS_PROPERTIES to also build it via a cmake CLI command, for nice developing with IDEs like CLion. However the problem persisits when removing the PARSE_CMAKEJS_PROPERTIES function.
I added the library with a add_subdirectory, if you remove https://github.com/Superlokkus/spielwiese/blob/napi/CMakeLists.txt#L47 aka add_subdirectory(src/lib_name) and change https://github.com/Superlokkus/spielwiese/blob/napi/CMakeLists.txt#L63 aka target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_JS_LIB} lib_name) to target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_JS_LIB}) and also remove
https://github.com/Superlokkus/spielwiese/blob/napi/src/spielwiese.cpp#L3 aka #include <lib_name/lib_name.hpp>,
the project builds again incl. the mocha test. However add the example library and you get the error again.
Root CMakeLists:
cmake_minimum_required(VERSION 3.2)
# The following function is just for nice CLion IDE support with cmake-js
function(PARSE_CMAKEJS_PROPERTIES)
function(GET_VARIABLE INPUT_STRING VARIABLE_TO_SELECT OUTPUT_VARIABLE)
set(SEARCH_STRING "${VARIABLE_TO_SELECT}=\"")
string(LENGTH "${SEARCH_STRING}" SEARCH_STRING_LENGTH)
string(LENGTH "${INPUT_STRING}" INPUT_STRING_LENGTH)
string(FIND "${INPUT_STRING}" "${VARIABLE_TO_SELECT}=\"" SEARCH_STRING_INDEX)
math(EXPR SEARCH_STRING_INDEX "${SEARCH_STRING_INDEX}+${SEARCH_STRING_LENGTH}")
string(SUBSTRING "${INPUT_STRING}" ${SEARCH_STRING_INDEX} ${INPUT_STRING_LENGTH} AFTER_SEARCH_STRING)
string(FIND "${AFTER_SEARCH_STRING}" "\"" QUOTE_INDEX)
string(SUBSTRING "${AFTER_SEARCH_STRING}" "0" "${QUOTE_INDEX}" RESULT_STRING)
set("${OUTPUT_VARIABLE}" "${RESULT_STRING}" PARENT_SCOPE)
endfunction(GET_VARIABLE)
string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER)
if (CMAKE_BUILD_TYPE_LOWER STREQUAL "debug")
exec_program(./node_modules/.bin/cmake-js ${CMAKE_CURRENT_SOURCE_DIR}
ARGS print-configure --debug
OUTPUT_VARIABLE CMAKE_JS_OUTPUT
)
else ()
exec_program(./node_modules/.bin/cmake-js ${CMAKE_CURRENT_SOURCE_DIR}
ARGS print-configure
OUTPUT_VARIABLE CMAKE_JS_OUTPUT
)
endif ()
get_variable("${CMAKE_JS_OUTPUT}" "CMAKE_JS_INC" CMAKE_JS_INC)
set(CMAKE_JS_INC "${CMAKE_JS_INC}" PARENT_SCOPE)
get_variable("${CMAKE_JS_OUTPUT}" "CMAKE_LIBRARY_OUTPUT_DIRECTORY" CMAKE_LIBRARY_OUTPUT_DIRECTORY)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}" PARENT_SCOPE)
endfunction(PARSE_CMAKEJS_PROPERTIES)
# Name of the project (will be the name of the plugin)
project(spielwiese VERSION 1.0)
if (NOT CMAKE_JS_INC)
parse_cmakejs_properties()
endif ()
add_subdirectory(src/lib_name)
# Essential include files to build a node addon,
# you should add this line in every CMake.js based project.
include_directories(${CMAKE_JS_INC})
# Declare the location of the source files
file(GLOB SOURCE_FILES "src/*.cpp")
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES})
# This line will give our library file a .node extension without any "lib" prefix
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")
# Essential library files to link to a node addon,
# you should add this line in every CMake.js based project.
target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_JS_LIB} lib_name)
# Include N-API wrappers
execute_process(COMMAND node -p "require('node-addon-api').include"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE NODE_ADDON_API_DIR
)
string(REPLACE "\"" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR})
Libnames CMakeLists
cmake_minimum_required(VERSION 3.0)
project(lib_name VERSION 1.0)
string(TOUPPER ${PROJECT_NAME} PROJECT_NAME_UPPER_CASE)
configure_file(
${PROJECT_NAME}_version.hpp.in
${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}/${PROJECT_NAME}_version.hpp
)
set(${PROJECT_NAME}_implementation_files
src/lib.cpp
)
add_library(${PROJECT_NAME} ${${PROJECT_NAME}_implementation_files})
set_property(TARGET ${PROJECT_NAME} PROPERTY LINKER_LANGUAGE CXX)
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 17)
target_include_directories(${PROJECT_NAME}
PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
)
install(TARGETS ${PROJECT_NAME}
ARCHIVE
DESTINATION lib)
install(TARGETS ${PROJECT_NAME}
PUBLIC_HEADER
DESTINATION include)
Update
I did some experimentation and it seems
target_include_directories(${PROJECT_NAME}
PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
)
is doing the trouble, so it looks like cmakejs has some problem with that command?!
Turns out the snippet taken from the official cmake-js documentation
execute_process(COMMAND node -p "require('node-addon-api').include"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE NODE_ADDON_API_DIR
)
is invoking node to get the path to the node headers. But the output of node might contain a newline, which will ruin the day of all includes after that. So the correct part of the root CMakeLists.txt would be
# Include N-API wrappers
execute_process(COMMAND node -p "require('node-addon-api').include"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE NODE_ADDON_API_DIR
)
string(REPLACE "\n" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
string(REPLACE "\"" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR})
So a addition of string(REPLACE "\n" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR}) to get rid of the newline fixed the problem.
Thanks #fred for the help to find the problem
Already created a pull request to fix the cmake-js documentation: https://github.com/cmake-js/cmake-js/issues/175

Trouble linking sdl_mixer with cmake

I specifically get an undefined reference when I call Mix_OpenAudio(...). I am able to include 'SDL_mixer.h' just fine.
This is my CMakeList.txt. Sorry about all the other packages included. I'll include the FindSDL_MIXER.cmake as well.
I am able to create SDL_Mixer data types just fine as well. I installed SDL_Mixer using apt-get install.
PROJECT(Window)
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/CMakeModules")
add_definitions( -DMAGICKCORE_QUANTUM_DEPTH=16 )
add_definitions( -DMAGICKCORE_HDRI_ENABLE=0 )
FIND_PACKAGE(OpenGL REQUIRED)
FIND_PACKAGE(SDL2 REQUIRED)
FIND_PACKAGE(GLEW REQUIRED)
FIND_PACKAGE(GLM REQUIRED)
FIND_PACKAGE(Bullet REQUIRED)
FIND_PACKAGE(ASSIMP REQUIRED)
FIND_PACKAGE(ImageMagick COMPONENTS Magick++ REQUIRED )
FIND_PACKAGE(SDL_MIXER)
SET(CXX11_FLAGS "-std=gnu++11 -lassimp")
SET(CDEBUG_FLAGS -g)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX11_FLAGS} ${CDEBUG_FLAGS}")
SET(TARGET_LIBRARIES "${OPENGL_LIBRARY} ${SDL2_LIBRARY} ${ASSIMP_LIBRARIES}")
# Find where Magick++-config lives
IF(UNIX)
ADD_DEFINITIONS(-DUNIX)
ENDIF(UNIX)
IF(NOT APPLE)
IF(GLEW_FOUND)
INCLUDE_DIRECTORIES(${GLEW_INCLUDE_DIRS})
LINK_LIBRARIES(${GLEW_LIBRARIES})
ENDIF(GLEW_FOUND)
IF(ASSIMP_FOUND)
INCLUDE_DIRECTORIES(${ASSIMP_INCLUDE_DIRS})
LINK_LIBRARIES(${ASSIMP_LIBRARIES})
ENDIF(ASSIMP_FOUND)
ENDIF(NOT APPLE)
INCLUDE_DIRECTORIES(
"${PROJECT_SOURCE_DIR}/include"
${SDL2_INCLUDE_DIR}
${GLM_INCLUDE_DIRS}
${ASSIMP_INCLUDE_DIRS}
${ImageMagick_INCLUDE_DIRS}
${BULLET_INCLUDE_DIRS}
)
# Copy shaders, models, and default config
# FILE(COPY src/shaders DESTINATION .)
# FILE(COPY models DESTINATION .)
# FILE(COPY textures DESTINATION .)
# FILE(COPY config.json DESTINATION .)
# Set Includes
SET(INCLUDES ${PROJECT_SOURCE_DIR}/include)
INCLUDE_DIRECTORIES(${INCLUDES} ${ASSIMP_INCLUDE_DIRS} ${ImageMagick_INCLUDE_DIRS} ${BULLET_INCLUDE_DIRS})
# Set sources
FILE(GLOB_RECURSE SOURCES "src/*.cpp")
ADD_EXECUTABLE(${PROJECT_NAME} ${SOURCES})
add_custom_target("${PROJECT_NAME}_SUCCESSFUL" ALL
DEPENDS ${PROJECT_NAME}
COMMAND ${CMAKE_COMMAND} -E echo ""
COMMAND ${CMAKE_COMMAND} -E echo "====================="
COMMAND ${CMAKE_COMMAND} -E echo " Compile complete!"
COMMAND ${CMAKE_COMMAND} -E echo "====================="
COMMAND ${CMAKE_COMMAND} -E echo "${CMAKE_CURRENT_BINARY_DIR}"
)
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${OPENGL_LIBRARY} ${SDL2_LIBRARY} ${ASSIMP_LIBRARY} ${ImageMagick_LIBRARIES} ${BULLET_LIBRARIES} ${SDL_MIXER_LIBRARY})
Now here is FindSDL_MIXER.cmake.
#
# Find SDL_MIXER
#
# Additional modules
include(FindPackageHandleStandardArgs)
# Find include files
find_path(
SDL_MIXER_INCLUDE_DIR
PATHS
/usr/include
/usr/local/include
/sw/include
/opt/local/include
${SDL_MIXER_ROOT_DIR}/include
DOC "The directory where SDL_mixer.h resides")
# Handle REQUIRD argument, define *_FOUND variable
#find_package_handle_standard_args(SDL_MIXER_INCLUDE_DIR)
# Hide some variables
mark_as_advanced(SDL_MIXER_INCLUDE_DIR)
It's because the FindSDL_MIXER.cmake isn't setting the library variable. The cmake 3.13 has an updated FindSDL_mixer.cmake that sets this correctly you can use it as an example. Specifically you need a line like this in FindSDL_MIXER.cmake to find the library and set the variable.
find_library(SDL_MIXER_LIBRARY
NAMES SDL2_mixer
HINTS
ENV SDLMIXERDIR
ENV SDLDIR
PATH_SUFFIXES lib
)
I've also noticed that SDL_MIXER_INCLUDE_DIR does not appear to be used in the CMakeLists.txt for the target. It just happens that it is probably located in the same place as some of the other header files being included.
Also you probably want to rename the file to FindSDL2_MIXER.cmake and change the corresponding FIND_PACKAGE(SDL2_MIXER). I'm not entirely sure if the posted FindSDL_MIXER.cmake is being used because it looks like the find_path is not syntactically correct, it's missing the name of the file to be found.

Using cmake to create protobuf / grpc cc files

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!
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})
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})
For me the blogpost https://www.falkoaxmann.de/dev/2020/11/08/grpc-plugin-cmake-support.html lead to success because it provides 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}"
)

How to automatically move *.pb.h and *.pb.h under /include and /src directories?

I am developing a library which uses Google Protocol Buffers (protobuf) and CMake. The project has the following directory tree.
MyProject/
MyProject/include/myproject/
MyProject/include/myproject/some_classes.h
MyProject/src/
MyProject/src/some_classes.cc
MyProject/src/foo.proto
MyProject/CMakeList.txt
CMakeList.txt has the following lines to generate protobuf source and header files.
include_directories(${libCHEC_SOURCE_DIR}/include)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SOURCES)
find_package(Protobuf REQUIRED)
file(GLOB ProtoFiles "${CMAKE_CURRENT_SOURCE_DIR}/src/*.proto")
protobuf_generate_cpp(ProtoSources ProtoHeaders ${ProtoFiles})
list(APPEND EXTLIBS ${PROTOBUF_LIBRARIES})
add_library(MyLibrary SHARED ${SOURCES} ${ProtoSources})
target_link_libraries(MyLibrary ${EXTLIBS})
When I execute cmake, foo.pb.h and foo.pb.cc are generated under the build directory (i.e. the directory where I executed cmake). It looks that this is the default behavior. But I would like to put foo.pb.h and foo.pb.cc under include/myproject and src directories, respectively.
How can I change the locations of the files generated by protoc?
I would strictly advise against placing generated files in the source tree.
CMake puts a lot of effort into separating the build and source trees. Forcing it to give up that separation has several disadvantages. Among the most prominent is the fact that version control will then have to deal with unversioned generated files in the source tree, and furthermore it may no longer be possible to have multiple builds targetting different architectures sharing the same source tree.
A better approach is to keep the files in the binary tree and adjust your target_include_directories accordingly. There is no shame in using generated files from the binary tree as sources, so don't hesitate to do it.
I added MyProject/misc/myprotobuf.cmake which had a modified version of PROTOBUF_GENERAGE_CPP.
function(MY_PROTOBUF_GENERATE_CPP PATH SRCS HDRS)
if(NOT ARGN)
message(SEND_ERROR "Error: PROTOBUF_GENERATE_CPP() called without any proto files")
return()
endif()
if(PROTOBUF_GENERATE_CPP_APPEND_PATH)
# 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}/${PATH}/${FIL_WE}.pb.cc")
list(APPEND ${HDRS} "${CMAKE_CURRENT_BINARY_DIR}/${PATH}/${FIL_WE}.pb.h")
execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/${PATH})
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${PATH}/${FIL_WE}.pb.cc"
"${CMAKE_CURRENT_BINARY_DIR}/${PATH}/${FIL_WE}.pb.h"
COMMAND ${PROTOBUF_PROTOC_EXECUTABLE}
ARGS --cpp_out ${CMAKE_CURRENT_BINARY_DIR}/${PATH} ${_protobuf_include_path} ${ABS_FIL}
DEPENDS ${ABS_FIL}
COMMENT "Running 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()
MyProject/CMakeLists.txt has the following two lines now.
include(${CMAKE_CURRENT_SOURCE_DIR}/misc/myprotobuf.cmake)
file(GLOB ProtoFiles "${CMAKE_CURRENT_SOURCE_DIR}/misc/*.proto")
my_protobuf_generate_cpp(generated_files/myproject ProtoSources ProtoHeaders ${ProtoFiles})
include_directories(${CMAKE_CURRENT_BINARY_DIR}/generated_files)
You can manipulate the directory structure for your generated protobuf sources by modifying the source directory and cmakelists in a particular way.
An example:
MyProject/
MyProject/src
MyProject/src/CMakeLists.txt (1)
MyProject/src/ProtoProject/CMakeLists.txt (2)
MyProject/src/ProtoProject/MyProject/foo.proto
CMakeLists.txt (1):
add_subdirectory(MyProject)
# generated headers
target_include_directories(ProtoProject PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
CMakeLists.txt (2):
set(proto_files "foo.proto")
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${proto_files})
add_library(ProtoProject STATIC ${PROTO_SRCS} ${PROTO_HDRS} ${proto_files})
Why this works
Each call to add_subdirectory() adds a directory level to the $CMAKE_CURRENT_BINARY_DIRECTORY variable. The generated files will end up at MyProject/src/ProtoProject/MyProject/foo.pb.h but your include directory will be MyProject/src/ProtoProject, so you can include files as MyProject/foo.pb.h