CMake how to configure this C++ project? - c++

I want a cmake project with two configurations BUILD and TEST.
BUILD compiles all sources not in test subdirectory into a shared library.
TEST compiles all sources, including those in test subdirectory (which includes main.cpp) into an executable that runs the tests. I don't want TEST to build the shared library. I don't want BUILD to build the test executable.
I currently have it on disk as:
project/
test/
test_foo.cpp
main.cpp
bar.hpp
widget.hpp
bar.cpp
widget.cpp
...
I can move things around if it makes it easier. What do I put in my CMakeLists.txt files?

It looks to me like you want to use cmake's OPTION command. Pick one configuration to be on by default (or neither if you want to force the person compiling the code to choose)
OPTION( BUILD_SHARED_LIBRARY "Compile sources into shared library" ON )
OPTION( RUN_TESTS "Compile test executable and run it" OFF )
You'll want to make sure that the options are mutually exclusive, and error out otherwise
if ( BUILD_SHARED_LIBRARY AND RUN_TESTS )
message(FATAL_ERROR "Can't build shared library and run tests at same time")
endif()
You can then put the rest of your commands inside if blocks based on those variables
if ( BUILD_SHARED_LIBRARY )
#add subdirectories except for test, add sources to static library, etc
endif()
if ( RUN_TESTS )
#compile an executable and run it, etc.
endif()

Related

Is there a way to add library changes as a dependency in cmake?

I have an application and a library that I build with CMake and I would like to make the build process easier if possible. As it stands, if there is a change to the library and I rebuild the application, CMake doesn't rebuild the modified library files. Similarly, if I rebuild the library and then run make on the application, it will say there is nothing to do. My current workaround is to use bash scripts to rebuild everything, but that unnecessarily re-compiles a lot of files and I would like to handle it all within the build directory for the application if possible.
Update with simple example:
There are 3 folders: app, lib, and include. include contains test_program.h, lib contains test_program.cpp, and app contains test.cpp which includes test_program.h.
Here is the CMakeLists.txt for lib:
include_directories(.)
add_library (test_lib STATIC
test_program.cpp
)
Here is the CMakeLists.txt for app:
include_directories(
.
../include
)
link_directories(
../../lib/build
)
add_executable(test_exe
test.cpp
)
target_link_libraries(
test_exe
test_lib
)
I would like to make it so that if I make a change to test_program.cpp, I can simply run cmake ../ and then make in app/build and it will use the updated version of test_program.cpp.
Update:
I have added a top level CMakeLists.txt and a build to the top level of the project. The file is very simple and seems to do what I was wanting from the beginning:
add_subdirectory(app)
add_subdirectory(lib)
I would be happy to take suggestions if there are any improvements to be made here.

CMake post-build custom command on multiple targets?

I'm learning CMake through a C++ hobby project using Visual Studio 2017, and the way I have it set up, I have one folder for source code and one folder for test code. I build the source code as a static library, and build the test code as an executable (using Catch2). The problem I have now is that these are two separate targets, and whenever one or both of these targets are rebuilt I want to run the testing executable. Now I can find out how to run a post-build event using ADD_CUSTOM_COMMAND, but that only works for a single target. Putting multiple targets after "TARGET" leads to only the last target being used (I tested this), and duplicating the custom command can lead to the tests being run twice, and also it seems like poor code style. Is there any way to do this elegantly? My CMake file looks like this:
# CMakeList.txt : Top-level CMake project file, do global configuration
# and include sub-projects here.
#
cmake_minimum_required (VERSION 3.8)
project ("SheepyEngine")
set (CMAKE_CXX_STANDARD 17)
set (HEADER_FILES 3rdParty/CImg/CImg.h)
set (SOURCE_DIRECTORY Source)
set (TEST_DIRECTORY Test)
# Include sub-projects.
add_subdirectory ("Source")
add_subdirectory ("Test")
# Include libraries
include_directories (
"${CMAKE_CURRENT_LIST_DIR}/3rdParty/CImg"
"${CMAKE_CURRENT_LIST_DIR}/3rdParty/Catch2/single_include"
)
add_library (SheepyEngine STATIC
"${SOURCE_DIRECTORY}/Game.cpp"
"${SOURCE_DIRECTORY}/Game.h"
"${SOURCE_DIRECTORY}/GameObject.h"
${HEADER_FILES})
target_include_directories(SheepyEngine PRIVATE ${CMAKE_CURRENT_LIST_DIR}/3rdParty/CImg/)
add_executable(SheepyEngineTest "${TEST_DIRECTORY}/test.cpp" "3rdParty/Catch2/single_include/catch.hpp")
target_include_directories(SheepyEngineTest PRIVATE ${CMAKE_CURRENT_LIST_DIR}/3rdParty/Catch2/)
# TODO: Add tests and install targets if needed.
if(${RUN_TESTS})
ADD_CUSTOM_COMMAND(
TARGET SheepyEngineTest SheepyEngine
POST_BUILD
COMMAND ${CMAKE_CURRENT_LIST_DIR}/Build/Debug/SheepyEngineTest.exe
)
endif()
The SheepyTestProgram target needs to be dependent on SheepyEngine:
target_link_libraries(SheepyEngineTest SheepyEngine)
Then the target of add_custom_command will be just SheepyEngineTest (add_custom_command accepts only a single target).

