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.

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

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:

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!

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

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.

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?

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

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.

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.

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.

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.

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.

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.

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.

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.

CI builds for Windows (VS20015) have been added for DownloadProject, the general implementation on GitHub associated with this article. This service provided by the great guys at AppVeyor complements the existing Linux and Mac builds provided by TravisCI, bringing full CI coverage for all major desktop platforms. Yay!

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.

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.

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.

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.

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.

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.

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.

I’m not aware of any issues. You could try opening an issue against the DownloadProject github project if you have steps to reproduce the problem and can provide the error messages you are currently seeing.

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!

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.

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.

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

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

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.

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.

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.

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?

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.

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.

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?

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.

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