From c6dad6f531b4d91ccad6a416394c20a55dc74ed7 Mon Sep 17 00:00:00 2001 From: James Lamb Date: Wed, 2 Sep 2026 14:41:41 -0500 Subject: [PATCH 1/2] statically link GTest, fetch it with CPM --- cmake/CPMFindGTest.cmake | 65 ++++++++++++++++++++++++++++++++++++++++ cmake/CPMSetup.cmake | 33 ++++++++++++++++++++ cmake/ExternalLibs.cmake | 28 ++--------------- 3 files changed, 100 insertions(+), 26 deletions(-) create mode 100644 cmake/CPMFindGTest.cmake create mode 100644 cmake/CPMSetup.cmake diff --git a/cmake/CPMFindGTest.cmake b/cmake/CPMFindGTest.cmake new file mode 100644 index 00000000..63866a8c --- /dev/null +++ b/cmake/CPMFindGTest.cmake @@ -0,0 +1,65 @@ +# Description: +# +# Find / build GTest. Prefers a system install if one is found, +# otherwise downloads sources with CPM (https://github.com/cpm-cmake/CPM.cmake) +# and builds GTest. +# +# Targets: +# +# GTest::gtest +# Gtest::mock +# +# Usage: +# +# include(CPMFindGTest.cmake) +# + +function(_treelite_find_gtest gtest_version) + + # Prefer a system-installed GTest if one is found + find_package(GTest ${gtest_version}) + if(GTEST_FOUND) + return() + endif() + + # not found, use CPM to download sources and build it + message(STATUS "GTest not found, fetching GTest via CPM.") + + # For MSVC, ensure GTest uses the same C runtime (/MD vs. /MT) as Treelite. + # + # As of v1.14.0, googletest did literal string replacements on CMAKE_CXX_FLAGS, + # and doesn't respect CMAKE_MSVC_RUNTIME_LIBRARY. + # + # TODO(jameslamb): remove this once this project uses at least GTest v1.18.0 + # That version has this fix: https://github.com/google/googletest/pull/4877 + # + set(gtest_force_shared_crt ${FORCE_SHARED_CRT} CACHE BOOL "" FORCE) + + # populate local CPM cache with googletest source (if not already populated), build targets + include(${treelite_SOURCE_DIR}/cmake/CPMSetup.cmake) + + # prefer statically linking GTest, so test executables are relocatable + CPMAddPackage( + NAME googletest + GITHUB_REPOSITORY google/googletest + GIT_TAG v${gtest_version} + VERSION ${gtest_version} + EXCLUDE_FROM_ALL YES + OPTIONS + "BUILD_GMOCK ON" + "BUILD_SHARED_LIBS OFF" + "CMAKE_POSITION_INDEPENDENT_CODE ON" + "INSTALL_GTEST OFF" + ) + + if(MSVC) + foreach(target gtest gmock) + # For MSVC, ensure GTest targets propagate the same MSVC C Runtime + # to anything linking against them. + set_target_properties(${target} PROPERTIES + MSVC_RUNTIME_LIBRARY "${CMAKE_MSVC_RUNTIME_LIBRARY}") + endforeach() + endif() +endfunction() + +_treelite_find_gtest(1.14.0) diff --git a/cmake/CPMSetup.cmake b/cmake/CPMSetup.cmake new file mode 100644 index 00000000..fdd39ed0 --- /dev/null +++ b/cmake/CPMSetup.cmake @@ -0,0 +1,33 @@ +# Ensure CPM ("CMake Package Manager") is available. +# +# ref: https://github.com/cpm-cmake/cpm.cmake + +function(_setup_cpm cpm_version) + # look for already-downloaded CPM source in this order: + # + # 1. already-defined CMake variable 'CPM_SOURCE_CACHE' + # 2. environment variable 'CPM_SOURCE_CACHE' + # 3. path relative to the top-level build directory + # + if(CPM_SOURCE_CACHE) + set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${cpm_version}.cmake") + elseif(DEFINED ENV{CPM_SOURCE_CACHE}) + set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${cpm_version}.cmake") + else() + set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${cpm_version}.cmake") + endif() + + # download source if necessary + if(NOT EXISTS ${CPM_DOWNLOAD_LOCATION}) + message(STATUS "Downloading CPM.cmake to ${CPM_DOWNLOAD_LOCATION}") + file(DOWNLOAD + https://github.com/cpm-cmake/CPM.cmake/releases/download/v${cpm_version}/CPM.cmake + ${CPM_DOWNLOAD_LOCATION} + ) + endif() + + set(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} PARENT_SCOPE) +endfunction() + +_setup_cpm(0.43.1) +include(${CPM_DOWNLOAD_LOCATION}) diff --git a/cmake/ExternalLibs.cmake b/cmake/ExternalLibs.cmake index a4c39e30..0f516dc3 100644 --- a/cmake/ExternalLibs.cmake +++ b/cmake/ExternalLibs.cmake @@ -63,32 +63,8 @@ endif() # Google C++ tests if(BUILD_CPP_TEST) - find_package(GTest 1.14.0) - if(NOT GTest_FOUND) - message(STATUS "Did not find Google Test in the system root. Fetching Google Test now...") - FetchContent_Declare( - googletest - URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz - ) - set(gtest_force_shared_crt ${DMLC_FORCE_SHARED_CRT} CACHE BOOL "" FORCE) - FetchContent_MakeAvailable(googletest) - - add_library(GTest::gtest ALIAS gtest) - add_library(GTest::gmock ALIAS gmock) - target_compile_definitions(gtest PRIVATE ${ENABLE_GNU_EXTENSION_FLAGS}) - target_compile_definitions(gmock PRIVATE ${ENABLE_GNU_EXTENSION_FLAGS}) - foreach(target gtest gmock) - target_compile_features(${target} PUBLIC cxx_std_14) - if(MSVC) - set_target_properties(${target} PROPERTIES - MSVC_RUNTIME_LIBRARY "${Treelite_MSVC_RUNTIME_LIBRARY}") - endif() - endforeach() - if(IS_DIRECTORY "${googletest_SOURCE_DIR}") - # Do not install gtest - set_property(DIRECTORY ${googletest_SOURCE_DIR} PROPERTY EXCLUDE_FROM_ALL YES) - endif() - endif() + # find/build GTest + include(${treelite_SOURCE_DIR}/cmake/CPMFindGTest.cmake) endif() # fmtlib From f91ecf07f5f987bc1b7835f7aeb4903bef7ef188 Mon Sep 17 00:00:00 2001 From: James Lamb Date: Wed, 2 Sep 2026 21:18:37 -0500 Subject: [PATCH 2/2] use GoogleTest 1.18.0 --- cmake/CPMFindGTest.cmake | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/cmake/CPMFindGTest.cmake b/cmake/CPMFindGTest.cmake index 63866a8c..84eeb01f 100644 --- a/cmake/CPMFindGTest.cmake +++ b/cmake/CPMFindGTest.cmake @@ -25,16 +25,6 @@ function(_treelite_find_gtest gtest_version) # not found, use CPM to download sources and build it message(STATUS "GTest not found, fetching GTest via CPM.") - # For MSVC, ensure GTest uses the same C runtime (/MD vs. /MT) as Treelite. - # - # As of v1.14.0, googletest did literal string replacements on CMAKE_CXX_FLAGS, - # and doesn't respect CMAKE_MSVC_RUNTIME_LIBRARY. - # - # TODO(jameslamb): remove this once this project uses at least GTest v1.18.0 - # That version has this fix: https://github.com/google/googletest/pull/4877 - # - set(gtest_force_shared_crt ${FORCE_SHARED_CRT} CACHE BOOL "" FORCE) - # populate local CPM cache with googletest source (if not already populated), build targets include(${treelite_SOURCE_DIR}/cmake/CPMSetup.cmake) @@ -51,15 +41,6 @@ function(_treelite_find_gtest gtest_version) "CMAKE_POSITION_INDEPENDENT_CODE ON" "INSTALL_GTEST OFF" ) - - if(MSVC) - foreach(target gtest gmock) - # For MSVC, ensure GTest targets propagate the same MSVC C Runtime - # to anything linking against them. - set_target_properties(${target} PROPERTIES - MSVC_RUNTIME_LIBRARY "${CMAKE_MSVC_RUNTIME_LIBRARY}") - endforeach() - endif() endfunction() -_treelite_find_gtest(1.14.0) +_treelite_find_gtest(1.18.0)