How to specify a dependency graph when generating files with CMake? - c++

I'm integrating protobuf files compilation in my project's CMake-based build process. Currently I'm using the following CMake script:
set (TARGET_NAME ProtobufMessages)
file (GLOB_RECURSE PROTOBUF_FILES "protobuf/*.proto")
set (HEADER_FILES "")
set (SOURCE_FILES "")
foreach (FILE ${PROTOBUF_FILES})
string (REPLACE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} FILE ${FILE})
string (REPLACE ".proto" ".pb.h" HEADER_FILE ${FILE})
string (REPLACE ".proto" ".pb.cc" SOURCE_FILE ${FILE})
list (APPEND HEADER_FILES ${HEADER_FILE})
list (APPEND SOURCE_FILES ${SOURCE_FILE})
endforeach ()
add_library(${TARGET_NAME} STATIC
${HEADER_FILES}
${SOURCE_FILES}
${PROTOBUF_FILES}
)
set_target_properties (${TARGET_NAME} PROPERTIES
FOLDER ${COMMON_PROJECTS_FOLDER_NAME})
set (PROTOBUF_COMPILE_COMMAND
${CONAN_BIN_DIRS_PROTOBUF_RELEASE}/protoc
--cpp_out=${CMAKE_CURRENT_BINARY_DIR}/protobuf
-I${CMAKE_CURRENT_SOURCE_DIR}/protobuf
${PROTOBUF_FILES}
)
add_custom_command(OUTPUT ${HEADER_FILES} ${SOURCE_FILES}
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/protobuf
COMMAND ${PROTOBUF_COMPILE_COMMAND}
DEPENDS ${PROTOBUF_FILES}
COMMENT "Compiling protocol buffer files ..."
)
source_group (TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES
${PROTOBUF_FILES}
)
set_target_properties (${TARGET_NAME} PROPERTIES
FOLDER ${COMMON_PROJECTS_FOLDER_NAME}
)
target_link_libraries (${TARGET_NAME}
CONAN_PKG::protobuf
)
It works fine, but the problem with this approach is that when a single proto file is changed all the files are regenerated.
A simple modification of this approach with calling add_custom_command in a loop partially solves the problem:
set (PROTOBUF_COMPILE_COMMAND
${CONAN_BIN_DIRS_PROTOBUF_RELEASE}/protoc
--cpp_out=${CMAKE_CURRENT_BINARY_DIR}/protobuf
-I${CMAKE_CURRENT_SOURCE_DIR}/protobuf
)
foreach (PROTOBUF_FILE ${PROTOBUF_FILES})
string (REPLACE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} TEMP_FILE ${PROTOBUF_FILE})
string (REPLACE ".proto" ".pb.h" HEADER_FILE ${TEMP_FILE})
string (REPLACE ".proto" ".pb.cc" SOURCE_FILE ${TEMP_FILE})
add_custom_command(OUTPUT ${HEADER_FILE} ${SOURCE_FILE}
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/protobuf
COMMAND ${PROTOBUF_COMPILE_COMMAND} ${PROTOBUF_FILE}
DEPENDS ${PROTOBUF_FILE}
COMMENT "Compiling ${PROTOBUF_FILE} ..."
)
endforeach ()
But there is still a problem, because protobuf supports including one protobuf file into another:
import "myproject/base.proto";
In this case when base.proto is changed the files including it also must be recompiled. Actually the dependencies can form an arbitrary DAG. Although the current problem is arising specifically with protobuf files compilation this is relevant to all code generation steps when some of the input files can depend on some of the other ones.
Is it possible to solve this problem with CMake?

Related

How do file paths and including files/directories with CMake work? [duplicate]

