How to use CMake FetchContent to link OpenSSL? - cmake

I don't quite understand how to integrate libraries by FetchContent if they were not found by find_package.
Currently, I have the following code:
# OpenSSL dependency
option(USE_SYSTEM_OPENSSL "Use preinstalled OpenSSL" ON)
# configure OpenSSL
if(USE_SYSTEM_OPENSSL)
find_package(OpenSSL COMPONENTS Crypto SSL)
endif()
if(NOT USE_SYSTEM_OPENSSL OR NOT OPENSSL_FOUND)
# get dependencies from net
include(FetchContent)
message(STATUS "Not using system OpenSSL, using built-in OpenSSL project instead.")
FetchContent_Declare(
OpenSSL
GIT_REPOSITORY https://github.com/openssl/openssl
GIT_TAG master
USES_TERMINAL_DOWNLOAD TRUE)
FetchContent_MakeAvailable(OpenSSL)
# (1) ...
endif()
#(2)
target_link_libraries(target OpenSSL::SSL OpenSSL::Crypto)
What should I write at (1) for (2) to work?

You cannot use FetchContent_Declare, you must use ExternalProject_Add:
set(OPENSSL_SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/openssl-src) # default path by CMake
set(OPENSSL_INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/openssl)
set(OPENSSL_INCLUDE_DIR ${OPENSSL_INSTALL_DIR}/include)
set(OPENSSL_CONFIGURE_COMMAND ${OPENSSL_SOURCE_DIR}/config)
ExternalProject_Add(
OpenSSL
SOURCE_DIR ${OPENSSL_SOURCE_DIR}
GIT_REPOSITORY https://github.com/openssl/openssl.git
GIT_TAG OpenSSL_1_1_1n
USES_TERMINAL_DOWNLOAD TRUE
CONFIGURE_COMMAND
${OPENSSL_CONFIGURE_COMMAND}
--prefix=${OPENSSL_INSTALL_DIR}
--openssldir=${OPENSSL_INSTALL_DIR}
BUILD_COMMAND make
TEST_COMMAND ""
INSTALL_COMMAND make install
INSTALL_DIR ${OPENSSL_INSTALL_DIR}
)
After doing that, you cannot include or link it with your other targets yet. To do that, you still need to declare the library, as if it was found using find_package, i.e. as if FindOpenSSL.cmake was used:
# We cannot use find_library because ExternalProject_Add() is performed at build time.
# And to please the property INTERFACE_INCLUDE_DIRECTORIES,
# we make the include directory in advance.
file(MAKE_DIRECTORY ${OPENSSL_INCLUDE_DIR})
add_library(OpenSSL::SSL STATIC IMPORTED GLOBAL)
set_property(TARGET OpenSSL::SSL PROPERTY IMPORTED_LOCATION ${OPENSSL_INSTALL_DIR}/lib/libssl.${OPENSSL_LIBRARY_SUFFIX})
set_property(TARGET OpenSSL::SSL PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${OPENSSL_INCLUDE_DIR})
add_dependencies(OpenSSL::SSL OpenSSL)
add_library(OpenSSL::Crypto STATIC IMPORTED GLOBAL)
set_property(TARGET OpenSSL::Crypto PROPERTY IMPORTED_LOCATION ${OPENSSL_INSTALL_DIR}/lib/libcrypto.${OPENSSL_LIBRARY_SUFFIX})
set_property(TARGET OpenSSL::Crypto PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${OPENSSL_INCLUDE_DIR})
add_dependencies(OpenSSL::Crypto OpenSSL)
Now you can include the OpenSSL and link with it normally.

Related

CMake - How to get include directories of external project?

