Professional CMake:

A Practical Guide

Learn to use CMake effectively with practical advice from a CMake co-maintainer. You can also have the author work directly with your team!

Building GoogleTest and GoogleMock directly in a CMake project

UPDATED December 2015:
Since the original article was written, gtest and gmock have been merged and moved into a single repository on Github under the name GoogleTest. I’ve updated the content here to reflect the changes and the article now also covers both gtest and gmock. I’ve also revised the general purpose implementation to make it more flexible, expanded its documentation and made it available on Github under a MIT license. I hope you find it useful.

UPDATED September 2019:
The generalised implementation was extended further and became the FetchContent module, which was added to CMake in 3.11. The module documentation uses GoogleTest in some of its examples.


Using gtest/gmock with CMake is awesome. Not so awesome is when you don’t have a pre-built gtest/gmock available to use. This article demonstrates a convenient way to add them with automated source download and have them build directly as part of your project using add_subdirectory(). Unlike other common approaches, no manual information has to be provided other than the package to download. The approach is general enough to be applied to any CMake-based external project, not just gtest/gmock.

Before proceeding, I should highlight that if you are only interested in gtest and you do have a pre-built gtest available (e.g. it is provided by the system or you are happy to manually build it outside of your project), then CMake makes it trivial to bring gtest into your project with the find_package() command. See the FindGTest and GoogleTest modules for more information on this approach, which is simple and provides some nice extra features for defining tests.

The conventional approach

I’ll focus for the moment on gtest, since it’s a little simpler than gmock, but the concepts are similar for both. When a fully integrated download and build of gtest is required, typical advice for building it as part of your CMake project is based around using ExternalProject. The gtest library is created as part of your build, but not in a way which makes the CMake targets available to you automatically. This means you end up manually adding additional CMake code to define the libraries, import targets, etc. to make it seem like gtest is a fully integrated part of your build. This is unfortunate, since this is precisely the sort of thing CMake is supposed to be doing for you.

In reality, the typical way ExternalProject is used results in a separate, self-contained sub-build which your CMake project essentially sees as a black box. A simplified version of the regular ExternalProject approach looks something like this (adapted from a more complete example implementation available here, but using a now outdated URL):

include(ExternalProject)
ExternalProject_Add(googletest
    URL https://googletest.googlecode.com/files/gtest-1.7.0.zip
    URL_HASH SHA1=f85f6d2481e2c6c4a18539e391aa4ea8ab0394af
    INSTALL_COMMAND ""
)

ExternalProject_Get_Property(googletest binary_dir)
add_library(gtest UNKNOWN IMPORTED)
add_library(gtest_main UNKNOWN IMPORTED)
set_target_properties(gtest PROPERTIES
    IMPORTED_LOCATION ${binary_dir}/libgtest.a
)
set_target_properties(gtest_main PROPERTIES
    IMPORTED_LOCATION ${binary_dir}/libgtest_main.a
)
add_dependencies(gtest googletest)
add_dependencies(gtest_main googletest)

The annoying part of the above is the need to manually create the gtest and gtest_main import libraries. The above is just assuming a particular platform, etc., but to make this fully general for all platforms, compilers, etc. would make the above considerably more complex. CMake has all the required information already, but it is buried in the external project. What we really want is to have gtest included as part of our build directly, not built as a separate, external project.

Getting CMake to do all the work instead

When used in the normal way, ExternalProject performs its download and build steps during the main project’s build phase, but what we really want is to have the download and unpacking steps performed at configure time (ie when CMake is run) and then pull in the source directory with add_subdirectory() rather than having ExternalProject build it. This would make gtest/gmock a fully integrated part of our build and give us access to all the targets, etc. that CMake already defines for us.

While ExternalProject doesn’t natively allow us to perform the download and unpacking steps at configure time, we can make it do so. We achieve this by invoking ExternalProject as an external build which we perform at configure time. While this sounds convoluted, it is actually relatively straightforward.

We first create a template CMakeLists.txt file to use for the external build (we will use simple examples here which are specific to GoogleTest, but the general implementation provided on Github is set up to support any project). It’s contents look something like this:

cmake_minimum_required(VERSION 2.8.2)
project(googletest-download NONE)

include(ExternalProject)
ExternalProject_Add(googletest
    GIT_REPOSITORY    https://github.com/google/googletest.git
    GIT_TAG           main
    SOURCE_DIR        "${CMAKE_BINARY_DIR}/googletest-src"
    BINARY_DIR        "${CMAKE_BINARY_DIR}/googletest-build"
    CONFIGURE_COMMAND ""
    BUILD_COMMAND     ""
    INSTALL_COMMAND   ""
    TEST_COMMAND      ""
)

