Best practice to run a prebuild step with CMake - c++

Suppose you have a repository with a folder (named dataset) with several .csv files and a python script (named csvcut.py) that takes all the .csv in dataset and generates corresponding .h files.
Those .h files are included in some .cpp files to build an executable (add_executable(testlib...) used for testing.
Suppose you use add_custom_target(test_pattern... to make a target (named test_pattern) that runs csvcut.py, and add_dependencies(testlib test_pattern) to run the script before building testlib.
This works, but it would be better if:
the script was run only when the files in dataset folder or the script itself changes (not when .cpp changes);
the .h files was generated in a subfolder of the build folder (i.e. build/tests/dataset/), and included in the .cpp files like so #include <tests/dataset/generated.h>.
Do you have any suggestions for making these improvements / optimizations?
Thanks, Alberto

This requires multiple steps, but can all be handled with standard CMake. First, we'll use add_custom_command to actually generate the files. I'm also adding a custom target, but only since I couldn't figure out how to make an INTERFACE library work without it.
add_custom_command(
OUTPUT
"${CMAKE_CURRENT_BINARY_DIR}/include/foo.h"
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/gen.py"
DEPENDS
gen.py
foo.h.in
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include"
)
add_custom_target(gen_files
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/include/foo.h"
)
For my case, gen.py just spits out a basic header file, but it shouldn't matter. List whatever files you need as output, and your csv file should be under DEPENDS (for me, foo.h.in tries to simulate this).
Since you only mentioned generating header files, I created an INTERFACE library that depends on the gen_files target. I also added the appropriate include directory.
add_library(foo INTERFACE)
target_include_directories(foo
INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/include"
)
add_dependencies(foo
gen_files
)
If building a STATIC/SHARED library, I was able to add the generated files directly as sources and dependencies worked, but the INTERFACE library required the extra target (even when I tried listing the files under add_dependencies). Since you already have a custom target, I assume this won't be a huge issue.
Lastly, I have an executable that links against foo.
add_executable(main
main.c
)
target_link_libraries(main
PRIVATE
foo
)
Demo:
$ make clean
$ ls
CMakeCache.txt CMakeFiles cmake_install.cmake include Makefile
$ make
[ 33%] Generating include/foo.h
[ 33%] Built target gen_files
[ 66%] Building C object CMakeFiles/main.dir/main.c.o
[100%] Linking C executable main
[100%] Built target main
$ make clean
$ make main
[ 33%] Generating include/foo.h
[ 33%] Built target gen_files
[ 66%] Building C object CMakeFiles/main.dir/main.c.o
[100%] Linking C executable main
[100%] Built target main
$ make
[ 33%] Built target gen_files
[100%] Built target main
$ touch ../foo.h.in
$ make
[ 33%] Generating include/foo.h
[ 33%] Built target gen_files
[100%] Built target main
$ touch ../gen.py
$ make
[ 33%] Generating include/foo.h
[ 33%] Built target gen_files
[100%] Built target main
$ ls include/
foo.h
If either the input (foo.h.in) or generation script (gen.py) change, the targets are rebuilt.

Related

I would like to clarify how to connect shared libs properly

Let's take a real example - I have two native android lib modules (CMake). First module name is - libtetdecoder (further here - A) and another one is - libtetplayer(further here - B). So, lib A use opencv and lib B use lib A. Then, opencv is shared lib and it means that in order to make it work in lib A I need to include headers as well as .so files.
I do it in CMakeList.txt this way
...
include_directories(${pathToOpenCv}/sdk/native/jni/include)
add_library(lib_opencv SHARED IMPORTED)
set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION
${pathToOpenCv}/sdk/native/libs/${CMAKE_ANDROID_ARCH_ABI}/libopencv_java3.so)
...
As I told above lib B use lib A (but not opencv), so chain looks like this B -> A -> opencv(shared). Question is - if I need in this case include opencv configuration(CMake) in lib B? Because as far as I understood if lib B is going to use lib A, so it means that lib B should implement all the dependencies the same as lib A.
Why I ask, because today I have this implementation in lib A and B, if I try to clean up lib B CMakeList.txt and remove opencv config line it doesn't work.
So, question is - if I really need to include this opencv dependency in all derivative libs?
If you are linking it all via target_link_libraries and link them PUBLICly, dependencies should be transitive, look here.
Directory structure:
- src
-- CMakeLists.txt
-- ecu.cpp
-- engine_ecu.cpp
-- ...
- test
-- CMakeLists.txt
-- integration_test.cpp
src/CMakeLists.txt:
project(EngineController)
add_library(Lib1 SHARED
ecu.cpp
)
add_library(Lib2 SHARED
engine_ecu.cpp
load_detector.cpp
math_helper.cpp
temperature_sensor.cpp
)
target_link_libraries(Lib1
PUBLIC
EngineControllerApi
)
target_link_libraries(Lib2
PUBLIC
EngineControllerApi
Lib1
)
test/CMakeLists.txt:
project(EngineControllerTest)
add_executable(${PROJECT_NAME}
integration_test.cpp
)
target_link_libraries(${PROJECT_NAME}
PUBLIC
Lib2
)
yields the following:
-- Build files have been written to: /diaggen/example/engine_controller/build
[ 11%] Building CXX object src/CMakeFiles/Lib1.dir/ecu.cpp.o
[ 22%] Linking CXX shared library libLib1.so
[ 22%] Built target Lib1
[ 33%] Building CXX object src/CMakeFiles/Lib2.dir/engine_ecu.cpp.o
[ 44%] Building CXX object src/CMakeFiles/Lib2.dir/load_detector.cpp.o
[ 55%] Building CXX object src/CMakeFiles/Lib2.dir/math_helper.cpp.o
[ 66%] Building CXX object src/CMakeFiles/Lib2.dir/temperature_sensor.cpp.o
[ 77%] Linking CXX shared library libLib2.so
[ 77%] Built target Lib2
[ 88%] Building CXX object test/CMakeFiles/EngineControllerTest.dir/integration_test.cpp.o
[100%] Linking CXX executable EngineControllerTest
[100%] Built target EngineControllerTest
I believe CMake should figure it out. First of all, make sure that you call find_package prior to referencing OpenCV at all. I believe that you could stick to target_link_libraries without having to alter PROPERTIES the way you do. Under the hood, I believe that target_link_libraries also modifies some PROPERTIES, but I believe it's much clearer this way.

Linking and UIC order in a CMake Qt project

Talking about a Qt 5.3.2 project which is buildt using cmake.
This is a calling order problem between UIC execution and target_link_libraries... unfortunately not in that order.
Below this text you will find
1.) a (still functional) excerpt section of my CMakeLists.txt and
2.) an excerpt of the the output of the command 'cmake .'
3.) the output of a following call to 'make' without using the
generated headers like 'ui_main.h'.
If (in the source of my library libqt.a) I require 'ui_main.h' the
make process crashes not finding the header. Looking at the
non-crashing make output shows why:
[..]
Scanning dependencies of target qt
[ 29%] Building CXX object CMakeFiles/qt.dir/home/kochmn/projects/sentinel/src/qt/form_main.cpp.o
[ 35%] Building CXX object CMakeFiles/qt.dir/qt_automoc.cpp.o
Linking CXX static library libqt.a
[..]
[ 52%] Generating ui_main.h
[..]
Make would generate libqt.a before generating the required header file.
So I experimented using code like
target_link_libraries(sentinel
${Qt5Widgets_LIBRARIES} ${Qt5Gui_LIBRARIES} ${Qt5Core_LIBRARIES})
add_library(optimization "${DIR_SRC}/optimization/linalg.cpp")
add_library(qt "${DIR_SRC}/qt/form_main.cpp")
target_link_libraries(sentinel qt optimization)
to no avail. The question: How can I motivate cmake to first run UIC generating the ui-header files and then compiling my libqt.a?
Appendix
# 2.8.11 instead of 2.8 required for automatic linking to the qtmain.lib
# library if this ever should expand to Windows.
# (http://doc.qt.io/qt-5/cmake-manual.html)
cmake_minimum_required(VERSION 2.8.11)
project(sentinel)
set( CMAKE_AUTOMOC ON )
# CMake uses uic in order to generate header files from .ui forms from designer.
set ( CMAKE AUTOUIC ON )
# Auto-generating functions write their headers into the build directory.
# Hence the build directory should be included.
set( CMAKE_INCLUDE_CURRENT_DIR ON )
#> Getting the Qt essentials. ----------------------------------------
# Widgets finds its own dependencies (QtGui and QtCore).
find_package(Qt5Widgets REQUIRED)
message ("Found Qt5Widgets Version ${Qt5Widgets_VERSION_STRING}")
# All those darling variables are explained here:
# http://doc.qt.io/qt-5/cmake-manual.html
message("Core FOUND: ${Qt5Core_FOUND}")
message("Gui FOUND: ${Qt5Gui_FOUND}")
message("Widgets FOUND: ${Qt5Widgets_FOUND}")
message("Core VERSION: ${Qt5Core_VERSION_STRING}")
message("Gui VERSION: ${Qt5Gui_VERSION_STRING}")
message("Widgets VERSION: ${Qt5Widgets_VERSION_STRING}")
message("Core INCLUDE: ${Qt5Core_INCLUDE_DIRS}")
message("Gui INCLUDE: ${Qt5Gui_INCLUDE_DIRS}")
message("Widgets INCLUDE: ${Qt5Widgets_INCLUDE_DIRS}")
message("Core LIBRARIES: ${Qt5Core_LIBRARIES}")
message("Gui LIBRARIES: ${Qt5Gui_LIBRARIES}")
message("Widgets LIBRARIES: ${Qt5Widgets_LIBRARIES}")
message("Core DEFINITIONS: ${Qt5Core_DEFINITIONS}")
message("Gui DEFINITIONS: ${Qt5Gui_DEFINITIONS}")
message("Widgets DEFINITIONS: ${Qt5Widgets_DEFINITIONS}")
message("Core COMPILE_DEFINITIONS: ${Qt5Core_COMPILE_DEFINITIONS}")
message("Gui COMPILE_DEFINITIONS: ${Qt5Gui_COMPILE_DEFINITIONS}")
message("Widgets COMPILE_DEFINITIONS: ${Qt5Widgets_COMPILE_DEFINITIONS}")
message("Core EXECUTABLE_COMPILE_FLAGS: ${Qt5Core_EXECUTABLE_COMPILE_FLAGS}")
message("Gui EXECUTABLE_COMPILE_FLAGS: ${Qt5Gui_EXECUTABLE_COMPILE_FLAGS}")
message("Widgets EXECUTABLE_COMPILE_FLAGS: ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
include_directories(
${Qt5Widgets_INCLUDE_DIRS} ${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS})
add_definitions(${Qt5Widgets_DEFINITIONS})
#add_definitions(${Qt5Core_DEFINITIONS}) # Unnecessary. In Widgets.
#add_definitions(${Qt5Gui_DEFINITIONS}) # Unnecessary. In Widgets.
#< -------------------------------------------------------------------
set (DEBUG 1)
set (SENTINEL_NAME "Sentinel GL")
set (SENTINEL_VERSION_MAJOR "0")
set (SENTINEL_VERSION_MINOR "1")
set (SENTINEL_VERSION "${SENTINEL_VERSION_MAJOR}.${SENTINEL_VERSION_MINOR}")
## Compiler flags
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
# ${Qt5Core_EXECUTABLE_COMPILE_FLAGS} ${Qt5Gui_EXECUTABLE_COMPILE_FLAGS} #<-- redundant.
if(CMAKE_COMPILER_IS_GNUCXX)
message("Using GnuCXX compiler.")
add_definitions("-O0 -std=c++0x -lSOIL -llapacke -lblas")
endif()
if (DEBUG MATCHES 1)
message("\nBuilding DEBUG build.")
add_definitions(-Wall)
set(CMAKE_BUILD_TYPE Debug)
endif()
set(DIR_BASE "${PROJECT_SOURCE_DIR}/..")
set(DIR_SRC "${PROJECT_SOURCE_DIR}/../src")
set(DIR_RES "${PROJECT_SOURCE_DIR}/../resources")
set(DIR_BUILD "${PROJECT_SOURCE_DIR}/../build")
# Generated using uic FormMain.ui > ui_FormMain.h
set(qt_H
"${DIR_BUILD}/ui_main.h" "${DIR_BUILD}/ui_dialog_setup_game.h")
# Generated using the trusty QtDesigner.
set(qt_UI
"${DIR_SRC}/ui/main.ui" "${DIR_SRC}/ui/dialog_setup_game.ui")
# My own hand-written XML describing the internal resources.
set(qt_QRC "${DIR_RES}/application.qrc")
# generate rules for building source files that moc generates
QT5_WRAP_CPP(qt_H_MOC ${qt_H})
# generate rules for building header files from the ui files
QT5_WRAP_UI(qt_UI_H ${qt_UI})
# Resource Handling. QRC: "Qt Resource Collection"
QT5_ADD_RESOURCES(qt_RCCS ${qt_QRC})
# btw.: rcc generates a C program from ./resources/application.qrc
# However, this is not needed. cmake sees to that. :-)
#< -------------------------------------------------------------------
include_directories("${DIR_SRC}/include" "${PROJECT_SOURCE_DIR}")
add_executable(sentinel "${DIR_SRC}/sentinel.cpp" ${qt_H_MOC} ${qt_UI_H} ${qt_RCCS})
# Available modules are listed here: http://doc.qt.io/qt-5/qtmodules.html
# find /usr/lib/x86_64-linux-gnu/cmake -iname "*.cmake*" | less
# Note: http://stackoverflow.com/questions/20266235/cmake-error-qglwidget-no-such-file-or-directory
qt5_use_modules(sentinel Widgets Gui Core)
add_library(optimization "${DIR_SRC}/optimization/linalg.cpp")
add_library(qt "${DIR_SRC}/qt/form_main.cpp")
target_link_libraries(sentinel
${Qt5Widgets_LIBRARIES} ${Qt5Gui_LIBRARIES} ${Qt5Core_LIBRARIES}
qt optimization
)
kochmn#Ulyss:~/projects/sentinel/build$ cmake .
-- The C compiler identification is GNU 4.9.2
-- The CXX compiler identification is GNU 4.9.2
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
Found Qt5Widgets Version 5.3.2
Core FOUND: 1
Gui FOUND: 1
Widgets FOUND: 1
Core VERSION: 5.3.2
Gui VERSION: 5.3.2
Widgets VERSION: 5.3.2
Core INCLUDE: /usr/include/x86_64-linux-gnu/qt5/;/usr/include/x86_64-linux-gnu/qt5/QtCore;/usr/lib/x86_64-linux-gnu/qt5//mkspecs/linux-g++-64
Gui INCLUDE: /usr/include/x86_64-linux-gnu/qt5/;/usr/include/x86_64-linux-gnu/qt5/QtGui;/usr/include/x86_64-linux-gnu/qt5/QtCore;/usr/lib/x86_64-linux-gnu/qt5//mkspecs/linux-g++-64
Widgets INCLUDE: /usr/include/x86_64-linux-gnu/qt5/;/usr/include/x86_64-linux-gnu/qt5/QtWidgets;/usr/include/x86_64-linux-gnu/qt5/QtGui;/usr/include/x86_64-linux-gnu/qt5/QtCore;/usr/lib/x86_64-linux-gnu/qt5//mkspecs/linux-g++-64
Core LIBRARIES: Qt5::Core
Gui LIBRARIES: Qt5::Gui
Widgets LIBRARIES: Qt5::Widgets
Core DEFINITIONS: -DQT_CORE_LIB
Gui DEFINITIONS: -DQT_GUI_LIB;-DQT_CORE_LIB
Widgets DEFINITIONS: -DQT_WIDGETS_LIB;-DQT_GUI_LIB;-DQT_CORE_LIB
Core COMPILE_DEFINITIONS: QT_CORE_LIB
Gui COMPILE_DEFINITIONS: QT_GUI_LIB;QT_CORE_LIB
Widgets COMPILE_DEFINITIONS: QT_WIDGETS_LIB;QT_GUI_LIB;QT_CORE_LIB
Core EXECUTABLE_COMPILE_FLAGS: -fPIE
Gui EXECUTABLE_COMPILE_FLAGS: -fPIE
Widgets EXECUTABLE_COMPILE_FLAGS: -fPIE
Using GnuCXX compiler.
Building DEBUG build.
Sentinel GL -- C++ Project V 0.1.
(c) Markus-Hermann Koch, mhk#markuskoch.eu, 2015/04/28-?
Primary directory is /home/kochmn/projects/sentinel/build
System is Linux
Generating configuration header: "/home/kochmn/projects/sentinel/build/../build/mhk_cmake_config.h"
-- Configuring done
-- Generating done
-- Build files have been written to: /home/kochmn/projects/sentinel/build
kochmn#Ulyss:~/projects/sentinel/build$ make
Scanning dependencies of target optimization_automoc
[ 5%] Automatic moc for target optimization
[ 5%] Built target optimization_automoc
Scanning dependencies of target optimization
[ 11%] Building CXX object CMakeFiles/optimization.dir/home/kochmn/projects/sentinel/src/optimization/linalg.cpp.o
[ 17%] Building CXX object CMakeFiles/optimization.dir/optimization_automoc.cpp.o
Linking CXX static library liboptimization.a
[ 17%] Built target optimization
Scanning dependencies of target qt_automoc
[ 23%] Automatic moc for target qt
[ 23%] Built target qt_automoc
Scanning dependencies of target qt
[ 29%] Building CXX object CMakeFiles/qt.dir/home/kochmn/projects/sentinel/src/qt/form_main.cpp.o
[ 35%] Building CXX object CMakeFiles/qt.dir/qt_automoc.cpp.o
Linking CXX static library libqt.a
[ 35%] Built target qt
Scanning dependencies of target sentinel_automoc
[ 41%] Automatic moc for target sentinel
[ 41%] Built target sentinel_automoc
[ 47%] Generating qrc_application.cpp
[ 52%] Generating ui_main.h
[ 58%] Generating moc_ui_main.cpp
/home/kochmn/projects/sentinel/build/ui_main.h:0: Note: No relevant classes found. No output generated.
[ 64%] Generating ui_dialog_setup_game.h
[ 70%] Generating moc_ui_dialog_setup_game.cpp
/home/kochmn/projects/sentinel/build/ui_dialog_setup_game.h:0: Note: No relevant classes found. No output generated.
Scanning dependencies of target sentinel
[ 76%] Building CXX object CMakeFiles/sentinel.dir/home/kochmn/projects/sentinel/src/sentinel.cpp.o
[ 82%] Building CXX object CMakeFiles/sentinel.dir/moc_ui_main.cpp.o
[ 88%] Building CXX object CMakeFiles/sentinel.dir/moc_ui_dialog_setup_game.cpp.o
[ 94%] Building CXX object CMakeFiles/sentinel.dir/qrc_application.cpp.o
[100%] Building CXX object CMakeFiles/sentinel.dir/sentinel_automoc.cpp.o
Linking CXX executable sentinel
[100%] Built target sentinel
CMake generation order is computed from dependencies between files and targets. If your qt library depends on headers generated from .ui files, then you have to add ${qt_UI_H} in inputs of target qt:
QT5_WRAP_UI(qt_UI_H ${qt_UI})
[...]
add_library(qt "${DIR_SRC}/qt/form_main.cpp" ${qt_UI_H})
And CMake should normally execute UIC on .ui files before compiling libqt
By the way using target_link_libraries only set dependencies between targets at link time. At compile time, the normal behavior is "All source files should be found". In your case, some headers are generated, so setting these headers as input of a target ensures that the macro which generates them (QT5_WRAP_UI) will be executed before the compilation of the target.

How can I copy/move a specific set of files during 'make install' using cmake?

I would like to install a certain set of headers (which is not identical to all headers in a directory) to an include directory during make install of my project.
Here is the file structure that I have:
MyProject
|-build
|-lib
|-include
|-src
|-A
| |-a1.cpp/.h
| |-a2.cpp/.h
| |-a3.cpp/.h
|
|-B
| |-b1.cpp/.h
| |-b1.cpp/.h
|
|-CMakeLists.txt
In MyProject/src/CMakeLists.txt (which is the main CMakeLists file), I setup my project and compile everything into a library.
During the install step (i.e. make install) I would like to move my build libmyproject.a to MyProject/lib and some headers, say A/a1.h and B/b1.h to the MyProject/include folder.
However, I want to maintain the relative paths of those headers!
I.e., I want to achieve this:
MyProject
|-build
|-lib
| |-libmyproject.a
|
|-include
| |-A
| | |-a1.h
| |
| |-B
| |-b1.h
|
|-src
|-...
Installing the library is easy with CMake:
set( CMAKE_INSTALL_PREFIX "path_to_my_project")
install(TARGETS myproject
ARCHIVE DESTINATION lib
)
Where I struggle is the headers. I have a simple list of my API headers:
set(my_api_headers
A/a1.h
B/b1.h
)
But I cannot simply use the 'install(FILES ...)' CMake command as that strips away the relative paths. All the other suggestions I have found so far involce the 'install(DIRECTORY ...)' form with a 'PATTERN', but I don't think I can use that as that
would copy all headers. (Compare, for example, How can I install a hierarchy of files using cmake?)
I tried a simple function
############################################################
#
# FUNCTION: install API header files
#
function(install_api_headers
API_HDRS
DST_DIR
)
foreach( _header ${${API_HDRS}} )
file(INSTALL ${_header}
DESTINATION ${DST_DIR}
)
endforeach( _header )
endfunction(install_api_headers)
#
############################################################
install_api_headers(my_api_headers "MyProject/include")
but that obviously (well, I remembered when I ran it... :/) is executed during CMake runtime and not during 'make install'...
Trying the easy fix with a 'install(CODE ...)' wrapper also didn't work... :(
install(CODE "install_api_headers(workmodel_API_HDRS
{CMAKE_INSTALL_PREFIX}/${include_DIR_NAME}/WorkModels/${workmodel_NAME})"
)
So, bottom line:Any hint, idea, link related on how to get this done would be greatly appreciated.
Thanks,
Claus
semipol.de/archives/251 appears to solve the issue:
From the link:
SET(HS folder/test.h folder/other/test2.h)
A simple call to INSTALL doesn’t preserve the folder structure:
INSTALL(FILES ${HS} DESTINATION include)
This results in all files being directly under $prefix/include.
To preserve the structure you can use this simple macro:
MACRO(INSTALL_HEADERS_WITH_DIRECTORY HEADER_LIST)
FOREACH(HEADER ${${HEADER_LIST}})
STRING(REGEX MATCH "(.*)[/\]" DIR ${HEADER})
INSTALL(FILES ${HEADER} DESTINATION include/${DIR})
ENDFOREACH(HEADER)
ENDMACRO(INSTALL_HEADERS_WITH_DIRECTORY)
INSTALL_HEADERS_WITH_DIRECTORY(HS)
I made an example which I thought solved the problem (my way :)):
https://dl.dropboxusercontent.com/u/68798379/cmake-install-example.tar.bz2
$ ./gen-linux.sh
-- The C compiler identification is GNU 4.8.2
..
$ cd linux/
$ make
Scanning dependencies of target LIBB
[ 20%] Building CXX object src/B/CMakeFiles/LIBB.dir/testB.cpp.o
[ 40%] Building CXX object src/B/CMakeFiles/LIBB.dir/internalB.cpp.o
Linking CXX static library libLIBB.a
[ 40%] Built target LIBB
Scanning dependencies of target LIBA
[ 60%] Building CXX object src/A/CMakeFiles/LIBA.dir/testA.cpp.o
[ 80%] Building CXX object src/A/CMakeFiles/LIBA.dir/internalA.cpp.o
Linking CXX static library libLIBA.a
[ 80%] Built target LIBA
Scanning dependencies of target TESTLIB
[100%] Building C object CMakeFiles/TESTLIB.dir/src/dummy.c.o
Linking C static library libTESTLIB.a
[100%] Built target TESTLIB
$ make install
[ 40%] Built target LIBB
[ 80%] Built target LIBA
[100%] Built target TESTLIB
Install the project...
-- Install configuration: ""
-- Installing: cmake-install-example/build/linux/../../lib/libTESTLIB.a
-- Installing: cmake-install-example/build/linux/../../include/testA.hpp
-- Installing: cmake-install-example/build/linux/../../include/testB.hpp
Highlights:
create two static libraries (LIBA, LIBB) which contain private header files.
merge the two libs defined in src into one final library (TESTLIB).
install the merged library into "lib" directory.
install only the specified header files into "include" directory.
Kindly notice what CMAKE_INSTALL_PREFIX is set to in the build/gen-linux.sh script.

