CMake Tutorial – Chapter 4: Libraries and Subdirectories

Introduction

So far our project is rather simple. A real project would be more complicated than the one we’ve created. Let’s add subdirectories, libraries, and proper unit tests to make our project more realistic.

In this chapter we will split up our project to have a library which we can put in a subdirectory. Then we will use Google Test and Google Mock to add a more realistic unit test.

The Library in a Subdirectory

We will make the ToDo class its own library, and put it in a subdirectory. Even though it is a single source file making it a library actually has one significant advantage. CMake will compile source files once for each target that includes them. So if the ToDo class is used by our command line tool, a unit test, and perhaps a GUI App it would be compiled three times. Imagine if we had a collection of classes instead of just one. This results in a lot of unnecessary compilation.

There were some minor changes to the C++, grab the files here: [zip file] Source
(CMakeLists.txt listed below)

CMakeLists.txt

New or modified lines in bold.
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
set(CMAKE_LEGACY_CYGWIN_WIN32 0)

project("To Do List")

enable_testing()


if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR
    "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
    set(warnings "-Wall -Wextra -Werror")
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
    set(warnings "/W4 /WX /EHsc")
endif()
if (NOT CONFIGURED_ONCE)
    set(CMAKE_CXX_FLAGS "${warnings}"
        CACHE STRING "Flags used by the compiler during all build types." FORCE)
    set(CMAKE_C_FLAGS   "${warnings}"
        CACHE STRING "Flags used by the compiler during all build types." FORCE)
endif()


include_directories(${CMAKE_CURRENT_SOURCE_DIR})

add_subdirectory(ToDoCore)

add_executable(toDo main.cc)
target_link_libraries(toDo toDoCore)

add_test(toDoTest toDo)


set(CONFIGURED_ONCE TRUE CACHE INTERNAL
    "A flag showing that CMake has configured at least once.")

So now our executable “toDo” only depends on the file “main.cc” and the new library “toDoCore”. Our project also has a new subdirectory “ToDoCore”.

include_directories(directories)
Add directories to the end of this directory’s include paths. We didn’t need this before because all of our files were in the same directory.
include_directories() documentation
CMAKE_CURRENT_SOURCE_DIR
The full path to the source directory that CMake is currently processing.
CMAKE_CURRENT_SOURCE_DIR documentation
add_subdirectory(source_dir)
Include the directory source_dir in your project. This directory must contain a CMakeLists.txt file.
Note: We’re omitting the optional second parameter. This only works with subdirectories of the current directory. We will see how to add external directories later.
add_subdirectory documentation
target_link_libraries(target library…)
Specify that target needs to be linked against one or more libraries. If a library name matches another target dependencies are setup automatically so that the libraries will be built first and target will be updated whenever any of the libraries are.
If the target is an executable then it will be linked against the listed libraries.
If the target is a library then its dependency on these libraries will be recorded. Then when something else links against target it will also link against target‘s dependencies. This makes it much easier to handle a library’s dependencies since you only have to define them once when you define library itself.
For the moment we are using the simplest form of this command. For more information see the documentation .

When describing add_subdirectory() I stated that the subdirectory must contain a CMakeLists.txt file. So here’s the new file.

ToDoCore/CMakeLists.txt

add_library(toDoCore ToDo.cc)

Conveniently this file is rather simple.

add_library(target STATIC | SHARED | MODULE sources…)
This command creates a new library target built from sources. As you may have noticed this command is very similar to add_executable.
With STATIC, SHARED, and MODULE you can specify what kind of library to build. STATIC libraries are archives of object files that are linked directly into other targets. SHARED libraries are linked dynamically and loaded at runtime. MODULE libraries are plug-ins that aren’t linked against but can be loaded dynamically at runtime.
If the library type is not specified it will be either STATIC or SHARED. The default type is controlled by the BUILD_SHARED_LIBS variable. By default static libraries are created.
add_library() documentation

Testing – for Real

We have a rudimentary test but if we were really developing software we’d write a real test using a real testing framework. As mentioned earlier we will use Google Test 1.6.0 and Google Mock 1.6.0. Conveniently they include their own CMakeLists.txt files, which makes them easy for us to use.

First the test:

ToDoCore/unit_test/ToDoTest.cc

#include "ToDoCore/ToDo.h"

#include <string>
  using std::string;

#include <gmock/gmock.h>
  using ::testing::Eq;
#include <gtest/gtest.h>
  using ::testing::Test;


namespace ToDoCore
{
namespace testing
{
    class ToDoTest : public Test
    {
    protected:
        ToDoTest(){}
        ~ToDoTest(){}

        virtual void SetUp(){}
        virtual void TearDown(){}


        ToDo list;

        static const size_t taskCount = 3;
        static const string tasks[taskCount];
    };

    const string ToDoTest::tasks[taskCount] = {"write code",
                                               "compile",
                                               "test"};


    TEST_F(ToDoTest, constructor_createsEmptyList)
    {
        EXPECT_THAT(list.size(), Eq(size_t(0)));
    }

    TEST_F(ToDoTest, addTask_threeTimes_sizeIsThree)
    {
        list.addTask(tasks[0]);
        list.addTask(tasks[1]);
        list.addTask(tasks[2]);

        EXPECT_THAT(list.size(), Eq(taskCount));
    }

    TEST_F(ToDoTest, getTask_withOneTask_returnsCorrectString)
    {
        list.addTask(tasks[0]);

        ASSERT_THAT(list.size(),     Eq(size_t(1)));
        EXPECT_THAT(list.getTask(0), Eq(tasks[0]));
    }

    TEST_F(ToDoTest, getTask_withThreeTasts_returnsCorrectStringForEachIndex)
    {
        list.addTask(tasks[0]);
        list.addTask(tasks[1]);
        list.addTask(tasks[2]);

        ASSERT_THAT(list.size(),     Eq(taskCount));
        EXPECT_THAT(list.getTask(0), Eq(tasks[0]));
        EXPECT_THAT(list.getTask(1), Eq(tasks[1]));
        EXPECT_THAT(list.getTask(2), Eq(tasks[2]));
    }

} // namespace testing
} // namespace ToDoCore

This is a rather simple test, but ToDo is still a rather simple class. It may look strange if you are unfamiliar with Google Test, taking a look at Google Test Primer may be helpful. I also use a little functionality from Google Mock so Google Mock for Dummies may also be useful.

Now we need to build the test:

ToDoCore/CMakeLists.txt

New or modified lines in bold.
add_library(toDoCore ToDo.cc)

add_subdirectory(unit_test)

ToDoCore/unit_test/CMakeLists.txt

set(GMOCK_DIR "../../../../../gmock/gmock-1.6.0"
    CACHE PATH "The path to the GoogleMock test framework.")

if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
    # force this option to ON so that Google Test will use /MD instead of /MT
    # /MD is now the default for Visual Studio, so it should be our default, too
    option(gtest_force_shared_crt
           "Use shared (DLL) run-time lib even when Google Test is built as static lib."
           ON)
elseif (APPLE)
    add_definitions(-DGTEST_USE_OWN_TR1_TUPLE=1)
endif()
add_subdirectory(${GMOCK_DIR} ${CMAKE_BINARY_DIR}/gmock)

include_directories(SYSTEM ${GMOCK_DIR}/gtest/include
                           ${GMOCK_DIR}/include)


add_executable(ToDoTest ToDoTest.cc)
target_link_libraries(ToDoTest toDoCore
                               gmock_main)

add_test(ToDoTest ToDoTest)

First we add the Google Mock directory to our project then we add our test. The path to Google Mock is stored in a cached variable so that you can easily set it to the correct value either from the command line or via one of the GUIs. There are several potential problems with that line but we will worry about those later, for now it’s good enough. Okay I oversimplified a little. We don’t just add the Google Mock directory, we also work around some OS-specific problems.

When using Visual Studio to build our test we would run into a problem. Even when building static libraries, CMake’s default, MSVC defaults to linking against the multi-threaded, DLL-specific version of the standard library. By default Google Test overrides this so that the non-DLL version of the multi-threaded standard library is used. Then when our test links against both toDoCore and gmock_main the linker will output a large number of errors since we would be linking against two different copies of the standard library. To avoid this problem we force Google Test to use the DLL-specific version to match Visual Studio’s default by setting the gtest_force_shared_crt option to ON. See Microsoft C/C++ Compiler Run-Time Library.