Copying directory from source tree to binary tree. For example: How to copy www to bin folder.
work
├─bin
└─src
├─doing
│ └─www
├─include
└─lib
Thanks.
Since version 2.8, the file command has a COPY sub-command:
file(COPY yourDir DESTINATION yourDestination)
Note that:
Relative input paths are evaluated with respect to the current source
directory, and a relative destination is evaluated with respect to the
current build directory
With CMake 2.8 or later, use the file(COPY ...) command.
With CMake versions below 2.8, the following macro copies files from one directory to another. If you don't want to substitute variables in the copied files, then change the configure_file #ONLY argument (for example to COPYONLY).
# Copy files from source directory to destination directory, substituting any
# variables. Create destination directory if it does not exist.
macro(configure_files srcDir destDir)
message(STATUS "Configuring directory ${destDir}")
make_directory(${destDir})
file(GLOB templateFiles RELATIVE ${srcDir} "${srcDir}/*")
foreach(templateFile ${templateFiles})
set(srcTemplatePath ${srcDir}/${templateFile})
if(NOT IS_DIRECTORY ${srcTemplatePath})
message(STATUS "Configuring file ${templateFile}")
configure_file(
${srcTemplatePath}
${destDir}/${templateFile}
#ONLY)
endif(NOT IS_DIRECTORY ${srcTemplatePath})
endforeach(templateFile)
endmacro(configure_files)
As nobody has mentioned cmake -E copy_directory as a custom target, here's what I've used:
add_custom_target(copy-runtime-files ALL
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/runtime-files-dir ${CMAKE_BINARY_DIR}/runtime-files-dir
DEPENDS ${MY_TARGET})
The configure command will only copy files when cmake is run. Another option is to create a new target, and use the custom_command option. Here's one that I use (if you run it more than once, you'll have to modify the add_custom_target line to make it unique for each call).
macro(copy_files GLOBPAT DESTINATION)
file(GLOB COPY_FILES
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
${GLOBPAT})
add_custom_target(copy ALL
COMMENT "Copying files: ${GLOBPAT}")
foreach(FILENAME ${COPY_FILES})
set(SRC "${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}")
set(DST "${DESTINATION}/${FILENAME}")
add_custom_command(
TARGET copy
COMMAND ${CMAKE_COMMAND} -E copy ${SRC} ${DST}
)
endforeach(FILENAME)
endmacro(copy_files)
Use execute_process and call cmake -E. If you want a deep copy, you can use the copy_directory command. Even better, you could create a symlink (if your platform supports it) with the create_symlink command. The latter can be achieved like this:
execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_SOURCE_DIR}/path/to/www
${CMAKE_BINARY_DIR}/path/to/www)
From: http://www.cmake.org/pipermail/cmake/2009-March/028299.html
Thank! That is really helpful advice to use bunch of add_custom_target and add_custom_command. I wrote the following function to use everywhere in my projects. Is also specifies the installation rule. I use it primarily to export interface header files.
#
# export file: copy it to the build tree on every build invocation and add rule for installation
#
function (cm_export_file FILE DEST)
if (NOT TARGET export-files)
add_custom_target(export-files ALL COMMENT "Exporting files into build tree")
endif (NOT TARGET export-files)
get_filename_component(FILENAME "${FILE}" NAME)
add_custom_command(TARGET export-files COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/${FILE}" "${CMAKE_CURRENT_BINARY_DIR}/${DEST}/${FILENAME}")
install(FILES "${FILE}" DESTINATION "${DEST}")
endfunction (cm_export_file)
Usage looks like this:
cm_export_file("API/someHeader0.hpp" "include/API/")
cm_export_file("API/someHeader1.hpp" "include/API/")
Based on the answer from Seth Johnson; wrote for more convenience:
# Copy files
macro(resource_files files)
foreach(file ${files})
message(STATUS "Copying resource ${file}")
file(COPY ${file} DESTINATION ${Work_Directory})
endforeach()
endmacro()
# Copy directories
macro(resource_dirs dirs)
foreach(dir ${dirs})
# Replace / at the end of the path (copy dir content VS copy dir)
string(REGEX REPLACE "/+$" "" dirclean "${dir}")
message(STATUS "Copying resource ${dirclean}")
file(COPY ${dirclean} DESTINATION ${Work_Directory})
endforeach()
endmacro()

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.

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)