I tried to used https://github.com/julianxhokaxhiu/SteamworksSDKCI to use steam api on a simple SFML application (helloworld).
I wanted to use cmake to learn it, but I am struggling to understand how the provided CMakeLists and Find*.cmake file are expected to be used.
Currently, I have modified the CMakeLists to change the INSTALL_DIR
INSTALL_DIR "${CMAKE_BINARY_DIR}/../../vendor"
and my CMakeLists is :
cmake_minimum_required(VERSION 3.19)
project(SfmlWithCMake VERSION 1.0)
include(FetchContent)
set (CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_SOURCE_DIR}/cmake_steam")
# Configure external project
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/cmake_steam)
execute_process(
COMMAND ${CMAKE_COMMAND} ${CMAKE_SOURCE_DIR}/cmake_steam
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/cmake_steam
)
# Build external project
execute_process(
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}/cmake_steam
)
set(BUILD_SHARED_LIBS OFF)
FetchContent_Declare(
SFML
GIT_REPOSITORY https://github.com/SFML/SFML.git
GIT_TAG 2.5.1
)
FetchContent_MakeAvailable(SFML)
find_package(STEAMWORKSSDK REQUIRED)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED true)
# Generate config.h
configure_file(config.h.in config.h)
add_executable(
SfmlWithCMake
main.cpp
)
get_target_property(STEAMSDK STEAMWORKSSDK::STEAMWORKSSDK INCLUDE_DIRECTORIES)
target_include_directories(
SfmlWithCMake
PRIVATE
"${PROJECT_BINARY_DIR}"
"${STEAMSDK}"
)
target_link_libraries(
SfmlWithCMake
sfml-graphics
STEAMWORKSSDK::STEAMWORKSSDK
-static gcc stdc++ winpthread -dynamic
)
install(TARGETS SfmlWithCMake DESTINATION bin)
How to get include directories?
I do not succeed to add the steam include to the target_include_directories.
Here the ${STEAMSDK} is my last attempt to get the directory.
If I replace this by ${PROJECT_BINARY_DIR}/vendor/include, everything works.
Also, why does the SFML include are automatically added to my target include directories and not the steam one?
Am I using the Find*.cmake file the right way?
I understood that ExternalProject_Add was performed at build time and thus, as the find_package is needed at configue time, I added the two "execute_process". But, the readme on github only says to do the find package and add the target to target_link_libraries...
Thanks.

cmake linker error with library installed in custom path

