cmake: how to separately compile production code and test code - unit-testing

I read this brilliant tutorial about how to integrate Google Test with CMake. The outline of the project there looks like this:
+-- CMakeLists.txt
+-- main
| +-- CMakeLists
| +-- main.cpp
|
+-- test
| +-- CMakeLists.txt
| +-- testfoo
| +-- CMakeLists.txt
| +-- main.cpp
| +-- testfoo.h
| +-- testfoo.cpp
| +-- mockbar.h
|
+-- libfoo
| +-- CMakeLists.txt
| +-- foo.h
| +-- foo.cpp
|
+-- libbar
+-- CMakeLists.txt
+-- bar.h
+-- bar.cpp
(For the interested, the entire code of this example project can be checked out from here)
The top-level CMakeLists.txt contains (among others) the statements enable_testing() and add_subdirectory(test). Compiling and running test cases works perfectly with this setup, simply by running
mkdir build && cd build
cmake ..
make
make test
But how would I compile this project into production code, i.e. only the components test, libfoo and libbar, without all the unit tests?
Should I make the statements enable_testing() and add_subdirectory(test) somehow dependent on some build configuration variables? Or what's the best practice for this?

To build the tests only on request, I do it in this way:
Add an option option(BUILD_TEST "Build the unit tests" ON)
Include the test subdirs only in case BUILD_TEST is on
if(BUILD_TEST)
add_subdirectory(test)
endif()
In your case you can modify this to testfoo.
As you asked for production, you can use the following instead to build in debug mode only:
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
add_subdirectory(test)
endif()