The second problem occurs on newer version of Mac OS X which default to using a different standard library that fully supports C++11. GTest uses the tuple class from the draft TR1 standard and therefore looks for it in the std::tr1 namespace. The tr1 namespace is not present in the C++11 standard library that Apple uses so GTest cannot find it and won’t compile. We fix this by telling GTest to use its own tuple implementation.

add_subdirectory(source_dir binary_dir)
Add the directory source_dir to the current project with binary_dir as its corresponding binary output directory. When adding a directory that is a subdirectory of the current directory CMake will automatically determine what the binary output directory should be, making the second argument optional. However if you add a directory that isn’t a subdirectory you need to specify the binary output directory.
add_subdirectory documentation
CMAKE_BINARY_DIR
This variable holds the path to the top level binary output directory, i.e. the directory in which you ran the cmake command or the path you chose for “Where to build the binaries” in the GUI.
CMAKE_BINARY_DIR documentation
include_directories(AFTER|BEFORE SYSTEM directory…)
AFTER|BEFORE
Specify whether or not these include directories should be appended or prepended to the list of include directories. If omitted then the default behavior is used.
By default directories are appended to the list. This behavior can be changed by setting CMAKE_INCLUDE_DIRECTORIES_BEFORE to TRUE.
SYSTEM
Specify that these directories are system include directories. This only has an affect on compilers that support the distinction. This can change the order in which the compiler searches include directories or the handling of warnings from headers found in these directories.
directory…
The directories to be added to the list of include directories.
include_directories() documentation
option(name docstring initialValue)
Provide a boolean option to the user. This will be displayed in the GUI as a checkbox. Once created the value of the option can be accessed as the variable name. The docstring will be displayed in the GUI to tell the user what this option does. If no initial value is provided it defaults to OFF.
While this boolean option is stored in the cache and accessible as a variable you cannot override the initialValue by setting a variable of the same name beforehand, not even by passing a -D command line option to CMake. Which is why we have to define the option ourselves before Google Test does.
option() documentation
add_definitions(flags…)
Add preprocessor definitions to the compiler command line for targets in the current directory and those below it. While this command is intended for adding definitions you still need to precede them with -D.
Because this command modifies the COMPILE_DEFINITIONS directory property it affects all targets in the directory, even those that were defined before this command was used. If this is not the desired effect then modifying the COMPILE_DEFINITIONS property of particular targets or source files will work better. (Properties are introduced below.)
add_definitions() documentation
COMPILE_DEFINITIONS directory property documentation
COMPILE_DEFINITIONS target property documentation
COMPILE_DEFINITIONS source file property documentation

Let’s go ahead and try out our new test!
[zip file] Source

 > mkdir build
 > cd build
 > cmake -G "Unix Makefiles" ..
-- The C compiler identification is Clang 4.2.0
-- The CXX compiler identification is Clang 4.2.0
-- 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 PythonInterp: /usr/local/bin/python (found version "2.7.3")
-- Looking for include file pthread.h
-- Looking for include file pthread.h - found
-- Looking for pthread_create
-- Looking for pthread_create - found
-- Found Threads: TRUE
-- Configuring done
-- Generating done
-- Build files have been written to: /Documents/Programming/C++/CMake Tutorial/flavors/part4_step2/build
 > make
Scanning dependencies of target toDoCore
[ 14%] Building CXX object ToDoCore/CMakeFiles/toDoCore.dir/ToDo.cc.o
Linking CXX static library libtoDoCore.a
[ 14%] Built target toDoCore
Scanning dependencies of target toDo
[ 28%] Building CXX object CMakeFiles/toDo.dir/main.cc.o
Linking CXX executable toDo
[ 28%] Built target toDo
Scanning dependencies of target gtest
[ 42%] Building CXX object gmock/gtest/CMakeFiles/gtest.dir/src/gtest-all.cc.o
In file included from /Documents/Programming/C++/gmock/gmock-1.6.0/gtest/src/gtest-all.cc:42:
In file included from /Documents/Programming/C++/gmock/gmock-1.6.0/gtest/src/gtest.cc:132:
/Documents/Programming/C++/gmock/gmock-1.6.0/gtest/src/gtest-internal-inl.h:206:8: error: 
      private field 'pretty_' is not used [-Werror,-Wunused-private-field]
  bool pretty_;
       ^