CppUnit and CMake: .cpp files get compiled twice

I'm currently using CMake to build my project and CppUnit to Test it. In my CMake file i create two executables. sample is the compiled source itself. And with sample_test I run the tests. If i r
Here a part of my CMakeLists.txt
SET(SAMPLE_ROOT_PATH ${PROJECT_BINARY_DIR})
SET(SAMPLE_SOURCE_PATH ${SAMPLE_ROOT_PATH}/src)
SET(SAMPLE_TEST_SOURCE_PATH ${SAMPLE_ROOT_PATH}/test)
SET(SAMPLE_BIN_PATH ${SAMPLE_ROOT_PATH}/bin)
SET(SAMPLE_EXEC_NAME sample)
SET(SAMPLE_TEST_EXEC_NAME sample_test)
SET(EXECUTABLE_OUTPUT_PATH ${SAMPLE_BIN_PATH})
FILE(GLOB_RECURSE SAMPLE_SOURCE_FILES ${SAMPLE_SOURCE_PATH}/*.cpp)
FILE(GLOB_RECURSE SAMPLE_TEST_SOURCE_FILES ${SAMPLE_TEST_SOURCE_PATH}/*.cpp)
SET(SAMPLE_TEST_SOURCE_FILES ${SAMPLE_TEST_SOURCE_FILES} ${SAMPLE_SOURCE_FILES}
)
LIST(REMOVE_ITEM SAMPLE_TEST_SOURCE_FILES ${SAMPLE_SOURCE_PATH}/main.cpp)
SET(CMAKE_CXX_FLAGS "-g -Wall")
ADD_EXECUTABLE(${SAMPLE_EXEC_NAME} ${SAMPLE_SOURCE_FILES})
ADD_EXECUTABLE(${SAMPLE_TEST_EXEC_NAME} ${SAMPLE_TEST_SOURCE_FILES})
this is the output of make
[ 8%] Building CXX object CMakeFiles/sample.dir/src/KeyBuffer.cpp.obj
[ 12%] Building CXX object CMakeFiles/sample.dir/src/main.cpp.obj
[ 20%] Building CXX object CMakeFiles/sample.dir/src/Object.cpp.obj
[ 45%] Building CXX object CMakeFiles/sample.dir/src/World.cpp.obj
Linking CXX executable bin/sample.exe
[ 45%] Built target sample
[ 50%] Building CXX object CMakeFiles/sample_test.dir/test/KeyBufferTest.cpp.obj
[ 54%] Building CXX object CMakeFiles/sample_test.dir/test/ObjectTest.cpp.obj
[ 66%] Building CXX object CMakeFiles/sample_test.dir/src/KeyBuffer.cpp.obj
[ 75%] Building CXX object CMakeFiles/sample_test.dir/src/Object.cpp.obj
[100%] Building CXX object CMakeFiles/sample_test.dir/src/World.cpp.obj
Linking CXX executable bin/sample_test.exe
As you can see Object.cpp, World.cpp and KeyBuffer.cpp get compiled twice! How can i prevent it? Or is there a better way to handle the CppUnit tests using CMake?
Each target may have different compiler flags configured, so if you add one source files to two targets, separate object files need to be produced from this one source file for both targets.
The usual solution is to compile the shared source files into a static library which is then linked into both application targets.
add_library(base STATIC ${shared_SOURCES}) # except e.g. foo_main.cpp
add_executable(foo ${foo_only_SOURCES})
target_link_libraries(foo base)
add_executable(bar ${bar_only_SOURCES})
target_link_libraries(bar base)
Conceptually CMake treats each target (i.e., executable or library) as a separate build unit. The generated build system will produce an object file for each source file belonging to the target. By default CMake does not avoid redundant compilations of source files that are used in multiple targets, even if the compilation settings (compilation flags, preprocessor definitions ...) are exactly the same.
CMake 2.8.8 introduced a new feature called OBJECT_LIBRARY target to address the problem of avoiding redundant compilations:
To generate an object library use add_library:
FILE(GLOB_RECURSE SAMPLE_SOURCE_FILES ${SAMPLE_SOURCE_PATH}/*.cpp)
ADD_LIBRARY(sample_objects OBJECT EXCLUDE_FROM_ALL ${SAMPLE_SOURCE_FILES})
Other targets created by add_library or add_executable may reference the objects using an expression of the form $<TARGET_OBJECTS:objlib>:
ADD_EXECUTABLE(${SAMPLE_EXEC_NAME} $<TARGET_OBJECTS:sample_objects>)
FILE(GLOB_RECURSE SAMPLE_TEST_SOURCE_FILES ${SAMPLE_TEST_SOURCE_PATH}/*.cpp)
ADD_EXECUTABLE(${SAMPLE_TEST_EXEC_NAME} $<TARGET_OBJECTS:sample_objects> ${SAMPLE_TEST_SOURCE_FILES})
Compared with using a regular static library to avoid redundant compilation, an object library has the advantage that it does not need to be linked. On top of that it cannot be imported, exported or installed.

How to choose which CMake executable target will be the default one?

I have written a CMakeLists.txt file including 2 executables (target1 and target2):
ADD_EXECUTABLE(target1 ${CXX_FILES})
TARGET_LINK_LIBRARIES(target1 ${requiredlibs})
ADD_EXECUTABLE(target2 ${CXX_FILES} ${OTHER_CXX_FILES})
TARGET_LINK_LIBRARIES(target2 ${requiredlibs})
Now every time when I run make without any parameters both targets are rebuilt. But I want to define target1 as default executable so that running make without any parameters only builds target1. For building target2 I would run make target2.
Is this possible?
In the Makefile created by CMake there is the following definition:
default_target: all
I think I need a way to set this default_target to target1.
Another problem I have is that make always rebuilds the targets, even if no source file has been changed.
An example CMakeLists.txt that does what you requested:
ADD_EXECUTABLE(target1 a.c)
ADD_EXECUTABLE(target2 EXCLUDE_FROM_ALL b.c)
For me it does not rebuild the target if the source files are not changed (or modification time did not change). Output I get:
$ make -f Makefile
Scanning dependencies of target target1
[100%] Building C object CMakeFiles/target1.dir/a.c.o
Linking C executable target1
[100%] Built target target1
[$ make -f Makefile
[100%] Built target target1
Note that the second make does not rebuild anything.
(you could read the CMake manual for this type of information)