summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Kruse <llvm-project@meinersbur.de>2025-11-12 11:50:33 +0100
committerGitHub <noreply@github.com>2025-11-12 11:50:33 +0100
commit0957656a4077b6a70f3e3dbb3a011b40d53e1a97 (patch)
tree1077c36eb7a5349ae1f17a2e07b042d4f97a2e8f
parenta276624b2e34bfea36dd472ffc71bb4f78bbd39a (diff)
[runtimes][GTest] LLVM-independent unittests (#164794)
The LLVM-customized GTest has a dependency on LLVM to support `llvm::raw_ostream` and hence has to link to LLVMSupport. The runtimes use the LLVMSupport from the bootstrapping LLVM build. The problem is that the boostrapping compiler and the runtimes target can diverge in their ABI, even in the runtimes default build. For instance, Clang is built using gcc which uses libstdc++, but the runtimes is built by Clang which can be configured to use libcxx by default. Altough it does not use gcc, this issue has caused [flang-aarch64-libcxx](https://lab.llvm.org/buildbot/#/builders/89)) to break, and is still (again?) broken. This patch makes the runtimes' GTest independent from LLVMSupport so we do not link any runtimes component with LLVM components. Runtime projects that use GTest unittests: * flang-rt * libc * compiler-rt: Adds `gtest-all.cpp` with [GTEST_NO_LLVM_SUPPORT=1](https://github.com/llvm/llvm-project/blob/f801b6f67ea896d6e4d2de38bce9a79689ceb254/compiler-rt/CMakeLists.txt#L723) to each unittest without using `llvm_gtest`. Not touched by this PR. * openmp: Handled by #159416. Not touched for now by this PR to avoid conflict. The current state of this PR tries to reuse https://github.com/llvm/llvm-project/blob/main/third-party/unittest/CMakeLists.txt as much as possible, altough personally I would prefer to make it use "modern CMake" style. third-party/unittest/CMakeLists.txt will detect whether it is used in runtimes build and adjaust accordingly. It creates a different target for LLVM (`llvm_gtest`, NFCI) and another one for the runtimes (`runtimes_gtest`). It is not possible to reuse `llvm_gtest` for both since `llvm_gtest` is imported using `find_package(LLVM)` if configured using LLVM_INSTALL_GTEST. An alias `default_gtest` is used to select between the two. `default_gtest` could also be used for openmp which also supports standalone and [LLVM_ENABLE_PROJECTS](https://github.com/llvm/llvm-project/pull/152189) build mode.
-rw-r--r--flang-rt/unittests/CMakeLists.txt8
-rw-r--r--flang-rt/unittests/Evaluate/ISO-Fortran-binding.cpp21
-rw-r--r--flang-rt/unittests/Runtime/AccessTest.cpp7
-rw-r--r--flang-rt/unittests/Runtime/CrashHandlerFixture.cpp11
-rw-r--r--flang-rt/unittests/Runtime/Descriptor.cpp4
-rw-r--r--flang-rt/unittests/Runtime/ExternalIOTest.cpp1
-rw-r--r--libc/benchmarks/CMakeLists.txt6
-rw-r--r--llvm/CMakeLists.txt2
-rw-r--r--llvm/cmake/modules/AddLLVM.cmake8
-rw-r--r--runtimes/CMakeLists.txt14
-rw-r--r--third-party/unittest/CMakeLists.txt95
-rw-r--r--third-party/unittest/UnitTestMain/CMakeLists.txt16
12 files changed, 109 insertions, 84 deletions
diff --git a/flang-rt/unittests/CMakeLists.txt b/flang-rt/unittests/CMakeLists.txt
index 53cd54dfd215..e1ab73d7d930 100644
--- a/flang-rt/unittests/CMakeLists.txt
+++ b/flang-rt/unittests/CMakeLists.txt
@@ -22,12 +22,8 @@ if (CMAKE_CROSSCOMPILING)
return ()
endif ()
-if (NOT TARGET llvm_gtest)
- message(WARNING "Flang-RT unittests disabled due to GTest being unavailable; "
- "Try LLVM_INSTALL_GTEST=ON for the LLVM build")
- return ()
-endif ()
-
+# Make the targets default_gtest and default_gtest_main available.
+build_gtest()
add_dependencies(flang-rt-test-depends
FlangRTUnitTests
diff --git a/flang-rt/unittests/Evaluate/ISO-Fortran-binding.cpp b/flang-rt/unittests/Evaluate/ISO-Fortran-binding.cpp
index 8c0a6f29b696..1a9817cc665d 100644
--- a/flang-rt/unittests/Evaluate/ISO-Fortran-binding.cpp
+++ b/flang-rt/unittests/Evaluate/ISO-Fortran-binding.cpp
@@ -9,7 +9,6 @@
#include "flang-rt/runtime/descriptor.h"
#include "flang/Common/ISO_Fortran_binding_wrapper.h"
#include "flang/Testing/testing.h"
-#include "llvm/Support/raw_ostream.h"
#include <type_traits>
using namespace Fortran::runtime;
@@ -73,26 +72,9 @@ static void AddNoiseToCdesc(CFI_cdesc_t *dv, CFI_rank_t rank) {
}
}
-#ifdef VERBOSE
-static void DumpTestWorld(const void *bAddr, CFI_attribute_t attr,
- CFI_type_t ty, std::size_t eLen, CFI_rank_t rank,
- const CFI_index_t *eAddr) {
- llvm::outs() << " base_addr: ";
- llvm::outs().write_hex(reinterpret_cast<std::intptr_t>(bAddr))
- << " attribute: " << static_cast<int>(attr)
- << " type: " << static_cast<int>(ty) << " elem_len: " << eLen
- << " rank: " << static_cast<int>(rank) << " extent: ";
- llvm::outs().write_hex(reinterpret_cast<std::intptr_t>(eAddr)) << '\n';
- llvm::outs().flush();
-}
-#endif
-
static void check_CFI_establish(CFI_cdesc_t *dv, void *base_addr,
CFI_attribute_t attribute, CFI_type_t type, std::size_t elem_len,
CFI_rank_t rank, const CFI_index_t extents[]) {
-#ifdef VERBOSE
- DumpTestWorld(base_addr, attribute, type, elem_len, rank, extent);
-#endif
// CFI_establish reqs from F2018 section 18.5.5
int retCode{
CFI_establish(dv, base_addr, attribute, type, elem_len, rank, extents)};
@@ -305,9 +287,6 @@ static void check_CFI_allocate(CFI_cdesc_t *dv,
const CFI_type_t type{dv->type};
const void *base_addr{dv->base_addr};
const int version{dv->version};
-#ifdef VERBOSE
- DumpTestWorld(base_addr, attribute, type, elem_len, rank, nullptr);
-#endif
int retCode{CFI_allocate(dv, lower_bounds, upper_bounds, elem_len)};
Descriptor *desc = reinterpret_cast<Descriptor *>(dv);
if (retCode == CFI_SUCCESS) {
diff --git a/flang-rt/unittests/Runtime/AccessTest.cpp b/flang-rt/unittests/Runtime/AccessTest.cpp
index d431d0d19bd6..d44f0ec3ced2 100644
--- a/flang-rt/unittests/Runtime/AccessTest.cpp
+++ b/flang-rt/unittests/Runtime/AccessTest.cpp
@@ -12,8 +12,8 @@
#include "CrashHandlerFixture.h"
#include "gtest/gtest.h"
#include "flang/Runtime/extensions.h"
-#include "llvm/ADT/Twine.h"
+#include <cstring>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -82,8 +82,9 @@ static const char *temp_directory_path() {
static std::string createTemporaryFile(
const char *name, const AccessType &accessType) {
- std::string path =
- (llvm::Twine{temp_directory_path()} + "/" + addPIDSuffix(name)).str();
+ std::ostringstream pathS;
+ pathS << temp_directory_path() << "/" << addPIDSuffix(name);
+ std::string path = pathS.str();
// O_CREAT | O_EXCL enforces that this file is newly created by this call.
// This feels risky. If we don't have permission to create files in the
diff --git a/flang-rt/unittests/Runtime/CrashHandlerFixture.cpp b/flang-rt/unittests/Runtime/CrashHandlerFixture.cpp
index 8213edd1f922..34901b5cd213 100644
--- a/flang-rt/unittests/Runtime/CrashHandlerFixture.cpp
+++ b/flang-rt/unittests/Runtime/CrashHandlerFixture.cpp
@@ -17,12 +17,11 @@
char buffer[1000];
std::vsnprintf(buffer, sizeof buffer, message, ap);
va_end(ap);
- llvm::errs()
- << "Test "
- << ::testing::UnitTest::GetInstance()->current_test_info()->name()
- << " crashed in file "
- << (sourceFile ? sourceFile : "unknown source file") << '(' << sourceLine
- << "): " << buffer << '\n';
+ std::cerr << "Test "
+ << ::testing::UnitTest::GetInstance()->current_test_info()->name()
+ << " crashed in file "
+ << (sourceFile ? sourceFile : "unknown source file") << '('
+ << sourceLine << "): " << buffer << '\n';
std::exit(EXIT_FAILURE);
}
diff --git a/flang-rt/unittests/Runtime/Descriptor.cpp b/flang-rt/unittests/Runtime/Descriptor.cpp
index 3a4a7670fc62..4a7bb43a492a 100644
--- a/flang-rt/unittests/Runtime/Descriptor.cpp
+++ b/flang-rt/unittests/Runtime/Descriptor.cpp
@@ -32,8 +32,8 @@ TEST(Descriptor, FixedStride) {
extent[0] = 8;
descriptor.Establish(integer, four, data, 1, extent);
ASSERT_EQ(descriptor.rank(), 1);
- ASSERT_EQ(descriptor.Elements(), 8);
- ASSERT_EQ(descriptor.ElementBytes(), four);
+ ASSERT_EQ(descriptor.Elements(), 8u);
+ ASSERT_EQ(descriptor.ElementBytes(), static_cast<unsigned>(four));
ASSERT_EQ(descriptor.GetDimension(0).LowerBound(), 0);
ASSERT_EQ(descriptor.GetDimension(0).ByteStride(), four);
ASSERT_EQ(descriptor.GetDimension(0).Extent(), 8);
diff --git a/flang-rt/unittests/Runtime/ExternalIOTest.cpp b/flang-rt/unittests/Runtime/ExternalIOTest.cpp
index 6c148b1de6f8..6421194f4514 100644
--- a/flang-rt/unittests/Runtime/ExternalIOTest.cpp
+++ b/flang-rt/unittests/Runtime/ExternalIOTest.cpp
@@ -16,7 +16,6 @@
#include "flang/Runtime/io-api.h"
#include "flang/Runtime/main.h"
#include "flang/Runtime/stop.h"
-#include "llvm/Support/raw_ostream.h"
#include <cstring>
#include <string_view>
diff --git a/libc/benchmarks/CMakeLists.txt b/libc/benchmarks/CMakeLists.txt
index 60f522d7d8c6..c17cc106f96d 100644
--- a/libc/benchmarks/CMakeLists.txt
+++ b/libc/benchmarks/CMakeLists.txt
@@ -19,6 +19,8 @@ set(LLVM_LINK_COMPONENTS
# Add Unit Testing Support
#==============================================================================
+make_gtest_target()
+
function(add_libc_benchmark_unittest target_name)
if(NOT LLVM_INCLUDE_TESTS)
return()
@@ -38,8 +40,8 @@ function(add_libc_benchmark_unittest target_name)
)
target_link_libraries(${target_name}
PRIVATE
- llvm_gtest_main
- llvm_gtest
+ default_gtest_main
+ default_gtest
${LIBC_BENCHMARKS_UNITTEST_DEPENDS}
)
llvm_update_compile_flags(${target_name})
diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt
index f192cd05b5a3..c02c75f10b12 100644
--- a/llvm/CMakeLists.txt
+++ b/llvm/CMakeLists.txt
@@ -1340,9 +1340,7 @@ if( LLVM_INCLUDE_UTILS )
add_subdirectory(utils/mlgo-utils)
add_subdirectory(utils/llvm-test-mustache-spec)
if( LLVM_INCLUDE_TESTS )
- set(LLVM_SUBPROJECT_TITLE "Third-Party/Google Test")
add_subdirectory(${LLVM_THIRD_PARTY_DIR}/unittest ${CMAKE_CURRENT_BINARY_DIR}/third-party/unittest)
- set(LLVM_SUBPROJECT_TITLE)
endif()
else()
if ( LLVM_INCLUDE_TESTS )
diff --git a/llvm/cmake/modules/AddLLVM.cmake b/llvm/cmake/modules/AddLLVM.cmake
index 7d40d309d538..3c3695a77cb7 100644
--- a/llvm/cmake/modules/AddLLVM.cmake
+++ b/llvm/cmake/modules/AddLLVM.cmake
@@ -1802,7 +1802,13 @@ function(add_unittest test_suite test_name)
# libpthreads overrides some standard library symbols, so main
# executable must be linked with it in order to provide consistent
# API for all shared libaries loaded by this executable.
- target_link_libraries(${test_name} PRIVATE llvm_gtest_main llvm_gtest ${LLVM_PTHREAD_LIB})
+ # default_gtest should be an alias to either llvm_gtest or runtimes_gtest.
+ # If it is not defined, fall back to llvm_gtest.
+ if(TARGET default_gtest)
+ target_link_libraries(${test_name} PRIVATE default_gtest_main default_gtest ${LLVM_PTHREAD_LIB})
+ else ()
+ target_link_libraries(${test_name} PRIVATE llvm_gtest_main llvm_gtest ${LLVM_PTHREAD_LIB})
+ endif ()
add_dependencies(${test_suite} ${test_name})
endfunction()
diff --git a/runtimes/CMakeLists.txt b/runtimes/CMakeLists.txt
index cc756da426e6..b17bd8f66536 100644
--- a/runtimes/CMakeLists.txt
+++ b/runtimes/CMakeLists.txt
@@ -256,6 +256,20 @@ endif()
# This can be used to detect whether we're in the runtimes build.
set(LLVM_RUNTIMES_BUILD ON)
+# Make GTest available to all runtimes
+# The runtime must call make_gtest_target() for it to become available. This is
+# to avoid build failures when gtest is not actually needed. In particular,
+# mingw-incomplete-sysroot is missing C++ header files that GTest needs to
+# compile.
+function(build_gtest)
+ if(TARGET default_gtest)
+ # Already available
+ return()
+ endif()
+
+ add_subdirectory("${LLVM_THIRD_PARTY_DIR}/unittest" "${CMAKE_BINARY_DIR}/third-party/runtimes_gtest")
+endfunction()
+
foreach(entry ${runtimes})
get_filename_component(projName ${entry} NAME)
diff --git a/third-party/unittest/CMakeLists.txt b/third-party/unittest/CMakeLists.txt
index 3fa885a16ea1..79535a1de461 100644
--- a/third-party/unittest/CMakeLists.txt
+++ b/third-party/unittest/CMakeLists.txt
@@ -11,6 +11,34 @@
#
# Project-wide settings
+set(LLVM_SUBPROJECT_TITLE "Third-Party/Google Test")
+
+if(LLVM_RUNTIMES_BUILD)
+ # This instance of GTest is use for unittests for the runtime libraries. It
+ # must not link to LLVMSupport (used for llvm::raw_ostream and llvm::cl
+ # support) of the host build: It may be a different architecture and/or
+ # using a different C++ ABI such as libcxx/libstdc++.
+ set(GTEST_LLVM_COMPONENTS "")
+
+ # We cannot use llvm_gtest for the target; it would clash with
+ # find_package(LLVM) with LLVM_EXPORT_GTEST=ON. Instead, we define an alias
+ # default_gtest that points to llvm_gtest in the LLVM build and
+ # runtimes_gtest in an runtimes build.
+ set(gtest_name "runtimes_gtest")
+
+ # Override locally; never install the runtimes-GTest.
+ set(LLVM_INSTALL_GTEST OFF)
+
+ # Build the library containing main() so unittests need less boilerplate.
+ # UnitTestMain/TestMain.cpp always needs LLVMSupport, use GTest's original
+ # main when unavailable.
+ set(gtest_main_src googletest/src/gtest_main.cc)
+else()
+ set(GTEST_LLVM_COMPONENTS "Support")
+ set(gtest_name "llvm_gtest")
+ set(gtest_main_src UnitTestMain/TestMain.cpp)
+endif()
+
if(WIN32)
add_definitions(-DGTEST_OS_WINDOWS=1)
endif()
@@ -38,17 +66,13 @@ if (HAVE_LIBPTHREAD)
list(APPEND LIBS pthread)
endif()
-# Make available for runtimes using the LLVM buildtree
-# (required for unittests in bootstrapping builds)
-set(EXCLUDE_FROM_ALL OFF)
-
# Install GTest only if requested.
set(BUILDTREE_ONLY BUILDTREE_ONLY)
if (LLVM_INSTALL_GTEST)
set(BUILDTREE_ONLY "")
endif ()
-add_llvm_library(llvm_gtest
+add_llvm_library("${gtest_name}"
googletest/src/gtest-all.cc
googlemock/src/gmock-all.cc
@@ -56,7 +80,7 @@ add_llvm_library(llvm_gtest
${LIBS}
LINK_COMPONENTS
- Support # Depends on llvm::raw_ostream
+ ${GTEST_LLVM_COMPONENTS}
# This is a library meant only for the build tree.
${BUILDTREE_ONLY}
@@ -67,15 +91,14 @@ add_llvm_library(llvm_gtest
# that warning here for any targets that link to gtest.
if(CXX_SUPPORTS_SUGGEST_OVERRIDE_FLAG)
add_definitions("-Wno-suggest-override")
- set_target_properties(llvm_gtest PROPERTIES INTERFACE_COMPILE_OPTIONS "-Wno-suggest-override")
+ set_target_properties("${gtest_name}" PROPERTIES INTERFACE_COMPILE_OPTIONS "-Wno-suggest-override")
endif()
if (NOT LLVM_ENABLE_THREADS)
- target_compile_definitions(llvm_gtest PUBLIC GTEST_HAS_PTHREAD=0)
+ target_compile_definitions("${gtest_name}" PUBLIC GTEST_HAS_PTHREAD=0)
endif ()
-# Top-level include directory required for "llvm/Support/raw_os_ostream.h"
-target_include_directories(llvm_gtest
+target_include_directories("${gtest_name}"
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/googletest/include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/googlemock/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/>
@@ -84,21 +107,36 @@ target_include_directories(llvm_gtest
PRIVATE googletest googlemock
)
-# When used from the buildtree, also force use of buildtree LLVM headers,
-# (instead locally installed version)
-# FIXME: Shouldn't this be done for all LLVM libraries? Currently, LLVM uses a
-# big giant `include_directories( ${LLVM_INCLUDE_DIR} ${LLVM_MAIN_INCLUDE_DIR})`
-# which CMake does not add to the import library.
-target_include_directories(llvm_gtest BEFORE
- PUBLIC $<BUILD_INTERFACE:${LLVM_SOURCE_DIR}/include>
- $<BUILD_INTERFACE:${LLVM_BINARY_DIR}/include>
- )
+if(LLVM_RUNTIMES_BUILD)
+ target_compile_definitions("${gtest_name}" PUBLIC GTEST_NO_LLVM_SUPPORT=1)
+else()
+ # When used from the buildtree, also force use of buildtree LLVM headers,
+ # (instead locally installed version)
+ # FIXME: Shouldn't this be done for all LLVM libraries? Currently, LLVM uses a
+ # big giant `include_directories( ${LLVM_INCLUDE_DIR} ${LLVM_MAIN_INCLUDE_DIR})`
+ # which CMake does not add to the import library.
+ target_include_directories("${gtest_name}" BEFORE
+ PUBLIC $<BUILD_INTERFACE:${LLVM_SOURCE_DIR}/include>
+ $<BUILD_INTERFACE:${LLVM_BINARY_DIR}/include>
+ )
+endif()
+
-add_subdirectory(UnitTestMain)
+add_llvm_library("${gtest_name}_main"
+ ${gtest_main_src}
+
+ LINK_LIBS
+ "${gtest_name}"
+
+ LINK_COMPONENTS
+ ${GTEST_LLVM_COMPONENTS}
+
+ ${BUILDTREE_ONLY}
+)
if (LLVM_INSTALL_GTEST)
- install(DIRECTORY googletest/include/gtest/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/llvm-gtest/gtest/" COMPONENT llvm_gtest)
- install(DIRECTORY googlemock/include/gmock/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/llvm-gmock/gmock/" COMPONENT llvm_gtest)
+ install(DIRECTORY googletest/include/gtest/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/llvm-gtest/gtest/" COMPONENT "${gtest_name}")
+ install(DIRECTORY googlemock/include/gmock/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/llvm-gmock/gmock/" COMPONENT "${gtest_name}")
endif()
# When LLVM_LINK_LLVM_DYLIB is enabled, libLLVM.so is added to the interface
@@ -118,5 +156,14 @@ function (gtest_remove_dylib_from_link_interface target)
endif()
endfunction()
-gtest_remove_dylib_from_link_interface(llvm_gtest)
-gtest_remove_dylib_from_link_interface(llvm_gtest_main)
+if (NOT LLVM_RUNTIMES_BUILD)
+ gtest_remove_dylib_from_link_interface("${gtest_name}")
+ gtest_remove_dylib_from_link_interface("${gtest_name}_main")
+endif ()
+
+# The build processing this file always uses this GTest for unittests.
+# Projects that do not use the LLVM_ENABLE_PROJECTS mechanism, but are
+# standalone-builds using find_package(LLVM) can define these aliases
+# explicitly.
+add_library(default_gtest ALIAS "${gtest_name}")
+add_library(default_gtest_main ALIAS "${gtest_name}_main")
diff --git a/third-party/unittest/UnitTestMain/CMakeLists.txt b/third-party/unittest/UnitTestMain/CMakeLists.txt
deleted file mode 100644
index 729ea7e3fa7e..000000000000
--- a/third-party/unittest/UnitTestMain/CMakeLists.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-set(BUILDTREE_ONLY BUILDTREE_ONLY)
-if (LLVM_INSTALL_GTEST)
- set(BUILDTREE_ONLY "")
-endif ()
-
-add_llvm_library(llvm_gtest_main
- TestMain.cpp
-
- LINK_LIBS
- llvm_gtest
-
- LINK_COMPONENTS
- Support # Depends on llvm::cl
-
- ${BUILDTREE_ONLY}
- )