Cmake and coding style for common config code - c++

following the structure of my project:
/myproject
CMakeLists.txt (containing "add_subdirectory" and "include_directories" ..)
/src
/lib1
CMakeLists.txt (containing "add_library(lib1 .. )" )
..
/lib2
idem as lib1 ..
/mains
/common
=>config.cpp<=
=>gui.cpp<=
/main1
CMakeLists.txt
main1.cpp
/main2
CMakeLists.txt
main2.cpp
/data
/images_and_different_stuff
/conf
params.cfg
/bin
(output executables)
/release
(cmake build_files)
My question is about the linking of the configuration classes I placed in /common. These classes read the CLI or the configuration file params.cfg and initialize mains objects that are present in both main1.cpp and main2.cpp and main3.cpp ... (one config file for all exes)
For now I have in my /main1's CMakeLists.txt: set(main1_source main1.cpp ../common/config.cpp ..). So the common code is recompile for each exe. What is the best way to do this ? Make a static lib from common files ?

What is the best way to do this ? Make a static lib from common files ?
Exactly. Or use a shared library if you want to cut down your total install size.

Related

how to write cmake with 1 executable and multiple subdirectories

I have a project with many sub-directories that each 1 of them include both header and source files. I want 1 executable which include all my src files.
I managed to write the cmake so it compiles successfully,
but Im dealing with 2 problems, I dont now how to solve:
1 - The compile time is very slow, probably somthing I'm doing wrong.
2 - I have few flags like DEBUG, RELEASE that I couldnt make it define/ undefine in any of the CMakeLists.txt files.
I need this because I have some of my source files :
#ifndef RELEASE /DEBUG #endif
My project stracture looks similar to this:
root/ (project root)
3rd_party/
spdlog/ ...
src/
CMakeLists.txt
main.cpp (contains main method)
logger/
log.c
logger.cpp
logger.hpp
CMakeLists.txt
first/
first_c.h
first.cpp
first.hpp
CMakeLists.txt
second/
second.cpp
second.hpp
CMakeLists.txt
control/
control.cpp
control.hpp
control_2.hpp
CMakeLists.txt
some more sub directories/...
build/
... (executable)
This is part from what I currently have :
in src/CMakeLists.txt:
cmake_minimum_required(VERSION 3.18)
project(integration LANGUAGES CXX)
set(src_main main.cpp)
add_subdirectory(first)
add_subdirectory(second)
add_subdirectory(third)
add_subdirectory(control)
add_subdirectory(logger)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../3rd_party/spdlog ${CMAKE_CURRENT_BINARY_DIR}/spdlog)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
add_executable(${PROJECT_NAME} ${src_main})
target_link_libraries(${PROJECT_NAME} control first second third logger)
target_compile_definitions(${PROJECT_NAME} PUBLIC RELEASE) # not working???
in src/control/CMakeLists.txt:
set(control_source_files
control.cpp
control.hpp
)
add_library(control SHARED ${control_source_files})
find_library(LIB paho-mqtt3c ${PROJECT_SOURCE_DIR}/lib)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(control PUBLIC Threads::Threads)
target_link_libraries(control PUBLIC ${LIB})
target_include_directories(robot_control PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../second)
target_include_directories(robot_control PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../third)
in src/first/CMakeLists.txt:
set(first_source_files
first_c.h
first.cpp
first.hpp
)
add_library(first SHARED ${first_source_files})
target_include_directories(motor PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../second)
target_include_directories(motor PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../logger)
target_include_directories(motor PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../3rd_party/date/include/)
Im not sure if I need target_include_directories I did it becuase in first.hpp there are icludes for headers in those subdirectories.
any help to do the compile time faster and define right the flags ??
1 - The compile time is very slow, probably somthing I'm doing wrong.
There might be many reasons.
Main cause is usually to many includes in header files. Froward declarations in as many places as possible of header files is very effective cure for that issue. There is even a tool which helps to clean up existing project Include What You Use (IWYU).
2 - I have few flags like DEBUG, RELEASE that I couldnt make it
define/ undefine in any of the CMakeLists.txt files. I need this
because I have some of my source files :
target_compile_definitions(${PROJECT_NAME} PUBLIC RELEASE) # not working???
You are doing that wrong. Depending on generator you use (if it supports multiple configuration or not), you can control this in configuration step or in build step, by passing extra parameters:
In case if generator do not have mtuple configurations you configure this this way:
cd build
cmake -DCMAKE_BUILD_TYPE=Debug ..
cmake --build .
See doc
If generator supports multiple configurations then:
cd build
cmake ..
cmake --build . --config Debug
target_include_directories(robot_control PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../second)
target_include_directories(robot_control PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../third)
This is wrong. target_include_directories defines which paths are important for given target (library). Now when something is PUBLIC then any target which will do target_link_libraries(someTarget PUBLIC sourceTarget) where sourceTarget is your library will be impacted by this path. someTarget will inherit include paths from public (or interface) of source target so you don't have to add them explicitly.
So basically this quted lines are obsolete (if second and third targets have this include paths defined as PUBLIC).

