I am using CMAKE to for building and testing. I have it running code coverage. However, when I change the test source, it needs to clear the coverage data for the file being tested, but not other files.
For example, I touch TEST_Outputs.c but not Outputs.c. This causes TEST_Outputs.exe to be built.
This causes add_custom_command to execute TEST_Outputs.exe. A new TEST_Outputs.c.gcda is created. However, since Outputs.c was not rebuild, Outputs.c.gcda is not changed and accumulates coverage data from both the old set of tests and the new set of tests. I want to clear Outputs.c.gcda but not say Inputs.c.gcda.
My build folder has the following tree where the offending gcda are located in the .CMakeFiles/TEST_Outputs.dir/--/src/Outputs folder.
└───CMakeFiles
├───Test_LogicIn_Init.dir
│ ├───mocks
│ └───__
│ └───src
│ └───LogicIn
├───TEST_Outputs.dir
│ ├───mocks
│ └───__
│ └───src
│ └───Outputs
└───TEST_SysStatus.dir
├───mocks
└───__
└───src
└───System
CMakeLists.txt
set(CMAKE_BUILD_TYPE Debug)
SET(CMAKE_C_COMPILER C:/MinGW/bin/gcc.exe)
#set(CMAKE_VERBOSE_MAKEFILE ON)
# These arguments to gcc.exe are required in order to generate code coverage data
set(GCC_COVERAGE_COMPILE_FLAGS "-fprofile-arcs -ftest-coverage")
add_definitions(${GCC_COVERAGE_COMPILE_FLAGS})
include(${CMAKE_CURRENT_SOURCE_DIR}/cmocka-bin/AddCmocka.cmake)
#Add list of tests and their flags
#TEST_LogicIn test
LIST(APPEND tests_names "Test_LogicIn_Init")
LIST(APPEND tests_filename "TEST_LogicIn.c")
LIST(APPEND tests_flags "-Wl,--wrap=x_in_init,--wrap=x_in_get_value")
LIST(APPEND tests_mocks "mocks/mock_LogicIn.c")
LIST(APPEND tests_srcdir "../src/LogicIn/Logicin.c")
#TEST_SysStatus test
LIST(APPEND tests_names "TEST_SysStatus")
LIST(APPEND tests_filename "TEST_SysStatus.c")
LIST(APPEND tests_flags "-Wl,--wrap=x_sys_get_ignition")
LIST(APPEND tests_mocks "mocks/mock_SysStatus.c")
LIST(APPEND tests_srcdir "../src/System/SysStatus.c")
#TEST_Outputs test
LIST(APPEND tests_names "TEST_Outputs")
LIST(APPEND tests_filename "TEST_Outputs.c")
LIST(APPEND tests_flags "-Wl,--wrap=x_out_init,--wrap=x_out_set_value,--wrap=x_out_relay_set")
LIST(APPEND tests_mocks "mocks/mock_Outputs.c")
LIST(APPEND tests_srcdir "../src/Outputs/Outputs.c")
# Declare all tests targets
LIST(LENGTH tests_names count)
MATH(EXPR count "${count} - 1")
FOREACH(i RANGE ${count})
LIST(GET tests_names ${i} test_name)
LIST(GET tests_filename ${i} test_filename)
LIST(GET tests_flags ${i} test_flags)
LIST(GET tests_mocks ${i} test_mocks)
LIST(GET tests_srcdir ${i} test_srcdir)
ADD_EXECUTABLE(${test_name} ${test_filename} ${test_mocks} ${CMAKE_CURRENT_SOURCE_DIR}/${test_srcdir})
TARGET_LINK_LIBRARIES(
${test_name}
PRIVATE
cmocka
${CMAKE_PROJECT_NAME}
gcov
)
set_target_properties(
${test_name}
PROPERTIES
LINK_FLAGS ${test_flags}
)
ADD_CMOCKA_TEST(${test_name} ${test_srcdir})
ENDFOREACH()
AddCmocka.cmake
set(CMAKE_BUILD_TYPE Debug)
add_library(cmocka SHARED IMPORTED GLOBAL)
target_include_directories(cmocka INTERFACE ${CMAKE_CURRENT_LIST_DIR})
set_target_properties(cmocka
PROPERTIES
IMPORTED_LOCATION "${CMAKE_CURRENT_LIST_DIR}/cmocka.dll"
IMPORTED_IMPLIB "${CMAKE_CURRENT_LIST_DIR}/libcmocka.dll.a"
)
file(COPY "${CMAKE_CURRENT_LIST_DIR}/cmocka.dll"
DESTINATION "${CMAKE_CURRENT_BINARY_DIR}"
)
# Macro for adding and creating the test
MACRO(add_cmocka_test executable_name source)
add_test(NAME RunCTest${executable_name}
COMMAND ${executable_name}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_custom_command(TARGET ${executable_name}
#I want to delete old src.c.gcda here
COMMAND ${executable_name}
)
ENDMACRO()
The way I see it I can,
Add SourceFile.c.gcda to the list of targets somehow so that it is rebuild when the test executable is build. How would I do this?
Use CMake to delete the old *.gcda file. How do I get a reference to the subdirectory?
Related
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!
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.
Following is the my scenario. I have one top level CMakeList.txt and another 2 internal CMakeList.txt. In top level cmake I have 3 custom targets that are copy, build, copyandbuild. As name specifies make copy copies the source directories (i.e dir1, dir2) to ${CMAKE_SOURCE_DIR}. make build creates libs and executables. make copyandbuild (copy+build).
Running cmake .. from build directory completes successfully.
If I run make copyandbuild it is copying to ${CMAKE_SOURCE_DIR} but at the time of build it is showing error that
No rule to make target `dir1/libmylib.so', needed by `CMaketargetdbuild'. Stop
MyProject
dir1
CMakeLists.txt
dir2
CMakeLists.txt
CMakeLists.txt
It is working if i execute commands in below order.
cmake ..
make copyandbuild
cmake ..
make build
My requirement is it should work with out running cmake and make build again as copyandbuild doing the same work.
Top level CMakeLists.txt:
cmake_minimum_required(VERSION 2.6)
cmake_minimum_required(VERSION 2.6)
set(RE_BUILD make rebuild_cache)
set(OUTPUT_DIR ${CMAKE_SOURCE_DIR}/../)
if(EXISTS ${CMAKE_SOURCE_DIR}/dir1)
message(WARNING "Found dir1 dir")
add_subdirectory(dir1 EXCLUDE_FROM_ALL)
else()
message(WARNING "Couldn't find dir1 directory ")
endif()
if(EXISTS ${CMAKE_SOURCE_DIR}/dir2)
add_subdirectory(dir2 EXCLUDE_FROM_ALL)
else()
message(WARNING "Couldn't find dir2 directory")
endif()
set(MOVE_LIB_COMMAND mv src/myapp . && mv dir1/mylib .)
set(COPY_COMMAND cp -r ../sourceCode1 ../dir1 && cp -r ../sourceCode2 ../dir2)
set(CLEAN_DIR1_COMMAND cmake -E remove_directory ${CMAKE_SOURCE_DIR}/dir1)
set(CLEAN_DIR2_COMMAND cmake -E remove_directory ${CMAKE_SOURCE_DIR}/dir2)
set(SET_SLEEP sync)
#Copy command
add_custom_target(
copy ${COPY_COMMAND}
COMMAND ${RE_BUILD}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
#Compilation
add_custom_target(
build
COMMAND ${MOVE_LIB_COMMAND}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS mylib myapp
)
#copy and compile
add_custom_target(
copyandbuild
COMMAND ${MOVE_LIB_COMMAND}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS copy mylib myapp
)
add_custom_command(TARGET copy POST_BUILD
COMMAND ${SET_SLEEP}
)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++11")
dir1 CMake is :
cmake_minimum_required(VERSION 2.6)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++11")
include_directories(
${MY_APP_INCLUDE_DIRS}
)
link_directories(
${MY_APP_LIBDIR}
)
add_library(mylib
SHARED
com/*.cpp
)
target_link_libraries(mylib myapp_lib)
dir2 CMake is :
cmake_minimum_required(VERSION 2.6)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++11")
include_directories(
${MY_APP_INCLUDE_DIRS}
)
link_directories(
${MY_APP_LIBDIR}
)
You are using CMake in a way that prevents its proper function. By explicitly invoking shell commands in many places, when you could use CMake built in features, you are robbing CMake of any context that it could use to build your programs. Also, using wildcards like *.cpp in CMake is considered bad practice. And you have a number of duplicate statements--you do not need cmake_minimum_required() or setting compiler flags other than at the top level.
In short, your CMakeLists.txt at the top level should look more like this:
cmake_minimum_required(VERSION 2.6)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++11")
add_subdirectory(dir1 EXCLUDE_FROM_ALL)
You should not need to copy source files around--just build them from where they are, for example your dir1/CMakeLists.txt might be:
add_library(mylib
SHARED
sourceCode1/mylib.cpp
)
Keep it simple. Get it working. Then ask questions if you need to add missing features.
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)
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