CMake how to collect auto generated source and header files - c++

To put a bit of context first, I'm trying to decouple a repo that includes protobuf definitions as well as proto-using entities. I want to create a repo with only protobuf definitions and scripts that export the resulting classes in different languages, to be imported by other projects that use them.
Generating protobuf classes in c++ is actually quite simple (few lines of python script), but after that it gets tricky. You end up with an unorganized set of source files. I want to use CMake to collect all of the files and generate the library. The file structure should look something like this:
/root
/proto-package-1
foo.pb.h
foo.pb.cc
/proto-package-2
/proto-package-3
bar.pb.h
bar.pb.cc
bazz.pb.h
bazz.pb.cc
I'm a bit of a noob in CMake but I've read that it is good practice to explicitly list all the source files in the CMakeLists.txt file. But I'd like to make the generation of this c++ library to be fully automatic. Would that be a good reason to violate this rule? And what CMake commands can help me out?
cmake_minimum_required (VERSION 3.5)
project ("CMakeProject1")
set(TARGET_NAME CMakeProject1)
# Include sub-projects.
include_directories(${CMAKE_SOURCE_DIR})
add_library(${TARGET_NAME} STATIC
${CMAKE_SOURCE_DIR}/root/proto-package-1/foo.pb.h
${CMAKE_SOURCE_DIR}/root/proto-package-1/foo.pb.cc
//This is not going to cut it. Cannot add a proto and auto-generate.
)

You can also generate them in the cmake and create a list of generated files
# Get list of .proto files
file(GLOB_RECURSE proto_files RELATIVE ${PROJECT_SOURCE_DIR} "*.proto")
## Get Protobuf include dirs
get_target_property(protobuf_dirs protobuf::libprotobuf INTERFACE_INCLUDE_DIRECTORIES)
foreach(dir IN LISTS protobuf_dirs)
if ("${dir}" MATCHES "BUILD_INTERFACE")
message(STATUS "Adding proto path: ${dir}")
list(APPEND PROTO_DIRS "--proto_path=${dir}")
endif()
endforeach()
# Generate Protobuf cpp sources
set(PROTO_HDRS)
set(PROTO_SRCS)
foreach(PROTO_FILE IN LISTS proto_files)
#message(STATUS "protoc proto(cc): ${PROTO_FILE}")
get_filename_component(PROTO_DIR ${PROTO_FILE} DIRECTORY)
get_filename_component(PROTO_NAME ${PROTO_FILE} NAME_WE)
set(PROTO_HDR ${PROJECT_BINARY_DIR}/${PROTO_DIR}/${PROTO_NAME}.pb.h)
set(PROTO_SRC ${PROJECT_BINARY_DIR}/${PROTO_DIR}/${PROTO_NAME}.pb.cc)
#message(STATUS "protoc hdr: ${PROTO_HDR}")
#message(STATUS "protoc src: ${PROTO_SRC}")
add_custom_command(
OUTPUT ${PROTO_SRC} ${PROTO_HDR}
COMMAND protobuf::protoc
"--proto_path=${PROJECT_SOURCE_DIR}"
${PROTO_DIRS}
"--cpp_out=${PROJECT_BINARY_DIR}"
${PROTO_FILE}
DEPENDS ${PROTO_FILE} protobuf::protoc
COMMENT "Generate C++ protocol buffer for ${PROTO_FILE}"
VERBATIM)
list(APPEND PROTO_HDRS ${PROTO_HDR})
list(APPEND PROTO_SRCS ${PROTO_SRC})
endforeach()
# [optional] Create a library of all generated C++ files
add_library(${PROJECT_NAME}_proto ${PROTO_SRCS} ${PROTO_HDRS})
set_target_properties(${PROJECT_NAME}_proto PROPERTIES POSITION_INDEPENDENT_CODE ON)
set_target_properties(${PROJECT_NAME}_proto PROPERTIES CXX_STANDARD 11)
set_target_properties(${PROJECT_NAME}_proto PROPERTIES CXX_STANDARD_REQUIRED ON)
set_target_properties(${PROJECT_NAME}_proto PROPERTIES CXX_EXTENSIONS OFF)
target_include_directories(${PROJECT_NAME}_proto PRIVATE
${PROJECT_SOURCE_DIR}
${PROJECT_BINARY_DIR}
$<TARGET_PROPERTY:protobuf::libprotobuf,INTERFACE_INCLUDE_DIRECTORIES>
)
target_link_libraries(${PROJECT_NAME}_proto PRIVATE protobuf::libprotobuf)
ref: https://github.com/google/or-tools/blob/a0a56698ba8fd07b7f84aee4fc45d891a8cd9828/cmake/cpp.cmake#L234-L294

