1################################################################################
2# Build python extension
3################################################################################
4function(add_mlir_python_extension libname extname)
5  cmake_parse_arguments(ARG
6  ""
7  "INSTALL_DIR"
8  "SOURCES;LINK_LIBS"
9  ${ARGN})
10  if (ARG_UNPARSED_ARGUMENTS)
11    message(FATAL_ERROR " Unhandled arguments to add_mlir_python_extension(${libname}, ... : ${ARG_UNPARSED_ARGUMENTS}")
12  endif()
13  if ("${ARG_SOURCES}" STREQUAL "")
14    message(FATAL_ERROR " Missing SOURCES argument to add_mlir_python_extension(${libname}, ...")
15  endif()
16
17  # Normally on unix-like platforms, extensions are built as "MODULE" libraries
18  # and do not explicitly link to the python shared object. This allows for
19  # some greater deployment flexibility since the extension will bind to
20  # symbols in the python interpreter on load. However, it also keeps the
21  # linker from erroring on undefined symbols, leaving this to (usually obtuse)
22  # runtime errors. Building in "SHARED" mode with an explicit link to the
23  # python libraries allows us to build with the expectation of no undefined
24  # symbols, which is better for development. Note that not all python
25  # configurations provide build-time libraries to link against, in which
26  # case, we fall back to MODULE linking.
27  if(Python3_LIBRARIES STREQUAL "" OR NOT MLIR_PYTHON_BINDINGS_VERSION_LOCKED)
28    set(PYEXT_LINK_MODE MODULE)
29    set(PYEXT_LIBADD)
30  else()
31    set(PYEXT_LINK_MODE SHARED)
32    set(PYEXT_LIBADD ${Python3_LIBRARIES})
33  endif()
34
35  # The actual extension library produces a shared-object or DLL and has
36  # sources that must be compiled in accordance with pybind11 needs (RTTI and
37  # exceptions).
38  add_library(${libname} ${PYEXT_LINK_MODE}
39    ${ARG_SOURCES}
40  )
41
42  target_include_directories(${libname} PRIVATE
43    "${Python3_INCLUDE_DIRS}"
44    "${pybind11_INCLUDE_DIR}"
45  )
46
47  target_link_directories(${libname} PRIVATE
48    "${Python3_LIBRARY_DIRS}"
49  )
50
51  # The extension itself must be compiled with RTTI and exceptions enabled.
52  # Also, some warning classes triggered by pybind11 are disabled.
53  target_compile_options(${libname} PRIVATE
54    $<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
55      # Enable RTTI and exceptions.
56      -frtti -fexceptions
57      # Noisy pybind warnings
58      -Wno-unused-value
59      -Wno-covered-switch-default
60    >
61    $<$<CXX_COMPILER_ID:MSVC>:
62      # Enable RTTI and exceptions.
63      /EHsc /GR>
64  )
65
66  # Configure the output to match python expectations.
67  set_target_properties(
68    ${libname} PROPERTIES
69    # Build-time RPath layouts require to be a directory one up from the
70    # binary root.
71    # TODO: Don't reference the LLVM_BINARY_DIR here: the invariant is that
72    # the output directory must be at the same level of the lib directory
73    # where libMLIR.so is installed. This is presently not optimal from a
74    # project separation perspective and a discussion on how to better
75    # segment MLIR libraries needs to happen.
76    LIBRARY_OUTPUT_DIRECTORY ${LLVM_BINARY_DIR}/python
77    OUTPUT_NAME "${extname}"
78    PREFIX "${PYTHON_MODULE_PREFIX}"
79    SUFFIX "${PYTHON_MODULE_SUFFIX}${PYTHON_MODULE_EXTENSION}"
80  )
81
82  if(WIN32)
83    # Need to also set the RUNTIME_OUTPUT_DIRECTORY on Windows in order to
84    # control where the .dll gets written.
85    set_target_properties(
86      ${libname} PROPERTIES
87      RUNTIME_OUTPUT_DIRECTORY ${LLVM_BINARY_DIR}/python
88    )
89  endif()
90
91  # pybind11 requires binding code to be compiled with -fvisibility=hidden
92  # For static linkage, better code can be generated if the entire project
93  # compiles that way, but that is not enforced here. Instead, include a linker
94  # script that explicitly hides anything but the PyInit_* symbols, allowing gc
95  # to take place.
96  set_target_properties(${libname} PROPERTIES CXX_VISIBILITY_PRESET "hidden")
97
98  # Python extensions depends *only* on the public API and LLVMSupport unless
99  # if further dependencies are added explicitly.
100  target_link_libraries(${libname}
101    PRIVATE
102    MLIRPublicAPI
103    LLVMSupport
104    ${ARG_LINK_LIBS}
105    ${PYEXT_LIBADD}
106  )
107
108  target_link_options(${libname}
109    PRIVATE
110      # On Linux, disable re-export of any static linked libraries that
111      # came through.
112      $<$<PLATFORM_ID:Linux>:LINKER:--exclude-libs,ALL>
113  )
114
115  llvm_setup_rpath(${libname})
116
117  ################################################################################
118  # Install
119  ################################################################################
120  if (ARG_INSTALL_DIR)
121    install(TARGETS ${libname}
122      COMPONENT ${libname}
123      LIBRARY DESTINATION ${ARG_INSTALL_DIR}
124      ARCHIVE DESTINATION ${ARG_INSTALL_DIR}
125      # NOTE: Even on DLL-platforms, extensions go in the lib directory tree.
126      RUNTIME DESTINATION ${ARG_INSTALL_DIR}
127    )
128  endif()
129
130  if (NOT LLVM_ENABLE_IDE)
131    add_llvm_install_targets(
132      install-${libname}
133      DEPENDS ${libname}
134      COMPONENT ${libname})
135  endif()
136
137endfunction()
138
139function(add_mlir_dialect_python_bindings tblgen_target)
140  cmake_parse_arguments(ARG
141    ""
142    "TD_FILE;DIALECT_NAME"
143    "DEPENDS"
144    ${ARGN})
145
146  set(dialect_filename "_${ARG_DIALECT_NAME}_ops_gen.py")
147  set(LLVM_TARGET_DEFINITIONS ${ARG_TD_FILE})
148  mlir_tablegen("${dialect_filename}" -gen-python-op-bindings
149                -bind-dialect=${ARG_DIALECT_NAME})
150  add_public_tablegen_target(
151    ${tblgen_target})
152  if(ARG_DEPENDS)
153    add_dependencies(${tblgen_target} ${ARG_DEPENDS})
154  endif()
155
156  add_custom_command(
157    TARGET ${tblgen_target} POST_BUILD
158    COMMENT "Copying generated python source \"dialects/${dialect_filename}\""
159    BYPRODUCTS "${PROJECT_BINARY_DIR}/python/mlir/dialects/${dialect_filename}"
160    COMMAND "${CMAKE_COMMAND}" -E copy_if_different
161      "${CMAKE_CURRENT_BINARY_DIR}/${dialect_filename}"
162      "${PROJECT_BINARY_DIR}/python/mlir/dialects/${dialect_filename}")
163endfunction()
164
165