From 1f213dd498d0e4bf66cccc7179486b8b8e6d9b99 Mon Sep 17 00:00:00 2001 From: chris Date: Sat, 8 Nov 2025 15:57:08 -0600 Subject: [PATCH] cmake: fix issue #25048 have cmake fail when pivy and swig use different API versions, update status messages --- cMake/FreeCAD_Helpers/SetupSwig.cmake | 164 ++++++++++++++++++++++++-- 1 file changed, 156 insertions(+), 8 deletions(-) diff --git a/cMake/FreeCAD_Helpers/SetupSwig.cmake b/cMake/FreeCAD_Helpers/SetupSwig.cmake index 9319d88a8e..8165a37cfa 100644 --- a/cMake/FreeCAD_Helpers/SetupSwig.cmake +++ b/cMake/FreeCAD_Helpers/SetupSwig.cmake @@ -1,12 +1,160 @@ macro(SetupSwig) -# -------------------------------- Swig ---------------------------------- - find_package(SWIG) +#-------------------------------- Swig ---------------------------------- - if (NOT SWIG_FOUND) - message("=====================================================\n" - "SWIG not found, will not build SWIG binding for pivy.\n" - "=====================================================\n") - endif(NOT SWIG_FOUND) +# force cmake to re-search for SWIG when CMAKE_PREFIX_PATH changes +unset(SWIG_EXECUTABLE CACHE) +unset(SWIG_DIR CACHE) +unset(SWIG_VERSION CACHE) +unset(SWIG_FOUND CACHE) -endmacro(SetupSwig) +if(BUILD_SKETCHER) + # SWIG is required for sketcher WB (use QUIET to provide custom error message) + find_package(SWIG QUIET) + + if(NOT SWIG_FOUND) + message(FATAL_ERROR + "-----------------------------------------------------\n" + "SWIG not found, swig & pivy required for sketcher WB.\n" + "-----------------------------------------------------\n") + # do not continue with check if swig not found + return() + endif() + + # check swig/pivy runtime compatibility + message(STATUS "checking SWIG/Pivy runtime compatibility...") + + # get SWIG's runtime version using -external-runtime flag + execute_process( + COMMAND ${SWIG_EXECUTABLE} -python -external-runtime "${CMAKE_BINARY_DIR}/swig_runtime_check.h" + RESULT_VARIABLE SWIG_EXTERNAL_RUNTIME_RESULT + ERROR_VARIABLE SWIG_EXTERNAL_RUNTIME_ERROR + ERROR_QUIET + ) + # NOTE: only print the below output if using `cmake --log-level=DEBUG` + message(DEBUG "SWIG external-runtime result: ${SWIG_EXTERNAL_RUNTIME_RESULT}") + message(DEBUG "SWIG external-runtime error: ${SWIG_EXTERNAL_RUNTIME_ERROR}") + + message(STATUS "Looking for: ${CMAKE_BINARY_DIR}/swig_runtime_check.h") + if(EXISTS "${CMAKE_BINARY_DIR}/swig_runtime_check.h") + message(STATUS "File exists: YES") + else() + message(STATUS "File exists: NO") + endif() + + if(EXISTS "${CMAKE_BINARY_DIR}/swig_runtime_check.h") + file(STRINGS "${CMAKE_BINARY_DIR}/swig_runtime_check.h" + SWIG_RUNTIME_VERSION_LINE + REGEX "^#define SWIG_RUNTIME_VERSION") + + message(STATUS "SWIG_RUNTIME_VERSION_LINE: ${SWIG_RUNTIME_VERSION_LINE}") + + if(SWIG_RUNTIME_VERSION_LINE) + # extract the version number (it's in quotes: "5") + string(REGEX MATCH "\"([0-9]+)\"" _ "${SWIG_RUNTIME_VERSION_LINE}") + set(SWIG_RUNTIME_VERSION "${CMAKE_MATCH_1}") + message(STATUS "Extracted SWIG_RUNTIME_VERSION: ${SWIG_RUNTIME_VERSION}") + endif() + + file(REMOVE "${CMAKE_BINARY_DIR}/swig_runtime_check.h") + else() + message(STATUS "swig_runtime_check.h not found!") + endif() + + # extract pivy's SWIG runtime version from the compiled module + # NOTE: python code can not be indented + set(PYTHON_CHECK_PIVY_RUNTIME [=[ +import sys +import os +import re + +try: + import pivy + pivy_dir = os.path.dirname(pivy.__file__) + print(f'DEBUG:pivy_dir={pivy_dir}', file=sys.stderr) + print(f'DEBUG:files={os.listdir(pivy_dir)}', file=sys.stderr) + + pivy_path = None + + # Look for _coin module with any extension (.so on Unix, .pyd on Windows) + for f in os.listdir(pivy_dir): + print(f'DEBUG:checking={f}', file=sys.stderr) + if f.startswith('_coin') and (f.endswith('.so') or f.endswith('.pyd')): + pivy_path = os.path.join(pivy_dir, f) + break + + print(f'DEBUG:pivy_path={pivy_path}', file=sys.stderr) + + if pivy_path and os.path.exists(pivy_path): + with open(pivy_path, 'rb') as f: + content = f.read().decode('latin-1', errors='ignore') + + print(f'DEBUG:content_len={len(content)}', file=sys.stderr) + + # Use regex to find swig_runtime_data followed by a number + match = re.search(r'swig_runtime_data(\d+)', content) + print(f'DEBUG:match={match}', file=sys.stderr) + if match: + print(match.group(1)) + +except ImportError as e: + print(f'DEBUG:import_error={e}', file=sys.stderr) + print('ERROR_IMPORT') +except Exception as e: + print(f'DEBUG:exception={e}', file=sys.stderr) +]=]) + +execute_process( + COMMAND ${Python3_EXECUTABLE} -c "${PYTHON_CHECK_PIVY_RUNTIME}" + OUTPUT_VARIABLE PIVY_RUNTIME_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_VARIABLE PIVY_DEBUG_OUTPUT + TIMEOUT 10 +) + +message(DEBUG "Pivy debug output: ${PIVY_DEBUG_OUTPUT}") + + # Handle errors and compare versions + if(PIVY_RUNTIME_VERSION STREQUAL "ERROR_IMPORT") + message(WARNING + "Could not import pivy to check SWIG compatibility.\n" + "Proceeding without SWIG version check." + ) + elseif(SWIG_RUNTIME_VERSION AND PIVY_RUNTIME_VERSION) + if(NOT SWIG_RUNTIME_VERSION STREQUAL PIVY_RUNTIME_VERSION) + message(FATAL_ERROR +" -------------------------------------------------------- + + SWIG / PIVY RUNTIME VERSION MISMATCH DETECTED! + + SWIG runtime API version: ${SWIG_RUNTIME_VERSION} + Pivy runtime API version: ${PIVY_RUNTIME_VERSION} + + These must match for compatibility. + This will cause runtime errors: 'No SWIG wrapped library loaded' + + swig v4.4.x is not compatible with pivy built with swig <=4.3.x + + FIX: Install a SWIG version that uses runtime ${PIVY_RUNTIME_VERSION} + or rebuild Pivy with your current SWIG ${SWIG_VERSION}. + +--------------------------------------------------------" + ) + else() + message(STATUS "SWIG/Pivy runtime compatibility: PASSED") + message(STATUS "swig runtime API version: ${SWIG_RUNTIME_VERSION}") + message(STATUS "pivy runtime API version: ${PIVY_RUNTIME_VERSION}") + message(STATUS "swig binary version building freecad: ${SWIG_VERSION}") + endif() + else() + if(NOT SWIG_RUNTIME_VERSION) + message(WARNING "Could not determine SWIG runtime version") + endif() + if(NOT PIVY_RUNTIME_VERSION) + message(WARNING "Could not determine Pivy runtime version") + endif() + message(WARNING "Proceeding without SWIG/Pivy compatibility check.") + endif() +endif() + +endmacro()