I have compiled and installed with CMake the library SDL_bgi to a custom prefix /custom/prefix/. This library uses SDL2.
Now I want to use it in another project with the structure below but I get a linker error when I compile with make:
/usr/bin/c++ CMakeFiles/test.dir/test.cpp.o -o test -Wl,-rpath,/custom/prefix/lib: /custom/prefix/lib/libSDL_bgi.so
/usr/bin/ld: /custom/prefix/lib/libSDL_bgi.so: undefined reference to `SDL_DestroyWindow'
/usr/bin/ld: /custom/prefix/lib/libSDL_bgi.so: undefined reference to `SDL_CreateRenderer'
I have also written the file cmake/modules/FindSDL_bgi.cmake so that may be wrong as well.
If I compile with the following command I can compile correctly:
g++ test.cpp -I . -lSDL_bgi -lSDL2 -I /custom/prefix/include/ -L /custom/prefix/lib/
What am I doing wrongly?
Project structure:
cmake/modules/FindSDL_bgi.cmake
src/test/CMakeLists.txt
src/test/test.cpp
CMakeLists.txt
Libraries:
/usr/lib/libSDL.so
/usr/include/SDL.h
/custom/prefix/lib/libSDL_bgi.so
/custom/prefix/include/graphics.h
/custom/prefix/include/SDL2/libSDL_bgi.h
cmake/modules/FindSDL_bgi.cmake:
# - Try to find LibXml2
# Once done this will define
# SDL_BGI_FOUND - System has LibXml2
# SDL_BGI_INCLUDE_DIRS - The LibXml2 include directories
# SDL_BGI_LIBRARIES - The libraries needed to use LibXml2
# Hardcoded for now
set(SDL_BGI_PATH
/custom/prefix/
)
set(SDL_BGI_SEARCH_PATHS
/usr
/usr/local
/opt
${SDL_BGI_PATH}
)
find_path(SDL_BGI_INCLUDE_DIR graphics.h
HINTS
$ENV{SDL2DIR}
PATH_SUFFIXES include
PATHS ${SDL2_SEARCH_PATHS}
)
find_library(SDL_BGI_LIBRARY
NAMES SDL_bgi
HINTS
$ENV{SDL2DIR}
PATH_SUFFIXES lib64 lib
PATHS ${SDL2_SEARCH_PATHS}
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SDL_bgi REQUIRED_VARS SDL_BGI_LIBRARY SDL_BGI_INCLUDE_DIR)
CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(programmi_kennedy)
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules")
set(COMPAT_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/include/
)
find_package(SDL_bgi REQUIRED)
add_subdirectory(src/test)
src/CMakeLists.txt:
add_executable(test test.cpp)
target_include_directories(test PUBLIC ${SDL_BGI_INCLUDE_DIR})
target_link_libraries(test PRIVATE ${SDL_BGI_LIBRARY})
install(TARGETS test DESTINATION bin)
/custom/prefix/include/graphics.h:
#include <SDL2/SDL_bgi.h>
What I was missing is to link to SDL2 with find_package(SDL2 REQUIRED) and link to SDL2::SDL2. (I did try to link to ${SDL2_LIBRARIES} but the syntax is different now). Thanks to #KamilCuk to point me to the right direction.
EDIT:
I changed the FindBGI_sdl.cmake module in order to search for the dependencies (SDL2) and link against them using the INTERFACE keyword. In this way the target test can link only against SDL_bgi and have the dependencies resolved automatically.
src/CMakeLists.txt:
add_executable(test test.cpp)
target_link_libraries(test PRIVATE SDL_bgi::SDL_bgi)
install(TARGETS test DESTINATION bin)
cmake/modules/FindSDL_bgi.cmake:
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
#[=======================================================================[.rst:
FindSDL_bgi
-------
Finds the SDL_bgi library.
Imported Targets
^^^^^^^^^^^^^^^^
This module provides the following imported targets, if found:
``SDL_bgi::SDL_bgi``
The SDL_bgi library
Result Variables
^^^^^^^^^^^^^^^^
This will define the following variables:
``SDL_bgi_FOUND``
True if the system has the SDL_bgi library.
``SDL_bgi_VERSION``
The version of the SDL_bgi library which was found.
``SDL_bgi_INCLUDE_DIRS``
Include directories needed to use SDL_bgi.
``SDL_bgi_LIBRARIES``
Libraries needed to link to SDL_bgi.
Cache Variables
^^^^^^^^^^^^^^^
The following cache variables may also be set:
``SDL_bgi_INCLUDE_DIR``
The directory containing ``foo.h``.
``SDL_bgi_LIBRARY``
The path to the SDL_bgi library.
#]=======================================================================]
find_package(SDL2 REQUIRED)
find_package(PkgConfig)
pkg_check_modules(PC_SDL_bgi QUIET SDL_bgi)
find_path(SDL_bgi_INCLUDE_DIR
NAMES graphics.h
PATHS ${PC_SDL_bgi_INCLUDE_DIRS}
)
find_library(SDL_bgi_LIBRARY
NAMES SDL_bgi
PATHS ${PC_SDL_bgi_LIBRARY_DIRS}
)
set(SDL_bgi_VERSION ${PC_SDL_bgi_VERSION})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SDL_bgi
FOUND_VAR SDL_bgi_FOUND
REQUIRED_VARS
SDL_bgi_LIBRARY
SDL_bgi_INCLUDE_DIR
VERSION_VAR SDL_bgi_VERSION
)
if(SDL_bgi_FOUND AND NOT TARGET SDL_bgi::SDL_bgi)
add_library(SDL_bgi::SDL_bgi UNKNOWN IMPORTED)
set_target_properties(SDL_bgi::SDL_bgi PROPERTIES
IMPORTED_LOCATION "${SDL_bgi_LIBRARY}"
INTERFACE_COMPILE_OPTIONS "${PC_SDL_bgi_CFLAGS_OTHER}"
INTERFACE_INCLUDE_DIRECTORIES "${SDL_bgi_INCLUDE_DIR}"
)
target_link_libraries(SDL_bgi::SDL_bgi INTERFACE SDL2::SDL2)
endif()
mark_as_advanced(
SDL_bgi_INCLUDE_DIR
SDL_bgi_LIBRARY
SDL2_DIR
)
Useful references:
https://pabloariasal.github.io/2018/02/19/its-time-to-do-cmake-right/
https://cmake.org/cmake/help/v3.17/manual/cmake-developer.7.html

Find directory where target is defined