Assuming the above was saved into a file called CMakeLists.txt.in, we would use it like so in our main project’s CMakeLists.txt:

# Download and unpack googletest at configure time
configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt)
execute_process(COMMAND "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" .
    WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/googletest-download"
)
execute_process(COMMAND "${CMAKE_COMMAND}" --build .
    WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/googletest-download"
)

# Prevent GoogleTest from overriding our compiler/linker options
# when building with Visual Studio
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)

# Add googletest directly to our build. This adds the following targets:
# gtest, gtest_main, gmock and gmock_main
add_subdirectory("${CMAKE_BINARY_DIR}/googletest-src"
                 "${CMAKE_BINARY_DIR}/googletest-build"
)

# The gtest/gmock targets carry header search path dependencies
# automatically when using CMake 2.8.11 or later. Otherwise we
# have to add them here ourselves.
if(CMAKE_VERSION VERSION_LESS 2.8.11)
    include_directories("${gtest_SOURCE_DIR}/include"
                        "${gmock_SOURCE_DIR}/include"
    )
endif()

# Now simply link your own targets against gtest, gmock,
# etc. as appropriate

A few key features should be noted. The configure_file() command copies our template CMakeLists.txt.in file to the build area and the target file name must be CMakeLists.txt (the add_subdirectory() command requires this). The configure_file() command also does variable substitution, so the actual value of CMAKE_BINARY_DIR will be replaced with the current value when the file is copied. Another key feature is the way we invoke CMake to setup and execute a sub-build of the CMakeLists.txt file we just copied (this is what the two execute_process() commands are doing). These are simply forcing CMake to immediately fully process the CMakeLists.txt file we copied rather than waiting until build time. This is the crucial feature of the technique.

It should be noted that GoogleTest will modify the compiler/linker options internally unless it is explicitly told not to. This is unfortunate, but easily handled by setting the inaccurately named gtest_force_shared_crt option to TRUE (all this does is prevent GoogleTest from modifying options rather than force it to use shared libraries!). Looking through the GoogleTest project reveals that setting BUILD_SHARED_LIBS to TRUE also has the same effect, but this latter option is not specific to GoogleTest and may have other side effects.

With the above, our project now has gtest, gtest_main, gmock and gmock_main targets directly available to it. CMake knows what library file names are relevant, it will use the same build settings as the rest of our project (e.g. build type Debug or Release, which compiler, etc.) and it will work on all platforms, compilers and CMake generators without having to specify anything manually. It will also add the relevant header search path to any target linking against any of these library targets.

CMake also manages the git clone and checkout steps for us and ensures it doesn’t repeat those steps unless it is required, such as if you change the URL of the git repository, change the git tag or delete the build output directory. In short, CMake is doing all the work for you, as it should!

Generalising for any external project

There’s no inherent assumption in the above which restricts this approach to just gtest or gmock. It can be generalised to support any external project which uses CMake as its build system. This essentially amounts to parameterising the project name and the download, source and binary directories inside a CMake function. It can also be made to support more than just git clone/checkout as a download method, it can easily support anything ExternalProject itself supports. I’ve already gone ahead and done all the work for you, so just grab it from the Github project associated with this article. It even includes a simple example with gtest and gmock test cases to show how to use it. Enjoy!

Have a CMake maintainer work on your project

Get the book for more CMake content