1 error generated.
make[2]: *** [gmock/gtest/CMakeFiles/gtest.dir/src/gtest-all.cc.o] Error 1
make[1]: *** [gmock/gtest/CMakeFiles/gtest.dir/all] Error 2
make: *** [all] Error 2

Oh noes! Newer versions of Clang have some pretty strict warnings and we have just run afoul of one. So we have a problem: we want to use strict compiler settings to ensure we write good code but we also don’t want to go changing Google Test. As it turns out CMake actually provides us the flexibility we need to disable warnings for just the gtest target.

This is a capability that can easily be abused. In the case of Google Test we didn’t write it and we know, or at least assume, that it works fine. Because of that we don’t care about any warnings we might find in Google Test’s code. We need to be careful not to use this feature to allow ourselves to write poor code.

ToDoCore/unit_test/CMakeLists.txt

New or modified lines in bold.
set(GMOCK_DIR "../../../../../gmock/gmock-1.6.0"
    CACHE PATH "The path to the GoogleMock test framework.")

if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
    # force this option to ON so that Google Test will use /MD instead of /MT
    # /MD is now the default for Visual Studio, so it should be our default, too
    option(gtest_force_shared_crt
           "Use shared (DLL) run-time lib even when Google Test is built as static lib."
           ON)
elseif (APPLE)
    add_definitions(-DGTEST_USE_OWN_TR1_TUPLE=1)
endif()
add_subdirectory(${GMOCK_DIR} ${CMAKE_BINARY_DIR}/gmock)
set_property(TARGET gtest APPEND_STRING PROPERTY COMPILE_FLAGS " -w")

include_directories(SYSTEM ${GMOCK_DIR}/gtest/include
                           ${GMOCK_DIR}/include)


add_executable(ToDoTest ToDoTest.cc)
target_link_libraries(ToDoTest toDoCore
                               gmock_main)

add_test(ToDoTest ToDoTest)
set_property(TARGET gtest APPEND_STRING PROPERTY COMPILE_FLAGS ” -w”)
There are a variety of things that have properties in CMake, in this case we are interested in a target’s properties. Each target can have it’s own compiler flags in addition the ones set in CMAKE_<LANG>_FLAGS. Here we append “ -w” to gtest‘s COMPILE_FLAGS. The flag “-w” disables all warnings for both GCC and Clang. When compiling with MSVC the “-w” will be automatically converted to “/w” which has the same function. (Although it will warn that “/w” is overriding “/W4“)
COMPILE_FLAGS documentation
GCC Warning Options , currently these work for Clang too.
Microsoft C/C++ Compiler Warning Level
set_property(TARGET target_name… APPEND|APPEND_STRING PROPERTY name value…)
TARGET
Specify that we want to set the property of a target. Several other types of things have properties you can set. For the moment we are only going to deal with targets, but the concept is the same for the rest.
target_name…
The name of the target whose property you want to set. You can list multiple targets and all will have the property set the same way for each.
APPEND | APPEND_STRING
Append to the property’s existing value instead of setting it. APPEND appends to the property as a list. APPEND_STRING appends to the property as a string.
Note: Do not provide a multiple values when using APPEND_STRING as the results will not be what you expect.
Don’t worry about lists we will cover them in the next chapter.
PROPERTY
name
The name of the property you want to set. See Properties on Targets.
value…
The value to set for the property. If multiple values are provided they are treated as a list. Only provide one value if also using APPEND_STRING.
Don’t worry about lists yet.
set_property() documentation

Let’s give this version a try.
[zip file] Source

 > mkdir build
 > cd build
 > cmake -G "Unix Makefiles" ..
-- The C compiler identification is Clang 4.2.0
-- The CXX compiler identification is Clang 4.2.0
-- 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 PythonInterp: /usr/local/bin/python (found version "2.7.3")
-- Looking for include file pthread.h
-- Looking for include file pthread.h - found
-- Looking for pthread_create
-- Looking for pthread_create - found
-- Found Threads: TRUE
-- Configuring done
-- Generating done
-- Build files have been written to: /Documents/Programming/C++/CMake Tutorial/flavors/part4_step3/build
 > make