Is there a way in cmake to find the source directory of the CMakeLists.txt file in which a target was defined?
Somethign like:
if (TARGET Foo)
message("Library Foo was alread built in ${LOCATION_OF_FOOS_CMAKE}")
else()
add_library(Foo ...)
endif()
Edit:
Unfortunately, my cmake scripts have to work on a default ubuntu 14.04 installation. So I'm limited to cmake 2.8
You can use target property SOURCE_DIR (added with CMake 3.7):
get_target_property(FOO_SOURCE_DIR Foo SOURCE_DIR)
For older versions of CMake you can overwrite e.g. add_library() and define your own SOURCE_DIR target property:
function(add_library _target)
_add_library(${_target} ${ARGN})
set_target_properties(${_target} PROPERTIES SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
endfunction(add_library)

How to add_subdirectory after a externalproject_add()?

I have to download zlib to use in my project.
I use externlproject_add() to downaload all zlib repository, build and install it.
After it, I want do install a lib that is part of zlib repository: minizip.
How to set this dependency on cmake?
zlib module:
cmake_minimum_required ( VERSION 2.8.7 )
include (ExternalProject)
if(UNIX)
# An external project for zlib
SET (GIT_URL https://github.com/madler/zlib.git)
SET (ZLIB_INSTALL ${CMAKE_CURRENT_BINARY_DIR})
SET (ZLIB_INCLUDE ${CMAKE_BINARY_DIR}/include/zlib)
SET (ZLIB_STATIC ${CMAKE_BINARY_DIR}/lib/libz.a )
SET (MINIZIP_DIR ${CMAKE_CURRENT_BINARY_DIR}/ZLIB/src/ZLIB/contrib/minizip)
ExternalProject_Add(zlib
PREFIX zlib
GIT_REPOSITORY ${GIT_URL}
INSTALL_DIR ${ZLIB_INSTALL}
PATCH_COMMAND ${CMAKE_COMMAND} -E remove <SOURCE_DIR>/zconf.h
BUILD_IN_SOURCE 1
PATCH_COMMAND ""
CONFIGURE_COMMAND <SOURCE_DIR>/configure --prefix=<INSTALL_DIR> --includedir=${ZLIB_INCLUDE}
)
find_package(ZLIB REQUIRED)
if(ZLIB_FOUND)
add_subdirectory (${MINIZIP_DIR})
endif(ZLIB_FOUND)
SET (ZLIB_INCLUDE_DIR ${ZLIB_INSTALL}/include/zlib)
SET (ZLIB_LIBRARY "${ZLIB_INSTALL}")
ADD_LIBRARY (ZLIB_LIB STATIC IMPORTED DEPENDS zlib)
SET_TARGET_PROPERTIES (ZLIB_LIB PROPERTIES IMPORTED_LOCATION "${ZLIB_STATIC}")
endif(UNIX)
With this zlib module I have an error that following directory
/home/lais/Imagens/agent/build/ZLIB/src/ZLIB/contrib/minizip
doesn't exist yet when I run
cmake ..
And it's true. Doesn't exist yet. I should tell cmake it. But I don't know how to do it.
I solved it download a zip file that have minizip.
cmake_minimum_required ( VERSION 2.8.7 )
include (ExternalProject)
project (zlib)
if(UNIX)
# An external project for zlib
SET (URL http://www.winimage.com/zLibDll/unzip101e.zip)
SET (MINIZIP_INSTALL ${CMAKE_CURRENT_BINARY_DIR})
SET (MINIZIP_STATIC ${CMAKE_BINARY_DIR}/lib/minizip.a )
message ("zlib = ${zlib}")
ExternalProject_Add(minizip
DEPENDS zlib
PREFIX minizip
URL ${URL}
INSTALL_DIR ${MINIZIP_INSTALL}
BUILD_IN_SOURCE make
CONFIGURE_COMMAND ""
)
SET (MINIZIP_LIBRARY "${MINIZIP_INSTALL}")
ADD_LIBRARY (MINIZIP_LIB STATIC IMPORTED DEPENDS minizip, zlib)
SET_TARGET_PROPERTIES (MINIZIP_LIB PROPERTIES IMPORTED_LOCATION "${MINIZIP_STATIC}")
endif(UNIX)

CMake + GoogleTest

I just downloaded googletest, generated its makefile with CMake and built it. Now, I need to use it in my testing project.
With CMake, I have been advised not pointing to gtest libraries directly (using include _directories or link_directories) but use find_package() instead.
The problem is, there is no install target for the gtest makefile generated. I cannot understand how find_package(GTest REQUIRED) could work without some kind of installation. Also, putting the gtest folder as a subfolder in my project is not possible.
Thanks for any help.
This is an unusual case; most projects specify install rules.
CMake's ExternalProject_Add module is maybe the best tool for this job. This allows you to download, configure and build gtest from within your project, and then link to the gtest libraries.
I've tested the following CMakeLists.txt on Windows with Visual Studio 10 and 11, and on Ubuntu using GCC 4.8 and Clang 3.2 - it might need adjusted for other platforms/compilers:
cmake_minimum_required(VERSION 2.8.7 FATAL_ERROR)
project(Test)
# Create main.cpp which uses gtest
file(WRITE src/main.cpp "#include \"gtest/gtest.h\"\n\n")
file(APPEND src/main.cpp "TEST(A, B) { SUCCEED(); }\n")
file(APPEND src/main.cpp "int main(int argc, char **argv) {\n")
file(APPEND src/main.cpp " testing::InitGoogleTest(&argc, argv);\n")
file(APPEND src/main.cpp " return RUN_ALL_TESTS();\n")
file(APPEND src/main.cpp "}\n")
# Create patch file for gtest with MSVC 2012
if(MSVC_VERSION EQUAL 1700)
file(WRITE gtest.patch "Index: cmake/internal_utils.cmake\n")
file(APPEND gtest.patch "===================================================================\n")
file(APPEND gtest.patch "--- cmake/internal_utils.cmake (revision 660)\n")
file(APPEND gtest.patch "+++ cmake/internal_utils.cmake (working copy)\n")
file(APPEND gtest.patch "## -66,6 +66,9 ##\n")
file(APPEND gtest.patch " # Resolved overload was found by argument-dependent lookup.\n")
file(APPEND gtest.patch " set(cxx_base_flags \"\${cxx_base_flags} -wd4675\")\n")
file(APPEND gtest.patch " endif()\n")
file(APPEND gtest.patch "+ if (MSVC_VERSION EQUAL 1700)\n")
file(APPEND gtest.patch "+ set(cxx_base_flags \"\${cxx_base_flags} -D_VARIADIC_MAX=10\")\n")
file(APPEND gtest.patch "+ endif ()\n")
file(APPEND gtest.patch " set(cxx_base_flags \"\${cxx_base_flags} -D_UNICODE -DUNICODE -DWIN32 -D_WIN32\")\n")
file(APPEND gtest.patch " set(cxx_base_flags \"\${cxx_base_flags} -DSTRICT -DWIN32_LEAN_AND_MEAN\")\n")
file(APPEND gtest.patch " set(cxx_exception_flags \"-EHsc -D_HAS_EXCEPTIONS=1\")\n")
else()
file(WRITE gtest.patch "")
endif()
# Enable ExternalProject CMake module
include(ExternalProject)
# Set the build type if it isn't already
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
# Set default ExternalProject root directory
set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/ThirdParty)
# Add gtest
ExternalProject_Add(
googletest
SVN_REPOSITORY http://googletest.googlecode.com/svn/trunk/
SVN_REVISION -r 660
TIMEOUT 10
PATCH_COMMAND svn patch ${CMAKE_SOURCE_DIR}/gtest.patch ${CMAKE_BINARY_DIR}/ThirdParty/src/googletest
# Force separate output paths for debug and release builds to allow easy
# identification of correct lib in subsequent TARGET_LINK_LIBRARIES commands
CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG:PATH=DebugLibs
-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE:PATH=ReleaseLibs
-Dgtest_force_shared_crt=ON
# Disable install step
INSTALL_COMMAND ""
# Wrap download, configure and build steps in a script to log output
LOG_DOWNLOAD ON
LOG_CONFIGURE ON
LOG_BUILD ON)
# Specify include dir
ExternalProject_Get_Property(googletest source_dir)
include_directories(${source_dir}/include)
# Add compiler flag for MSVC 2012
if(MSVC_VERSION EQUAL 1700)
add_definitions(-D_VARIADIC_MAX=10)
endif()
# Add test executable target
add_executable(MainTest ${PROJECT_SOURCE_DIR}/src/main.cpp)
# Create dependency of MainTest on googletest
add_dependencies(MainTest googletest)
# Specify MainTest's link libraries
ExternalProject_Get_Property(googletest binary_dir)
if(MSVC)
set(Suffix ".lib")
else()
set(Suffix ".a")
set(Pthread "-pthread")
endif()
target_link_libraries(
MainTest
debug ${binary_dir}/DebugLibs/${CMAKE_FIND_LIBRARY_PREFIXES}gtest${Suffix}
optimized ${binary_dir}/ReleaseLibs/${CMAKE_FIND_LIBRARY_PREFIXES}gtest${Suffix}
${Pthread})
If you create this as CMakeLists.txt in an empty directory (say MyTest), then:
cd MyTest
mkdir build
cd build
cmake ..
This should create a basic main.cpp in MyTest/src and create a project file (MyTest/build/Test.sln on Windows)
When you build the project, it should download the gtest sources to MyTest/build/ThirdParty/src/googletest, and build them in MyTest/build/ThirdParty/src/googletest-build. You should then be able to run the MainTest target successfully.
It is long past when the original question being asked, but for the benefit of others, it is possible to use ExternalProject to download the gtest source and then use add_subdirectory() to add it to your build. This has the following advantages:
gtest is built as part of your main build, so it uses the same compiler flags, etc. and doesn't need to be installed anywhere.
There's no need to add the gtest sources to your own source tree.
Used in the normal way, ExternalProject won't do the download and unpacking at configure time (i.e. when CMake is run), but you can get it to do so. I've written a blog post on how to do this which also includes a generalised implementation which works for any external project which uses CMake as its build system, not just gtest. You can find it here:
https://crascit.com/2015/07/25/cmake-gtest/
Update: The approach described above is now also part of the googletest documentation.
My answer is based on the answer from firegurafiku. I modified it in the following ways:
added CMAKE_ARGS to the ExternalProject_Add call so it works with msvc.
gets the gtest source from a file location rather than downloading
added portable (for MSVC and non-MSVC) definition and usage of IMPORTED_LOCATION
Addressed the problem with the call to set_target_properties not working at configure time when the INTERFACE_INCLUDE_DIRECTORIES does not yet exist because the external project has not yet been built.
I prefer keeping gtest as an external project rather than adding its source directly to my project. One reason is because I do not like having the gtest source code included when I am searching my code. Any special build flags that are needed by my code that should also be used when building gtest can be added to the definition of CMAKE_ARGS in the call to ExternalProject_Add
Here is my modified approach:
include(ExternalProject)
# variables to help keep track of gtest paths
set(GTEST_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/gtest")
set(GTEST_LOCATION "${GTEST_PREFIX}/src/GTestExternal-build")
set(GTEST_INCLUDES "${GTEST_PREFIX}/src/GTestExternal/include")
# external project download and build (no install for gtest)
ExternalProject_Add(GTestExternal
URL ${CMAKE_CURRENT_SOURCE_DIR}/../googletest
PREFIX "${GTEST_PREFIX}"
# cmake arguments
CMAKE_ARGS -Dgtest_force_shared_crt=ON
# Disable install step
INSTALL_COMMAND ""
# Wrap download, configure and build steps in a script to log output
LOG_DOWNLOAD ON
LOG_CONFIGURE ON
LOG_BUILD ON
)
# variables defining the import location properties for the generated gtest and
# gtestmain libraries
if (MSVC)
set(GTEST_IMPORTED_LOCATION
IMPORTED_LOCATION_DEBUG "${GTEST_LOCATION}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}"
IMPORTED_LOCATION_RELEASE "${GTEST_LOCATION}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}"
)
set(GTESTMAIN_IMPORTED_LOCATION
IMPORTED_LOCATION_DEBUG "${GTEST_LOCATION}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}"
IMPORTED_LOCATION_RELEASE "${GTEST_LOCATION}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}"
)
else()
set(GTEST_IMPORTED_LOCATION
IMPORTED_LOCATION "${GTEST_LOCATION}/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}")
set(GTESTMAIN_IMPORTED_LOCATION
IMPORTED_LOCATION "${GTEST_LOCATION}/${CMAKE_STATIC_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}")
endif()
# the gtest include directory exists only after it is build, but it is used/needed
# for the set_target_properties call below, so make it to avoid an error
file(MAKE_DIRECTORY ${GTEST_INCLUDES})
# define imported library GTest
add_library(GTest IMPORTED STATIC GLOBAL)
set_target_properties(GTest PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${GTEST_INCLUDES}"
IMPORTED_LINK_INTERFACE_LIBRARIES "${CMAKE_THREAD_LIBS_INIT}"
${GTEST_IMPORTED_LOCATION}
)
# define imported library GTestMain
add_library(GTestMain IMPORTED STATIC GLOBAL)
set_target_properties(GTestMain PROPERTIES
IMPORTED_LINK_INTERFACE_LIBRARIES GTest
${GTESTMAIN_IMPORTED_LOCATION}
)
# make GTest depend on GTestExternal
add_dependencies(GTest GTestExternal)
#
# My targets
#
project(test_pipeline)
add_executable(${PROJECT_NAME} test_pipeline.cpp)
set_target_properties(${PROJECT_NAME} PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})
target_link_libraries(${PROJECT_NAME} ${TBB_LIBRARIES})
target_link_libraries(${PROJECT_NAME} GTest)
There is a bit less complex solution using ExternalProject module and imported libraries feature of cmake. It checks out code from repository, builds it and creates target from built static libraries (they're libgtest.a and libgtest_main.a on my system).
find_package(Threads REQUIRED)
include(ExternalProject)
set(GTEST_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/gtest")
ExternalProject_Add(GTestExternal
SVN_REPOSITORY http://googletest.googlecode.com/svn/trunk
SVN_REVISION -r HEAD
TIMEOUT 10
PREFIX "${GTEST_PREFIX}"
INSTALL_COMMAND "")
set(LIBPREFIX "${CMAKE_STATIC_LIBRARY_PREFIX}")
set(LIBSUFFIX "${CMAKE_STATIC_LIBRARY_SUFFIX}")
set(GTEST_LOCATION "${GTEST_PREFIX}/src/GTestExternal-build")
set(GTEST_INCLUDES "${GTEST_PREFIX}/src/GTestExternal/include")
set(GTEST_LIBRARY "${GTEST_LOCATION}/${LIBPREFIX}gtest${LIBSUFFIX}")
set(GTEST_MAINLIB "${GTEST_LOCATION}/${LIBPREFIX}gtest_main${LIBSUFFIX}")
add_library(GTest IMPORTED STATIC GLOBAL)
set_target_properties(GTest PROPERTIES
IMPORTED_LOCATION "${GTEST_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${GTEST_INCLUDES}"
IMPORTED_LINK_INTERFACE_LIBRARIES "${CMAKE_THREAD_LIBS_INIT}")
add_library(GTestMain IMPORTED STATIC GLOBAL)
set_target_properties(GTestMain PROPERTIES
IMPORTED_LOCATION "${GTEST_MAINLIB}"
IMPORTED_LINK_INTERFACE_LIBRARIES
"${GTEST_LIBRARY};${CMAKE_THREAD_LIBS_INIT}")
add_dependencies(GTest GTestExternal)
You may want to replace SVN_REVISION or add LOG_CONFIGURE and LOG_BUILD options here. After GTest and GTestMain targets are created, they can be used like this:
add_executable(Test
test1.cc
test2.cc)
target_link_libraries(Test GTestMain)
or, if you have your own main() function:
add_executable(Test
main.cc
test1.cc
test2.cc)
target_link_libraries(Test GTest)
The topic is a bit old, but there appeared a new way of including external libraries in CMake.
#Requires CMake 3.16+
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG release-1.8.0
)
FetchContent_MakeAvailable(googletest)
If you want to support the earlier versions of cmake:
# Requires CMake 3.11+
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG release-1.8.0
)
FetchContent_GetProperties(googletest)
if(NOT googletest_POPULATED)
FetchContent_Populate(googletest)
add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
endif()
Then you just add
enable_testing()
add_executable(test ${SOURCES} )
target_link_libraries(test gtest_main ${YOUR_LIBS})
add_test(NAME tests COMMAND test)
Further reading: https://cmake.org/cmake/help/latest/module/FetchContent.html
When you get the libgtest-dev package via
sudo apt install libgtest-dev
The source is stored in location /usr/src/googletest
You can simply point your CMakeLists.txt to that directory so that it can find the necessary dependencies
Something like the following
add_subdirectory(/usr/src/googletest gtest)
target_link_libraries(your_executable gtest)