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

Version Date Comment
1 2013-05-05 Original version.
2 2013-07-14 Added line numbers and indication of changes to code samples. Added a link to the section on lists.
3 2014-10-01 Added 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()

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. 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!

  3. 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.

  4. 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.

  5. 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.

  6. 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’

  7. 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!

  8. 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 to Austin Salgat Cancel reply

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