How to properly use file GLOB?

I'm trying to use GLOB_RECURSE to specify my sources and headers files. Currently, my CMakeLists.txt for a static library is:
project(LinearSystemLib)
file(GLOB_RECURSE ${PROJECT_NAME}_headers ${PROJECT_SOURCE_DIR}/*.h)
file(GLOB_RECURSE ${PROJECT_NAME}_sources ${PROJECT_SOURCE_DIR}/*.cpp)
add_library(
${PROJECT_NAME} STATIC ${${PROJECT_NAME}_headers}
${${PROJECT_NAME}_sources}
)
install(
TARGETS ${PROJECT_NAME}
LIBRARY DESTINATION libs
ARCHIVE DESTINATION archives
)
The library directory looks like this:
LinearSystemLib
CMakeLists.txt
source
LinearSystemLib.cpp
include
LinearSystemLib.h
When I run command cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug (in the build directory) everything goes ok. Yet, command make it displays the following:
/home/felipe/Documents/Dados/SINMEC/Eclipse/LinearSystemLib/source/LinearSystemLib.cpp:1:29: fatal error: LinearSystemLib.h: No such file or directory
Is my CMakeLists wrong? I don't want to set specify the sources and headers files by name. I'm not finding information about glob_recurse easily.
I can make it work by listing the sources and headers files by name. However, it MUST be done with the glob_recurse or with glob.
I solved my problem, here's what LinearSystemLib directory looks like:
LinearSystemLib
CMakeLists.txt
source
LinearSystemLib.cpp
include
LinearSystemLib.h
The CMakeLists.txt contains:
project(LinearSystemLib)
#INCLUDE DIRECTORIES
include_directories(${CMAKE_SOURCE_DIR}/${PROJECT_NAME}/include)
#SEARCH FOR .CPP AND .H FILES
file(GLOB ${PROJECT_NAME}_headers ${CMAKE_SOURCE_DIR}/${PROJECT_NAME}/include/*.h)
file(GLOB ${PROJECT_NAME}_sources ${CMAKE_SOURCE_DIR}/${PROJECT_NAME}/source/*.cpp)
#ADD LIBRARY
add_library(${PROJECT_NAME} STATIC ${${PROJECT_NAME}_sources})
#DEFINE OUTPUT LOCATION
install(
TARGETS ${PROJECT_NAME}
ARCHIVE DESTINATION static_libs
)
You don't actually NEED to add the .h/.hpp files using GLOB. I did it because otherwise, Visual Studio (or CodeBlocks) wouldn't create a "Header Files" folder on the project menu.
I wasn't properly specifying the path where GLOB would find the files.
${CMAKE_SOURCE_DIR}/${PROJECT_NAME}/source/
You need to add
target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR})
so the correct -I argument is added to your compilation step. Use make VERBOSE=1 to see exactly what commands make is executing.

Create Qt translation files with CMake

I'm having a problem when i'm trying to add the process of generating the translations inside the CMake process.
Now i have the following CMakeLists.txt:
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/defines.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/defines.h)
file(GLOB_RECURSE UI_FILES *.ui)
file(GLOB_RECURSE CODE_FILES *.cpp)
qt5_wrap_ui(UI_HEADERS ${UI_FILES})
# Qt5LinguistTools
find_package(Qt5LinguistTools)
FILE(GLOB TS_FILES "${CMAKE_CURRENT_SOURCE_DIR}/../resources/langs/*.ts")
QT5_create_translation(QM_FILES ${CODE_FILES} ${TS_FILES})
# Resources
qt5_add_resources(RESOURCE_FILES ../resources/resources.qrc)
# Windows application icon
if (WIN32)
set(WINDOWS_RES_FILE ${CMAKE_CURRENT_BINARY_DIR}/resources.obj)
if (MSVC)
add_custom_command(OUTPUT ${WINDOWS_RES_FILE}
COMMAND rc.exe /fo ${WINDOWS_RES_FILE} resources.rc
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/cmake/win
)
else()
add_custom_command(OUTPUT ${WINDOWS_RES_FILE}
COMMAND windres.exe resources.rc ${WINDOWS_RES_FILE}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/cmake/win
)
endif()
endif()
#Move Qm files to resources langs folder
install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_PREFIX}/../resources/langs)
add_executable(${CMAKE_PROJECT_NAME} WIN32
${QM_FILES}
${UI_HEADERS}
${CODE_FILES}
${RESOURCE_FILES}
${WINDOWS_RES_FILE}
)
target_link_libraries(${CMAKE_PROJECT_NAME}
Qt5::Widgets
QtAwesome
)
if (UNIX)
install(TARGETS ${CMAKE_PROJECT_NAME}
RUNTIME DESTINATION bin)
elseif (WIN32)
install(TARGETS ${CMAKE_PROJECT_NAME}
DESTINATION .)
endif()
It seems that this code generates correctly the .qm files, but not before being read by the resources file. So I'm getting an error: NMAKE : fatal error U1073: don't know how to make '..\..\..\resources\langs\lang_en_US.qm'
Maybe I'm not doing that on the right way or i need to read the language files from other place that's not inside the resources file.
Could you provide me an advice on how to generate the QM files and adding them on the build process.
From the documentation:
Updating the translations can be done by adding the qm_files to the source list of your library/executable, so they are always updated, or by adding a custom target to control when they get updated/generated.
You can create custom targets and add dependencies:
add_custom_target(translations ALL DEPENDS ${QM_FILES})
add_custom_target(resources ALL DEPENDS ${RESOURCE_FILES})
add_dependencies(resources translations)
add_executable(${CMAKE_PROJECT_NAME} WIN32
${UI_HEADERS}
${CODE_FILES}
${RESOURCE_FILES}
${WINDOWS_RES_FILE}
)
add_dependencies(${CMAKE_PROJECT_NAME} resources)
This is what I'm doing for QOwnNotes: https://github.com/pbek/QOwnNotes/blob/develop/src/CMakeLists.txt
# Translation files
SET(QON_TS_FILES
languages/QOwnNotes_en.ts
languages/QOwnNotes_de.ts
languages/QOwnNotes_fr.ts
languages/QOwnNotes_pl.ts
languages/QOwnNotes_zh.ts
languages/QOwnNotes_ru.ts
languages/QOwnNotes_pt.ts
languages/QOwnNotes_es.ts
languages/QOwnNotes_nl.ts
languages/QOwnNotes_hu.ts
languages/QOwnNotes_ja.ts
languages/QOwnNotes_it.ts
languages/QOwnNotes_ar.ts
)
qt5_add_translation(QON_QM_FILES ${QON_TS_FILES})
add_custom_target(translations DEPENDS ${QON_QM_FILES})
if(NOT QT_TRANSLATIONS_DIR)
# If this directory is missing, we are in a Qt5 environment.
# Extract the qmake executable location
get_target_property(QT5_QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION)
# Ask Qt5 where to put the translations
execute_process( COMMAND ${QT5_QMAKE_EXECUTABLE} -query QT_INSTALL_TRANSLATIONS
OUTPUT_VARIABLE qt_translations_dir OUTPUT_STRIP_TRAILING_WHITESPACE )
# make sure we have / and not \ as qmake gives on windows
file( TO_CMAKE_PATH "${qt_translations_dir}" qt_translations_dir)
set( QT_TRANSLATIONS_DIR ${qt_translations_dir} CACHE PATH
"The location of the Qt translations" FORCE)
endif()
install(FILES ${QON_QM_FILES}
DESTINATION ${QT_TRANSLATIONS_DIR})
add_executable(QOwnNotes ${SOURCE_FILES} ${RESOURCE_ADDED} ${QON_QM_FILES})
Does this help you?