diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3cbdc25f5f..995c4bd74d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -56,6 +56,40 @@ function(orcaslicer_discover_tests TARGET) catch_discover_tests(${TARGET} ADD_TAGS_AS_LABELS) endfunction() +# Stages a private Python runtime beside TARGET's output and (on macOS) links +# in the matching rpath, so an embedded pybind11 interpreter in TARGET can find +# libpython/the stdlib at runtime without depending on the build machine's +# Python install. Apply this to every test target that embeds an interpreter - +# duplicating the WIN32/APPLE blocks by hand lets them drift out of sync. +function(orcaslicer_stage_test_python_runtime TARGET) + if (WIN32) + add_custom_command(TARGET ${TARGET} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory "$/python" + COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_PREFIX_PATH}/libpython" "$/python" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${CMAKE_PREFIX_PATH}/libpython/python${_bundled_python_abi}.dll" + "${CMAKE_PREFIX_PATH}/libpython/vcruntime140.dll" + "${CMAKE_PREFIX_PATH}/libpython/vcruntime140_1.dll" + "$" + COMMENT "Copying Python runtime for ${TARGET}" + VERBATIM + ) + elseif (APPLE) + target_link_options(${TARGET} PRIVATE + "LINKER:-rpath,@executable_path/python/lib") + + add_custom_command(TARGET ${TARGET} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E rm -rf + "$/python" + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${CMAKE_PREFIX_PATH}/libpython" + "$/python" + COMMENT "Copying Python runtime for ${TARGET}" + VERBATIM + ) + endif() +endfunction() + add_subdirectory(libnest2d) add_subdirectory(libslic3r) add_subdirectory(slic3rutils) diff --git a/tests/slic3rutils/CMakeLists.txt b/tests/slic3rutils/CMakeLists.txt index 16c19910ee..fb1fec2e73 100644 --- a/tests/slic3rutils/CMakeLists.txt +++ b/tests/slic3rutils/CMakeLists.txt @@ -31,32 +31,7 @@ set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests") orcaslicer_copy_test_dlls() -if (WIN32) - add_custom_command(TARGET ${_TEST_NAME}_tests POST_BUILD - COMMAND ${CMAKE_COMMAND} -E make_directory "$/python" - COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_PREFIX_PATH}/libpython" "$/python" - COMMAND ${CMAKE_COMMAND} -E copy_if_different - "${CMAKE_PREFIX_PATH}/libpython/python${_bundled_python_abi}.dll" - "${CMAKE_PREFIX_PATH}/libpython/vcruntime140.dll" - "${CMAKE_PREFIX_PATH}/libpython/vcruntime140_1.dll" - "$" - COMMENT "Copying Python runtime for slic3rutils plugin host API tests" - VERBATIM - ) -elseif (APPLE) - target_link_options(${_TEST_NAME}_tests PRIVATE - "LINKER:-rpath,@executable_path/python/lib") - - add_custom_command(TARGET ${_TEST_NAME}_tests POST_BUILD - COMMAND ${CMAKE_COMMAND} -E rm -rf - "$/python" - COMMAND ${CMAKE_COMMAND} -E copy_directory - "${CMAKE_PREFIX_PATH}/libpython" - "$/python" - COMMENT "Copying Python runtime for macOS plugin host API tests" - VERBATIM - ) -endif() +orcaslicer_stage_test_python_runtime(${_TEST_NAME}_tests) orcaslicer_discover_tests(${_TEST_NAME}_tests) @@ -71,10 +46,12 @@ endif () target_link_libraries(printer_agent_plugin_tests test_common libslic3r_gui libslic3r pybind11::embed Catch2::Catch2) set_property(TARGET printer_agent_plugin_tests PROPERTY FOLDER "tests") -# why: the existing target stages the complete bundled Python home under -# python/, which is the layout PythonInterpreter discovers beside the test exe. +# why: both targets' post-build steps stage/clear the same python/ directory +# beside the shared test output dir; serialize them so the copies can't race. add_dependencies(printer_agent_plugin_tests ${_TEST_NAME}_tests) orcaslicer_copy_test_dlls() +orcaslicer_stage_test_python_runtime(printer_agent_plugin_tests) + orcaslicer_discover_tests(printer_agent_plugin_tests) diff --git a/tests/slic3rutils/test_printer_agent.cpp b/tests/slic3rutils/test_printer_agent.cpp index c97d70099c..7a2fb24e7b 100644 --- a/tests/slic3rutils/test_printer_agent.cpp +++ b/tests/slic3rutils/test_printer_agent.cpp @@ -5,6 +5,8 @@ #include #include +#include "python_test_support.hpp" + #include #include @@ -209,29 +211,11 @@ TEST_CASE("unit: printer-agent registry register / lookup / duplicate-reject", " // every printer-agent plugin subclasses. If a binding is renamed or removed, // plugins fail at runtime even though C++ still compiles. // =========================================================================== -namespace { - -void ensure_python_initialized() -{ - // why: the `orca` module is embedded in this binary, so a bare interpreter - // can import it without a bundled Python home. The app interpreter expects - // that deployed layout, which is not present beside this test binary. - if (!Py_IsInitialized()) { - static py::scoped_interpreter interpreter; - (void) interpreter; - } -} - -py::module_ import_orca_module() -{ - ensure_python_initialized(); - // Force PythonPluginBridge.cpp into the binary so the embedded - // PYBIND11_EMBEDDED_MODULE(orca, ...) registration (incl. printer_agent) exists. - (void) Slic3r::PythonPluginBridge::instance(); - return py::module_::import("orca"); -} - -} // namespace +// ensure_python_initialized()/import_orca_module() come from python_test_support.hpp: +// a bare scoped_interpreter with no PyConfig.home falls back to the build-time +// stdlib path, which doesn't exist beside this test binary, so Py_Initialize() +// fails to load the codecs needed for the filesystem encoding. The shared helper +// points PyConfig.home at the python/ runtime staged next to the test executable. TEST_CASE("integration: orca.printer_agent binding surface", "[integration][Python]") {