Should I use only add_executable() with raw cpp files or make a library via add_library()?

I'm learning CMake and I'm struggling with it a little. My "project" is using JsonCpp "library" that was provided as one .cpp file and two .h files. The structure looks like this:
myProject
build/
json/
CMakeLists.txt
jsoncpp.cpp
include/
json.h
json-forward.h
CMakeLists.txt
main.cpp
build/CMakeLists.txt:
cmake_minimum_required(VERSION 3.6.0)
project(myProject)
add_subdirectory(json)
add_executable(app main.cpp)
target_link_libraries(app PRIVATE json)
# add_executable(app main.cpp json/jsoncpp.cpp json/include/json.h json/include/json-forwards.h)
json/CMakeLists.txt:
cmake_minimum_required(VERSION 3.6.0)
add_library(
json
jsoncpp.cpp
include/json.h
include/json-forwards.h
)
target_include_directories(json PUBLIC '${CMAKE_CURRENT_SOURCE_DIR}/include')
What's a difference between using only add_executable() with all .cpp files and using target_link_libraries that transforms jsoncpp into static library and then link it? What approach should I choose?
A next thing confusing me is a target_include_directories(). What are the benefits using this function? If I comment it, and run cmake (then makefile and launch the app) everything still works fine. If I delete "include/json.h" "include/json-forward.h" from add_library(), everything still works.
What's a difference between using only add_executable() with all .cpp files and using target_link_libraries that transforms jsoncpp into static library and then link it? What approach should I choose?
Using add_library is required when you have 2 executables using the same jsoncpp code. In this case, if you list jsoncpp sources in both add_executable() calls, you'd have to compile it twice. Grouping them into add_library() will make it compile only once and then linked to both executables.
Another reason to use add_library is purely logical composition of modules.

How to copy target files using cmake from major CMakeLists.txt?