Scanning dependencies of target toDoCore
[ 14%] Building CXX object ToDoCore/CMakeFiles/toDoCore.dir/ToDo.cc.o
Linking CXX static library libtoDoCore.a
[ 14%] Built target toDoCore
Scanning dependencies of target toDo
[ 28%] Building CXX object CMakeFiles/toDo.dir/main.cc.o
Linking CXX executable toDo
[ 28%] Built target toDo
Scanning dependencies of target gtest
[ 42%] Building CXX object gmock/gtest/CMakeFiles/gtest.dir/src/gtest-all.cc.o
Linking CXX static library libgtest.a
[ 42%] Built target gtest
Scanning dependencies of target gmock
[ 57%] Building CXX object gmock/CMakeFiles/gmock.dir/src/gmock-all.cc.o
Linking CXX static library libgmock.a
[ 57%] Built target gmock
Scanning dependencies of target gmock_main
[ 71%] Building CXX object gmock/CMakeFiles/gmock_main.dir/src/gmock_main.cc.o
Linking CXX static library libgmock_main.a
[ 71%] Built target gmock_main
Scanning dependencies of target ToDoTest
[ 85%] Building CXX object ToDoCore/unit_test/CMakeFiles/ToDoTest.dir/ToDoTest.cc.o
Linking CXX executable ToDoTest
[ 85%] Built target ToDoTest
Scanning dependencies of target gtest_main
[100%] Building CXX object gmock/gtest/CMakeFiles/gtest_main.dir/src/gtest_main.cc.o
Linking CXX static library libgtest_main.a
[100%] Built target gtest_main
 > make test
Running tests...
Test project /Documents/Programming/C++/CMake Tutorial/flavors/part4_step3/build
    Start 1: ToDoTest
1/1 Test #1: ToDoTest .........................   Passed    0.00 sec
100% tests passed, 0 tests failed out of 1
Total Test time (real) =   0.01 sec
 > ToDoCore/unit_test/ToDoTest
Running main() from gmock_main.cc
[==========] Running 4 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 4 tests from ToDoTest
[ RUN      ] ToDoTest.constructior_createsEmptyList
[       OK ] ToDoTest.constructior_createsEmptyList (0 ms)
[ RUN      ] ToDoTest.addTask_threeTimes_sizeIsThree
[       OK ] ToDoTest.addTask_threeTimes_sizeIsThree (0 ms)
[ RUN      ] ToDoTest.getTask_withOneTask_returnsCorrectString
[       OK ] ToDoTest.getTask_withOneTask_returnsCorrectString (0 ms)
[ RUN      ] ToDoTest.getTask_withThreeTasts_returnsCorrectStringForEachIndex
[       OK ] ToDoTest.getTask_withThreeTasts_returnsCorrectStringForEachIndex (1 ms)
[----------] 4 tests from ToDoTest (1 ms total)
[----------] Global test environment tear-down
[==========] 4 tests from 1 test case ran. (1 ms total)
[  PASSED  ] 4 tests.

Yay! Everything works now and our test passes, too.

Next we will focus on how we could add more unit tests (if we had more units) without duplicating the work we’ve done here. Also we will make it so that our unit tests are automatically run as needed whenever we build.

Revision History
VersionDateComment
12013-05-05Original version.
22013-07-14Added line numbers and indication of changes to code samples. Added a link to the section on lists.
32014-10-01Added the work around for a problem with Google Test and newer versions of Mac OS X along with an explanation and a description of add_definitions()
Creative Commons LicenseThis entry, "CMake Tutorial – Chapter 4: Libraries and Subdirectories," by John Lamp is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
CC0To the extent possible under law, John Lamp has waived all copyright and related or neighboring rights to the code samples in this entry, "CMake Tutorial – Chapter 4: Libraries and Subdirectories".