I believe you can use an internal function of the FindProtobuf package to generate static libraries containing protobuf implementations:
find_package(Protobuf 3.12 REQUIRED)
add_library(protopackage1 STATIC proto-package-1/foo.proto)
protobuf_generate(LANGUAGE cpp TARGET protopackage1)
You can pass extra arguments to protobuf_generate but you will have to figure out how protobuf_generate_cpp passes them.

Related

undefined reference because I can't find the according source files

I have a cpp file from a program that I want to open separately from the whole file structure. I need to do that in order to use this cpp file in ros. I have the header files included but I need to include the source files as well if I am correct.
my cpp file is called open_camera.cpp and includes a header file /usr/include/ids_peak-1.3.0/peak/backend/peak_backend.h
the peak_backend.h file contains declarations like this:
PEAK_C_API PEAK_Library_GetLastError(
PEAK_RETURN_CODE* lastErrorCode, char* lastErrorDescription, size_t* lastErrorDescriptionSize);
My Cmake File looks like this:
cmake_minimum_required(VERSION 3.0.2)
project(ros_package)
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
)
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES ros_package
# CATKIN_DEPENDS roscpp rospy std_msgs
# DEPENDS system_lib
)
###########
## Build ##
###########
## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
# include
${catkin_INCLUDE_DIRS}
)
add_executable(open_camera_node src/open_camera.cpp)
#############
## Install ##
#############
include_directories(/usr/include/ids_peak-1.3.0)
if I run catkin_make I get errors like:
/usr/bin/ld: CMakeFiles/open_camera_node.dir/src/open_camera.cpp.o: in function `void peak::core::ExecuteAndMapReturnCodes<(anonymous namespace)::CallAndCheckCInterfaceFunction(std::function<int ()> const&)::{lambda()#1}>((anonymous namespace)::CallAndCheckCInterfaceFunction(std::function<int ()> const&)::{lambda()#1} const&)':
open_camera.cpp:(.text+0x516): undefined reference to `PEAK_Library_GetLastError'
From my understanding the problem is that I need to link the source files for the header. How can I do that and where do I find the source files for my headers? I searched for quiet some time but could not locate them.
The open_camera.cpp has its own CMake file looking like this:
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
project ("open_camera_cpp")
message (STATUS "[${PROJECT_NAME}] Processing ${CMAKE_CURRENT_LIST_FILE}")
set (SAMPLE_TARGET_NAME ${PROJECT_NAME})
set (CMAKE_SCRIPTS_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../_cmake_scripts" CACHE STRING "The path of the cmake scripts directory.")
set (SAMPLE_OUTPUT_PATH "${CMAKE_BINARY_DIR}/output/bin")
include (${CMAKE_SCRIPTS_PATH}/cmake_detect_architecture.cmake)
detect_target_architecture (ARCH)
add_executable (${SAMPLE_TARGET_NAME}
open_camera.cpp
)
set (LIBRARY_NAME_VISION_API "ids_peak")
string (TOUPPER ${LIBRARY_NAME_VISION_API} LIBRARY_NAME_UPPER_VISION_API)
if (NOT TARGET ids_peak)
file (TO_CMAKE_PATH "$ENV{IDS_PEAK_SDK_PATH}/api" ${LIBRARY_NAME_UPPER_VISION_API}_PACKAGE_DIR)
set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${${LIBRARY_NAME_UPPER_VISION_API}_PACKAGE_DIR}/cmake_finder")
message (STATUS "[${PROJECT_NAME}] Will find IDS peak API library.. CMAKE_MODULE_PATH: ${CMAKE_MODULE_PATH}")
find_package (ids_peak REQUIRED)
endif ()
target_include_directories (${SAMPLE_TARGET_NAME}
PRIVATE ${${LIBRARY_NAME_UPPER_VISION_API}_INCLUDE_DIR}
)
find_package (Threads REQUIRED)
target_link_libraries (${SAMPLE_TARGET_NAME}
ids_peak
${CMAKE_THREAD_LIBS_INIT}
)
if ((CMAKE_CXX_COMPILER_ID MATCHES "GNU") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
target_link_libraries (${SAMPLE_TARGET_NAME}
atomic
)
endif ()
# Set output directories for all configuration types (Debug, Release, etc.)
if (NOT CMAKE_BUILD_TYPE)
set (CMAKE_BUILD_TYPE "Debug")
endif()
if (NOT CMAKE_CONFIGURATION_TYPES)
set (CMAKE_CONFIGURATION_TYPES ${CMAKE_BUILD_TYPE})
endif ()
if (CMAKE_CONFIGURATION_TYPES)
foreach (CONFIGURATION_TYPE ${CMAKE_CONFIGURATION_TYPES})
string (TOUPPER ${CONFIGURATION_TYPE} CONFIGURATION_TYPE_UPPER)
if (CONFIGURATION_TYPE_UPPER STREQUAL "RELEASE")
set (SAMPLE_RUNTIME_OUTPUT_NAME ${SAMPLE_TARGET_NAME})
set (SAMPLE_RUNTIME_OUTPUT_DIRECTORY ${SAMPLE_OUTPUT_PATH}/${ARCH})
else ()
string (TOLOWER ${CONFIGURATION_TYPE} CONFIGURATION_TYPE_LOWER)
set (SAMPLE_RUNTIME_OUTPUT_NAME "${SAMPLE_TARGET_NAME}_${CONFIGURATION_TYPE_LOWER}")
set (SAMPLE_RUNTIME_OUTPUT_DIRECTORY ${SAMPLE_OUTPUT_PATH}/${ARCH}/${CONFIGURATION_TYPE})
endif ()
set_target_properties (${SAMPLE_TARGET_NAME} PROPERTIES
RUNTIME_OUTPUT_NAME_${CONFIGURATION_TYPE_UPPER} ${SAMPLE_RUNTIME_OUTPUT_NAME}
RUNTIME_OUTPUT_DIRECTORY_${CONFIGURATION_TYPE_UPPER} ${SAMPLE_RUNTIME_OUTPUT_DIRECTORY}
)
message (STATUS "[${PROJECT_NAME}] Cfg ${CONFIGURATION_TYPE} -> Output directory: ${SAMPLE_RUNTIME_OUTPUT_DIRECTORY}, Output name: ${SAMPLE_RUNTIME_OUTPUT_NAME}")
endforeach ()
endif ()
set_target_properties(${SAMPLE_TARGET_NAME} PROPERTIES
CXX_STANDARD 14
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS NO
)
if (MSVC)
target_compile_options (${SAMPLE_TARGET_NAME}
PRIVATE "/bigobj"
PRIVATE "/MP"
)
endif ()
GET_PROPERTY(${LIBRARY_NAME_UPPER_VISION_API}_LIBRARIES_COPIED_LOCAL GLOBAL PROPERTY ${LIBRARY_NAME_UPPER_VISION_API}_LIBRARIES_COPIED)
if(NOT ${LIBRARY_NAME_UPPER_VISION_API}_LIBRARIES_COPIED_LOCAL)
file (GLOB ids_peak_LIBS
"${${LIBRARY_NAME_UPPER_VISION_API}_LIBRARY_DIR}/*${CMAKE_SHARED_LIBRARY_SUFFIX}"
)
foreach (ids_peak_LIBRARY ${ids_peak_LIBS})
message (STATUS "[${PROJECT_NAME}] Add PostBuildStep for copy of ${ids_peak_LIBRARY}.")
add_custom_command (TARGET ${SAMPLE_TARGET_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${ids_peak_LIBRARY}
$<TARGET_FILE_DIR:${SAMPLE_TARGET_NAME}>
COMMENT "Post build copy of ${ids_peak_LIBRARY} to output dir." VERBATIM
)
endforeach ()
SET_PROPERTY(GLOBAL PROPERTY ${LIBRARY_NAME_UPPER_VISION_API}_LIBRARIES_COPIED ON)
endif()
# For Unix Build we need the environment variable GENICAM_GENTL32_PATH respectivily GENICAM_GENTL64_PATH to find the GenTL producer libraries.
# To set these environment variables a shell script is used which can be found in the samples root folder in _cmake_scripts.
# To run the samples run this script not the binary.
if (UNIX)
string (TOLOWER ${CMAKE_BUILD_TYPE} CONFIGURATION_TYPE_LOWER)
if(${CONFIGURATION_TYPE_LOWER} STREQUAL "release")
set(VSSL_SAMPLE_BINARY_NAME ${PROJECT_NAME})
else()
set(VSSL_SAMPLE_BINARY_NAME ${PROJECT_NAME}_${CONFIGURATION_TYPE_LOWER})
endif()
configure_file(${CMAKE_SCRIPTS_PATH}/sample_starter.in ${CMAKE_CURRENT_BINARY_DIR}/${VSSL_SAMPLE_BINARY_NAME}.sh)
file(COPY ${CMAKE_CURRENT_BINARY_DIR}/${VSSL_SAMPLE_BINARY_NAME}.sh
DESTINATION ${SAMPLE_RUNTIME_OUTPUT_DIRECTORY}
FILE_PERMISSIONS
OWNER_READ OWNER_WRITE OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE
)
endif (UNIX)
I don't understand much of the original cmake file since I am quiet new to the topic.
The path of the cpp file is: /usr/local/src/ids/samples/peak/cpp/open_camera/open_camera.cpp
According to CMake documentation we shouldn't use the global settings for include directories or such anymore, but use the target_ versions. And to be honest, I don't think, that the second, complicated CMakeLists.txt is used (or needed), it doesn't seem to be included with the first one (but it is hard to say without knowing the directory structure.
But, never the less, if you want to use some library, you need two things: the header file(s) with the declaration of the provided items and usually the compiled library containing the definition/implementation of the items (static or dynamic library). In principle, you can also compile the library on your own, if you have access to the libraries source files. In this case my suggestion would be:
add_library(ids_peak
${IDS_PEAK_SOURCE_FILES}
)
target_include_directories(ids_peak PUBLIC /usr/include/ids_peak-1.3.0)
...
add_executable(open_camera
src/open_camera.cpp
)
target_include_directories(open_camera PRIVATE ${catkin_INCLUDE_DIRS})
target_link_libraries(open_camera PRIVATE ids_peak)
This will define two targets to compile:
a library target, compiling any source files which are in the list ${ID_PEAK_SOURCE_FILES} and with the corresponding include directories attached
an executable target, compiling your open_camera.cpp source file. There is this catkin include directory attached (perhaps we should opt for an other library target here? Are there sources to compile or is there only a lib+headers?). Last but not least a dependency is added to this target.
Since the include directories of the library target are declared public, they are forwarded to all targets, that depend on it (same happens with target_compile_definitions, target_link_libraries, target_link_options, etc.).
These links could be of interest to you:
https://cmake.org/cmake/help/latest/command/add_library.html
https://cmake.org/cmake/help/latest/command/add_executable.html
https://cmake.org/cmake/help/latest/command/target_include_directories.html
https://cmake.org/cmake/help/latest/command/target_link_libraries.html
What do linkers do?
And, if you'd be so kind as to drop the FILE(GLOB...) call. I was told by some CMake contributor once, that this feature wasn't released, but escaped and shouldn't be used at all being pretty error prone. I know it comes in handy, but you can't really control, what your build is really doing. It is better to name the files explicitly. Or, in case of install (https://cmake.org/cmake/help/latest/command/install.html#directory) or copy, you can apply to whole directories.

Detecting compiler configuration in cmake file

Either this is really easy and I'm just not able to find the correct way to do it, or I've wildly misunderstood something. I'm attempting to add a conditional to a CMakeLists.txt file to include the proper .lib file depending on which build configuration type is being used (within visual studio at the moment). So for example, if configuration in visual studio is set to Debug then use file zlibstaticd.lib vs zlibstatic.lib. Below is what I have that's not working:
add_library(ZLIB_LIBRARY OBJECT IMPORTED)
# zlib added via assimp, and I can't get CMAKE_DEBUG_POSTFIX value to overwrite (because it's set within zlibs cmake file when using MSVC)
# so we have to do this check
if($<CONFIG:Debug>)
set_target_properties(ZLIB_LIBRARY PROPERTIES IMPORTED_OBJECTS ${ASSIMP_INSTALL_DIR}/lib/zlibstaticd.lib)
else()
set_target_properties(ZLIB_LIBRARY PROPERTIES IMPORTED_OBJECTS ${ASSIMP_INSTALL_DIR}/lib/zlibstatic.lib)
endif()
I've also tried CMAKE_BUILD_TYPE but it's always an empty string. Below is my entire CMakeLists.txt file so you can see what it is I'm doing (building a singular static library which contains many other static libraries):
cmake_minimum_required(VERSION 3.20.0)
# Define our project name
set(PROJECT_NAME myProjectName)
project(${PROJECT_NAME})
# Make sure binary directory is not the same as source directory
if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
message(
FATAL_ERROR
"In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there."
)
endif()
# This Project Depends on External Project(s)
include(ExternalProject)
set(libGLFW glfw)
ExternalProject_Add(${libGLFW}
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLFW}
GIT_REPOSITORY https://github.com/glfw/glfw.git
GIT_TAG 3.3.4
GIT_SHALLOW ON
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLFW}/install
-DGLFW_BUILD_DOCS:BOOL=OFF
-DGLFW_BUILD_EXAMPLES:BOOL=OFF
-DGLFW_BUILD_TESTS:BOOL=OFF
)
set(libGLAD glad)
ExternalProject_Add(${libGLAD}
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLAD}
GIT_REPOSITORY https://github.com/Dav1dde/glad.git
GIT_TAG origin/master
GIT_SHALLOW ON
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLAD}/install
-DGLAD_INSTALL:BOOL=ON
-DGLAD_PROFILE:STRING="core"
-DGLAD_ALL_EXTENSIONS:BOOL=ON
-DUSE_MSVC_RUNTIME_LIBRARY_DLL:BOOL=OFF
)
set(libGLM glm)
ExternalProject_Add(${libGLM}
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLM}
GIT_REPOSITORY https://github.com/g-truc/glm.git
GIT_TAG origin/master
GIT_SHALLOW ON
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLM}/install
-DBUILD_SHARED_LIBS:BOOL=OFF
-DBUILD_STATIC_LIBS:BOOL=OFF
-DGLM_TEST_ENABLE:BOOL=OFF
)
set(libAssimp assimp)
ExternalProject_Add(${libAssimp}
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/dep/${libAssimp}
GIT_REPOSITORY https://github.com/assimp/assimp.git
GIT_TAG v5.0.1
GIT_SHALLOW ON
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/dep/${libAssimp}/install
-DASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT:BOOL=OFF
-DASSIMP_BUILD_ALL_EXPORTERS_BY_DEFAULT:BOOL=OFF
-DBUILD_SHARED_LIBS:BOOL=OFF
-DASSIMP_BUILD_ASSIMP_TOOLS:BOOL=OFF
-DASSIMP_BUILD_TESTS:BOOL=OFF
-DASSIMP_BUILD_FBX_IMPORTER:BOOL=ON
-DASSIMP_BUILD_OBJ_IMPORTER:BOOL=ON
-DASSIMP_BUILD_OBJ_EXPORTER:BOOL=ON
-DASSIMP_LIBRARY_SUFFIX:STRING=
-DLIBRARY_SUFFIX:STRING=
-DCMAKE_DEBUG_POSTFIX:STRING=
-DASSIMP_INJECT_DEBUG_POSTFIX:BOOL=OFF
)
# Note set_target_properties will need conditionals for windows/linux since extensions differ
# Create the oject files we will join together to create our singular static library, using the projects
# that were previously added above via ExternalProject_Add
# INSTALL_DIR not being set to value of CMAKE_INSTALL_PREFIX, so manuallysetting
#ExternalProject_Get_Property(${libGLFW} INSTALL_DIR)
# SETUP GLFW
set(GLFW_INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLFW}/install)
add_library(GLFW_LIBRARY OBJECT IMPORTED)
set_target_properties(GLFW_LIBRARY PROPERTIES IMPORTED_OBJECTS ${GLFW_INSTALL_DIR}/lib/glfw3.lib)
# SETUP GLAD
set(GLAD_INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLAD}/install)
add_library(GLAD_LIBRARY OBJECT IMPORTED)
set_target_properties(GLAD_LIBRARY PROPERTIES IMPORTED_OBJECTS ${GLAD_INSTALL_DIR}/lib/glad.lib)
# SETUP GLM
# GLM is header only library, so we simply include it's include directory in target_include_directories below
set(GLM_INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLM}/install)
# SETUP ASSIMP and it's dependencies
set(ASSIMP_INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/dep/${libAssimp}/install)
add_library(ASSIMP_LIBRARY OBJECT IMPORTED)
set_target_properties(ASSIMP_LIBRARY PROPERTIES IMPORTED_OBJECTS ${ASSIMP_INSTALL_DIR}/lib/assimp.lib)
add_library(IRRXML_LIBRARY OBJECT IMPORTED)
set_target_properties(IRRXML_LIBRARY PROPERTIES IMPORTED_OBJECTS ${ASSIMP_INSTALL_DIR}/lib/IrrXML.lib)
add_library(ZLIB_LIBRARY OBJECT IMPORTED)
# zlib added via assimp, and I can't get CMAKE_DEBUG_POSTFIX value to overwrite (because it's set within zlibs cmake file when using MSVC)
# so we have to do this check
if($<CONFIG:Debug>)
set_target_properties(ZLIB_LIBRARY PROPERTIES IMPORTED_OBJECTS ${ASSIMP_INSTALL_DIR}/lib/zlibstaticd.lib)
else()
set_target_properties(ZLIB_LIBRARY PROPERTIES IMPORTED_OBJECTS ${ASSIMP_INSTALL_DIR}/lib/zlibstatic.lib)
endif()
# Documentation states not to do this, but do it anyway for the time being since it prevents us from having
# to manually list all project files
file(GLOB_RECURSE headers CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/inc/*.h")
file(GLOB_RECURSE sources CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
# Create a single .lib file containing our compiled objects, and the compiled objects of all other dependencies
add_library(${PROJECT_NAME} STATIC ${headers} ${sources}
$<TARGET_OBJECTS:GLFW_LIBRARY>
$<TARGET_OBJECTS:GLAD_LIBRARY>
$<TARGET_OBJECTS:ASSIMP_LIBRARY>
$<TARGET_OBJECTS:IRRXML_LIBRARY>
$<TARGET_OBJECTS:ZLIB_LIBRARY>
)
# Add all include file paths
target_include_directories(${PROJECT_NAME}
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inc
PUBLIC ${GLFW_INSTALL_DIR}/include
PUBLIC ${GLAD_INSTALL_DIR}/include
PUBLIC ${GLM_INSTALL_DIR}/include
PUBLIC ${ASSIMP_INSTALL_DIR}/include
)
# Specify the order in which libs depend on each other, use the name of the ExternalProject, not the name of the
# library object you create and use with add_library
add_dependencies(${PROJECT_NAME} ${libGLFW} ${libGLAD} ${libGLM} ${libAssimp})
add_library(ZLIB_LIBRARY OBJECT IMPORTED)
# zlib added via assimp, and I can't get CMAKE_DEBUG_POSTFIX value to overwrite (because it's set within zlibs cmake file when using MSVC)
# so we have to do this check
if($<CONFIG:Debug>)
set_target_properties(ZLIB_LIBRARY PROPERTIES IMPORTED_OBJECTS ${ASSIMP_INSTALL_DIR}/lib/zlibstaticd.lib)
else()
set_target_properties(ZLIB_LIBRARY PROPERTIES IMPORTED_OBJECTS ${ASSIMP_INSTALL_DIR}/lib/zlibstatic.lib)
endif()
Generator expressions are evaluated after the configuration step has run, so they're just literal strings when the if() statement sees them. Basically, the CMake configure step is meta-programming a declarative language of targets and generator expressions that gets compiled into Ninja build files (or whatever) by the generator.
You can set the library up as follows:
add_library(zlib OBJECT IMPORTED)
set_target_properties(
zlib
PROPERTIES
IMPORTED_OBJECTS_RELEASE "${ASSIMP_INSTALL_DIR}/lib/zlibstatic.lib"
IMPORTED_OBJECTS_DEBUG "${ASSIMP_INSTALL_DIR}/lib/zlibstaticd.lib"
)
CMake first tries IMPORTED_OBJECTS_$<CONFIG> before trying IMPORTED_OBJECTS when resolving a library path.
All that said, I have to wonder why you don't just use find_package, vcpkg, Conan, or maybe add_subdirectory / FetchContent to manage your dependencies. This seems like a lot of pain given that all of those libraries (I think) either provide their own find_package config packages or CMake provides a find module for them.

CMake with protobuf file in subdirectory

I have seen a lot of similar questions and answers, but until now it seems not so obvious to get it working. I am quite new to CMake and until now everything was easy except the integration with protocol buffers.
I have a project with subdirectories, where each subdirectory has its own CMakeLists.txt
One of the subdirectory contains a .proto file. If the PROTOBUF_GENERATE_CPP macro is executed it generates the sources and the headers files. This macro is invoked from the CMakeLists.txt in the subdirectory containing the .proto file.
It seems however the make file is not invoked because no sources are added to the target. I can not add the sources to the target, because the files do not exist, they exist after generation, so this results in an error when CMake runs.
Setting the file properties to generated seems also not to help. In general, before the build process starts the macro should have been run to generated the source files.
How to do this, any working examples ?
Example:
./src/externals/protodef (from other repository, only contains .proto files)
./src/generated (supposed for the generated c and header files by protoc)
CMakeLists-1 (project root)
cmake_minimum_required (VERSION 2.6)
PROJECT (prototest)
ADD_SUBDIRECTORY("${PROJECT_SOURCE_DIR}/src/externals/protodef")
ADD_SUBDIRECTORY("${PROJECT_SOURCE_DIR}/src")
SET_SOURCE_FILES_PROPERTIES(${PROTO_SOURCES} ${PROTO_HEADERS} PROPERTIES GENERATED TRUE)
ADD_EXECUTABLE(prototest ${PROTO_SOURCES} ${SOURCE} )
TARGET_LINK_LIBRARIES(prototest ${EXTERNAL_LIBS} )
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
CMakeLists-2 (src)
SET(SOURCE ${SOURCE}
${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
PARENT_SCOPE
)
CMakeLists-3 (src/externals/protodef)
SET(PROTOBUF_PATH "D:/protobuf-3.0.0/" )
SET(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "${PROTOBUF_PATH}")
# Changing PROTO_SRCS and PROTO_HDRS does not work for setting the location
# of the generated files.
# Those variable are ignored by CMake for compiling the proto files.
# Using a dedicated CMakeLists.txt and settng CURRENT_BINARY dir is a
# workaround to get them where we want.
SET(GENERATED_DIR ${PROJECT_SOURCE_DIR}/src/generated )
SET(CMAKE_CURRENT_BINARY_DIR ${GENERATED_DIR} )
INCLUDE(FindProtobuf)
FIND_PACKAGE(Protobuf REQUIRED)
PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS${CMAKE_CURRENT_SOURCE_DIR}/test1.proto)
SET( EXTERNAL_LIBS ${PROTOBUF_PATH}/lib/libprotobuf.a PARENT_SCOPE)
# Propagate sources to the parant project
SET(PROTO_SOURCES ${PROTO_SRCS}
PARENT_SCOPE
)
SET(PROTO_HEADERS ${PROTO_HDRS}
PARENT_SCOPE
)
First generate the protobuf files, then add them to a CMake target.
CMakeLists (src) :
# Generate h/cpp proto files (./src/externals/protodef) into ./src/generated folder
PROTOBUF_GENERATE_CPP(...)
# Process subdir
ADD_SUBDIRECTORY(generated)
It seems that PROTOBUF_GENERATE_CPP can only be used in the same subdirectory. A possible workaround is to invoke protoc directly instead :
FILE(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/src/externals/protodef PROTOMODEL_PATH)
FILE(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/src/generated PROTOBINDING_PATH)
FILE(GLOB DATAMODEL_PROTOS "${CMAKE_CURRENT_SOURCE_DIR}/externals/protodef/*.proto")
FOREACH(proto ${DATAMODEL_PROTOS})
FILE(TO_NATIVE_PATH ${proto} proto_native)
EXECUTE_PROCESS(COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} --proto_path=${PROTOMODEL_PATH} --cpp_out=${PROTOBINDING_PATH} ${proto_native}
RESULT_VARIABLE rv)
# Optional, but that can show the user if something have gone wrong with the proto generation
IF(${rv})
MESSAGE("Generation of data model returned ${rv} for proto ${proto_native}")
ENDIF()
ENDFOREACH(proto)
CMakeLists (src/generated) :
## List generated sources files
FILE(GLOB HDRS "*.h")
FILE(GLOB SRCS "*.cc")
ADD_LIBRARY(protoBinding ${HDRS} ${SRCS})
# ${PROTOBUF_LIBRARIES} should be defined by FIND_PACKAGE(Protobuf REQUIRED)
TARGET_LINK_LIBRARIES(protoBinding ${PROTOBUF_LIBRARIES})
This way CMake will first generate the header/source files, and only then add the generated files to a CMake target.
You can then use protoBinding target to link the generated files to an other target (e.g at the end of src's CMakeLists.txt) :
ADD_LIBRARY(myModel ${myFiles})
TARGET_LINK_LIBRARIES(myModel protoBinding)

Cmake managed C++

I have cli wrapper function which i am trying to configure in cmake. After i generate the project with cmake the generated .proj file does not have the property of clr support is set to no common languaage runtime support. below is my cmake file
# This is the root ITK CMakeLists file.
cmake_minimum_required(VERSION 2.8.9)
if(COMMAND CMAKE_POLICY)
cmake_policy(SET CMP0003 NEW)
endif()
set_target_properties(${TargetName} PROPERTIES COMPILE_FLAGS "/clr")
SET(LINK_LIBRARIES
D:\\2016\\RandomSlicing\\Processing\\lib\\obliquePlane.lib
)
# The header files
SET(HEADERS
ObliquePlaneWrapper.h
obliquePlane.h
)
# The implementation files
SET(SOURCES
ObliquePlaneWrapper.cpp
)
# Find ITK.
find_package(ITK REQUIRED)
include(${ITK_USE_FILE})
# Add this as include directory
INCLUDE_DIRECTORIES(
${CMAKE_SOURCE_DIR}
${SOURCE_PATH}
${VXL_INCLUDE_DIRS}
)
# Main library
#ADD_EXECUTABLE(obliquePlane ${HEADERS} ${SOURCES})
ADD_LIBRARY(ObliquePlaneWrapper SHARED ${HEADERS} ${SOURCES})
TARGET_LINK_LIBRARIES(ObliquePlaneWrapper ${LINK_LIBRARIES} ${ITK_LIBRARIES})
I manually set this property in the All_build project and the corresponding .proj file. When i build the project it is searching for the ObliquePlaneWrapper.dll which it should be generating. Is this a problem because of some flag not set for common language runtime support
You can manually supply Compile Flags to specific sources to be compiled with specific flags. This includes \CLR for Visual C++. See example here.
https://cmake.org/pipermail/cmake/2011-April/043773.html

Integrate CMake subproject with another

I've wrote a C++ library MyLib and I'd like to integrate it with another project ExternPro. So in ExternPro I wrote CMakeLists.txt like this:
add_subdirectory(MyLib)
ADD_EXECUTABLE(test test.cpp)
include_directories(${MyLib_INCLUDE_DIRS})
target_link_libraries(test ${MyLib_LIBRARIES})
To set variables MyLib_LIBRARIES and MyLib_INCLUDE_DIRS I wrote:
set(MyLib_LIBRARIES ${PROJECT_SOURCE_DIR}/src/MyLib.a CACHE INTERNAL "")
set(MyLib_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/include CACHE INTERNAL "")
But something wrong "No rule to make target MyLib/src/MyLib.a, needed by test. Stop."
So my question is, how should I wrote CMakeLists.txt correctly so cmake could help me build MyLib first and then take care of dependencies of ExternPro?
If those are two separate projects, I normally use "CMake find scripts" to reference one library from the other: http://www.vtk.org/Wiki/CMake:How_To_Find_Libraries#Writing_find_modules
But I normally use slightly different find script than described there (FindMyLibrary.cmake):
# Find MyLibrary installation
#
# This module needs following variables specified (e.g. through cmake -Dvar=)
# MyLibrary_ROOT_DIR - root directory of the library installation
#
# This module defines the following variables:
# MyLibrary_INCLUDE_DIRS - Where to find the public headers
# MyLibrary_LIBRARIES - List of mandatory and optional libraries
# MyLibrary_FOUND - True if an installation was found
#
# Configuration variables for tis module:
# MyLibrary_USE_STATIC_LIBS - Set to ON to force the use of the static
# libraries. Default is OFF.
# If MyLibrary_ROOT_DIR was defined in the environment, use it.
if(NOT MyLibrary_ROOT_DIR AND NOT $ENV{MyLibrary_ROOT_DIR} STREQUAL "")
set(MyLibrary_ROOT_DIR $ENV{MyLibrary_ROOT_DIR})
endif()
if(NOT MyLibrary_ROOT_DIR)
set(MyLibrary_ROOT_DIR /usr)
endif()
message(STATUS "Using MyLibrary_ROOT_DIR: ${MyLibrary_ROOT_DIR}")
find_path(MyLibrary_INCLUDE_DIRS
NAMES mylib/mylib.hpp
PATHS ${MyLibrary_ROOT_DIR}
PATH_SUFFIXES include)
# Here we set the default components
if(NOT MyLibrary_FIND_COMPONENTS)
set(MyLibrary_FIND_COMPONENTS mylibrary)
endif()
# Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES
if(MyLibrary_USE_STATIC_LIBS)
set(_mylib_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
if(WIN32)
set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
else()
set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
endif()
endif()
foreach(COMPONENT ${MyLibrary_FIND_COMPONENTS})
find_library(MyLibrary_${COMPONENT}_LIBRARY
NAMES ${COMPONENT}
HINTS ${MyLibrary_ROOT_DIR}
PATH_SUFFIXES lib64 lib
NO_DEFAULT_PATH)
set(MyLibrary_LIBRARIES ${MyLibrary_LIBRARIES} ${MyLibrary_${COMPONENT}_LIBRARY})
endforeach()
# Restore the original find library ordering
if(MyLibrary_USE_STATIC_LIBS)
set(CMAKE_FIND_LIBRARY_SUFFIXES ${_mylib_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES})
endif()
# handle the QUIETLY and REQUIRED arguments and set MyLibrary_FOUND to
# TRUE if all listed variables are TRUE
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
MyLibrary "Could NOT find MyLibrary: set MyLibrary_ROOT_DIR to a proper location"
MyLibrary_LIBRARIES
MyLibrary_INCLUDE_DIRS)
mark_as_advanced(MyLibrary_INCLUDE_DIRS MyLibrary_LIBRARIES)
Then used like this:
find_package(MyLibrary REQUIRED)
include_directories(SYSTEM ${MyLibrary_INCLUDE_DIRS})
target_link_libraries(${TARGET}
${MyLibrary_LIBRARIES}
)
Basically, it goes like this:
The library is built and installed (make install) to either the default location (e.g. /usr), or some other location (usual in development).
The FindMyLibrary.cmake is part of the library installation (for RPM the library devel package) and installs as well (into ${instdir}/share/cmake/Modules, for example).
The dependent project then adds the path to the CMAKE_MODULE_PATH and uses the find script to find the public headers and libraries as installed.
The advantage is that this way you can either use it during the development (when you have the library sources and build the library), or without the library sources as well (with just the library and headers - the devel package - installed in the system).
Similar technique is normally used by Boost etc. (the find scripts provided already by the CMake).
Instead of path to the library, use library target for target_link_libraries.
Assuming your library project contains
add_library(MyLib ...)
Linking of executable in main project should be performed with
target_link_libraries(test MyLib)
First of all this doesn't work because variables set in a subdirectory are not set for the parent directory.
So to solve this properly you should define MyLib like:
add_library(MyLib ...)
target_include_directories(MyLib INTERFACE ${PROJECT_SOURCE_DIR}/include)
And for the ExternPro you just need to link to MyLib:
target_link_libraries(test MyLib)
This will automatically add the include directory to test and link MyLib properly.