# ************************************************************************ # Copyright (C) 2025-2026 Advanced Micro Devices, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # # ************************************************************************ cmake_minimum_required(VERSION 3.16) message(STATUS "Configuring StinkyTofu Python Module") # ============================================================================ # Find stinkytofu (supports standalone build against installed package) # ============================================================================ if(NOT TARGET stinkytofu) find_package(stinkytofu REQUIRED) set(_stinkytofu_installed TRUE) else() set(_stinkytofu_installed FALSE) endif() # ============================================================================ # Find Python and nanobind # ============================================================================ # nanobind requires find_package(Python ...) not Python3 find_package(Python COMPONENTS Interpreter Development.Module REQUIRED) include(FetchContent) FetchContent_Declare( nanobind GIT_REPOSITORY https://github.com/wjakob/nanobind.git GIT_TAG 9b3afa9dbdc23641daf26fadef7743e7127ff92f # v2.6.1 ) FetchContent_MakeAvailable(nanobind) # ============================================================================ # Create the Python Module # ============================================================================ # Temporarily remove global compile options to avoid inheriting -fno-rtti -fno-exceptions get_directory_property(SAVED_COMPILE_OPTIONS COMPILE_OPTIONS) set_directory_properties(PROPERTIES COMPILE_OPTIONS "") nanobind_add_module(stinkytofu_python NB_STATIC # Build as static module src/python_bindings.cpp src/logical_count.cpp src/HardwareCaps.cpp ) # Restore global compile options set_directory_properties(PROPERTIES COMPILE_OPTIONS "${SAVED_COMPILE_OPTIONS}") if(_stinkytofu_installed) # Installed stinkytofu: include dirs come from the exported target. # PythonBindings_generated.inc is installed under include/stinkytofu/bindings/python/. target_include_directories(stinkytofu_python PRIVATE $ ) else() # Source-tree build: use local paths and tablegen output. target_include_directories(stinkytofu_python PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../include" "${CMAKE_CURRENT_SOURCE_DIR}/../hardware/include" ${TABLEGEN_OUTPUT_DIR} ) endif() target_link_libraries(stinkytofu_python PRIVATE stinkytofu ) # Set output name to _stinkytofu (private module, loaded by __init__.py) # Place in stinkytofu/ subdirectory if(_stinkytofu_installed) get_target_property(_st_loc stinkytofu IMPORTED_LOCATION) if(_st_loc) cmake_path(GET _st_loc PARENT_PATH _st_dir) else() set(_st_dir "") endif() set_target_properties(stinkytofu_python PROPERTIES OUTPUT_NAME _stinkytofu LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/stinkytofu INSTALL_RPATH "$ORIGIN" BUILD_RPATH "${_st_dir}" ) else() set_target_properties(stinkytofu_python PROPERTIES OUTPUT_NAME _stinkytofu LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/stinkytofu INSTALL_RPATH "$ORIGIN" BUILD_RPATH "${CMAKE_BINARY_DIR}" ) endif() # On Windows there is no RPATH; copy stinkytofu.dll next to _stinkytofu.pyd # so the dynamic linker can find it at runtime. if(WIN32) add_custom_command(TARGET stinkytofu_python POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ COMMENT "Copying stinkytofu.dll next to _stinkytofu.pyd") endif() # Copy __init__.py and generate _build_info.py at configure time (not post-build) # so the package is importable immediately from the build tree after cmake configure. configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/stinkytofu/__init__.py" "${CMAKE_BINARY_DIR}/lib/stinkytofu/__init__.py" COPYONLY ) configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/stinkytofu/_build_info.py.in" "${CMAKE_BINARY_DIR}/lib/stinkytofu/_build_info.py" @ONLY ) # _build_info.py is intentionally NOT in install() rules so it is absent # from wheels and pre-built packages, acting as the source-build sentinel. # Enable RTTI and exceptions for the Python bindings target_compile_options(stinkytofu_python PRIVATE -frtti -fexceptions ) # Also ensure nanobind-static target has RTTI and exceptions enabled if(TARGET nanobind-static) get_target_property(NB_COMPILE_OPTIONS nanobind-static COMPILE_OPTIONS) if(NB_COMPILE_OPTIONS) list(REMOVE_ITEM NB_COMPILE_OPTIONS "-fno-rtti" "-fno-exceptions") set_target_properties(nanobind-static PROPERTIES COMPILE_OPTIONS "${NB_COMPILE_OPTIONS}") endif() target_compile_options(nanobind-static PRIVATE -frtti -fexceptions) endif() # ============================================================================ # Installation (Optional) # ============================================================================ # Use a relative path so the install respects CMAKE_INSTALL_PREFIX. # Python_SITEARCH is absolute (e.g. /usr/local/lib/python3.12/dist-packages) # which would bypass CMAKE_INSTALL_PREFIX and require root. set(STINKYTOFU_PYTHON_SITE_DIR "lib/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/dist-packages" CACHE PATH "Python module install directory (relative to CMAKE_INSTALL_PREFIX)") # Install C++ module (_stinkytofu.so) install(TARGETS stinkytofu_python LIBRARY DESTINATION ${STINKYTOFU_PYTHON_SITE_DIR}/stinkytofu COMPONENT python_bindings ) # Co-install libstinkytofu.so / stinkytofu.dll next to _stinkytofu so the # dynamic linker can find it at runtime. install(FILES $ DESTINATION ${STINKYTOFU_PYTHON_SITE_DIR}/stinkytofu COMPONENT python_bindings ) # Install Python wrapper (__init__.py) install(FILES stinkytofu/__init__.py DESTINATION ${STINKYTOFU_PYTHON_SITE_DIR}/stinkytofu COMPONENT python_bindings ) # scikit-build-core (pip install) install rules. # SKBUILD is set by scikit-build-core when building a wheel. # _build_info.py is intentionally excluded so wheels behave as pre-built packages. if(DEFINED SKBUILD) install(TARGETS stinkytofu_python DESTINATION stinkytofu) install(FILES $ DESTINATION stinkytofu) install(FILES stinkytofu/__init__.py DESTINATION stinkytofu) install(FILES "${CMAKE_BINARY_DIR}/intrinsics.st.bc" DESTINATION stinkytofu) endif() message(STATUS "StinkyTofu Python module configured successfully") message(STATUS " - Python module will be built as: ${CMAKE_BINARY_DIR}/python_module/stinkytofu${Python_SOABI}.so") message(STATUS " - Linking directly to core stinkytofu library (StinkyTofu and StinkyIR now in core)")