[mlir] Make Python bindings installable.

* Links against libMLIR.so if the project is built for DYLIBs.
* Puts things in the right place in build and install time python/ trees so that RPaths line up.
* Adds install actions to install both the extension and sources.
* Copies py source files to the build directory to match (consistent layout between build/install time and one place to point a PYTHONPATH for tests and interactive use).
* Finally, "import mlir" from an installed LLVM just works.

Differential Revision: https://reviews.llvm.org/D89167
This commit is contained in:
Stella Laurenzo 2020-10-09 15:50:07 -07:00
parent a324d8f964
commit 75ae846de6
3 changed files with 103 additions and 19 deletions

View File

@ -1,3 +1,30 @@
################################################################################
# Copy python source tree.
################################################################################
set(PY_SRC_FILES
mlir/__init__.py
)
add_custom_target(MLIRBindingsPythonSources ALL
DEPENDS ${PY_SRC_FILES}
)
foreach(PY_SRC_FILE ${PY_SRC_FILES})
set(PY_DEST_FILE "${PROJECT_BINARY_DIR}/python/${PY_SRC_FILE}")
add_custom_command(
TARGET MLIRBindingsPythonSources PRE_BUILD
COMMENT "Copying python source ${PY_SRC_FILE} -> ${PY_DEST_FILE}"
DEPENDS "${PY_SRC_FILE}"
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/${PY_SRC_FILE}" "${PY_DEST_FILE}"
)
endforeach()
################################################################################
# Build 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
@ -5,13 +32,15 @@
# 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.
if(MLIR_PYTHON_BINDINGS_VERSION_LOCKED)
set(PYEXT_LINK_MODE SHARED)
set(PYEXT_LIBADD ${PYTHON_LIBRARIES})
else()
# 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
@ -47,7 +76,14 @@ target_compile_options(MLIRBindingsPythonExtension PRIVATE
# Configure the output to match python expectations.
set_target_properties(
MLIRBindingsPythonExtension PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
# 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}"
@ -61,15 +97,62 @@ set_target_properties(
# 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")
if(NOT MSVC AND NOT APPLE)
set_target_properties(MLIRBindingsPythonExtension
PROPERTIES LINK_FLAGS
"-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/unix_version.lds")
set(PYEXT_DEPS)
if(LLVM_BUILD_LLVM_DYLIB)
list(APPEND PYEXT_DEPS
# Depend on libMLIR.so first so that deps primarily come from the shared
# library.
MLIR
)
endif()
# Full static dependencies are also added and will augment what is in the
# shared lib if needed (or in fully static builds, will result in mondo-built
# extension).
list(APPEND PYEXT_DEPS
# Depend only on the MLIR C-API.
MLIRCAPIIR
MLIRCAPIRegistration
)
target_link_libraries(MLIRBindingsPythonExtension
PRIVATE
MLIRCAPIIR
MLIRCAPIRegistration
${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
# such).
install(
DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/mlir
DESTINATION python
COMPONENT MLIRBindingsPythonSources
FILES_MATCHING PATTERN "*.py"
)
if (NOT LLVM_ENABLE_IDE)
add_llvm_install_targets(
install-MLIRBindingsPythonExtension
DEPENDS MLIRBindingsPythonExtension
COMPONENT MLIRBindingsPythonExtension)
add_llvm_install_targets(
install-MLIRBindingsPythonSources
DEPENDS MLIRBindingsPythonSources
COMPONENT MLIRBindingsPythonSources)
endif()

View File

@ -1,4 +0,0 @@
{
global: PyInit__mlir;
local: *;
};

View File

@ -64,7 +64,7 @@ tools = [
# The following tools are optional
tools.extend([
ToolSubst('%PYTHON', config.python_executable),
ToolSubst('%PYTHON', config.python_executable, unresolved='ignore'),
ToolSubst('toy-ch1', unresolved='ignore'),
ToolSubst('toy-ch2', unresolved='ignore'),
ToolSubst('toy-ch3', unresolved='ignore'),
@ -99,6 +99,11 @@ if config.target_triple:
# by copying/linking sources to build.
if config.enable_bindings_python:
llvm_config.with_environment('PYTHONPATH', [
os.path.join(config.mlir_src_root, "lib", "Bindings", "Python"),
os.path.join(config.mlir_obj_root, "lib", "Bindings", "Python"),
# TODO: Don't reference the llvm_obj_root here: the invariant is that
# the python/ 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. See also
# lib/Bindings/Python/CMakeLists.txt for where this is set up.
os.path.join(config.llvm_obj_root, 'python'),
], append_path=True)