85 thoughts on “Building GoogleTest and GoogleMock directly in a CMake project”

  1. Hello 🙂 Very nice stuff.

    I’ve tried doing the same for gmock, but failing miserably with really weird errors.
    Have you tried using this technique for gmock?

    br
    Frederic

    Reply
    • Hi Frederic. No, I haven’t done any work with gmock to date. I had a quick look just now, but it does seem to be a bit different to gtest (the latter defines all it needs for being pulled in via add_subdirectory, but it looks like gmock might not out of the box).

      Reply
  2. Hi Craig. By any change have in this time from your last reply configured gmock, it is just that I need to use it whit a similar technique you used for gtest, but since I am a beginner in this matters it looks quit complicated for me, any help you share I will appreciated a lot.

    Reply
  3. I’m fairly new to CMake, but this seems like a nice approach. I’m trying to do this with libtins (http://libtins.github.io/) and running into problems, because there the cmake file uses CMAKE_SOURCE_DIR, assuming it to be the top level of the library src hierarchy, which obviously fails when it is loaded via add_subdirectory. Is there a way to workaround this that doesn’t involve patching the library?

    Reply
    • From a quick look at libtins, it seems to only use CMAKE_SOURCE_DIR in two places. One is to add something to CMAKE_MODULE_PATH and the other is to test whether the gtest sources are in its source tree. The latter case you should be able to ignore if you are already adding gtest via your own top level project, or if you prefer to let libtins download/own gtest, you would need to make your libtins download step use git and do a clone/checkout which pulled in the git submodules (I’ll leave that as an exercise for you and your favourite search engine).

      For the case where libtins is adding a path to CMAKE_MODULE_PATH, you can do this yourself in your top level project’s CMakeLists.txt (i.e. add the path the libtins is trying to but will get wrong). You would need to do something like this (assuming you’ve followed a similar pattern for libtins as what this article does for gtest):


      list(APPEND CMAKE_MODULE_PATH "${CMAKE_BINARY_DIR}/libtins-src/cmake/Modules")
      add_subdirectory(${CMAKE_BINARY_DIR}/libtins-src
      ${CMAKE_BINARY_DIR}/libtins-build
      EXCLUDE_FROM_ALL )

      Reply
      • I’m assuming you mean for where the dependency is downloaded to (I corrected your comment since it referred to CMAKE_CURRENT_DIR rather than CMAKE_BINARY_DIR, but the former doesn’t exist). We originally used CMAKE_CURRENT_BINARY_DIR, but what we found was that sometimes developers wanted to go look at the source and it was often hard to find when it was buried deep in the build tree. Since moving our downloads to the top of the tree, our developers have been much happier.

        Reply
        • Yes I meant CMAKE_BINARY_DIR thanks for fixing. For me I had to change the variable for using not in top level. Otherwise cmake.exe was launching recursively.

          Reply
  4. I am trying to use this, but getting confused in how to add other things to the CMakeLists.txt file. Or should I make another CMakeLists.txt and put this one inside a src/gtest folder? Thoughts? Guidance? I am using your generic implementation linked in the article.

    Reply
    • In the generic implementation’s zip file, the CMakeLists.txt included there is just an example. It is meant to show how a top level CMakeLists.txt might be structured to implement the technique discussed in the article. The call to add_dl_project() can be modified to download whatever it is you want to download rather than gtest. Alternatively, if you want to keep gtest and add another project to download as well, then simply add another call to add_dl_project() later in the CMakeLists.txt file. In most cases, just a call to add_dl_project() with the appropriate URL and URL_HASH specified should be enough. Just make sure each call to add_dl_project() uses a different value for the PROJ argument.

      If you need to use GIT, SVN or something else instead of URL and URL_HASH, you would need to modify the add_dl_project.cmake and add_dl_project.CMakeLists.cmake.in files to handle that. I left them supporting just URL and URL_HASH to keep it simple.

      If that doesn’t get you going in the right direction, perhaps provide more information on where you are getting stuck.

      Reply
      • copy that. Mostly new usage issues. Lots that you can do, and this one is very useful. I am trying to setup my projects CMakeLists.txt and wanting to make sure of googleTest and GoogleMock (they moved them to github, btw). I thought it would be cool for someone (on my team) to be able to use this to get google test the right way (ie built for/in project and not installed). The other challenge is, what if I want this on a build server like Jenkins, is it sufficient enough to get all the libraries and such for building during CI. I am working this out right now and will post my progress.

        Reply
      • Ah…it is the out of source problem. I am looking in to how to do that. I added a folder
        RootProject
        ->test
        –>”This is where I put the googletest code”
        ->library1
        ->library2

        RootProjectBuild

        I generate an eclipse project after that has two projects…one for the source folder that is connected to my repo, and another that is the build folder.

        Thoughts on best way to approach this?

        Reply
      • So, I guess out of source isn’t the issue I should focus on..it is the top-level CMakeLists.txt
        Once I add a folder to the rootFolder, and put a CMakeLists.txt at the top level, and maybe add_subdirectory(test) , i get an error that that googletest-src isn’t an existing directory.
        Do I need to modify the source shown in DownloadProject.cmake?

        “`
        add_subdirectory given source
        “/home/wegunterjr/Documents/projects/gTestCrascit_DownloadOnlyBuild/DownloadProject/googletest-src”
        which is not an existing directory.

        CMake Error at test/CMakeLists.txt:27 (target_include_directories):
        Cannot specify include directories for target “gtest” which is not built by
        this project.

        CMake Error at test/CMakeLists.txt:28 (target_include_directories):
        Cannot specify include directories for target “gmock_main” which is not
        built by this project.

        “`

        Reply
      • aha…I found where in the Download.cmake where it was naming just the top level of the project, and made a change to look in the test folder.

        Reply
      • Thanks for the motivation to update the article. It now reflects the new combined home of gtest/gmock on Github. I’ve also cleaned up the generalised implementation so it should be more useful to others. There’s nothing in there that I’m aware of which would make any platform difficult. While I haven’t specifically tested it on Windows yet, I’d expect no particular problems with the approach presented.

        Reply
  5. It’s a very clever solution and an excellent post. Thanks.

    Notice that it won’t work if you are cross-compiling, i.e. using -DCMAKE_TOOLCHAIN_FILE=. It is unlikely that you would want to cross-compile your unit testing projects, though you may use gtest for other kind of tests. Anyways, it wouldn’t work to link third party libraries into your cross-compiled binaries.

    Reply
    • Sorry for the delay in replying. I don’t see why it wouldn’t work when cross compiling too. The download step doesn’t care what compiler you are using (the DownloadProject general implementation explicitly disables all languages for the ExternalProject build) and when you bring in the externally downloaded source into your main project with add_subdirectory(), it will use the same compiler settings as your main build. If there’s some other scenario you were thinking of though, by all means please clarify.

      Reply
  6. I really like the solution and applied it successfully. However, it appears to have one drawback. It automatically adds all the files from google test and mock to the default ‘install’ target, i.e. when I run ‘make install’ (or ‘ninja install’), it installs a lot of headers from gtest/gmock and event the static libraries.

    Do you see any way how to prevent this?

    Reply
    • Indeed, that’s an unfortunate downside. The usual way to control what gets installed is by specifying components in your install() commands. If you’re using cpack, then all you need to do is request just those components you want included and that should allow you to exclude any gtest or gmock content (see the CPACK_COMPONENTS_ALL variable of the CPackComponent module). If you’re just doing a straight make install, then you may want to consider the approach discussed in this stackoverflow answer where defining some custom install targets looks close to what you need.

      Another alternative which might work (untested) is you could try using the EXCLUDE_FROM_ALL flag in your call to add_subdirectory() when pulling in the googletest code. This might remove that subdir’s contents from the install, I’m not sure. I haven’t been able to find anything in the CMake docs which would confirm it either way. Note, however, that EXCLUDE_FROM_ALL has problems with the Xcode generator, so if you need to support that then this may not be a suitable approach for you. This issue may be worth a read.

      Reply
      • Thanks for the quick reply. Using EXCLUDE_FROM_ALL for the subdirectory did the trick. I didn’t know that it can also be applied to a call to add_subdirectory instead of individual targets.

        Reply
  7. This is a very nice solution. But I am having some problems (am sure simple ones, but can’t figure out myself). Any thoughts? I think the second error is because of the first one.

    I see that gtest-download directory contains CMakeLists.txt copied over from the template. I put the https link in double quotes, but that does not help or hurt.

    CMake Error at C:/Program Files/CMake/share/cmake-3.6/Modules/ExternalProject.cmake:1745 (message):
    error: could not find git for clone of googletest
    Call Stack (most recent call first):
    C:/Program Files/CMake/share/cmake-3.6/Modules/ExternalProject.cmake:2473 (_ep_add_download_command)
    CMakeLists.txt:6 (ExternalProject_Add)
    — Configuring incomplete, errors occurred!
    See also “C:/MyProject/build/gtest-download/CMakeFiles/CMakeOutput.log”.

    Microsoft (R) Build Engine version 14.0.24720.0
    Copyright (C) Microsoft Corporation. All rights reserved.
    MSBUILD : error MSB1009: Project file does not exist.
    Switch: ALL_BUILD.vcxproj

    CMake Error at CMakeLists.txt:36 (add_subdirectory):
    The source directory
    C:/MyProject/build/gtest-src
    does not contain a CMakeLists.txt file.

    Any idea, why the download is not happening? Thanks.

    Reply
    • The first error message is coming from inside the ExternalProject_Add() function at the point where it is checking whether it could find git. Inside ExternalProject_Add()‘s implementation, there is a call to find_package(Git) which should find Git, but in your case it would seem that no git command/package could be found. Check if you actually have a git client installed and whether it is on the PATH. Everything after that error doesn’t really have meaning, since if it can’t find git, it can’t download the package and naturally everything after that would fail.

      Reply
      • Thanks Scott. I did figure out that the PATH was not set correctly. Wish I had looked into it before posting … But thanks though for the response and a simple, clean solution to adding external projects.

        Reply
        • Quick question: after the initial set up of Gtest in Release/Debug configuration, I am wondering if I can execute some of the commands only on demand – saving a few seconds. I am thinking of introducing a project wide CMake variable, say, set (DONWLOAD_GTEST FALSE), and then only if it is true execute the 3 commands: External Project Add, CMake generator and CMake build for google test. It seems to work without interfering in the build of my own project and use of Gtest. Do you have a better idea? Thanks again for your time.

          Reply
          • It is pretty common to use a cache variable to enable/disable part of a build. I’d recommend you have a look at using the option() command rather than a raw set() command. Then you just need to ensure your project honours that setting everywhere it needs to (i.e. only download/build gtest and only build and add the tests relying on gtest if the option is on). It sounds like this is more or less what you are doing.

  8. Hi Craig,

    Love this solution, might even use it as an alternative to git submodules. I was wondering, however, why you chose to put the sources inside the binary directory?

    Cheers!

    Reply
    • The sources are downloaded at build time (well, technically at configure time, which is a little earlier), making them a build artifact. The source tree should not be modified by anything created as part of the configure/build steps. A developer may have one source tree but multiple build trees, e.g. for different build types like Debug, Release, etc. or perhaps with different sets of options which could theoretically result in different source dependencies being downloaded. The builds need to be fully independent of each other and you can’t do that if they put things in the source tree.

      In general, an out of source build should not modify the source tree even without this download functionality. A developer should be able to simply delete their build directory to completely remove the build and anything created by it.

      Reply
  9. Craig,

    Thanks for the informative article! I’m looking for more stability in my libs, so the zip/tarball-based option is better for me. However, it seems as though any code I try (yours or otherwise) downloads empty files, and subsequently tries to unarchive them, causing a segfault. I’ve tried cleaning out all the cmake files, creating new projects, running the downloads on different networks, and manually placing the files myself, but nothing seems to work. For reference, I’m using the EAP version of CLion, if that makes a difference. Any help would be much appreciated!

    Thanks!

    Reply
    • I can’t really offer much guidance without seeing the code. I suggest you post a question on stackoverflow with as much detail as you can (sample CMakeLists.txt, etc). There are people on there who may be able to help and it is a better medium for investigating questions like this.

      Reply
  10. Thanks for the post. This is the only reasonable way to deal with gtest. In general I am trying to stay away from it, but once the project stuck using it, this is the way to make it a bit less painfull.

    Reply
  11. What I do not like about this solution is that it spams the whole projects Cmake cache with variables from the googletest/googlemock cmake file that are (typically) unnecessary (e.g. INSTALL_GTEST INSTALL_GMOCK gtest_build_samples gtest_build_tests).

    Reply
    • Yes, that can be annoying. If it really bothers you though, there are ways to hide them. For example, you can mark each variable you want to hide as advanced or you can make them INTERNAL after you’ve pulled in googletest with add_subdirectory() (e.g. set(someVar "${someVar}" CACHE INTERNAL "") or set_property(CACHE someVar PROPERTY TYPE INTERNAL)).

      Reply
    • At build time, you can probably set CMAKE_RUNTIME_OUTPUT_DIRECTORY, CMAKE_LIBRARY_OUTPUT_DIRECTORY and/or CMAKE_ARCHIVE_OUTPUT_DIRECTORY just before you pull in gtest/gmock via add_subdirectory to control where the built targets are located. But you shouldn’t really need to care where it gets put. Just refer to the targets and let CMake work out where it all ends up on the file system. I typically build gtest/gmock as a static library so that on Windows you don’t have to add the gtest/gmock library’s directory to the PATH for a shared library to be found.

      Reply
      • Sorry, I was totally unclear. I mean IDE feature ‘virtual folders’. Consider from the link above set_property(TARGET math PROPERTY FOLDER "libraries").

        Reply
        • You’ve probably answered your own question then. Does something like the following not do what you want:

          set_property(TARGET gtest      PROPERTY FOLDER GoogleTest)
          set_property(TARGET gtest_main PROPERTY FOLDER GoogleTest)
          set_property(TARGET gmock      PROPERTY FOLDER GoogleTest)
          set_property(TARGET gmock_main PROPERTY FOLDER GoogleTest)
          
          Reply
    • Once the project is downloaded, very frequently you want to add it to your project immediately with add_subdirectory(), but since the source won’t typically be an immediate subdirectory, you have to explicitly give a second argument to add_subdirectory() to indicate where its binary directory should be. You can see this in the example CMakeLists.txt where the binary directory is given to add_subdirectory() as ${CMAKE_BINARY_DIR}/googletest-build.

      The example is actually a simplification of a more general situation where the add_subdirectory() call might be nested deep in some source hierarchy. It makes sense to keep the downloaded source and its associated build directories close to each other, which you can only do if you explicitly specify the build directory with the add_subdirectory() call.

      Reply
    • The method relies on being able to substitute some variables as part of the call to configure_file(), so it can’t use COPYONLY. Without this substitution, the technique will not work. Note that calling configure_file() doesn’t modify or clear any variables in the scope of its caller, so the problem you mention you encountered is hard to follow. Perhaps if you can provide a clear example, you can open an issue in the GitHub project of the general implementation linked to at the end of the article.

      Reply
  12. Hi, I found your reply on this StackOverflow post: https://stackoverflow.com/questions/24917260/how-to-build-gtest-within-project-and-use-find-packagegtest-and-gtest-add-test , but unfortunately I don’t have the reputation to comment there, so I’ll do it here.

    Just to make sure I understand: If I build GoogleTest directly in my CMake project, there is no way of using functions like gtest_add_tests or gtest_discover_tests, which are defined in CMake’s GoogleTest module?

    Reply
    • In CMake 3.9, those functions were split out from the FindGTest module to the new GoogleTest module precisely so that they could be used for a gtest that you build as part of your project. The signature of the gtest_add_tests() function was also improved in that same release. The gtest_discover_tests() function was only added in CMake 3.10. There’s no reason you can’t use those functions with a gtest built as part of the project and most of my projects at work do exactly that.

      Reply
  13. Thank you very much for the solution. Nevertheless, I’m getting the following errors:

    CMakeFiles/gtest-test-suite.dir/test-gtest/data_testsuite.cpp.o: In function _GLOBAL__sub_I_Trace_Module_IDs_T:
    data_testsuite.cpp:(.text.startup+0x54): undefined reference to testing::internal::MakeAndRegisterTestInfo(char const*, char const*, char const*, char const*, void const*, void (*)(), void (*)(), testing::internal::TestFactoryBase*)
    data_testsuite.cpp:(.text.startup+0x93): undefined reference to testing::internal::MakeAndRegisterTestInfo(char const*, char const*, char const*, char const*, void const*, void (*)(), void (*)(), testing::internal::TestFactoryBase*)
    data_testsuite.cpp:(.text.startup+0xd2): undefined reference to testing::internal::MakeAndRegisterTestInfo(char const*, char const*, char const*, char const*, void const*, void (*)(), void (*)(), testing::internal::TestFactoryBase*)
    data_testsuite.cpp:(.text.startup+0x112): undefined reference to testing::internal::MakeAndRegisterTestInfo(char const*, char const*, char const*, char const*, void const*, void (*)(), void (*)(), testing::internal::TestFactoryBase*)
    data_testsuite.cpp:(.text.startup+0x153): undefined reference to testing::internal::MakeAndRegisterTestInfo(char const*, char const*, char const*, char const*, void const*, void (*)(), void (*)(), testing::internal::TestFactoryBase*)
    CMakeFiles/gtest-test-suite.dir/test-gtest/data_testsuite.cpp.o:data_testsuite.cpp:(.text.startup+0x193): more undefined references to testing::internal::MakeAndRegisterTestInfo(char const*, char const*, char const*, char const*, void const*, void (*)(), void (*)(), testing::internal::TestFactoryBase*) follow
    collect2: error: ld returned 1 exit status
    CMakeFiles/data-test-gtest.dir/build.make:75: recipe for target 'data-test-gtest' failed
    make[3]: *** [data-test-gtest] Error 1
    CMakeFiles/Makefile2:260: recipe for target 'CMakeFiles/data-test-gtest.dir/all' failed
    make[2]: *** [CMakeFiles/data-test-gtest.dir/all] Error 2
    CMakeFiles/Makefile2:272: recipe for target 'CMakeFiles/data-test-gtest.dir/rule' failed
    make[1]: *** [CMakeFiles/data-test-gtest.dir/rule] Error 2
    Makefile:240: recipe for target 'data-test-gtest' failed
    make: *** [data-test-gtest] Error 2
    

    Do you know why that could be?

    Reply
    • I haven’t seen that error specifically and don’t have any immediate suggestions. Perhaps post your question on somewhere like stackoverflow with a link to the full project, which would allow interested parties to understand more about how you’ve set things up.

      Reply
  14. I stumbled upon this solution and it’s really nice. However, I’m wondering how it can be used for a project that doesn’t use cmake. Here, add_subdirectory(.. gtest) relies on the fact that it defines its own library targest with cmake and integrates nicely. But if I need to setup with add_library EXTERNAL how might this look?

    Reply
    • The main reason why you would typically want to download at configure time rather than build time is so that you can bring the downloaded project into the main project via add_subdirectory() or include(). If the other project is not a CMake project, then there’s likely not much reason to do the download during configure, you’d be better off doing it at build time using ExternalProject_Add() in the more traditional manner. That allows you to support any other build system at build time, not just CMake.

      Reply
  15. Excellent post. Thanks.

    Just a heads up that If your main build is making use of toolchain files you may have issues where CC & CXX environment variables are set to the values from the toolchain file.

    See this issue for more details: https://gitlab.kitware.com/cmake/cmake/issues/16356

    The following worked for me:

    execute_process(COMMAND
    ${CMAKE_COMMAND} -E env –unset=CC –unset=CXX
    ${CMAKE_COMMAND} . WORKING_DIRECTORY “${build_dir}”)
    execute_process(COMMAND
    ${CMAKE_COMMAND} -E env –unset=CC –unset=CXX
    ${CMAKE_COMMAND} –build . WORKING_DIRECTORY “${build_dir}”)

    Reply
    • The CC and CXX environment variables shouldn’t matter here. The two external_process() calls used by the technique presented in the article don’t actually build anything, they only download things. The project(... NONE) command explicitly disables all language evaluation, so there should be no use of the compilers at all. Therefore, this technique should have no problem with cross compilation and using toolchain files (indeed we have been doing this frequently at the company where I work).

      Reply
      • Aha, and that’s the crux of my issue. I somehow managed to miss the “project(… NONE)” command and I was hitting all kinds of issues when cross compiling. Thanks very much for clarifying this for me. Again excellent post!

        Reply
  16. I’m looking for a way to get cmake to include existing msvc projects in a generated solution. Could FetchContent somehow help with that?

    Reply
  17. I have a project compiled using arm-none-eabi-gcc, and I want to integrate googletest. In one CMakeLists.txt, I would like to

    Compile the .elf for my embedded target using arm-none-eabi-gcc
    Compile a googletest binary that I can execute on x86 machine (i.e. g++)

    In the post you mentioned that, “… [CMake] will use the same build settings as the rest of our project (e.g. build type Debug or Release, which compiler, etc.” Does this mean that I am only use one compiler? It would be either arm-none-eabi-gcc or g++ to compile both the embedded binary and googletest binary? How can I workaround this?

    Reply
  18. CMake only supports using one compiler in a build. If you need to use a different compiler for part of your build, you have to arrange for that to take place as a separate build off to the side. Something like ExternalProject is likely to be useful in achieving this.

    Reply
  19. Thanks for the suggestion Craig. I don’t know too much about ExternalProject yet but noticed the code in CMakeLists.txt.in uses it. Are you suggesting that I modify the ExternalProject_Add command in there?

    Or are you perhaps suggesting that I should wrap everything below into another ExternalProject?

    # Download and unpack googletest at configure time
    
    configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt)
    execute_process(COMMAND "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" .
        WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/googletest-download"
    )
    execute_process(COMMAND "${CMAKE_COMMAND}" --build .
        WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/googletest-download"
    )
    
    # Prevent GoogleTest from overriding our compiler/linker options
    # when building with Visual Studio
    
    set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
    
    # Add googletest directly to our build. This adds the following targets:
    #  gtest, gtest_main, gmock and gmock_main
    
    add_subdirectory("${CMAKE_BINARY_DIR}/googletest-src"
                     "${CMAKE_BINARY_DIR}/googletest-build"
    )
    
    # The gtest/gmock targets carry header search path dependencies
    # automatically when using CMake 2.8.11 or later. Otherwise we
    # have to add them here ourselves.
    
    if(CMAKE_VERSION VERSION_LESS 2.8.11)
        include_directories("${gtest_SOURCE_DIR}/include"
                            "${gmock_SOURCE_DIR}/include"
        )
    endif()
    

    Now simply link your own targets against gtest, gmock,
    etc. as appropriate

    Reply
  20. Are you suggesting that I modify the ExternalProject_Add command in there?

    No, that use of ExternalProject is just to get the gtest sources downloaded. It isn’t doing any actual build. What I’m talking about in my suggestion here is to use ExternalProject to do your actual build with a different compiler.

    But taking a step back, it sounds strange that you would build for one target platform but then build test binaries for a different platform. Perhaps instead you want to be building your test code for the target platform as well, but run it under an emulator on your host? If that’s the case, have a look into the CMAKE_CROSSCOMPILING_EMULATOR variable as a way to get that set up. If you have my book, there’s a small section about it (see the “Cross-compiling And Emulators” section in the “Testing” chapter).

    Reply
  21. Hi Craig, I’m trying to download gtest in a different directory by using the option DOWNLOAD_DIR but I’m getting the following error.

    CMake Error: The source directory "/home/lebowski/Dune_Learning/vfs/ext_modules" does not appear to contain CMakeLists.txt.
    Specify --help for usage, or press the help button on the CMake GUI.
    CMake Error at DownloadProject.cmake:173 (message):
      CMake step for googletest failed: 1
     Call Stack (most recent call first):
       CMakeLists.txt:16 (download_project)
    
    
    -- Configuring incomplete, errors occurred!
    See also "/home/lebowski/Dune_Learning/vfs/build/CMakeFiles/CMakeOutput.log".
    

    This is my CMake file.

    cmake_minimum_required(VERSION 3.0)
    set(This vfs)
    project(${This} C CXX)
    set(CMAKE_CXX_STANDARD 11)
    
    #set(VFSMODULE vfs)
    
    #add_subdirectory(${VFSMODULE})
    #add_subdirectory(test)
    
    Download GTEST and GMOCK
    
    GTEST and GMOCK got combined into a single project
    
    include(DownloadProject.cmake)
    download_project(PROJ                googletest
                     GIT_REPOSITORY      https://github.com/google/googletest.git
                     GIT_TAG             master
                     DOWNLOAD_DIR        ext_modules
                     PREFIX              ext_modules
    )
    
    add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
    
    Reply
  22. Hi, I’m having some problem. My environment work with Zephyr and when I’m trying to launch the google test, I receive this error message;

    CMake Error at build/googletest-src/googletest/cmake/internal_utils.cmake:193 (target_compile_features):
    target_compile_features no known features for CXX compiler

    “GNU”

    version 8.3.1.

    I created the new CMakeLists.txt.in with the exact same code and in my CMakeLists.txt I added the other code, exactly the same also. But I’m not able to compile the code. Could you try to help me?

    Reply
  23. This looks like a problem in GoogleTest itself, so you should follow up with the GoogleTest community to see if they can help you out. If you have no luck there, you could try the CMake forum, but I think it’s really a problem for GoogleTest to resolve (looks like they have listed a compiler feature that CMake doesn’t support, not sure where it is coming from though).

    Reply
  24. So all this does is jus download gtest. Now when I try to add the executable
    add_executable(runTests tests.cpp) in CMakeLists.txt

    and try to do:
    cmake .
    make
    in make it gives an error
    [100%] Building CXX object CMakeFiles/runTests.dir/tests.cpp.o
    tests.cpp:3:25: fatal error: gtest/gtest.h: No such file or directory
    #include <gtest/gtest.h>
    ^
    compilation terminated.
    collect2: error: ld returned 1 exit status
    make[2]: *** [runTests] Error 1
    make[1]: *** [CMakeFiles/runTests.dir/all] Error 2
    make: *** [all] Error 2

    I am using Linux platform and I really can not figure why it is not able to find the gtest.h

    Reply
  25. Looks like you didn’t link your target to the gtest or gtest_main target. If you did, it should have automatically added the required directory to that target’s header search path. You might also want to consider linking to the GTest::GTest or GTest::Main targets instead if they are available (I don’t recall what targets the gtest sources provide and I seem to recall it might differ from the ones CMake’s own FindGTest module provides, at least for some versions of gtest).

    Reply
  26. Hey Craig! I am trying to do #include<gmock/gmock.h> but it isn’t working. Neither is #include<gtest/gmock.h> working. Can you please let me know the exact lines of code required for including the gmock into out code?

    Reply
    • Make sure your target links to the gmock or gmock_main target. My recollection is that should add the necessary header search paths. If not, I’m afraid you’ll need to investigate this one yourself (maybe ask in the forums if you need further help). I haven’t used gmock in a long time and don’t recall any special steps apart from linking to the relevant target.

      Reply

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.