llvm-project/mlir/cmake/modules/AddMLIRPythonExtension.cmake

163 lines
5.7 KiB
CMake

################################################################################
# Build python extension
################################################################################
function(add_mlir_python_extension libname extname)
cmake_parse_arguments(ARG
""
"INSTALL_DIR"
"SOURCES;LINK_LIBS"
${ARGN})
if (ARG_UNPARSED_ARGUMENTS)
message(FATAL_ERROR " Unhandled arguments to add_mlir_python_extension(${libname}, ... : ${ARG_UNPARSED_ARGUMENTS}")
endif()
if ("${ARG_SOURCES}" STREQUAL "")
message(FATAL_ERROR " Missing SOURCES argument to add_mlir_python_extension(${libname}, ...")
endif()
# Normally on unix-like platforms, extensions are built as "MODULE" libraries
# and do not explicitly link to the python shared object. This allows for
# some greater deployment flexibility since the extension will bind to
# symbols in the python interpreter on load. However, it also keeps the
# linker from erroring on undefined symbols, leaving this to (usually obtuse)
# runtime errors. Building in "SHARED" mode with an explicit link to the
# python libraries allows us to build with the expectation of no undefined
# symbols, which is better for development. Note that not all python
# configurations provide build-time libraries to link against, in which
# case, we fall back to MODULE linking.
if(Python3_LIBRARIES STREQUAL "" OR NOT MLIR_PYTHON_BINDINGS_VERSION_LOCKED)
set(PYEXT_LINK_MODE MODULE)
set(PYEXT_LIBADD)
else()
set(PYEXT_LINK_MODE SHARED)
set(PYEXT_LIBADD ${Python3_LIBRARIES})
endif()
# The actual extension library produces a shared-object or DLL and has
# sources that must be compiled in accordance with pybind11 needs (RTTI and
# exceptions).
add_library(${libname} ${PYEXT_LINK_MODE}
${ARG_SOURCES}
)
target_include_directories(${libname} PRIVATE
"${Python3_INCLUDE_DIRS}"
"${pybind11_INCLUDE_DIR}"
)
target_link_directories(${libname} PRIVATE
"${Python3_LIBRARY_DIRS}"
)
# The extension itself must be compiled with RTTI and exceptions enabled.
# Also, some warning classes triggered by pybind11 are disabled.
target_compile_options(${libname} PRIVATE
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
# Enable RTTI and exceptions.
-frtti -fexceptions
# Noisy pybind warnings
-Wno-unused-value
-Wno-covered-switch-default
>
$<$<CXX_COMPILER_ID:MSVC>:
# Enable RTTI and exceptions.
/EHsc /GR>
)
# Configure the output to match python expectations.
set_target_properties(
${libname} PROPERTIES
# Build-time RPath layouts require to be a directory one up from the
# binary root.
# TODO: Don't reference the LLVM_BINARY_DIR here: the invariant is that
# the output directory must be at the same level of the lib directory
# where libMLIR.so is installed. This is presently not optimal from a
# project separation perspective and a discussion on how to better
# segment MLIR libraries needs to happen.
LIBRARY_OUTPUT_DIRECTORY ${LLVM_BINARY_DIR}/python
OUTPUT_NAME "${extname}"
PREFIX "${PYTHON_MODULE_PREFIX}"
SUFFIX "${PYTHON_MODULE_SUFFIX}${PYTHON_MODULE_EXTENSION}"
)
if(WIN32)
# Need to also set the RUNTIME_OUTPUT_DIRECTORY on Windows in order to
# control where the .dll gets written.
set_target_properties(
${libname} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${LLVM_BINARY_DIR}/python
)
endif()
# pybind11 requires binding code to be compiled with -fvisibility=hidden
# For static linkage, better code can be generated if the entire project
# compiles that way, but that is not enforced here. Instead, include a linker
# script that explicitly hides anything but the PyInit_* symbols, allowing gc
# to take place.
set_target_properties(${libname} PROPERTIES CXX_VISIBILITY_PRESET "hidden")
# Python extensions depends *only* on the public API and LLVMSupport unless
# if further dependencies are added explicitly.
target_link_libraries(${libname}
PRIVATE
MLIRPublicAPI
LLVMSupport
${ARG_LINK_LIBS}
${PYEXT_LIBADD}
)
target_link_options(${libname}
PRIVATE
# On Linux, disable re-export of any static linked libraries that
# came through.
$<$<PLATFORM_ID:Linux>:LINKER:--exclude-libs,ALL>
)
llvm_setup_rpath(${libname})
################################################################################
# Install
################################################################################
if (ARG_INSTALL_DIR)
install(TARGETS ${libname}
COMPONENT ${libname}
LIBRARY DESTINATION ${ARG_INSTALL_DIR}
ARCHIVE DESTINATION ${ARG_INSTALL_DIR}
# NOTE: Even on DLL-platforms, extensions go in the lib directory tree.
RUNTIME DESTINATION ${ARG_INSTALL_DIR}
)
endif()
if (NOT LLVM_ENABLE_IDE)
add_llvm_install_targets(
install-${libname}
DEPENDS ${libname}
COMPONENT ${libname})
endif()
endfunction()
function(add_mlir_dialect_python_bindings tblgen_target)
cmake_parse_arguments(ARG
""
"TD_FILE;DIALECT_NAME"
"DEPENDS"
${ARGN})
set(LLVM_TARGET_DEFINITIONS ${ARG_TD_FILE})
mlir_tablegen("${ARG_DIALECT_NAME}.py" -gen-python-op-bindings
-bind-dialect=${ARG_DIALECT_NAME})
add_public_tablegen_target(
${tblgen_target})
if(ARG_DEPENDS)
add_dependencies(${tblgen_target} ${ARG_DEPENDS})
endif()
add_custom_command(
TARGET ${tblgen_target} POST_BUILD
COMMENT "Copying generated python source \"dialects/${ARG_DIALECT_NAME}.py\""
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${CMAKE_CURRENT_BINARY_DIR}/${ARG_DIALECT_NAME}.py"
"${PROJECT_BINARY_DIR}/python/mlir/dialects/${ARG_DIALECT_NAME}.py")
endfunction()