[mlir] Use StringRef::{starts,ends}_with (NFC)
[llvm-project.git] / mlir / cmake / modules / AddMLIRPython.cmake
blob012380603a4c4522ce1af4cdc50bdc06dddd875c
1 ################################################################################
2 # Python modules
3 # MLIR's Python modules are both directly used by the core project and are
4 # available for use and embedding into external projects (in their own
5 # namespace and with their own deps). In order to facilitate this, python
6 # artifacts are split between declarations, which make a subset of
7 # things available to be built and "add", which in line with the normal LLVM
8 # nomenclature, adds libraries.
9 ################################################################################
11 # Function: declare_mlir_python_sources
12 # Declares pure python sources as part of a named grouping that can be built
13 # later.
14 # Arguments:
15 #   ROOT_DIR: Directory where the python namespace begins (defaults to
16 #     CMAKE_CURRENT_SOURCE_DIR). For non-relocatable sources, this will
17 #     typically just be the root of the python source tree (current directory).
18 #     For relocatable sources, this will point deeper into the directory that
19 #     can be relocated. For generated sources, can be relative to
20 #     CMAKE_CURRENT_BINARY_DIR. Generated and non generated sources cannot be
21 #     mixed.
22 #   ADD_TO_PARENT: Adds this source grouping to a previously declared source
23 #     grouping. Source groupings form a DAG.
24 #   SOURCES: List of specific source files relative to ROOT_DIR to include.
25 #   SOURCES_GLOB: List of glob patterns relative to ROOT_DIR to include.
26 function(declare_mlir_python_sources name)
27   cmake_parse_arguments(ARG
28     ""
29     "ROOT_DIR;ADD_TO_PARENT"
30     "SOURCES;SOURCES_GLOB"
31     ${ARGN})
33   if(NOT ARG_ROOT_DIR)
34     set(ARG_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
35   endif()
36   set(_install_destination "src/python/${name}")
38   # Process the glob.
39   set(_glob_sources)
40   if(ARG_SOURCES_GLOB)
41     set(_glob_spec ${ARG_SOURCES_GLOB})
42     list(TRANSFORM _glob_spec PREPEND "${ARG_ROOT_DIR}/")
43     file(GLOB_RECURSE _glob_sources
44       RELATIVE "${ARG_ROOT_DIR}"
45       ${_glob_spec}
46     )
47     list(APPEND ARG_SOURCES ${_glob_sources})
48   endif()
50   # We create a custom target to carry properties and dependencies for
51   # generated sources.
52   add_library(${name} INTERFACE)
53   set_target_properties(${name} PROPERTIES
54     # Yes: Leading-lowercase property names are load bearing and the recommended
55     # way to do this: https://gitlab.kitware.com/cmake/cmake/-/issues/19261
56     EXPORT_PROPERTIES "mlir_python_SOURCES_TYPE;mlir_python_DEPENDS"
57     mlir_python_SOURCES_TYPE pure
58     mlir_python_DEPENDS ""
59   )
61   # Use the interface include directories and sources on the target to carry the
62   # properties we would like to export. These support generator expressions and
63   # allow us to properly specify paths in both the local build and install scenarios.
64   # The one caveat here is that because we don't directly build against the interface
65   # library, we need to specify the INCLUDE_DIRECTORIES and SOURCES properties as well
66   # via private properties because the evaluation would happen at configuration time
67   # instead of build time.
68   # Eventually this could be done using a FILE_SET simplifying the logic below.
69   # FILE_SET is available in cmake 3.23+, so it is not an option at the moment.
70   target_include_directories(${name} INTERFACE
71     "$<BUILD_INTERFACE:${ARG_ROOT_DIR}>"
72     "$<INSTALL_INTERFACE:${_install_destination}>"
73   )
74   set_property(TARGET ${name} PROPERTY INCLUDE_DIRECTORIES ${ARG_ROOT_DIR})
76   if(ARG_SOURCES)
77     list(TRANSFORM ARG_SOURCES PREPEND "${ARG_ROOT_DIR}/" OUTPUT_VARIABLE _build_sources)
78     list(TRANSFORM ARG_SOURCES PREPEND "${_install_destination}/" OUTPUT_VARIABLE _install_sources)
79     target_sources(${name}
80       INTERFACE
81         "$<INSTALL_INTERFACE:${_install_sources}>"
82         "$<BUILD_INTERFACE:${_build_sources}>"
83       PRIVATE ${_build_sources}
84     )
85   endif()
87   # Add to parent.
88   if(ARG_ADD_TO_PARENT)
89     set_property(TARGET ${ARG_ADD_TO_PARENT} APPEND PROPERTY mlir_python_DEPENDS ${name})
90   endif()
92   # Install.
93   set_property(GLOBAL APPEND PROPERTY MLIR_EXPORTS ${name})
94   if(NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
95     _mlir_python_install_sources(
96       ${name} "${ARG_ROOT_DIR}" "${_install_destination}"
97       ${ARG_SOURCES}
98     )
99   endif()
100 endfunction()
102 # Function: declare_mlir_python_extension
103 # Declares a buildable python extension from C++ source files. The built
104 # module is considered a python source file and included as everything else.
105 # Arguments:
106 #   ROOT_DIR: Root directory where sources are interpreted relative to.
107 #     Defaults to CMAKE_CURRENT_SOURCE_DIR.
108 #   MODULE_NAME: Local import name of the module (i.e. "_mlir").
109 #   ADD_TO_PARENT: Same as for declare_mlir_python_sources.
110 #   SOURCES: C++ sources making up the module.
111 #   PRIVATE_LINK_LIBS: List of libraries to link in privately to the module
112 #     regardless of how it is included in the project (generally should be
113 #     static libraries that can be included with hidden visibility).
114 #   EMBED_CAPI_LINK_LIBS: Dependent CAPI libraries that this extension depends
115 #     on. These will be collected for all extensions and put into an
116 #     aggregate dylib that is linked against.
117 function(declare_mlir_python_extension name)
118   cmake_parse_arguments(ARG
119     ""
120     "ROOT_DIR;MODULE_NAME;ADD_TO_PARENT"
121     "SOURCES;PRIVATE_LINK_LIBS;EMBED_CAPI_LINK_LIBS"
122     ${ARGN})
124   if(NOT ARG_ROOT_DIR)
125     set(ARG_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
126   endif()
127   set(_install_destination "src/python/${name}")
129   add_library(${name} INTERFACE)
130   set_target_properties(${name} PROPERTIES
131     # Yes: Leading-lowercase property names are load bearing and the recommended
132     # way to do this: https://gitlab.kitware.com/cmake/cmake/-/issues/19261
133     EXPORT_PROPERTIES "mlir_python_SOURCES_TYPE;mlir_python_EXTENSION_MODULE_NAME;mlir_python_EMBED_CAPI_LINK_LIBS;mlir_python_DEPENDS"
134     mlir_python_SOURCES_TYPE extension
135     mlir_python_EXTENSION_MODULE_NAME "${ARG_MODULE_NAME}"
136     mlir_python_EMBED_CAPI_LINK_LIBS "${ARG_EMBED_CAPI_LINK_LIBS}"
137     mlir_python_DEPENDS ""
138   )
140   # Set the interface source and link_libs properties of the target
141   # These properties support generator expressions and are automatically exported
142   list(TRANSFORM ARG_SOURCES PREPEND "${ARG_ROOT_DIR}/" OUTPUT_VARIABLE _build_sources)
143   list(TRANSFORM ARG_SOURCES PREPEND "${_install_destination}/" OUTPUT_VARIABLE _install_sources)
144   target_sources(${name} INTERFACE
145     "$<BUILD_INTERFACE:${_build_sources}>"
146     "$<INSTALL_INTERFACE:${_install_sources}>"
147   )
148   target_link_libraries(${name} INTERFACE
149     ${ARG_PRIVATE_LINK_LIBS}
150   )
152   # Add to parent.
153   if(ARG_ADD_TO_PARENT)
154     set_property(TARGET ${ARG_ADD_TO_PARENT} APPEND PROPERTY mlir_python_DEPENDS ${name})
155   endif()
157   # Install.
158   set_property(GLOBAL APPEND PROPERTY MLIR_EXPORTS ${name})
159   if(NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
160     _mlir_python_install_sources(
161       ${name} "${ARG_ROOT_DIR}" "${_install_destination}"
162       ${ARG_SOURCES}
163     )
164   endif()
165 endfunction()
167 function(_mlir_python_install_sources name source_root_dir destination)
168   foreach(source_relative_path ${ARGN})
169     # Transform "a/b/c.py" -> "${install_prefix}/a/b" for installation.
170     get_filename_component(
171       dest_relative_dir "${source_relative_path}" DIRECTORY
172       BASE_DIR "${source_root_dir}"
173     )
174     install(
175       FILES "${source_root_dir}/${source_relative_path}"
176       DESTINATION "${destination}/${dest_relative_dir}"
177       COMPONENT mlir-python-sources
178     )
179   endforeach()
180   get_target_export_arg(${name} MLIR export_to_mlirtargets
181     UMBRELLA mlir-python-sources)
182   install(TARGETS ${name}
183     COMPONENT mlir-python-sources
184     ${export_to_mlirtargets}
185   )
186 endfunction()
188 # Function: add_mlir_python_modules
189 # Adds python modules to a project, building them from a list of declared
190 # source groupings (see declare_mlir_python_sources and
191 # declare_mlir_python_extension). One of these must be called for each
192 # packaging root in use.
193 # Arguments:
194 #   ROOT_PREFIX: The directory in the build tree to emit sources. This will
195 #     typically be something like ${MY_BINARY_DIR}/python_packages/foobar
196 #     for non-relocatable modules or a deeper directory tree for relocatable.
197 #   INSTALL_PREFIX: Prefix into the install tree for installing the package.
198 #     Typically mirrors the path above but without an absolute path.
199 #   DECLARED_SOURCES: List of declared source groups to include. The entire
200 #     DAG of source modules is included.
201 #   COMMON_CAPI_LINK_LIBS: List of dylibs (typically one) to make every
202 #     extension depend on (see mlir_python_add_common_capi_library).
203 function(add_mlir_python_modules name)
204   cmake_parse_arguments(ARG
205     ""
206     "ROOT_PREFIX;INSTALL_PREFIX;COMMON_CAPI_LINK_LIBS"
207     "DECLARED_SOURCES"
208     ${ARGN})
209   # Helper to process an individual target.
210   function(_process_target modules_target sources_target)
211     get_target_property(_source_type ${sources_target} mlir_python_SOURCES_TYPE)
213     if(_source_type STREQUAL "pure")
214       # Pure python sources to link into the tree.
215       set(_pure_sources_target "${modules_target}.sources.${sources_target}")
216       add_mlir_python_sources_target(${_pure_sources_target}
217         INSTALL_COMPONENT ${modules_target}
218         INSTALL_DIR ${ARG_INSTALL_PREFIX}
219         OUTPUT_DIRECTORY ${ARG_ROOT_PREFIX}
220         SOURCES_TARGETS ${sources_target}
221       )
222       add_dependencies(${modules_target} ${_pure_sources_target})
223     elseif(_source_type STREQUAL "extension")
224       # Native CPP extension.
225       get_target_property(_module_name ${sources_target} mlir_python_EXTENSION_MODULE_NAME)
226       # Transform relative source to based on root dir.
227       set(_extension_target "${modules_target}.extension.${_module_name}.dso")
228       add_mlir_python_extension(${_extension_target} "${_module_name}"
229         INSTALL_COMPONENT ${modules_target}
230         INSTALL_DIR "${ARG_INSTALL_PREFIX}/_mlir_libs"
231         OUTPUT_DIRECTORY "${ARG_ROOT_PREFIX}/_mlir_libs"
232         LINK_LIBS PRIVATE
233           ${sources_target}
234           ${ARG_COMMON_CAPI_LINK_LIBS}
235       )
236       add_dependencies(${modules_target} ${_extension_target})
237       mlir_python_setup_extension_rpath(${_extension_target})
238     else()
239       message(SEND_ERROR "Unrecognized source type '${_source_type}' for python source target ${sources_target}")
240       return()
241     endif()
242   endfunction()
244   # Build the modules target.
245   add_custom_target(${name} ALL)
246   _flatten_mlir_python_targets(_flat_targets ${ARG_DECLARED_SOURCES})
247   foreach(sources_target ${_flat_targets})
248     _process_target(${name} ${sources_target})
249   endforeach()
251   # Create an install target.
252   if(NOT LLVM_ENABLE_IDE)
253     add_llvm_install_targets(
254       install-${name}
255       DEPENDS ${name}
256       COMPONENT ${name})
257   endif()
258 endfunction()
260 # Function: declare_mlir_dialect_python_bindings
261 # Helper to generate source groups for dialects, including both static source
262 # files and a TD_FILE to generate wrappers.
264 # This will generate a source group named ${ADD_TO_PARENT}.${DIALECT_NAME}.
266 # Arguments:
267 #   ROOT_DIR: Same as for declare_mlir_python_sources().
268 #   ADD_TO_PARENT: Same as for declare_mlir_python_sources(). Unique names
269 #     for the subordinate source groups are derived from this.
270 #   TD_FILE: Tablegen file to generate source for (relative to ROOT_DIR).
271 #   DIALECT_NAME: Python name of the dialect.
272 #   SOURCES: Same as declare_mlir_python_sources().
273 #   SOURCES_GLOB: Same as declare_mlir_python_sources().
274 #   DEPENDS: Additional dependency targets.
275 #   GEN_ENUM_BINDINGS: Generate enum bindings.
276 #   GEN_ENUM_BINDINGS_TD_FILE: Optional Tablegen file to generate enums for (relative to ROOT_DIR).
277 #     This file is where the *EnumAttrs are defined, not where the *Enums are defined.
278 #     **WARNING**: This arg will shortly be removed when the just-below TODO is satisfied. Use at your
279 #     risk.
281 # TODO: Right now `TD_FILE` can't be the actual dialect tablegen file, since we
282 #       use its path to determine where to place the generated python file. If
283 #       we made the output path an additional argument here we could remove the
284 #       need for the separate "wrapper" .td files
285 function(declare_mlir_dialect_python_bindings)
286   cmake_parse_arguments(ARG
287     "GEN_ENUM_BINDINGS"
288     "ROOT_DIR;ADD_TO_PARENT;TD_FILE;DIALECT_NAME"
289     "SOURCES;SOURCES_GLOB;DEPENDS;GEN_ENUM_BINDINGS_TD_FILE"
290     ${ARGN})
291   # Sources.
292   set(_dialect_target "${ARG_ADD_TO_PARENT}.${ARG_DIALECT_NAME}")
293   declare_mlir_python_sources(${_dialect_target}
294     ROOT_DIR "${ARG_ROOT_DIR}"
295     ADD_TO_PARENT "${ARG_ADD_TO_PARENT}"
296     SOURCES "${ARG_SOURCES}"
297     SOURCES_GLOB "${ARG_SOURCES_GLOB}"
298   )
300   # Tablegen
301   if(ARG_TD_FILE)
302     set(tblgen_target "${_dialect_target}.tablegen")
303     set(td_file "${ARG_ROOT_DIR}/${ARG_TD_FILE}")
304     get_filename_component(relative_td_directory "${ARG_TD_FILE}" DIRECTORY)
305     file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${relative_td_directory}")
306     set(dialect_filename "${relative_td_directory}/_${ARG_DIALECT_NAME}_ops_gen.py")
307     set(LLVM_TARGET_DEFINITIONS ${td_file})
308     mlir_tablegen("${dialect_filename}"
309       -gen-python-op-bindings -bind-dialect=${ARG_DIALECT_NAME}
310       DEPENDS ${ARG_DEPENDS}
311     )
312     add_public_tablegen_target(${tblgen_target})
314     set(_sources ${dialect_filename})
315     if(ARG_GEN_ENUM_BINDINGS OR ARG_GEN_ENUM_BINDINGS_TD_FILE)
316       if(ARG_GEN_ENUM_BINDINGS_TD_FILE)
317         set(td_file "${ARG_ROOT_DIR}/${ARG_GEN_ENUM_BINDINGS_TD_FILE}")
318         set(LLVM_TARGET_DEFINITIONS ${td_file})
319       endif()
320       set(enum_filename "${relative_td_directory}/_${ARG_DIALECT_NAME}_enum_gen.py")
321       mlir_tablegen(${enum_filename} -gen-python-enum-bindings)
322       list(APPEND _sources ${enum_filename})
323     endif()
325     # Generated.
326     declare_mlir_python_sources("${_dialect_target}.ops_gen"
327       ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}"
328       ADD_TO_PARENT "${_dialect_target}"
329       SOURCES ${_sources}
330     )
331   endif()
332 endfunction()
334 # Function: declare_mlir_dialect_extension_python_bindings
335 # Helper to generate source groups for dialect extensions, including both
336 # static source files and a TD_FILE to generate wrappers.
338 # This will generate a source group named ${ADD_TO_PARENT}.${EXTENSION_NAME}.
340 # Arguments:
341 #   ROOT_DIR: Same as for declare_mlir_python_sources().
342 #   ADD_TO_PARENT: Same as for declare_mlir_python_sources(). Unique names
343 #     for the subordinate source groups are derived from this.
344 #   TD_FILE: Tablegen file to generate source for (relative to ROOT_DIR).
345 #   DIALECT_NAME: Python name of the dialect.
346 #   EXTENSION_NAME: Python name of the dialect extension.
347 #   SOURCES: Same as declare_mlir_python_sources().
348 #   SOURCES_GLOB: Same as declare_mlir_python_sources().
349 #   DEPENDS: Additional dependency targets.
350 #   GEN_ENUM_BINDINGS: Generate enum bindings.
351 #   GEN_ENUM_BINDINGS_TD_FILE: Optional Tablegen file to generate enums for (relative to ROOT_DIR).
352 #     This file is where the *Attrs are defined, not where the *Enums are defined.
353 #     **WARNING**: This arg will shortly be removed when the TODO for
354 #     declare_mlir_dialect_python_bindings is satisfied. Use at your risk.
355 function(declare_mlir_dialect_extension_python_bindings)
356   cmake_parse_arguments(ARG
357     "GEN_ENUM_BINDINGS"
358     "ROOT_DIR;ADD_TO_PARENT;TD_FILE;DIALECT_NAME;EXTENSION_NAME"
359     "SOURCES;SOURCES_GLOB;DEPENDS;GEN_ENUM_BINDINGS_TD_FILE"
360     ${ARGN})
361   # Source files.
362   set(_extension_target "${ARG_ADD_TO_PARENT}.${ARG_EXTENSION_NAME}")
363   declare_mlir_python_sources(${_extension_target}
364     ROOT_DIR "${ARG_ROOT_DIR}"
365     ADD_TO_PARENT "${ARG_ADD_TO_PARENT}"
366     SOURCES "${ARG_SOURCES}"
367     SOURCES_GLOB "${ARG_SOURCES_GLOB}"
368   )
370   # Tablegen
371   if(ARG_TD_FILE)
372     set(tblgen_target "${ARG_ADD_TO_PARENT}.${ARG_EXTENSION_NAME}.tablegen")
373     set(td_file "${ARG_ROOT_DIR}/${ARG_TD_FILE}")
374     get_filename_component(relative_td_directory "${ARG_TD_FILE}" DIRECTORY)
375     file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${relative_td_directory}")
376     set(output_filename "${relative_td_directory}/_${ARG_EXTENSION_NAME}_ops_gen.py")
377     set(LLVM_TARGET_DEFINITIONS ${td_file})
378     mlir_tablegen("${output_filename}" -gen-python-op-bindings
379                   -bind-dialect=${ARG_DIALECT_NAME}
380                   -dialect-extension=${ARG_EXTENSION_NAME})
381     add_public_tablegen_target(${tblgen_target})
382     if(ARG_DEPENDS)
383       add_dependencies(${tblgen_target} ${ARG_DEPENDS})
384     endif()
386     set(_sources ${output_filename})
387     if(ARG_GEN_ENUM_BINDINGS OR ARG_GEN_ENUM_BINDINGS_TD_FILE)
388       if(ARG_GEN_ENUM_BINDINGS_TD_FILE)
389         set(td_file "${ARG_ROOT_DIR}/${ARG_GEN_ENUM_BINDINGS_TD_FILE}")
390         set(LLVM_TARGET_DEFINITIONS ${td_file})
391       endif()
392       set(enum_filename "${relative_td_directory}/_${ARG_EXTENSION_NAME}_enum_gen.py")
393       mlir_tablegen(${enum_filename} -gen-python-enum-bindings)
394       list(APPEND _sources ${enum_filename})
395     endif()
397     declare_mlir_python_sources("${_extension_target}.ops_gen"
398       ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}"
399       ADD_TO_PARENT "${_extension_target}"
400       SOURCES ${_sources}
401     )
402   endif()
403 endfunction()
405 # Function: mlir_python_setup_extension_rpath
406 # Sets RPATH properties on a target, assuming that it is being output to
407 # an _mlir_libs directory with all other libraries. For static linkage,
408 # the RPATH will just be the origin. If linking dynamically, then the LLVM
409 # library directory will be added.
410 # Arguments:
411 #   RELATIVE_INSTALL_ROOT: If building dynamically, an RPATH entry will be
412 #     added to the install tree lib/ directory by first traversing this
413 #     path relative to the installation location. Typically a number of ".."
414 #     entries, one for each level of the install path.
415 function(mlir_python_setup_extension_rpath target)
416   cmake_parse_arguments(ARG
417     ""
418     "RELATIVE_INSTALL_ROOT"
419     ""
420     ${ARGN})
422   # RPATH handling.
423   # For the build tree, include the LLVM lib directory and the current
424   # directory for RPATH searching. For install, just the current directory
425   # (assumes that needed dependencies have been installed).
426   if(NOT APPLE AND NOT UNIX)
427     return()
428   endif()
430   set(_origin_prefix "\$ORIGIN")
431   if(APPLE)
432     set(_origin_prefix "@loader_path")
433   endif()
434   set_target_properties(${target} PROPERTIES
435     BUILD_WITH_INSTALL_RPATH OFF
436     BUILD_RPATH "${_origin_prefix}"
437     INSTALL_RPATH "${_origin_prefix}"
438   )
440   # For static builds, that is all that is needed: all dependencies will be in
441   # the one directory. For shared builds, then we also need to add the global
442   # lib directory. This will be absolute for the build tree and relative for
443   # install.
444   # When we have access to CMake >= 3.20, there is a helper to calculate this.
445   if(BUILD_SHARED_LIBS AND ARG_RELATIVE_INSTALL_ROOT)
446     get_filename_component(_real_lib_dir "${LLVM_LIBRARY_OUTPUT_INTDIR}" REALPATH)
447     set_property(TARGET ${target} APPEND PROPERTY
448       BUILD_RPATH "${_real_lib_dir}")
449     set_property(TARGET ${target} APPEND PROPERTY
450       INSTALL_RPATH "${_origin_prefix}/${ARG_RELATIVE_INSTALL_ROOT}/lib${LLVM_LIBDIR_SUFFIX}")
451   endif()
452 endfunction()
454 # Function: add_mlir_python_common_capi_library
455 # Adds a shared library which embeds dependent CAPI libraries needed to link
456 # all extensions.
457 # Arguments:
458 #   INSTALL_COMPONENT: Name of the install component. Typically same as the
459 #     target name passed to add_mlir_python_modules().
460 #   INSTALL_DESTINATION: Prefix into the install tree in which to install the
461 #     library.
462 #   OUTPUT_DIRECTORY: Full path in the build tree in which to create the
463 #     library. Typically, this will be the common _mlir_libs directory where
464 #     all extensions are emitted.
465 #   RELATIVE_INSTALL_ROOT: See mlir_python_setup_extension_rpath().
466 #   DECLARED_HEADERS: Source groups from which to discover headers that belong
467 #     to the library and should be installed with it.
468 #   DECLARED_SOURCES: Source groups from which to discover dependent
469 #     EMBED_CAPI_LINK_LIBS.
470 #   EMBED_LIBS: Additional libraries to embed (must be built with OBJECTS and
471 #     have an "obj.${name}" object library associated).
472 function(add_mlir_python_common_capi_library name)
473   cmake_parse_arguments(ARG
474     ""
475     "INSTALL_COMPONENT;INSTALL_DESTINATION;OUTPUT_DIRECTORY;RELATIVE_INSTALL_ROOT"
476     "DECLARED_HEADERS;DECLARED_SOURCES;EMBED_LIBS"
477     ${ARGN})
478   # Collect all explicit and transitive embed libs.
479   set(_embed_libs ${ARG_EMBED_LIBS})
480   _flatten_mlir_python_targets(_all_source_targets ${ARG_DECLARED_SOURCES})
481   foreach(t ${_all_source_targets})
482     get_target_property(_local_embed_libs ${t} mlir_python_EMBED_CAPI_LINK_LIBS)
483     if(_local_embed_libs)
484       list(APPEND _embed_libs ${_local_embed_libs})
485     endif()
486   endforeach()
487   list(REMOVE_DUPLICATES _embed_libs)
489   # Generate the aggregate .so that everything depends on.
490   add_mlir_aggregate(${name}
491     SHARED
492     DISABLE_INSTALL
493     EMBED_LIBS ${_embed_libs}
494   )
496   # Process any headers associated with the library
497   _flatten_mlir_python_targets(_flat_header_targets ${ARG_DECLARED_HEADERS})
498   set(_header_sources_target "${name}.sources")
499   add_mlir_python_sources_target(${_header_sources_target}
500     INSTALL_COMPONENT ${ARG_INSTALL_COMPONENT}
501     INSTALL_DIR "${ARG_INSTALL_DESTINATION}/include"
502     OUTPUT_DIRECTORY "${ARG_OUTPUT_DIRECTORY}/include"
503     SOURCES_TARGETS ${_flat_header_targets}
504   )
505   add_dependencies(${name} ${_header_sources_target})
507   if(MSVC)
508     set_property(TARGET ${name} PROPERTY WINDOWS_EXPORT_ALL_SYMBOLS ON)
509   endif()
510   set_target_properties(${name} PROPERTIES
511     LIBRARY_OUTPUT_DIRECTORY "${ARG_OUTPUT_DIRECTORY}"
512     BINARY_OUTPUT_DIRECTORY "${ARG_OUTPUT_DIRECTORY}"
513     # Needed for windows (and don't hurt others).
514     RUNTIME_OUTPUT_DIRECTORY "${ARG_OUTPUT_DIRECTORY}"
515     ARCHIVE_OUTPUT_DIRECTORY "${ARG_OUTPUT_DIRECTORY}"
516   )
517   mlir_python_setup_extension_rpath(${name}
518     RELATIVE_INSTALL_ROOT "${ARG_RELATIVE_INSTALL_ROOT}"
519   )
520   install(TARGETS ${name}
521     COMPONENT ${ARG_INSTALL_COMPONENT}
522     LIBRARY DESTINATION "${ARG_INSTALL_DESTINATION}"
523     RUNTIME DESTINATION "${ARG_INSTALL_DESTINATION}"
524   )
525 endfunction()
527 function(_flatten_mlir_python_targets output_var)
528   set(_flattened)
529   foreach(t ${ARGN})
530     get_target_property(_source_type ${t} mlir_python_SOURCES_TYPE)
531     get_target_property(_depends ${t} mlir_python_DEPENDS)
532     if(_source_type)
533       list(APPEND _flattened "${t}")
534       if(_depends)
535         _flatten_mlir_python_targets(_local_flattened ${_depends})
536         list(APPEND _flattened ${_local_flattened})
537       endif()
538     endif()
539   endforeach()
540   list(REMOVE_DUPLICATES _flattened)
541   set(${output_var} "${_flattened}" PARENT_SCOPE)
542 endfunction()
544 # Function: add_mlir_python_sources_target
545 # Adds a target corresponding to an interface target that carries source
546 # information. This target is responsible for "building" the sources by
547 # placing them in the correct locations in the build and install trees.
548 # Arguments:
549 #   INSTALL_COMPONENT: Name of the install component. Typically same as the
550 #     target name passed to add_mlir_python_modules().
551 #   INSTALL_DESTINATION: Prefix into the install tree in which to install the
552 #     library.
553 #   OUTPUT_DIRECTORY: Full path in the build tree in which to create the
554 #     library. Typically, this will be the common _mlir_libs directory where
555 #     all extensions are emitted.
556 #   SOURCES_TARGETS: List of interface libraries that carry source information.
557 function(add_mlir_python_sources_target name)
558   cmake_parse_arguments(ARG
559   ""
560   "INSTALL_COMPONENT;INSTALL_DIR;OUTPUT_DIRECTORY"
561   "SOURCES_TARGETS"
562   ${ARGN})
564   if(ARG_UNPARSED_ARGUMENTS)
565     message(FATAL_ERROR "Unhandled arguments to add_mlir_python_sources_target(${name}, ... : ${ARG_UNPARSED_ARGUMENTS}")
566   endif()
568   add_custom_target(${name})
570   # On Windows create_symlink requires special permissions. Use copy_if_different instead.
571   if(CMAKE_HOST_WIN32)
572     set(_link_or_copy copy_if_different)
573   else()
574     set(_link_or_copy create_symlink)
575   endif()
577   foreach(_sources_target ${ARG_SOURCES_TARGETS})
578     add_dependencies(${name} ${_sources_target})
580     get_target_property(_src_paths ${_sources_target} SOURCES)
581     if(NOT _src_paths)
582       get_target_property(_src_paths ${_sources_target} INTERFACE_SOURCES)
583       if(NOT _src_paths)
584         break()
585       endif()
586     endif()
588     get_target_property(_root_dir ${_sources_target} INCLUDE_DIRECTORIES)
589     if(NOT _root_dir)
590       get_target_property(_root_dir ${_sources_target} INTERFACE_INCLUDE_DIRECTORIES)
591     endif()
593     foreach(_src_path ${_src_paths})
594       file(RELATIVE_PATH _source_relative_path "${_root_dir}" "${_src_path}")
595       set(_dest_path "${ARG_OUTPUT_DIRECTORY}/${_source_relative_path}")
597       get_filename_component(_dest_dir "${_dest_path}" DIRECTORY)
598       file(MAKE_DIRECTORY "${_dest_dir}")
600       add_custom_command(
601         TARGET ${name} PRE_BUILD
602         COMMENT "Copying python source ${_src_path} -> ${_dest_path}"
603         DEPENDS "${_src_path}"
604         BYPRODUCTS "${_dest_path}"
605         COMMAND "${CMAKE_COMMAND}" -E ${_link_or_copy}
606             "${_src_path}" "${_dest_path}"
607       )
608       if(ARG_INSTALL_DIR)
609         # We have to install each file individually because we need to preserve
610         # the relative directory structure in the install destination.
611         # As an example, ${_source_relative_path} may be dialects/math.py
612         # which would be transformed to ${ARG_INSTALL_DIR}/dialects
613         # here. This could be moved outside of the loop and cleaned up
614         # if we used FILE_SETS (introduced in CMake 3.23).
615         get_filename_component(_install_destination "${ARG_INSTALL_DIR}/${_source_relative_path}" DIRECTORY)
616         install(
617           FILES ${_src_path}
618           DESTINATION "${_install_destination}"
619           COMPONENT ${ARG_INSTALL_COMPONENT}
620         )
621       endif()
622     endforeach()
623   endforeach()
624 endfunction()
626 ################################################################################
627 # Build python extension
628 ################################################################################
629 function(add_mlir_python_extension libname extname)
630   cmake_parse_arguments(ARG
631   ""
632   "INSTALL_COMPONENT;INSTALL_DIR;OUTPUT_DIRECTORY"
633   "SOURCES;LINK_LIBS"
634   ${ARGN})
635   if(ARG_UNPARSED_ARGUMENTS)
636     message(FATAL_ERROR "Unhandled arguments to add_mlir_python_extension(${libname}, ... : ${ARG_UNPARSED_ARGUMENTS}")
637   endif()
639   # The actual extension library produces a shared-object or DLL and has
640   # sources that must be compiled in accordance with pybind11 needs (RTTI and
641   # exceptions).
642   pybind11_add_module(${libname}
643     ${ARG_SOURCES}
644   )
646   # The extension itself must be compiled with RTTI and exceptions enabled.
647   # Also, some warning classes triggered by pybind11 are disabled.
648   set(eh_rtti_enable)
649   if (MSVC)
650     set(eh_rtti_enable /EHsc /GR)
651   elseif(LLVM_COMPILER_IS_GCC_COMPATIBLE)
652     set(eh_rtti_enable -frtti -fexceptions)
653   endif ()
654   target_compile_options(${libname} PRIVATE ${eh_rtti_enable})
656   # Configure the output to match python expectations.
657   set_target_properties(
658     ${libname} PROPERTIES
659     LIBRARY_OUTPUT_DIRECTORY ${ARG_OUTPUT_DIRECTORY}
660     OUTPUT_NAME "${extname}"
661     NO_SONAME ON
662   )
664   if(WIN32)
665     # Need to also set the RUNTIME_OUTPUT_DIRECTORY on Windows in order to
666     # control where the .dll gets written.
667     set_target_properties(
668       ${libname} PROPERTIES
669       RUNTIME_OUTPUT_DIRECTORY ${ARG_OUTPUT_DIRECTORY}
670       ARCHIVE_OUTPUT_DIRECTORY ${ARG_OUTPUT_DIRECTORY}
671     )
672   endif()
674   target_link_libraries(${libname}
675     PRIVATE
676     ${ARG_LINK_LIBS}
677   )
679   target_link_options(${libname}
680     PRIVATE
681       # On Linux, disable re-export of any static linked libraries that
682       # came through.
683       $<$<PLATFORM_ID:Linux>:LINKER:--exclude-libs,ALL>
684   )
686   if(WIN32)
687     # On Windows, pyconfig.h (and by extension python.h) hardcode the version of the
688     # python library which will be used for linkage depending on the flavor of the build.
689     # pybind11 has a workaround which depends on the definition of Py_DEBUG (if Py_DEBUG
690     # is not passed in as a compile definition, pybind11 undefs _DEBUG when including
691     # python.h, so that the release python library would be used).
692     # Since mlir uses pybind11, we can leverage their workaround by never directly
693     # pyconfig.h or python.h and instead relying on the pybind11 headers to include the
694     # necessary python headers. This results in mlir always linking against the
695     # release python library via the (undocumented) cmake property Python3_LIBRARY_RELEASE.
696     target_link_libraries(${libname} PRIVATE ${Python3_LIBRARY_RELEASE})
697   endif()
699   ################################################################################
700   # Install
701   ################################################################################
702   if(ARG_INSTALL_DIR)
703     install(TARGETS ${libname}
704       COMPONENT ${ARG_INSTALL_COMPONENT}
705       LIBRARY DESTINATION ${ARG_INSTALL_DIR}
706       ARCHIVE DESTINATION ${ARG_INSTALL_DIR}
707       # NOTE: Even on DLL-platforms, extensions go in the lib directory tree.
708       RUNTIME DESTINATION ${ARG_INSTALL_DIR}
709     )
710   endif()
711 endfunction()