CMake with googletest

I am trying to build a project with cpp and test it with google tests.
I took the layout from https://github.com/tfc/nix_cmake_example/tree/master/server. but when I try to compile the cmake it throws an error

/nix/store/p792j5f44l3f0xi7ai5jllwnxqwnka88-binutils-2.31.1/bin/ld: CMakeFiles/g_test.dir/tests/correctness.cpp.o: undefined reference to symbol '_ZTIN7testing4TestE'
/nix/store/p792j5f44l3f0xi7ai5jllwnxqwnka88-binutils-2.31.1/bin/ld: /nix/store/vbfv5wqhizxlqpsm3p07xjh7fpl371i1-gtest-1.10.0/lib/libgtest.so: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/g_test.dir/build.make:103: g_test] Error 1
make[1]: *** [CMakeFiles/Makefile2:124: CMakeFiles/g_test.dir/all] Error 2
make: *** [Makefile:114: all] Error 2

My CmakeFile.txt looks like this

make_minimum_required (VERSION 3.10)

#### BASIC SETTINGS ############################################################

project(priority-queues)
set(CMAKE_CXX_STANDARD 17)

include_directories(.)

set (CMAKE_CXX_FLAGS "-Wall -Wextra -pedantic -g -O3 -march=native")

#### PriorityQueues ###############################################################

set(PQ_LIST "pq_std;pq_a;pq_b;pq_d;pq_c;pq_dpointer")

foreach(pq ${PQ_LIST})
  string(TOUPPER ${pq} pq_uc)
  add_executable(${pq} tests/time.cpp)
  target_compile_definitions(${pq} PRIVATE -D ${pq_uc})
endforeach()

#### TESTS #####################################################################
add_executable(pq_test tests/correctness.cpp)
target_link_libraries(pq_test gtest_main)

include(GoogleTest)
gtest_discover_tests(pq_test)

I think the problem is, that I didn’t add the subdirectory of googletest, so it doesn’t know where to find the libgtest. But I don’t know where to find it on nixos. A friend of mine gave me his googletest/gtest folder(from his ubuntu mashine, where the folder is in some /usr/ directory) and when I add it to the project folder and as a subdirectory in the CMakeFile.txt it finds everything and can compile.
How can I link the googletest folder in a proper way on nix without putting the folder into the project directory?

my default.nix looks like this:

let
  pkgs = import <nixpkgs> {  overlays = [ (import ./overlay.nix) ]; };
in pkgs.pq

my overlay.nix looks like this:

self: super: {
  pq = self.callPackage ./derivation.nix{};
} 

and my derivation.nix looks like this:

{pkgs, stdenv, lib, libpqxx, boost, cmake, gtest, static ? false }:
let my-python-packages = python-packages: with python-packages; [
      matplotlib  
      numpy
    ];
  python-with-my-packages = pkgs.python38.withPackages my-python-packages;
in  
  stdenv.mkDerivation {
    name = "pq";
    nativeBuildInputs = [ cmake ];
    PYTHONPATH = "/home/felix/Documents/uni/c/efficient-parallel-cpp-2020";
    buildInputs = with pkgs; [ 
      python-with-my-packages
      gmock
      gtest
      coreutils-prefixed
      zeromq ];
      checkInputs = [ gtest ];
      cmakeFlags = [
      (lib.optional static "-DBUILD_STATIC=1")
      (lib.optional (!static) "-DENABLE_TESTS=1")
      ];
      makeTarget = "pq";
    doCheck = true;
    checkTarget = "test";
  }

Ya it’s a little different than a traditional c++ project structure since nixpkgs automagically handles the folder location and adds it to your include path and link path.

Since gtest is part of your checkInputs, in cmake you should switch:
target_link_libraries(pq_test gtest_main)
to:
target_link_libraries(pq_test gtest)

you may also need to remove any explicit references to other local test folders or explicit includes in Cmake, for example I’ve never seen this syntax before:

include(GoogleTest)
gtest_discover_tests(pq_test)

so you may want to remove it if switching gtest_main to gtest doesn’t fix the problem.

In my own project which is private right now:

  1. adding gtest as checkInput
  2. having the necessary includes in my test source file:
    #include "gtest/gtest.h"
  3. linking to gtest like:
    target_link_libraries(recut_test PRIVATE gtest)

passes all compilation. I do not have a call to add_subdirectory since it’s redundant with nixpkgs. I can link back on this thread for an example when I make the repo public, if it would be helpful.

Note: I use a conditional approach so users can still include the library as Cmake project without nix by using this flag in my default.nix:

cmakeFlags = ["-DFROM_NIX_BUILD=ON"];
then:

# NIX-BUILD overrides some dependencies
option( FROM_NIX_BUILD OFF)
if (FROM_NIX_BUILD)
  message(STATUS " FROM_NIX_BUILD ON")
endif()

# nix-build handles gtest and gbenchmark libraries independently from submodules
# they are automatically added to the include path
if (RECUT_ENABLE_TESTING AND NOT FROM_NIX_BUILD)
  # build tests (targets: gtest, gtest_main)
  add_subdirectory(extern/googletest/googletest)
endif()

if (RECUT_ENABLE_BENCHMARKING AND NOT FROM_NIX_BUILD)
  # build google benchmark (target: benchmark)
  # do not build tests of benchmarking lib
  set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Suppressing benchmark's tests" FORCE)
  set(BENCHMARK_ENABLE_INSTALL OFF CACHE BOOL "Don't install benchmark" FORCE)
  add_subdirectory(extern/benchmark)
endif()

 # other dependencies
 if (NOT FROM_NIX_BUILD) 
  target_include_directories(${I} PRIVATE extern/range-v3/include)
endif()

this was because github actions didn’t include those git submodule directories, so I handle them with nix.

With only gtest i get this error:

/nix/store/p792j5f44l3f0xi7ai5jllwnxqwnka88-binutils-2.31.1/bin/ld: /nix/store/9df65igwjmf2wbw0gbrrgair6piqjgmi-glibc-2.31/lib/crt1.o: in function `_start':
/build/glibc-2.31/csu/../sysdeps/x86_64/start.S:104: undefined reference to `main'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/pq_test.dir/build.make:103: pq_test] Error 1
make[1]: *** [CMakeFiles/Makefile2:107: CMakeFiles/pq_test.dir/all] Error 2
make: *** [Makefile:103: all] Error 2

My CMakeList.txt looks like this now

#### TESTS #####################################################################
add_executable(pq_test tests/correctness.cpp)
target_link_libraries(pq_test gtest)

It looks like you solved your gtest build issue but are missing the correct main function from your tests/correctness.cpp file. This probably involves adding this:

#include "gtest/gtest.h"

int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

From what I see, this is separate from nix related setup so you should consult the googletest readme.md, or review c/c++ compiling/linking:

Also the linking should be private like:
target_link_libraries(pq_test PRIVATE gtest)

1 Like

Another possibility that relies more on Cmake and might be more compatible for builders of your executable or users of your library that aren’t using nix is to do something like the following:

find_package(GTest REQUIRED)
message(STATUS "GTEST_INCLUDE_DIR: ${GTEST_INCLUDE_DIR}")
message(STATUS "GTEST_LIBRARIES: ${GTEST_LIBRARIES}")

target_include_directories(pq_test ${GTEST_INCLUDE_DIR})
target_link_libraries(pq_test ${GTEST_LIBRARIES})

The capitalization scheme and the variables that cmake populates once it finds gtest will likely change for future readers, so just print out your expected cmake variables that your including and linking to make sure they aren’t (silently) empty strings.