Cmake and Gtest - c++

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)

Related

How can I write a CMakeList.txt file for heavily nested project?

Suppose my source code has the following directory structure:
C:\USERS\PC\SOURCE\REPOS\my_app_src
├───apps {a.hh, b.cc, c.hh, d.cc}
│ └───biosimulations {main1.hh, main1.cc, x.hh, y.cc}
└───core {w.cc, x.hh, y.hh, z.cc}
└───algorithms {p.hh, p.cc, q.hh, r.cc, s.hh, s.cc}
└───trees {r.hh, r.cc, main2.hh, main2.cc}
Each folder has any number of header and source files with any name.
How can I write a CMakeList.txt file for this project?
This is scientific software.
I need to be able to use various parts of the same library to compile and build executables for multiple applications.
For example, in the above sample, main1.exe and main2.exe are supposed to be two different executable files.
Sometimes, I need to be able to switch off one or another executable from compiling.
Do you want the project to support testing, installation, and/or packaging?
No, I don't need them. I just need to be able to compile and execute the apps.
What is in core?
Model classes. e.g., Atom, Protein, Chain, etc.
Are the source files for core part of a single library or executable?
Part of executable. There is no static or dynamic library in the project.
Using add_subdirectory and add_library commands, recursively add all of the source files in the directory structure.
cmake_minimum_required(VERSION 3.x)
project(my_app)
# Add subdirectories recursively
add_subdirectory(apps)
add_subdirectory(core)
add_subdirectory(simulations)
add_subdirectory(ui)
add_subdirectory(utils)
# Create the final executable
add_executable(my_app main.cpp)
# Link the libraries to the executable
target_link_libraries(my_app core simulations apps ui utils)
Then in each subdirectories(apps,core,simulations,ui, utils) you would need to add a new CMakeLists.txt that tells which source files are in that directory and create a library.
cmake_minimum_required(VERSION 3.x)
SET(SRC_FILES
program.cpp
)
#for static libraries
add_library(core STATIC ${SRC_FILES})
#for dynamically linking libraries
add_library(core SHARED ${SRC_FILES})
# If you need executable here
# add_executable(core ${SRC_FILES})
This needs to be repeated for all subdirectories, untill all sources are covered. Above examples gives general structure, you need to define CMAKE flags as required.

How do I build a parameterized third-party library in cmake?

I have a project in which I have a third party library checked out as a git submodule. The third party library is just source code with no build system. In addition, The third party library must configured on a per-executable basis by way of compiler definitions and selectively compiling only the parts of the library that I need. I use this library in a lot of different repositories, so I want to make a reusable component to generate an instantiation of the library for any particular executable.
The way I've currently attempted this is by creating a generate_thirdparty.cmake. This file looks something like this:
function(generate_thirdparty parameters)
# several calls to add_library for the different components of this third party library
# the generated libraries depend on the parameters argument
# the parameters configure compile definitions and which source files are compiled
endfunction()
Then in my project CMakeLists.txt I have something like:
cmake_minimum_required(VERSION 3.8)
project(test C)
include(generate_thirdparty.cmake)
set(parameters <some parameters here>)
generate_thirdparty(${parameters})
add_executable(my_exe main.c)
target_link_libraries(my_exe <library names from generate_thirdparty>)
It seems like what I have works, but I'm confused on how you're supposed to do this. I've read through other posts and seen people suggest using find_package or ExternalProject_add. Given a third party repository that contains source code and no build system, which you have no control over, how do you create a reusable way to build that library, especially in the case that the library must be parameterized any given executable?
EDIT: I would like to have the flexibility to have multiple instantiations of the library in the same project.
Let's sum up:
The third-party library does not provide its own build.
You need many instantiations of the library within a single build.
You use these instantiations across multiple different repositories.
I think you're pretty much taking the right approach. Let's call the third-party library libFoo for brevity. Here's what I think you should do...
Create a wrapper repository for libFoo that contains a FindFoo.cmake file and the actual foo repository submodule next to it. This is to avoid the contents of FindFoo.cmake from being independently versioned across your various projects.
Include the wrapper as your submodule in dependent projects, say in the directory third_party/foo_wrapper
In those dependent projects, write:
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/third_party/foo_wrapper")
find_package(Foo REQUIRED)
generate_foo(config1 ...)
generate_foo(config2 ...)
add_executable(app1 src/app1/main.cpp)
target_link_libraries(app1 PRIVATE foo::config1)
add_executable(app2 src/app2/main.cpp)
target_link_libraries(app2 PRIVATE foo::config2)
The contents of FindFoo.cmake will simply be:
cmake_minimum_required(VERSION 3.18)
function(generate_foo target)
# Use CMAKE_CURRENT_FUNCTION_LIST_DIR to reference foo's source files
# for example:
set(sources "src/src1.cpp" "src/src2.cpp" ...)
list(TRANSFORM sources PREPEND "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/foo/")
add_library(${target} ${sources})
add_library(foo::${target} ALIAS ${target})
# Do other things with ARGN
endfunction()
# Define a version for this dependency + script combo.
set(Foo_VERSION 0.1.0)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Foo VERSION_VAR Foo_VERSION
HANDLE_COMPONENTS)
The CMAKE_CURRENT_FUNCTION_LIST_DIR is the absolute path to the file containing the function being called. Remember that this is the root of your wrapper repository, so the actual sources for libFoo will be in the adjacent foo directory. list(TRANSFORM) lets us write relative paths in the sources list that get converted to absolute paths for the sake of add_library (passing a relative path to add_library would be relative to the caller's source directory).
I also create an ALIAS target so that callers of generate_foo can link to the alias. This is important because CMake considers names that contain :: to be targets when a library is expected. This very helpfully turns typos from unintended linker flags into configure-time "target not found" errors.
Then we define the function like normal and call the find_package_handle_standard_args to handle find_package arguments like REQUIRED, COMPONENTS (even just to check that none were incorrectly specified), and VERSION. See the docs for it, here: https://cmake.org/cmake/help/latest/module/FindPackageHandleStandardArgs.html

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.

CMake how to configure this C++ project?

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()