26 thoughts on “CMake Tutorial – Chapter 4: Libraries and Subdirectories

    • Which version of OS X are you using?

      Regardless this was written on OS X 10.8 and with 10.9 Apple switched the default standard library to one that fully supports C++11. This causes issues with GTest/GMock 1.6 because it was designed before C++11 was commonly available. Oddly enough the solution is to tell GTest to use its own tuple implementation. Simply add the following before you add the GMock directory.
      add_definitions(-DGTEST_USE_OWN_TR1_TUPLE=1)

      I will update this page to add that. As to your issue with GTest/GMock 1.7 I’m not sure off the top of my head but I will look into it.

    • As for the problem with GMock 1.7.0 they are related to the above line
      set_property(TARGET gtest APPEND_STRING PROPERTY COMPILE_FLAGS " -w")
      and a fix to GMock that allows you to link against it as a DLL. To fix a very peculiar linking problem on Windows when building gmock as a DLL gmock and gmock_main no longer link against gtest instead they compile GTest in directly. However this means the problem in gtest we sidestepped now appears in gmock and gmock_main. So if we modify our workaround to be
      set_property(TARGET gtest gmock gmock_main APPEND_STRING PROPERTY COMPILE_FLAGS " -w")
      it should all build again!

  1. Hi John, thx for this tuto realy nice (and better than the official documentation ^^).

    I have a question about the first example. It should be include_directories(${CMAKE_CURRENT_SOURCE_DIR}/ToDoCore) instead of include_directories(${CMAKE_CURRENT_SOURCE_DIR}) ?

  2. Forget what I said, it is because you set the full path to the header into your main.cc, and not in my own file.

  3. This is one hell of a great tutorial. Although to be honest it would have been easier to first show how to include all the libraries and then add a separate chapter dedicated to testing only. Imho that’s a good way of separating the two and making it easier to follow. Anyways thanks a lot!

  4. Following this on Windows, I downloaded the latest version of googletest (which now includes googlemock) and when generating the solution for Visual Studio, it generated new projects for gmock, gmock_main, gtest, and gtest_main — none of which do anything when I run them. And I’m pretty sure that the RUN_TESTS project is still doing the old tests. At least, it doesn’t say anything specific about doing google tests, and the output looks nothing like what is in the example output.

    Am I doing something wrong (I mean, aside from not following the tutorial exactly, but the tutorial tells me to use old versions and I’d rather learn the newer versions…)?

    • After sleeping on it and coming back, I figured out that I need to select the unit test project (ToDoTest), build it, and then running it shows up a command prompt briefly. But if I run that from the prompt itself, I see all the output as it should be.

  5. Your article needs updating~

    COMPILE_FLAGS is deprecated. see https://cmake.org/cmake/help/v3.6/prop_tgt/COMPILE_FLAGS.html

    better to use these instead:

    target_compile_options(gtest PRIVATE -w)
    target_compile_options(gtest_main PRIVATE -w)
    target_compile_options(gmock PRIVATE -w)
    target_compile_options(gmock_main PRIVATE -w)

    I found that I have to append to the Clang Compile Flags of all of these targets. maybe because I am using gtest1.7 and gmock1.7.

    • Ah, yes, CMake has been changing a lot since version 3 came out. I’ll admit I haven’t kept up with all of them, I suppose I should at least write up what needs to be fixed and continue (I should do that part anyway) with newer versions of CMake. Although some Linux distributions, e.g. RHEL, still ship with CMake 2.8: https://cmake.org/Wiki/CMake_Life_Cycle_Considerations. I suppose the best move is to update to be compatible (no warnings) with new versions of CMake while still supporting CMake 2.8 for compatibility.

      As for needing to update all four gtest/gmock targets that is because of a change in version 1.7. Rather than having the other targets link against gtest they all compile it in directly. That change works around a problem when building them a DLLs on Windows.

  6. This is a great tutorial. I’ve learnt a lot about CMake from here and it is also the first I heard of Google Test and Google Mock which look fantastic. Thanks for this incredible resource!

    I’d just like to point out that strictly speaking you don’t need Google Mock for the code you’ve shown, but only Google Test. They’ve been merged into one project now (https://github.com/google/googletest), but they are separate entities. Your CMakeLists.txt causes both gmock and gtest to compile and link to the code even though gmock doesn’t actually do anything. I modified my CMakeLists.txt to link only to Google Test instead of Mock and it works. This way it doesn’t have to compile and link to the unnecessary gmock. I guess in the future I’d like it compile and link to both once I start using gmock functionality, but I prefer to keep them separate at this point just to have it clear in my head which functionality comes from gtest and which comes from gmock.

    Also, you seem to be using outdated syntax for the tests. According to the current primer (https://github.com/google/googletest/blob/master/googletest/docs/Primer.md) you should be using tests like

    EXPECT_EQ(list.size(), size_t(0));

    instead of

    EXPECT_THAT(list.size(), Eq(size_t(0)));

    Also, your Google Test and Google Mock documentation links are out of date and they redirect. The Primer I used it is at https://github.com/google/googletest/blob/master/googletest/docs/Primer.md and the Google Mock for Dummies is at https://github.com/google/googletest/blob/master/googlemock/docs/ForDummies.md

    PS One error I had a problem with when compiling is that once I updated the tests that refer to the variable taskCount to

    EXPECT_EQ(list.size(), taskCount);

    the final linking stage would fail saying there is an undefined reference to taskCount. The easiest fix I found was to enclose it in size_t() like

    EXPECT_EQ(list.size(), size_t(taskCount));

    which I suspect is due to some properties of C++ macros that I am unaware of.

    • You are correct that Google Mock isn’t actually needed. I also suppose it is small consolation that my links were correct when I wrote this, I should probably update them. I just went ahead and included Google Mock as it might be useful on same projects, although I haven’t really used it myself.

      As for EXPECT_EQ() versus EXPECT_THAT() I have to disagree a little. You are correct that Google Test specifies EXPECT_EQ(), but if you read the documentation for Google Mock it provides EXPECT_THAT(), which uses a Google Mock matcher for the comparison. I prefer EXPECT_THAT() over EXPECT_EQ(), etc., because I find the order of the arguments to be clearer and I like the messages it produces when the values don’t match.

      So yes, Google Mock is not needed, but I actually am using it.

      Your issue with taskCount and the linker is interesting; I would guess the compiler is optimizing out taskCount and then whatever the EXPECT_EQ() macro resolved to probably tries to get its address.

  7. This is a bit of a confusing statement: “non-DLL version of the multi-threaded standard library”. Rather than calling the library “non-DLL”, just call it the “static version of the run-time library (.lib)” since on Windows static libraries have the file extension of “.lib” and dynamic libraries have “.dll”.

    Keep on truckin’

    • Good point. I don’t use, or develop for, Windows much. I probably got confused by the fact that building dynamic libraries creates for .dll and .lib files.

  8. Now that GoogleTest and GoogleMock are in the same repository, the includes and ToDoCore/CMakeLists.txt needs to updated.

    Your includes should now be this for your ToDoTest.cxx:

        #include 
        #include 
    
        using std::string;
        using ::testing::Eq;
        using ::testing::Test;
    

    And for the ToDoCore/unit_test/CMakeLists.txt:

        set(GMOCK_DIR "../../gtest"
            CACHE PATH "The path to the GoogleMock test framework.")
    
        if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
            # force this option to ON so that GoogleTest will use /MD instead of /MT
            # /MD is now the default for Vistual Studio, so it should be our default, too
            option(gtest_force_shared_crt
                   "Use shared (DLL) runtime lib even when GoogleTest is static"
                   ON)
        elseif (APPLE)
            add_definitions(-DGTEST_USE_OWN_TR1_TUPLE=1)
        endif()
        add_subdirectory(${GMOCK_DIR} ${CMAKE_BINARY_DIR}/gmock/googlemock)
    
        include_directories(SYSTEM ${GMOCK_DIR}/googletest/include
                                   ${GMOCK_DIR}/googlemock/include)
    
    
        add_executable(ToDoTest ToDoTest.cxx)
        target_link_libraries(ToDoTest ToDoCore
                              gmock_main)
    
        add_test(ToDoTest ToDoTest)
    

    That should do it!

    • Thanks! In the meantime there have been a bunch of changes that require updates. Than you for the updates for Google Test and Google Mock.

  9. I have no idea how to modify this for the newest google test version. Thanks for your write-ups and effort for the past 4 posts. I’ve read three “how to cmake” tutorials and this was was the most comprehensible of them all.

Leave a Reply

Your email address will not be published. Required fields are marked *