diff --git a/mlir/cmake/modules/AddMLIRPythonExtension.cmake b/mlir/cmake/modules/AddMLIRPythonExtension.cmake new file mode 100644 --- /dev/null +++ b/mlir/cmake/modules/AddMLIRPythonExtension.cmake @@ -0,0 +1,117 @@ +################################################################################ +# 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() + if(NOT LLVM_BUILD_LLVM_DYLIB) + message(FATAL_ERROR "Building MLIR Python extension require -DLLVM_BUILD_LLVM_DYLIB=ON") + 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(PYTHON_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 ${PYTHON_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 + "${PYTHON_INCLUDE_DIRS}" + "${pybind11_INCLUDE_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 + $<$,$,$>: + # Enable RTTI and exceptions. + -frtti -fexceptions + # Noisy pybind warnings + -Wno-unused-value + -Wno-covered-switch-default + > + $<$: + # 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 "_mlirTransforms" + PREFIX "${PYTHON_MODULE_PREFIX}" + SUFFIX "${PYTHON_MODULE_SUFFIX}${PYTHON_MODULE_EXTENSION}" + ) + + # 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") + + target_link_libraries(${libname} + PRIVATE + MLIR # Always link to libMLIR.so + ${ARG_LINK_LIBS} + ${PYEXT_LIBADD} + ) + + llvm_setup_rpath(${libname}) + + ################################################################################ + # Install + ################################################################################ + if (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() diff --git a/mlir/lib/Bindings/Python/CMakeLists.txt b/mlir/lib/Bindings/Python/CMakeLists.txt --- a/mlir/lib/Bindings/Python/CMakeLists.txt +++ b/mlir/lib/Bindings/Python/CMakeLists.txt @@ -2,6 +2,8 @@ message(FATAL_ERROR "Building the MLIR Python bindings require -DLLVM_BUILD_LLVM_DYLIB=ON") endif() +include(AddMLIRPythonExtension) + ################################################################################ # Copy python source tree. ################################################################################ @@ -29,108 +31,18 @@ endforeach() ################################################################################ -# Build python extension +# Build core python extension ################################################################################ - -# 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(PYTHON_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 ${PYTHON_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). -# TODO: Link the libraries separately once a helper function is available -# to more generically add a pybind11 compliant library. -add_library(MLIRBindingsPythonExtension ${PYEXT_LINK_MODE} - MainModule.cpp - IRModules.cpp - PybindUtils.cpp -) - -target_include_directories(MLIRBindingsPythonExtension PRIVATE - "${PYTHON_INCLUDE_DIRS}" - "${pybind11_INCLUDE_DIRS}") - -# The extension itself must be compiled with RTTI and exceptions enabled. -# Also, some warning classes triggered by pybind11 are disabled. -target_compile_options(MLIRBindingsPythonExtension PRIVATE - $<$,$,$>: - # Enable RTTI and exceptions. - -frtti -fexceptions - # Noisy pybind warnings - -Wno-unused-value - -Wno-covered-switch-default - > - $<$: - # Enable RTTI and exceptions. - /EHsc /GR> -) - -# Configure the output to match python expectations. -set_target_properties( - MLIRBindingsPythonExtension 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 "_mlir" - PREFIX "${PYTHON_MODULE_PREFIX}" - SUFFIX "${PYTHON_MODULE_SUFFIX}${PYTHON_MODULE_EXTENSION}" -) - -# 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. -# TODO: Add a Windows .def file and figure out the right thing to do on MacOS. -set_target_properties( - MLIRBindingsPythonExtension PROPERTIES CXX_VISIBILITY_PRESET "hidden") - -set(PYEXT_DEPS) -list(APPEND PYEXT_DEPS - # Depend on libMLIR.so first so that deps primarily come from the shared - # library. - MLIR +add_mlir_python_extension(MLIRBindingsPythonExtension _mlir + INSTALL_DIR + python + SOURCES + MainModule.cpp + IRModules.cpp + Pass.cpp + PybindUtils.cpp ) - -target_link_libraries(MLIRBindingsPythonExtension - PRIVATE - ${PYEXT_DEPS} - ${PYEXT_LIBADD} -) - add_dependencies(MLIRBindingsPythonExtension MLIRBindingsPythonSources) -llvm_setup_rpath(MLIRBindingsPythonExtension) - -################################################################################ -# Install -################################################################################ - -install(TARGETS MLIRBindingsPythonExtension - COMPONENT MLIRBindingsPythonExtension - LIBRARY DESTINATION python - ARCHIVE DESTINATION python - # NOTE: Even on DLL-platforms, extensions go in the lib directory tree. - RUNTIME DESTINATION python) # Note that we copy from the source tree just like for headers because # it will not be polluted with py_cache runtime artifacts (from testing and @@ -143,10 +55,6 @@ ) if (NOT LLVM_ENABLE_IDE) - add_llvm_install_targets( - install-MLIRBindingsPythonExtension - DEPENDS MLIRBindingsPythonExtension - COMPONENT MLIRBindingsPythonExtension) add_llvm_install_targets( install-MLIRBindingsPythonSources DEPENDS MLIRBindingsPythonSources