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_BINDINGS_PYTHON_LOCK_VERSION)
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}
39    ${PYEXT_LINK_MODE}
40    ${ARG_SOURCES}
41  )
42
43  target_include_directories(${libname} PRIVATE
44    "${Python3_INCLUDE_DIRS}"
45    "${pybind11_INCLUDE_DIR}"
46  )
47
48  target_link_directories(${libname} PRIVATE
49    "${Python3_LIBRARY_DIRS}"
50  )
51
52  # The extension itself must be compiled with RTTI and exceptions enabled.
53  # Also, some warning classes triggered by pybind11 are disabled.
54  target_compile_options(${libname} PRIVATE
55    $<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
56      # Enable RTTI and exceptions.
57      -frtti -fexceptions
58      # Noisy pybind warnings
59      -Wno-unused-value
60      -Wno-covered-switch-default
61    >
62    $<$<CXX_COMPILER_ID:MSVC>:
63      # Enable RTTI and exceptions.
64      /EHsc /GR>
65  )
66
67  # Configure the output to match python expectations.
68  set_target_properties(
69    ${libname} PROPERTIES
70    # Build-time RPath layouts require to be a directory one up from the
71    # binary root.
72    # TODO: Don't reference the LLVM_BINARY_DIR here: the invariant is that
73    # the output directory must be at the same level of the lib directory
74    # where libMLIR.so is installed. This is presently not optimal from a
75    # project separation perspective and a discussion on how to better
76    # segment MLIR libraries needs to happen.
77    LIBRARY_OUTPUT_DIRECTORY ${LLVM_BINARY_DIR}/python
78    OUTPUT_NAME "${extname}"
79    PREFIX "${PYTHON_MODULE_PREFIX}"
80    SUFFIX "${PYTHON_MODULE_SUFFIX}${PYTHON_MODULE_EXTENSION}"
81  )
82
83  if(WIN32)
84    # Need to also set the RUNTIME_OUTPUT_DIRECTORY on Windows in order to
85    # control where the .dll gets written.
86    set_target_properties(
87      ${libname} PROPERTIES
88      RUNTIME_OUTPUT_DIRECTORY ${LLVM_BINARY_DIR}/python
89    )
90  endif()
91
92  # pybind11 requires binding code to be compiled with -fvisibility=hidden
93  # For static linkage, better code can be generated if the entire project
94  # compiles that way, but that is not enforced here. Instead, include a linker
95  # script that explicitly hides anything but the PyInit_* symbols, allowing gc
96  # to take place.
97  set_target_properties(${libname} PROPERTIES CXX_VISIBILITY_PRESET "hidden")
98
99  # Python extensions depends *only* on the public API and LLVMSupport unless
100  # if further dependencies are added explicitly.
101  target_link_libraries(${libname}
102    PRIVATE
103    ${ARG_LINK_LIBS}
104    ${PYEXT_LIBADD}
105  )
106
107  target_link_options(${libname}
108    PRIVATE
109      # On Linux, disable re-export of any static linked libraries that
110      # came through.
111      $<$<PLATFORM_ID:Linux>:LINKER:--exclude-libs,ALL>
112  )
113
114  llvm_setup_rpath(${libname})
115
116  ################################################################################
117  # Install
118  ################################################################################
119  if (ARG_INSTALL_DIR)
120    install(TARGETS ${libname}
121      COMPONENT ${libname}
122      LIBRARY DESTINATION ${ARG_INSTALL_DIR}
123      ARCHIVE DESTINATION ${ARG_INSTALL_DIR}
124      # NOTE: Even on DLL-platforms, extensions go in the lib directory tree.
125      RUNTIME DESTINATION ${ARG_INSTALL_DIR}
126    )
127  endif()
128
129  if (NOT LLVM_ENABLE_IDE)
130    add_llvm_install_targets(
131      install-${libname}
132      DEPENDS ${libname}
133      COMPONENT ${libname})
134  endif()
135
136endfunction()
137
138function(add_mlir_dialect_python_bindings tblgen_target)
139  cmake_parse_arguments(ARG
140    ""
141    "TD_FILE;DIALECT_NAME"
142    "DEPENDS"
143    ${ARGN})
144
145  set(dialect_filename "_${ARG_DIALECT_NAME}_ops_gen.py")
146  set(LLVM_TARGET_DEFINITIONS ${ARG_TD_FILE})
147  mlir_tablegen("${dialect_filename}" -gen-python-op-bindings
148                -bind-dialect=${ARG_DIALECT_NAME})
149  add_public_tablegen_target(
150    ${tblgen_target})
151  if(ARG_DEPENDS)
152    add_dependencies(${tblgen_target} ${ARG_DEPENDS})
153  endif()
154
155  add_custom_command(
156    TARGET ${tblgen_target} POST_BUILD
157    COMMENT "Copying generated python source \"dialects/${dialect_filename}\""
158    BYPRODUCTS "${PROJECT_BINARY_DIR}/python/mlir/dialects/${dialect_filename}"
159    COMMAND "${CMAKE_COMMAND}" -E copy_if_different
160      "${CMAKE_CURRENT_BINARY_DIR}/${dialect_filename}"
161      "${PROJECT_BINARY_DIR}/python/mlir/dialects/${dialect_filename}")
162endfunction()
163
164