CMake - objects being built twice unnecessarily?

I have a list of files that need to be compiled for my main executable. My tests also need these files. When the test executable(s) are built, the object files are built again, even though earlier in the build they were built when the main executable was built.
Am I wrong in thinking this is not needed? If so is there a way to disable this?
Example:
set(SOURCES
${SOURCE_DIR}/file.c
${SOURCE_DIR}/another_file.c)
set(MAIN ${SOURCE_DIR}/main.c)
add_executable(main_executable ${SOURCES} ${MAIN})
add_executable(test1_ex ${PROJECT_SOURCE_DIR}/test/test1.cc ${SOURCES})
Put the common code in a library and link the library to both your application and tests.
# The application's sources - except main
set(SOURCES
${SOURCE_DIR}/file.c
${SOURCE_DIR}/another_file.c)
# build an application library
add_library(app_lib ${SOURCES})
# build an executable in terms of the application library
set(MAIN ${SOURCE_DIR}/main.c)
add_executable(main_executable ${MAIN})
target_link_libraries(main_executable PRIVATE app_lib)
# build a test executable in terms of the application library
add_executable(test1_ex ${PROJECT_SOURCE_DIR}/test/test1.cc)
target_link_libraries(test1_ex PRIVATE app_lib)
This could be due to the following scenario:
add_library(mylib1 file1.cpp common.cpp)
add_library(mylib2 file2.cpp common.cpp)
That created the same problem you're having for me, where common.cpp was being rebuilt for every library. My solution was this:
add_library(common_lib common.cpp)
add_library(mylib1 file1.cpp)
add_library(mylib2 file2.cpp)
target_link_libraries(mylib1 common_lib)
target_link_libraries(mylib2 common_lib)
That resolved the problem for me as the building (object creation) was done once, but it was linked to every other library.

How to link an exe project to the classes in another exe project

Suppose you want to do some unit testing on classes in an executable but you don't want to refactor them out into a lib where you can add the lib using target_link_libraries( target library ) in cmake.
How do you give the test class access to the other classes?
1) Build test project with the source files from the other project?
another thing
include_directories(${otherExeProjectDir})
set( SOURCE_FILES
main.cpp
tests.h
tests.cpp
${otherExeProjectDir}/otherclass1.h
${otherExeProjectDir}/otherclass2.h
)
2) Link test project with obj files from the other project?
some sort of add_library( otherclass.obj ) craziness?
3)
If your main executable source locations are simple or flat, then something like this could work:
cmake_minimum_required(VERSION 3.9)
project(tests)
# Get main executable source location properties
get_target_property(exe_sources exe SOURCES)
get_target_property(exe_source_dir exe SOURCE_DIR)
# Remove main entry point file
list(REMOVE_ITEM exe_sources main.cpp)
# Add test sources
add_executable(test1 test1.cpp)
# Add exe sources to test (assumes sources are relative paths)
foreach(src IN LISTS exe_sources)
target_sources(test1 PRIVATE "${exe_source_dir}/${src}")
endforeach()
# Add exe include directories to test
target_include_directories(test1 PRIVATE
${exe_source_dir}
$<TARGET_PROPERTY:exe,INCLUDE_DIRECTORIES>)
Otherwise the general solution is, unfortunately, to rely on some external information, e.g. top level source file locations or adding your own source properties to the main executable target.

Cmake and Gtest

