################################################################################ # # # This file is part of IfcOpenShell. # # # # IfcOpenShell is free software: you can redistribute it and/or modify # # it under the terms of the Lesser GNU General Public License as published by # # the Free Software Foundation, either version 3.0 of the License, or # # (at your option) any later version. # # # # IfcOpenShell is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # # Lesser GNU General Public License for more details. # # # # You should have received a copy of the Lesser GNU General Public License # # along with this program. If not, see . # # # ################################################################################ if(POLICY CMP0148) # 3.27 cmake_policy(SET CMP0148 OLD) endif() if(POLICY CMP0177) # 3.31 cmake_policy(SET CMP0177 OLD) endif() FIND_PACKAGE(SWIG) IF(NOT SWIG_FOUND) MESSAGE( FATAL_ERROR "BUILD_IFCPYTHON enabled, but unable to find SWIG. Disable BUILD_IFCPYTHON or fix SWIG paths to proceed. " "Likely SWIG_EXECUTABLE is missing (current value - '${SWIG_EXECUTABLE}')." ) ENDIF() include(GNUInstallDirs) INCLUDE(${SWIG_USE_FILE}) IF(NOT "${PYTHON_INCLUDE_DIR}" STREQUAL "") # Hint for FindPython where to find Python_INCLUDE_DIRS. set(Python_INCLUDE_DIR "${PYTHON_INCLUDE_DIR}") MESSAGE(STATUS "Looking for Python header files in: ${Python_INCLUDE_DIR}") ENDIF() IF(NOT "${PYTHON_LIBRARY}" STREQUAL "") # Hint for FindPython where to find Python_LIBRARIES. set(Python_LIBRARY "${PYTHON_LIBRARY}") MESSAGE(STATUS "Looking for Python library file in: ${Python_LIBRARY}") ENDIF() # NOTE PYTHONLIBS_FOUND and PYTHONINTERP_FOUND cannot seem to be trusted so # we need further checks to see whether the packages were actually found or not. FIND_PACKAGE(Python COMPONENTS Development) IF(NOT Python_Development_FOUND) MESSAGE(FATAL_ERROR "BUILD_IFCPYTHON enabled, but unable to find Python lib or header. Disable BUILD_IFCPYTHON or fix Python paths to proceed.") ENDIF() # Ensure version is saved here, from wasm libraries, # not from Python interpreter that might be unrelated to pyodide Python version. set(_python_libs_version "${Python_VERSION_MAJOR}${Python_VERSION_MINOR}") INCLUDE_DIRECTORIES(${Python_INCLUDE_DIRS}) INCLUDE_DIRECTORIES(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}) SET(CMAKE_SWIG_FLAGS ${SWIG_DEFINES}) # NOTE Workaround for most likely missing debug Python libraries on Windows (requires Python built from the source). # Python 3.5 intaller and onwards will have an option to install the debug libraries too. # NOTE PYTHON_DEBUG_LIBRARIES appears to be a deprecated variable IF (WIN32 AND NOT PYTHON_DEBUG_LIBRARIES) MESSAGE(STATUS "PYTHON_DEBUG_LIBRARIES not found, defining SWIG_PYTHON_INTERPRETER_NO_DEBUG workaround for IfcWrap.") ADD_DEFINITIONS(-DSWIG_PYTHON_INTERPRETER_NO_DEBUG) ENDIF() if (WITH_CGAL) set(LIBSVGFILL svgfill) endif() SET_SOURCE_FILES_PROPERTIES(IfcPython.i PROPERTIES CPLUSPLUS ON) # Rebuild on changes in other .i files. SET_PROPERTY( SOURCE IfcPython.i PROPERTY DEPENDS IfcGeomWrapper.i IfcParseWrapper.i utils/type_conversion.i utils/typemaps_in.i utils/typemaps_out.i ) # Debug build on Windows add required `_d` suffix to the resulting .pyd file. # If we won't override -interface name, # swig py wrapper will try to import `_ifcopenshell_wrapper_d` module and would fail. set(SWIG_MODULE_ifcopenshell_wrapper_EXTRA_FLAGS "-interface" "_ifcopenshell_wrapper") swig_add_library(ifcopenshell_wrapper LANGUAGE python SOURCES IfcPython.i) SET_PROPERTY(TARGET ${SWIG_MODULE_ifcopenshell_wrapper_REAL_NAME} PROPERTY SWIG_DEPENDS ${IFCOPENSHELL_LIBRARIES}) if (WASM_BUILD) # SIDE_MODULE=1 - add to .so all symbols from linked archives (default used by pyodide). # Since currently libIfcGeom.a seems to be linked twice it results in duplicated symbols and compilation errors. # Possibly in the future we can clean up linked libs and try `=1`. # SIDE_MODULE=2 - add to .so only explicitly exported functions (`-s EXPORTED_FUNCTIONS). # Have to provide it here to override possible `=1` coming from pyodide. target_link_options( ${SWIG_MODULE_ifcopenshell_wrapper_REAL_NAME} PRIVATE "SHELL:-s EXPORTED_FUNCTIONS=_PyInit__ifcopenshell_wrapper -s SIDE_MODULE=2" ) endif() if("$ENV{LDFLAGS}" MATCHES ".undefined.suppress") # On osx there is some state in the python dylib. With `-Wl,undefined,suppress` we can ignore the missing symbols at compile time. SWIG_LINK_LIBRARIES(ifcopenshell_wrapper ${IFCOPENSHELL_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${Boost_LIBRARIES} ${LIBSVGFILL}) else() SWIG_LINK_LIBRARIES(ifcopenshell_wrapper ${IFCOPENSHELL_LIBRARIES} ${Python_LIBRARIES} ${LIBSVGFILL}) endif() if ((NOT WIN32) AND BUILD_SHARED_LIBS) SET_INSTALL_RPATHS(${SWIG_MODULE_ifcopenshell_wrapper_REAL_NAME} "${IFCDIRS};${OCC_LIBRARY_DIR}") endif() # Try to find the Python interpreter to get the site-packages # directory in which the wrapper can be installed. if(NOT ${PYTHON_EXECUTABLE} STREQUAL "") set(Python_EXECUTABLE "${PYTHON_EXECUTABLE}") MESSAGE(STATUS "Looking for Python interpreter in: ${PYTHON_EXECUTABLE}") endif() FIND_PACKAGE(Python COMPONENTS Interpreter) IF(Python_Interpreter_FOUND OR PYTHON_MODULE_INSTALL_DIR) # Python_SOABI example value is 'cp311-win_amd64'. if(WASM_BUILD) # For WASM we usually don't provide interpreter, # so `find_package` couldn't find `Python_SOABI`. # So we just hardcode it. # .cpython-313-wasm32-emscripten.so set(PYTHON_EXTENSION_SUFFIX ".cpython-${_python_libs_version}-wasm32-emscripten.so") elseif(WIN32) set(PYTHON_EXTENSION_SUFFIX ".${Python_SOABI}.pyd") else() set(PYTHON_EXTENSION_SUFFIX ".${Python_SOABI}.so") endif() # On Windows there is '_d' prefix for debug builds - e.g. `_d.cp311-win_amd64.pyd`. if(CMAKE_SYSTEM_NAME STREQUAL "Windows") set_target_properties( ${SWIG_MODULE_ifcopenshell_wrapper_REAL_NAME} PROPERTIES SUFFIX ${PYTHON_EXTENSION_SUFFIX} DEBUG_POSTFIX "_d" ) else() set_target_properties( ${SWIG_MODULE_ifcopenshell_wrapper_REAL_NAME} PROPERTIES SUFFIX ${PYTHON_EXTENSION_SUFFIX} ) endif() if (PYTHON_MODULE_INSTALL_DIR) set(python_package_dir "${PYTHON_MODULE_INSTALL_DIR}") else() IF (USERSPACE_PYTHON_PREFIX) EXECUTE_PROCESS( COMMAND ${PYTHON_EXECUTABLE} -c "import sys; import site; sys.stdout.write(site.USER_SITE)" OUTPUT_VARIABLE python_package_dir ) ELSE () set(python_package_dir "${Python_SITEARCH}") ENDIF() if (BUILD_PACKAGE) set(python_package_dir ${CMAKE_INSTALL_LIBDIR}/python${PYTHON_VERSION_MAJOR}/dist-packages/) endif() endif() IF("${python_package_dir}" STREQUAL "") MESSAGE(WARNING "Unable to locate Python site-package directory, unable to install the Python wrapper") ELSE() message(STATUS "Python wrapper will be installed to '${python_package_dir}'.") FILE(GLOB_RECURSE sourcefiles "${CMAKE_CURRENT_SOURCE_DIR}/../ifcopenshell-python/ifcopenshell/*" ) FOREACH(file ${sourcefiles}) FILE(RELATIVE_PATH relative "${CMAKE_CURRENT_SOURCE_DIR}/../ifcopenshell-python/ifcopenshell/" "${file}") GET_FILENAME_COMPONENT(dir "${relative}" DIRECTORY) if(NOT IS_DIRECTORY "${file}") INSTALL(FILES "${file}" DESTINATION "${python_package_dir}/ifcopenshell/${dir}") endif() ENDFOREACH() INSTALL(FILES "${CMAKE_BINARY_DIR}/ifcwrap/ifcopenshell_wrapper.py" DESTINATION "${python_package_dir}/ifcopenshell") INSTALL(TARGETS ${SWIG_MODULE_ifcopenshell_wrapper_REAL_NAME} DESTINATION "${python_package_dir}/ifcopenshell") if (MSVC) INSTALL(FILES $ DESTINATION bin OPTIONAL) endif() ENDIF() ELSE() MESSAGE(WARNING "No Python interpreter found, unable to install the Python wrapper") ENDIF()