What I do is to make a custom macro for unit test creation which does this:
set_target_properties(${NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/test)
Then your tests will end up in a special directory (test) instead of the regular one (usually bin). Then for production you just copy the regular directory without the test directory.

Related

Tradeof between CMAKE_BINARY_DIR and PROJECT_BINARY_DIR when setting submodule targets' output directories

I'm working on a library which will be used by another project. The main project uses a couple of libraries linked as git submodules. Inside the library project used as a submodule, I can set the LIBRARY_OUTPUT_DIRECTORY and the ARCHIVE_OUTPUT_DIRECTORY in two different ways.
In option A the targets are built and saved to their own subdirectories using the ${PROJECT_BINARY_DIR}/lib, but in option B all targets are built and saved to the ${CMAKE_BINARY_DIR}/lib directory.
This could also be extended to binaries built by the submodules. Thus, which of the two options will scale better / is normally used as best-practice?
OPTION A
CMakeLists.txt
# Set target properties
set_target_properties(${PROJECT_NAME}_lib_shared PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
set_target_properties(${PROJECT_NAME}_lib_static PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib/static)
Folder Structure
.
+-- build
| +-- bin
| +-- MainProject
| +-- test
| +-- lib
| +-- SubModuleA
| +-- SubModuleA_lib_shared.a
| +-- static
| +-- SubModuleA_lib_static.so
| +-- SubModuleB
| +-- SubModuleB_lib_shared.a
| +-- static
| +-- SubModuleB_lib_static.so
+-- lib
| +-- SubModuleA
| | +-- CMakeLists.txt
| +--- SubModuleB
| | +-- CMakeLists.txt
+-- inc
| +-- foo.h
+-- src
| +-- foo.cpp
| +-- main.cpp
| +-- CMakeLists.txt
+-- test
| +-- test.cpp
| +-- CMakeLists.txt
+-- CMakeLists.txt
OPTION B
CMakeLists.txt
# Set target properties
set_target_properties(${PROJECT_NAME}_lib_shared PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set_target_properties(${PROJECT_NAME}_lib_static PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/static)
Folder Structure
.
+-- build
| +-- bin
| +-- MainProject
| +-- test
| +-- lib
| +-- SubModuleA_lib_shared.a
| +-- SubModuleB_lib_shared.a
| +-- static
| +-- SubModuleA_lib_static.so
+-- SubModuleB_lib_static.so
+-- lib
| +-- SubModuleA
| | +-- CMakeLists.txt
| +--- SubModuleB
| | +-- CMakeLists.txt
+-- inc
| +-- foo.h
+-- src
| +-- foo.cpp
| +-- main.cpp
| +-- CMakeLists.txt
+-- test
| +-- test.cpp
| +-- CMakeLists.txt
+-- CMakeLists.txt
Unless there is a good reason you shouldn't set the output directory for your targets in the library project at all. If consumers link to the CMake targets provided by the library project then they usually don't even need to know where exactly these libraries will be located, and even if they do they have access to the location through target-dependent generator-expressions.
Moreover, even if you do have a good reason (e.g. because you'd like to archive the build-artifacts in a CI build and don't use CPack to generate proper packages yet), you still shouldn't force your setup upon consumers. If you allow consumers to easily overwrite the defaults they can e.g. use that mechanism to ensure the shared libraries will be located next to a binary their project builds, which is required on Windows to run the executable without having to manually modify the PATH to include all directories where the dependencies are located, i.e. consumers can ensure that the executable can be run from the build tree by changing the default output directory of your library target.
You thus shouldn't set the target properties, and instead set the defaults for these properties by setting
if (NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib/static")
endif()
if (NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
endif()
if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin")
endif()
to make it more convenient for consumers to change the output location of all targets all at once without having to set the properties manually for each target again.
Whether you use ${PROJECT_BINARY_DIR} or ${CMAKE_BINARY_DIR} then is largely irrelevant, but I personally prefer to use ${PROJECT_BINARY_DIR} because it ensures there will be no conflicts, even if two subprojects chose the same name for their target (e.g. several targets might define an executable that is just called tests).

Travis CI does not find header files

Basically I wanted to learn CMake and GTest. On top of that I wanted to try out CI's and I have added my simple project to Travis CI and AppVeyor. The project compiles well on my local machine (tested with vs 2017 and g++, not sure about the versions, because I am not near it at the moment) as well as on AppVeyor.
The project consists of a few headers compiled to a static library, executable for "manual testing" the static library and gtest (no tests at the moment, just a template).
The structure looks like that:
.
+-- .appveyor.yml
+-- .travis.yml
+-- CMakeLists.txt
+-- main
| +-- main.cpp [The manual testing exec]
| +-- CMakeLists.txt
+-- include
| +-- Utility.h
| +-- CMakeLists.txt
| +-- Other header files to compile to static library
+-- test
| +-- CMakeLists.txt
| +-- CMakeLists.txt.in
| +-- testutility
| | +-- main.cpp [sample exe file for gtest, not used yet]
| | +-- CMakeLists.txt
During build on Travis CI, it cannot find headers for the static library.
I have tried using
include_directories(".") on main CMakeLists.
Main CMakeLists.txt
cmake_minimum_required (VERSION 3.1)
project (primlibrary)
enable_testing()
include_directories(".")
add_subdirectory(include)
add_subdirectory(main)
add_subdirectory(test)
Travis CI error message
CMake Error at include/CMakeLists.txt:12 (add_library):
Cannot find source file:
Utility.h
Tried extensions .c .C .c++ .cc .cpp .cxx .m .M .mm .h .hh .h++ .hm .hpp
.hxx .in .txx
-- Generating done
-- Build files have been written to: /home/travis/build/SoIAS/PrimLibrary/build
The command "cmake .." exited with 1.
$ make
Scanning dependencies of target prim_library
[ 9%] Linking CXX static library libprim_library.a
[ 9%] Built target prim_library
Scanning dependencies of target manualtestingapp
[ 18%] Building CXX object main/CMakeFiles/manualtestingapp.dir/main.cpp.o
/home/travis/build/SoIAS/PrimLibrary/main/main.cpp:2:29: fatal error: include/Utility.h: No such file or directory
#include "include/Utility.h"
And include/CMakeLists.txt:
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(prim_library_srcs
ForwardList.h
LinkedList.h
Utility.h
tempfix.cpp
)
add_library(prim_library STATIC ${prim_library_srcs})
set_target_properties(prim_library PROPERTIES LINKER_LANGUAGE CXX)
target_include_directories(prim_library PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
CI and git links:
- Travis CI last build
- Appveyor build
- Github
Do you know what am I doing wrong?
The problem was solved literally a dozen minutes after posting. The Utility.h file locally had capitalized first letter while the one on git was lower case. It was solved by using git command to capitalize the first letter of the file on git:
git mv include/utility.h include/Utility.h

CMake Build a target as part of project and independently

I am attempting to write CMakeLists.txt for a platform. The platform has multiple projects which share some libraries and have their own executables. I wish to be able to build each project as a whole, as well as individual executables from within the project.
out-of-source root build works fine:
mkdir root-build && cd root-build && cmake ../root
out-of-source executable build does not work:
mkdir ui-build && cd ui-build && cmake ../root/project1/ui
My current directory structure is as follows:
root/
+-- CMakeListst.txt
+-- lib/
+-- CMakeLists.txt
+-- sharedLib1/
+-- CMakeLists.txt
+-- sharedLIb2/
+-- CMakeLists.txt
+-- project1/
+-- CMakeLists.txt
+-- ui/
+-- CMakeLists.txt
+-- executable1/
+-- CMakeLists.txt
+-- shared/
+-- CMakeLists.txt
+-- project2/
+-- CMakeLists.txt
+-- executable/
+-- CMakeLists.txt
My current project structure builds all targets and libraries as part of the project great, but I can't figure out how to build an executable in isolation.
The main cmake file for a project is root/project*/CMakeLists.txt. This file INCLUDES root/CMakeLists.txt, which does some setup common to all projects (e.g setting CMAKE_MODULE_PATH and find_package for third-party libraries). The project cmake file also calls add_subdirectory for each of it's executables and add_subdirectory(../lib libs).
This allows executables to link against any of the targets defined in the root/lib subdirectory. If I then try to build the executable independently of the project, CMake will not be able to find the target the executable needs to link against. Is there an elegant solution / pattern that would allow this? One way I thought of was to just add_subdirectory(../../ root) from the ui/CMakeLists.txt, with something resembling header guards to prevent an infinite add_subdirectory loop.
Use make executable1 (for example) to build individual targets.

Cmake 'find_libraries', NAMES keyword not working

First time user here so pleasy be gentle.
I have problem with linking libraries using cmake.
I'am working on part of the bigger project. Each part has its own build system, but can use libraries from other parts. Eample project tree:
+-- Project
| +-- Part1
| +-- Lib1
| +-- libLib1.a
| +-- Lib2
| +-- libLib2.a
| +-- Part2
| +-- Lib3
| +-- libLib3.a
| +-- Lib4
| +-- libLib4.a
| +-- MyPart
| +-- Lib5
| +-- libLib5.a
| +-- Lib1.cpp
| +-- CMakeLists.txt
| +-- main.cpp
| +-- CMakeLists.txt
I try to link my part MyPart, with libraries Lib1, Lib2, Lib4, Lib5.
With Lib5 there is no problem as it is build with the same cmake tree.
But how to link outside libraries?
I choose to use find_library to obtain path to each of the other needed library, for example:
find_library(LIB1_LIBRARY
NAMES Lib1
PATH ../Part1/Lib1/)
And then use target_link_libraries. Unfortunetly as a result of find_library i get LIB1_LIBRARY-NOTFOUND.
But if keyword NAMES is to be removed, all start working as expected.
What is wrong here? And also is there better strategy for linking libraries from outside of my build system tree.
thank you in advance

Build and use GTest globally in a project

First, I need to claim that I have been going around Stack Overflow and arrive answers of how to use ExternalProject to build Google Test within a project, for example here.
Now let's say what I have in my project is something like this
+-- CMakeLists.txt (the big CMake File for the entire project)
+-- tests
| +-- CMakeLists.txt (contain of all the small project in the tests)
| +-- Test1
| +-- CMakeLists.txt (test file for Test1 program)
| +-- test_1.cpp
| +-- test_1.h
| +-- Test2
| +-- CMakeLists.txt (test file for Test2 program)
| +-- test_2.cpp
| +-- test_2.h
| +-- Test3
| +-- CMakeLists.txt (test file for Test3 program)
| +-- test_3.cpp
| +-- test_3.h
Now, is there anyway that I can configure and build Google Test using ExternalProject in the big CMakeLists.txt file (at the root folder), and then use those library to build each separate tests in their corresponding folder. At the moment I can only download-built-link an entire new set of GTest libraries in each of the sub folder Test1, Test2, Test3 which is very inefficient. Is there an alternative way to get around this?
It is unclear without seeing your actual CMakeLists.txt files what's happening in your case. That said, used in the normal way, ExternalProject doesn't give you the CMake targets to link to directly, you have to do more manual work to get something you can add to the linked library list of your targets needing to use GTest. So depending on how you've manually defined those things, that could be why you are finding you need to build GTest in each TestX subdirectory.
In the other SO question you linked to, my answer there and the linked article therein mentions how to download and build GTest as part of the CMake stage rather than at build time. This gives you the CMake targets which you can then link against directly without having to define them manually. If you follow that method to download and build GTest in your top level CMakeLists.txt file, then the gtest and gtest main targets will be visible to all subdirectories. Furthermore, you won't have to go figure out the name of the library for each platform, etc. since the CMake target already handles that for you. Thus, in your various Test1, Test2 and Test3 subdirectories, all you'd have to do is to add gtest or gtest_main to the list of libraries each test program linked against.
My approach is to create a directory at the same level of your test projects:
+-- CMakeLists.txt (the big CMake File for the entire project)
+-- tests
| +-- CMakeLists.txt (contain of all the small project in the tests)
| +-- gtest
| +-- CMakeLists.txt (use ExternalProject_Add to import GTest)
| +-- Test1
| +-- CMakeLists.txt (test file for Test1 program)
| +-- test_1.cpp
| +-- test_1.h
| +-- Test2
| ...
In the CMakeLists.txt for gtest I define the gtest target and export the variables for the include dir and library path for the other projects to use.
It works for me and I get gtest built only once so all test projects can link against it. But I'll try to give a shot to Craig Scott's approach to build gtest during Makefile generation instead of at build time.
The CMakeLists.txt file under the gtest folder looks like this:
cmake_minimum_required(VERSION 2.8.11)
include(ExternalProject)
set(GMOCK_VERSION "1.7.0")
set(GMOCK_DIR "${CMAKE_CURRENT_BINARY_DIR}/gmock-${GMOCK_VERSION}")
ExternalProject_Add(project_gmock
SVN_REPOSITORY http://googlemock.googlecode.com/svn/tags/release-${GMOCK_VERSION}
PREFIX ${GMOCK_DIR}
CMAKE_ARGS -DCMAKE_C_COMPILER:PATH=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER:PATH=${CMAKE_CXX_COMPILER}
# Disable update step
UPDATE_COMMAND ""
# Disable install step
INSTALL_COMMAND ""
)
ExternalProject_Get_Property(project_gmock source_dir)
ExternalProject_Get_Property(project_gmock binary_dir)
include_directories(${source_dir}/gtest/include)
add_library(gtest STATIC IMPORTED GLOBAL)
set_target_properties(gtest PROPERTIES IMPORTED_LOCATION ${binary_dir}/gtest/libgtest.a)
set_target_properties(gtest PROPERTIES INCLUDE_DIRECTORIES ${source_dir}/gtest/include)
add_dependencies(gtest project_gmock)
get_property(GTEST_INCLUDE_DIR TARGET gtest PROPERTY INCLUDE_DIRECTORIES)
set(GTEST_INCLUDE_DIR "${GTEST_INCLUDE_DIR}" PARENT_SCOPE)
add_library(gtest_main STATIC IMPORTED GLOBAL)
set_target_properties(gtest_main PROPERTIES IMPORTED_LOCATION ${binary_dir}/gtest/libgtest_main.a)
add_dependencies(gtest_main project_gmock)
include_directories(${source_dir}/include)
add_library(gmock STATIC IMPORTED GLOBAL)
set_target_properties(gmock PROPERTIES IMPORTED_LOCATION ${binary_dir}/libgmock.a)
set_target_properties(gmock PROPERTIES INCLUDE_DIRECTORIES ${source_dir}/include)
add_dependencies(gmock project_gmock)
get_property(GMOCK_INCLUDE_DIR TARGET gmock PROPERTY INCLUDE_DIRECTORIES)
set(GMOCK_INCLUDE_DIR "${GMOCK_INCLUDE_DIR}" PARENT_SCOPE)
add_library(gmock_main STATIC IMPORTED GLOBAL)
set_target_properties(gmock_main PROPERTIES IMPORTED_LOCATION ${binary_dir}/libgmock_main.a)
add_dependencies(gmock_main project_gmock)