diff --git a/.gitignore b/.gitignore index 69733a901..4272b5da7 100644 --- a/.gitignore +++ b/.gitignore @@ -28,8 +28,8 @@ *.app # Directories -build -install +build* +install* # CLion files .idea diff --git a/unittests/CppInterOp/CMakeLists.txt b/unittests/CppInterOp/CMakeLists.txt index adb2acade..e63c0169d 100644 --- a/unittests/CppInterOp/CMakeLists.txt +++ b/unittests/CppInterOp/CMakeLists.txt @@ -106,9 +106,12 @@ add_cppinterop_unittest(TracingTests TracingTests.cpp Utils.cpp ) +# CPPINTEROP_SRC_DIR: source checkout (the coverage test reads .cpp sources). +# CPPINTEROP_BIN_DIR: default artifacts prefix; the CPPINTEROP_BIN_DIR +# environment variable overrides it at runtime (see TestPaths.h). target_compile_definitions(TracingTests PRIVATE - "CPPINTEROP_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/../../\"" - "CPPINTEROP_BINARY_DIR=\"${CMAKE_CURRENT_BINARY_DIR}/../../\"" + "CPPINTEROP_SRC_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/../../\"" + "CPPINTEROP_BIN_DIR=\"${CMAKE_BINARY_DIR}\"" ) # GCC 12 and (less often) 13/14 emit a -Wmaybe-uninitialized false positive # inside libstdc++'s headers when the std::function subobject of @@ -182,8 +185,10 @@ if(NOT EMSCRIPTEN AND BUILD_SHARED_LIBS) ${CMAKE_CURRENT_BINARY_DIR} ${GTEST_INCLUDE_DIR}) target_link_libraries(DispatchTests PRIVATE ${gtest_libs} ${link_pthreads_lib}) # Deliberately no target_link_libraries(... clangCppInterOp). + # Default artifacts prefix; the CPPINTEROP_BIN_DIR environment variable + # overrides it at runtime (see TestPaths.h). target_compile_definitions(DispatchTests PRIVATE - CPPINTEROP_LIB_PATH="${CMAKE_BINARY_DIR}/lib/libclangCppInterOp${CMAKE_SHARED_LIBRARY_SUFFIX}" + CPPINTEROP_BIN_DIR="${CMAKE_BINARY_DIR}" ) set_output_directory(DispatchTests BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/bin/$/ diff --git a/unittests/CppInterOp/DispatchInit.cpp b/unittests/CppInterOp/DispatchInit.cpp index 7a5c5723b..2b71a97c4 100644 --- a/unittests/CppInterOp/DispatchInit.cpp +++ b/unittests/CppInterOp/DispatchInit.cpp @@ -1,8 +1,12 @@ // Storage definitions and initialization for dispatch function pointers. // Linked only by DispatchTests — not by the normal test suite. +#include "TestPaths.h" #include "CppInterOp/Dispatch.h" +#include +#include + // Define storage for all raw dispatch function pointers. using namespace Cpp; #define CPPINTEROP_API_FUNC(DN, CN, Ret, DeclArgs, CallArgs, RawTypes) \ @@ -12,8 +16,18 @@ using namespace Cpp; namespace { struct DispatchInitializer { DispatchInitializer() { - if (!Cpp::LoadDispatchAPI(CPPINTEROP_LIB_PATH)) { - std::abort(); + std::string libPath = TestUtils::GetCppInterOpLibPath(); + if (libPath.empty()) { + std::cerr << "DispatchTests: cannot locate libclangCppInterOp: set the " + "CPPINTEROP_BIN_DIR environment variable to the CppInterOp " + "artifacts prefix (the directory containing lib/ and " + "include/).\n"; + std::exit(EXIT_FAILURE); + } + if (!Cpp::LoadDispatchAPI(libPath.c_str())) { + std::cerr << "DispatchTests: failed to load dispatch API from '" + << libPath << "'.\n"; + std::exit(EXIT_FAILURE); } } }; diff --git a/unittests/CppInterOp/DispatchSmokeTest.cpp b/unittests/CppInterOp/DispatchSmokeTest.cpp index 11d4ae0b0..c5fc8e90b 100644 --- a/unittests/CppInterOp/DispatchSmokeTest.cpp +++ b/unittests/CppInterOp/DispatchSmokeTest.cpp @@ -1,3 +1,4 @@ +#include "TestPaths.h" #include "CppInterOp/Dispatch.h" #include "gtest/gtest.h" @@ -186,8 +187,11 @@ TEST(DispatchSmokeTest, CAPI_ScalarFunctions) { Cpp::CreateInterpreter({}); Cpp::Declare("namespace DispCAPI { class Cls {}; int var = 1; }"); - DlHandle lib(CPPINTEROP_LIB_PATH); - ASSERT_TRUE(lib) << "Failed to dlopen " << CPPINTEROP_LIB_PATH; + std::string libPath = TestUtils::GetCppInterOpLibPath(); + ASSERT_FALSE(libPath.empty()) + << "Set CPPINTEROP_BIN_DIR to the CppInterOp artifacts prefix"; + DlHandle lib(libPath.c_str()); + ASSERT_TRUE(lib) << "Failed to dlopen " << libPath; using IsClassFn = bool (*)(void*); using GetNameFn = char* (*)(void*); @@ -215,8 +219,11 @@ TEST(DispatchSmokeTest, CAPI_CollectionFunctions) { Cpp::CreateInterpreter({}); Cpp::Declare("enum DispCAPIEnum { DA, DB, DC };"); - DlHandle lib(CPPINTEROP_LIB_PATH); - ASSERT_TRUE(lib) << "Failed to dlopen " << CPPINTEROP_LIB_PATH; + std::string libPath = TestUtils::GetCppInterOpLibPath(); + ASSERT_FALSE(libPath.empty()) + << "Set CPPINTEROP_BIN_DIR to the CppInterOp artifacts prefix"; + DlHandle lib(libPath.c_str()); + ASSERT_TRUE(lib) << "Failed to dlopen " << libPath; using GetNamedFn = void* (*)(const char*, void*); using GetEnumConstantsFn = Cpp::CppInterOpArray (*)(void*); diff --git a/unittests/CppInterOp/DispatchTest.cpp b/unittests/CppInterOp/DispatchTest.cpp index 157bb395c..7c141963a 100644 --- a/unittests/CppInterOp/DispatchTest.cpp +++ b/unittests/CppInterOp/DispatchTest.cpp @@ -1,4 +1,5 @@ #include "CppInterOp/Dispatch.h" +#include "TestPaths.h" #include "gtest/gtest.h" @@ -39,18 +40,22 @@ TEST(DispatchTest, DlGetProcAddress_Unimplemented) { } TEST(DispatchTest, LoadUnloadCycle) { + std::string libPath = TestUtils::GetCppInterOpLibPath(); + ASSERT_FALSE(libPath.empty()) + << "Set CPPINTEROP_BIN_DIR to the CppInterOp artifacts prefix"; + Cpp::UnloadDispatchAPI(); // make sure we're not loaded already... EXPECT_FALSE(Cpp::LoadDispatchAPI("some/random/invalid/directory.so")); Cpp::UnloadDispatchAPI(); // should reset for next load to be successful - EXPECT_TRUE(Cpp::LoadDispatchAPI(CPPINTEROP_LIB_PATH)); + EXPECT_TRUE(Cpp::LoadDispatchAPI(libPath.c_str())); Cpp::UnloadDispatchAPI(); // should reset for next load to fail EXPECT_FALSE(Cpp::LoadDispatchAPI("some/other/random/invalid/directory.so")); // NOTE: minimize side-effects: reload assuming the static set is still // and other test may depend on this being loaded - EXPECT_TRUE(Cpp::LoadDispatchAPI(CPPINTEROP_LIB_PATH)); + EXPECT_TRUE(Cpp::LoadDispatchAPI(libPath.c_str())); } // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast) diff --git a/unittests/CppInterOp/TestPaths.h b/unittests/CppInterOp/TestPaths.h new file mode 100644 index 000000000..ca441e329 --- /dev/null +++ b/unittests/CppInterOp/TestPaths.h @@ -0,0 +1,40 @@ +#ifndef CPPINTEROP_UNITTESTS_LIBCPPINTEROP_TESTPATHS_H +#define CPPINTEROP_UNITTESTS_LIBCPPINTEROP_TESTPATHS_H + +#include +#include + +namespace TestUtils { + +/// CppInterOp artifacts prefix (a directory containing lib/ and include/). +/// The CPPINTEROP_BIN_DIR environment variable overrides the compile-time +/// default so relocated test binaries can find the library. Empty if +/// neither is available. +inline std::string GetCppInterOpDirPath() { + if (const char* env = std::getenv("CPPINTEROP_BIN_DIR")) + return env; +#ifdef CPPINTEROP_BIN_DIR + return CPPINTEROP_BIN_DIR; +#else + return {}; +#endif +} + +/// Full path to libclangCppInterOp under the artifacts prefix; empty if the +/// prefix is unknown. +inline std::string GetCppInterOpLibPath() { + std::string dir = GetCppInterOpDirPath(); + if (dir.empty()) + return {}; +#if defined(_WIN32) + return dir + "/lib/libclangCppInterOp.dll"; +#elif defined(__APPLE__) + return dir + "/lib/libclangCppInterOp.dylib"; +#else + return dir + "/lib/libclangCppInterOp.so"; +#endif +} + +} // end namespace TestUtils + +#endif // CPPINTEROP_UNITTESTS_LIBCPPINTEROP_TESTPATHS_H diff --git a/unittests/CppInterOp/TracingTests.cpp b/unittests/CppInterOp/TracingTests.cpp index e5be411da..452e50a91 100644 --- a/unittests/CppInterOp/TracingTests.cpp +++ b/unittests/CppInterOp/TracingTests.cpp @@ -1,5 +1,7 @@ #include "../../lib/CppInterOp/Tracing.h" +#include "TestPaths.h" + #include "clang/Basic/Version.h" #include "CppInterOp/CppInterOp.h" @@ -1191,9 +1193,9 @@ TEST_F(TracingTest, ReproducerCompilesViaInterpreter) { // Create a fresh interpreter and #include the reproducer file as-is. Cpp::CreateInterpreter({}); - Cpp::AddIncludePath(CPPINTEROP_DIR "/include"); - // Generated .inc files live under the build tree. - Cpp::AddIncludePath(CPPINTEROP_BINARY_DIR "/include"); + Cpp::AddIncludePath(CPPINTEROP_SRC_DIR "/include"); + // Generated .inc files live under the artifacts prefix. + Cpp::AddIncludePath((TestUtils::GetCppInterOpDirPath() + "/include").c_str()); std::string includeDirective = "#include \"" + Path + "\""; EXPECT_EQ(Cpp::Process(includeDirective.c_str()), 0) @@ -1389,7 +1391,7 @@ static std::string ReadFileToString(const std::string& path) { return {std::istreambuf_iterator(ifs), std::istreambuf_iterator()}; } -// This test reads source files from the build tree via CPPINTEROP_DIR. +// This test reads source files from the checkout via CPPINTEROP_SRC_DIR. TEST_F(TracingTest, StartStopTracingWritesToFile) { // StartTracing begins recording; StopTracing writes the file. std::string Path = CppInterOp::Tracing::StartTracing(/*WriteOnStdErr=*/false); @@ -1543,13 +1545,15 @@ TEST(TracingCoverageTest, AllPublicAPIsAreTraced) { // 1. Parse the header and generated declarations to extract all // CPPINTEROP_API function names. std::string Header = - ReadFileToString(CPPINTEROP_DIR "/include/CppInterOp/CppInterOp.h"); + ReadFileToString(CPPINTEROP_SRC_DIR "/include/CppInterOp/CppInterOp.h"); ASSERT_FALSE(Header.empty()) << "Could not read CppInterOp.h"; - // Function declarations are generated into CppInterOpDecl.inc. - std::string DeclInc = ReadFileToString( - CPPINTEROP_BINARY_DIR "/include/CppInterOp/CppInterOpDecl.inc"); + // Function declarations are generated into CppInterOpDecl.inc, which + // lives under the artifacts prefix. + std::string DeclInc = + ReadFileToString(TestUtils::GetCppInterOpDirPath() + + "/include/CppInterOp/CppInterOpDecl.inc"); if (DeclInc.empty()) - DeclInc = ReadFileToString(CPPINTEROP_DIR + DeclInc = ReadFileToString(CPPINTEROP_SRC_DIR "/include/CppInterOp/CppInterOpDecl.inc"); Header += DeclInc; @@ -1578,7 +1582,7 @@ TEST(TracingCoverageTest, AllPublicAPIsAreTraced) { // 2. For each API name, find its definition in the implementation and // verify INTEROP_TRACE appears shortly after the opening brace. std::string Impl = - ReadFileToString(CPPINTEROP_DIR "/lib/CppInterOp/CppInterOp.cpp"); + ReadFileToString(CPPINTEROP_SRC_DIR "/lib/CppInterOp/CppInterOp.cpp"); ASSERT_FALSE(Impl.empty()) << "Could not read CppInterOp.cpp"; std::vector Missing;