As an example suppose four folders(app1, app2, app3 and main) such as below
main
|__ CMakeLists.txt
\__ module1
|______ CMakeLists.txt
|______ sub1.cpp
|______ sub1.h
\__ library5
|______ CMakeLists.txt
|______ sub5.cpp
|______ sub5.h
\__app1
\__app2
\__app3
Which output of module1 is module1.dll and output of library5 is lib5.dll. Folder of app1 must contain module1.dll and lib5.dll, app2 needs lib5.dll and finally app3 needs module1.dll(number of apps, modules and libs are more than this example and as I explain below we don't want to change modules/libraries's CMakeLists.txt, just main's CMakeLists.txt is ours).
PS:
I have a cmake project which has several libraries and modules. They included in my project using add_subdirectory command (note that my project just made up from multiple modules and it has not any add_library or add_target).
I need to copy outputs of libraries/modules without changing their CMakeLists.txt (add_custom_command with POST_BUILD option actually is not a good choice because at this point I need to change CMakeLists.txt of libraries/modules which they are not just belong to my project). On the other hand it must done in outer(major) CMakeLists.txt which has others(libraries/modules).
I tried some other commands such as file (COPY ) and configure_file() but I think they operate in generating cmake-cache phase and just can copy resource files which are exist in pre-build phase.
Moreover, In another approach I write a bash script file to copy the files and call it in major CMakeLists.txt via bellow command.
add_custom_target (copy_all
COMMAND ${CMAKE_SOURCE_DIR}/copy.sh ${files}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
The files has the list of files. But the copy not performed! I manually test the script which works as desired. But I don't have any idea why it can not operate at call in CMakeLists.txt.
What can I do to copy sub-projects outputs to some locations from major CMakeLists.txt?
The Setup
To simplify it a little, let's say you have:
CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(PostBuildCopyFromRoot)
add_subdirectory(module)
module/CMakeLists.txt
file(WRITE "module.h" "int ModuleFunc();")
file(WRITE "module.cpp" "int ModuleFunc() { return 1; }")
add_library(module SHARED "module.cpp" "module.h")
target_include_directories(module PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
set_target_properties(module PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS 1)
app/app.mexw64
The Problem
If you now just add to following to the root CMakeLists.txt:
add_custom_command(
TARGET module
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"$<TARGET_FILE:module>"
"app/$<TARGET_FILE_NAME:module>"
)
You will get from CMake:
CMake Warning (dev) at CMakeLists.txt:8 (add_custom_command):
Policy CMP0040 is not set: The target in the TARGET signature of
add_custom_command() must exist. Run "cmake --help-policy CMP0040" for
policy details. Use the cmake_policy command to set the policy and
suppress this warning.
TARGET 'module' was not created in this directory.
Solutions
You can always overwrite command behaviors:
function(add_library _target)
_add_library(${_target} ${ARGN})
add_custom_command(
TARGET ${_target}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"$<TARGET_FILE:${_target}>"
"${CMAKE_SOURCE_DIR}/app/$<TARGET_FILE_NAME:${_target}>"
)
endfunction()
NOTE: Put the code snippets before the add_subdirectory() call
References
Copying executable and DLLs to User specified location using CMake in windows
Parent CMakeLists.txt overwriting child CMakeLists.txt output directory options
Proper usage of CMAKE_*_OUTPUT_DIRECTORY
Is there a way to include and link external libraries throughout my project only editing my top level CMakeList?

Execute output of cmake target as dependency for another

I have the following directory structure:
.
├── CMakeLists.txt
├── generator
│   ├── CMakeLists.txt
│   └── main.cpp
├── include
└── src
├── CMakeLists.txt
└── mylib.cpp
I would like to build generator, then use generator to generate a source file that will be used to build mylib. I tried this:
generator/CMakeLists.txt:
add_executable(gen main.cpp)
add_custom_command(
OUTPUT
${CMAKE_BINARY_DIR}/generated.cpp
DEPENDS
gen
COMMAND
${CMAKE_BINARY_DIR}/gen -d /tmp
VERBATIM
)
src/CMakeLists.txt:
add_library(py-cppast
${CMAKE_BINARY_DIR}/generated.cpp
mylib.cpp)
CMakeLists.txt:
cmake_minimum_required(VERSION 3.1.2)
project(my-project)
add_subdirectory(generator)
add_subdirectory(src)
However, the command is not executed. Instead, I just get the error:
CMake Error at src/CMakeLists.txt:2 (add_library):
Cannot find source file:
/home/....(the cmake binary dir)..../generated.cpp
Tried extensions .c .C .c++ .cc .cpp .cxx .m .M .mm .h .hh .h++ .hm .hpp
.hxx .in .txx
How can I tell cmake to execute the program I'm building with it? Is this even possible in a single build?
The problem comes from the fact that you are generating the file generated.cpp in one directory and then trying to add it to a target defined in a different directory. CMake only supports adding generated files to targets defined in the same directory scope. The add_custom_command() documentation explicitly mentions this restriction.
You probably want to move the generation of generated.cpp into the src directory. You should also use just the gen target name to refer to the executable to run, not ${CMAKE_BINARY_DIR}/gen which is not going to be correct with all CMake generator types. It would also be better style to use the current binary directory rather than the top level binary directory as the output dir. Your src/CMakeLists.txt should look something like this:
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated.cpp
DEPENDS gen
COMMAND gen -d /tmp
VERBATIM
)
add_library(py-cppast
${CMAKE_CURRENT_BINARY_DIR}/generated.cpp
mylib.cpp
)
CMake will automatically substitute the location of the gen target for you, even though it was defined in a different directory. There are some further subtleties to be aware of when using generated sources, particularly relating to dependencies. You may find this article helpful to fill in some gaps.

Cmake configuration for multiple sub-libraries in a "packages" directory

Here is a sample project I am trying to build with a "Packages" directory which includes all the libraries to be used in the main code.
I am trying to keep my root cmake file as clean as possible and avoid relative path such as
include_directory(packages/lib1)
but I am struggling. Is there a way of including sub-directories of a directory for the purposes of header inclusion.
First a few minor remarks:
always name the CMake configuration files CMakeLists.txt (because of)
bookmark the documentation on CMake: https://cmake.org/documentation/
Sometimes it's not that easy to read, but very specific once you adopt your head to the "CMake world" ;-)
make yourself comfortable with the scope of CMake variables
include_directories(DIR1 [DIR2 [...]])
Tells CMake where the compiler should look for header files, i.e. -IDIR1 -IDIR2 ....
add_library(NAME [STATIC|SHARED] SOURCES)
This command creates the required compiler commands to create a static or shared library out of a given list of source files. No need to add in the header files. The make target will be called NAME and the library target is known to CMake as NAME.
add_subdirectory(DIR)
Tells CMake to look into DIR and parse the included CMakeLists.txt with all its content.
target_link_libraries(TARGET LIB1 [LIB2 [...]])
Tells CMake to instruct the linker to link LIB1, LIB2, etc. to the TARGET, i.e. -LLIB1 -LLIB2 .... TARGET is a CMake/make target previously defined/created with a call to add_{library,executable,custom_target}.
CMakeLists.txt:
include_directories(libraries)
# a header file in `libraries/lib1/foo.hpp` can be included
# in the whole CMake project by `#include "lib1/foo.hpp"`.
add_subdirectory(libraries)
add_subdirectory(tests)
libraries/CMakeLists.txt:
add_subdirectory(lib1)
add_subdirectory(lib2)
libraries/lib1/CMakeLists.txt:
add_library(lib1 STATIC ${LIB1_SOURCES})
libraries/lib2/CMakeLists.txt:
add_library(lib2 STATIC ${LIB2_SOURCES})
tests/CMakeLists.txt:
add_executable(tests ${TEST_SOURCES})
target_link_libraries(tests lib1 lib2)