I want to use Google C++ Testing and I am completely beginner to cmake and gtest.
I have a class called Filter which uses a 3d party library called jane.
For this case I have a cmakeFile which builds my project nicely as follows:
cmake_minimum_required(VERSION 3.1.2)
project(Filter)
include(../../../cmake/CMakeMacros.txt)
set_variables()
#add 3rdparty libraries
add_jane()
#add framework libraries
add_framework_libs(
ip/Image
)
include_directories(
../include
${FW_INCLUDE_DIRS}
)
#set project's source and include files
set(INCS
../include/${PROJECT_NAME}.h
../include/${PROJECT_NAME}.tpp
../include/FilterMask.h
)
set(SRCS
../src/${PROJECT_NAME}.cpp
../src/FilterMask.cpp
)
#set link directories
link_directories(
${FW_LIBRARY_DIRS}
)
#build project as static library (*.lib)
add_library(${PROJECT_NAME} STATIC
${INCS}
${SRCS}
)
#link libraries against project
target_link_libraries( ${PROJECT_NAME}
${FW_LIBRARIES}
)
#if a test executable should be build
if(Test_BUILD_EXAMPLES)
#build test executable
add_executable(${PROJECT_NAME}Test
../src/main.cpp
)
#link library against executable
target_link_libraries(${PROJECT_NAME}Test
${PROJECT_NAME}
)
endif(Test_BUILD_EXAMPLES)
and also I have read this simple tutorial on https://github.com/snikulov/google-test-examples with this cmake file https://github.com/snikulov/google-test-examples/blob/master/CMakeLists.txt and tried to build my project again to combine these cmake files together (may be in very silly way) but I can not achieve it since days.
The problem is that when I want to test a simple project with just a header file I can use this cmake file but as soon as I try to test my project containing a 3rd party library I run into different errors.
Can someone please tell me how I can edit a correct cmake file to test my project with googleTest using a cmake file !?
If you want to link against a 3rd party library you typically first:
find_package() or use the pkg config support to check the library is available on the build host and pick up a reference to it.
Include the reference from step #1 in target_link_libraries()
So that's what you need to do for your 3rd party lib. For your own code which you want to bring under test you probably want to put it all inside your own libraries and then link your tests against those.
If you have multiple test executables to separate & isolate each test suite into its own binaries you probably want an alternative technique to avoid over linking and limit the code inside the test suite to the actual unit under test. (This is also quite useful when your code base is in flux and builds only partially but you still wish to check that what builds continues to pass relevant tests.)
In that case you may want to define your units under test as OBJECT type libraries and then instead of doing target_link_libraries() against those object libs you include the objects as part of the sources for the executable using this syntax: $<TARGET_OBJECTS:NameOfObjLibHere> (cmake generator expressions).
So in the case of units which depend on a 3rd party lib, say, Qt5 Core, you'd have snippets like this:
# define the dependency on 3rd party project Qt5, (sub)component: Core, Test)
set(MY_QT_VERSION "5.4.0")
find_package(Qt5 ${MY_QT_VERSION} REQUIRED COMPONENTS Core CONFIG)
# define the object lib for a unit to be tested (Item1)
set(item1_srcs item1.cpp util1.cpp)
add_library(Item1 TYPE OBJECT ${item1_srcs})
# ensure that necessary compiler flags are passed
# when building "Item1" separately
# note that PRIVATE may also be INTERFACE or PUBLIC
# read the cmake docs on target_include_*** to determine which applies.
# you probably want to hide this behind a convenience macro.
target_include_directories(Item1 PRIVATE $<TARGET_PROPERTY:Qt5::Core,INTERFACE_INCLUDE_DIRECTORIES>)
target_compile_options(Item1 PRIVATE $<TARGET_PROPERTY:Qt5::Core,INTERFACE_COMPILE_OPTIONS>)
# find the unit testing framework (dependency)
# this sample uses Qt5 Test (Qt5::Test) but you could use GTest, too
find_package(Qt5 ${MY_QT_VERSION} REQUIRED COMPONENTS Test CONFIG)
# define an executable which contains test sources + unit under test
# link against the testing framework (obviously) as per normal
# note the Qt5::Core dependency here: remember Item1 depends on Qt5::Core (!)
set(test_item1_srcs, test_item1.cpp $<TARGET_OBJECTS:Item1>)
add_executable(test_item1 ${test_item1_srcs)
target_link_libraries(test_item1 Qt5::Core Qt5::Test)
# inform cmake/ctest integration about our test
# so it knows to execute it during `make test` phase.
# and other cmake/ctest integration falls into place as well, possibly
add_test(test_item1 test_item1)