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################################################################################ 10 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. 26function(declare_mlir_python_sources name) 27 cmake_parse_arguments(ARG 28 "" 29 "ROOT_DIR;ADD_TO_PARENT" 30 "SOURCES;SOURCES_GLOB" 31 ${ARGN}) 32 33 if(NOT ARG_ROOT_DIR) 34 set(ARG_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") 35 endif() 36 set(_install_destination "src/python/${name}") 37 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() 49 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 ) 60 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}) 75 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() 86 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() 91 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() 100endfunction() 101 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. 117function(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}) 123 124 if(NOT ARG_ROOT_DIR) 125 set(ARG_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") 126 endif() 127 set(_install_destination "src/python/${name}") 128 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 ) 139 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 ) 151 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() 156 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() 165endfunction() 166 167function(_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 "${name}" 178 ) 179 endforeach() 180 get_target_export_arg(${name} MLIR export_to_mlirtargets UMBRELLA mlir-libraries) 181 install(TARGETS ${name} 182 COMPONENT ${name} 183 ${export_to_mlirtargets} 184 ) 185endfunction() 186 187# Function: add_mlir_python_modules 188# Adds python modules to a project, building them from a list of declared 189# source groupings (see declare_mlir_python_sources and 190# declare_mlir_python_extension). One of these must be called for each 191# packaging root in use. 192# Arguments: 193# ROOT_PREFIX: The directory in the build tree to emit sources. This will 194# typically be something like ${MY_BINARY_DIR}/python_packages/foobar 195# for non-relocatable modules or a deeper directory tree for relocatable. 196# INSTALL_PREFIX: Prefix into the install tree for installing the package. 197# Typically mirrors the path above but without an absolute path. 198# DECLARED_SOURCES: List of declared source groups to include. The entire 199# DAG of source modules is included. 200# COMMON_CAPI_LINK_LIBS: List of dylibs (typically one) to make every 201# extension depend on (see mlir_python_add_common_capi_library). 202function(add_mlir_python_modules name) 203 cmake_parse_arguments(ARG 204 "" 205 "ROOT_PREFIX;INSTALL_PREFIX;COMMON_CAPI_LINK_LIBS" 206 "DECLARED_SOURCES" 207 ${ARGN}) 208 # Helper to process an individual target. 209 function(_process_target modules_target sources_target) 210 get_target_property(_source_type ${sources_target} mlir_python_SOURCES_TYPE) 211 212 if(_source_type STREQUAL "pure") 213 # Pure python sources to link into the tree. 214 set(_pure_sources_target "${modules_target}.sources.${sources_target}") 215 add_mlir_python_sources_target(${_pure_sources_target} 216 INSTALL_COMPONENT ${modules_target} 217 INSTALL_DIR ${ARG_INSTALL_PREFIX} 218 OUTPUT_DIRECTORY ${ARG_ROOT_PREFIX} 219 SOURCES_TARGETS ${sources_target} 220 ) 221 add_dependencies(${modules_target} ${_pure_sources_target}) 222 elseif(_source_type STREQUAL "extension") 223 # Native CPP extension. 224 get_target_property(_module_name ${sources_target} mlir_python_EXTENSION_MODULE_NAME) 225 # Transform relative source to based on root dir. 226 set(_extension_target "${modules_target}.extension.${_module_name}.dso") 227 add_mlir_python_extension(${_extension_target} "${_module_name}" 228 INSTALL_COMPONENT ${modules_target} 229 INSTALL_DIR "${ARG_INSTALL_PREFIX}/_mlir_libs" 230 OUTPUT_DIRECTORY "${ARG_ROOT_PREFIX}/_mlir_libs" 231 LINK_LIBS PRIVATE 232 ${sources_target} 233 ${ARG_COMMON_CAPI_LINK_LIBS} 234 ) 235 add_dependencies(${modules_target} ${_extension_target}) 236 mlir_python_setup_extension_rpath(${_extension_target}) 237 else() 238 message(SEND_ERROR "Unrecognized source type '${_source_type}' for python source target ${sources_target}") 239 return() 240 endif() 241 endfunction() 242 243 # Build the modules target. 244 add_custom_target(${name} ALL) 245 _flatten_mlir_python_targets(_flat_targets ${ARG_DECLARED_SOURCES}) 246 foreach(sources_target ${_flat_targets}) 247 _process_target(${name} ${sources_target}) 248 endforeach() 249 250 # Create an install target. 251 if(NOT LLVM_ENABLE_IDE) 252 add_llvm_install_targets( 253 install-${name} 254 DEPENDS ${name} 255 COMPONENT ${name}) 256 endif() 257endfunction() 258 259# Function: declare_mlir_dialect_python_bindings 260# Helper to generate source groups for dialects, including both static source 261# files and a TD_FILE to generate wrappers. 262# 263# This will generate a source group named ${ADD_TO_PARENT}.${DIALECT_NAME}. 264# 265# Arguments: 266# ROOT_DIR: Same as for declare_mlir_python_sources(). 267# ADD_TO_PARENT: Same as for declare_mlir_python_sources(). Unique names 268# for the subordinate source groups are derived from this. 269# TD_FILE: Tablegen file to generate source for (relative to ROOT_DIR). 270# DIALECT_NAME: Python name of the dialect. 271# SOURCES: Same as declare_mlir_python_sources(). 272# SOURCES_GLOB: Same as declare_mlir_python_sources(). 273# DEPENDS: Additional dependency targets. 274function(declare_mlir_dialect_python_bindings) 275 cmake_parse_arguments(ARG 276 "" 277 "ROOT_DIR;ADD_TO_PARENT;TD_FILE;DIALECT_NAME" 278 "SOURCES;SOURCES_GLOB;DEPENDS" 279 ${ARGN}) 280 # Sources. 281 set(_dialect_target "${ARG_ADD_TO_PARENT}.${ARG_DIALECT_NAME}") 282 declare_mlir_python_sources(${_dialect_target} 283 ROOT_DIR "${ARG_ROOT_DIR}" 284 ADD_TO_PARENT "${ARG_ADD_TO_PARENT}" 285 SOURCES "${ARG_SOURCES}" 286 SOURCES_GLOB "${ARG_SOURCES_GLOB}" 287 ) 288 289 # Tablegen 290 if(ARG_TD_FILE) 291 set(tblgen_target "${_dialect_target}.tablegen") 292 set(td_file "${ARG_ROOT_DIR}/${ARG_TD_FILE}") 293 get_filename_component(relative_td_directory "${ARG_TD_FILE}" DIRECTORY) 294 file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${relative_td_directory}") 295 set(dialect_filename "${relative_td_directory}/_${ARG_DIALECT_NAME}_ops_gen.py") 296 set(LLVM_TARGET_DEFINITIONS ${td_file}) 297 mlir_tablegen("${dialect_filename}" 298 -gen-python-op-bindings -bind-dialect=${ARG_DIALECT_NAME} 299 DEPENDS ${ARG_DEPENDS} 300 ) 301 add_public_tablegen_target(${tblgen_target}) 302 303 # Generated. 304 declare_mlir_python_sources("${_dialect_target}.ops_gen" 305 ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}" 306 ADD_TO_PARENT "${_dialect_target}" 307 SOURCES "${dialect_filename}" 308 ) 309 endif() 310endfunction() 311 312# Function: declare_mlir_dialect_extension_python_bindings 313# Helper to generate source groups for dialect extensions, including both 314# static source files and a TD_FILE to generate wrappers. 315# 316# This will generate a source group named ${ADD_TO_PARENT}.${EXTENSION_NAME}. 317# 318# Arguments: 319# ROOT_DIR: Same as for declare_mlir_python_sources(). 320# ADD_TO_PARENT: Same as for declare_mlir_python_sources(). Unique names 321# for the subordinate source groups are derived from this. 322# TD_FILE: Tablegen file to generate source for (relative to ROOT_DIR). 323# DIALECT_NAME: Python name of the dialect. 324# EXTENSION_NAME: Python name of the dialect extension. 325# SOURCES: Same as declare_mlir_python_sources(). 326# SOURCES_GLOB: Same as declare_mlir_python_sources(). 327# DEPENDS: Additional dependency targets. 328function(declare_mlir_dialect_extension_python_bindings) 329 cmake_parse_arguments(ARG 330 "" 331 "ROOT_DIR;ADD_TO_PARENT;TD_FILE;DIALECT_NAME;EXTENSION_NAME" 332 "SOURCES;SOURCES_GLOB;DEPENDS" 333 ${ARGN}) 334 # Source files. 335 set(_extension_target "${ARG_ADD_TO_PARENT}.${ARG_EXTENSION_NAME}") 336 declare_mlir_python_sources(${_extension_target} 337 ROOT_DIR "${ARG_ROOT_DIR}" 338 ADD_TO_PARENT "${ARG_ADD_TO_PARENT}" 339 SOURCES "${ARG_SOURCES}" 340 SOURCES_GLOB "${ARG_SOURCES_GLOB}" 341 ) 342 343 # Tablegen 344 if(ARG_TD_FILE) 345 set(tblgen_target "${ARG_ADD_TO_PARENT}.${ARG_EXTENSION_NAME}.tablegen") 346 set(td_file "${ARG_ROOT_DIR}/${ARG_TD_FILE}") 347 get_filename_component(relative_td_directory "${ARG_TD_FILE}" DIRECTORY) 348 file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${relative_td_directory}") 349 set(output_filename "${relative_td_directory}/_${ARG_EXTENSION_NAME}_ops_gen.py") 350 set(LLVM_TARGET_DEFINITIONS ${td_file}) 351 mlir_tablegen("${output_filename}" -gen-python-op-bindings 352 -bind-dialect=${ARG_DIALECT_NAME} 353 -dialect-extension=${ARG_EXTENSION_NAME}) 354 add_public_tablegen_target(${tblgen_target}) 355 if(ARG_DEPENDS) 356 add_dependencies(${tblgen_target} ${ARG_DEPENDS}) 357 endif() 358 359 declare_mlir_python_sources("${_extension_target}.ops_gen" 360 ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}" 361 ADD_TO_PARENT "${_extension_target}" 362 SOURCES "${output_filename}" 363 ) 364 endif() 365endfunction() 366 367# Function: mlir_python_setup_extension_rpath 368# Sets RPATH properties on a target, assuming that it is being output to 369# an _mlir_libs directory with all other libraries. For static linkage, 370# the RPATH will just be the origin. If linking dynamically, then the LLVM 371# library directory will be added. 372# Arguments: 373# RELATIVE_INSTALL_ROOT: If building dynamically, an RPATH entry will be 374# added to the install tree lib/ directory by first traversing this 375# path relative to the installation location. Typically a number of ".." 376# entries, one for each level of the install path. 377function(mlir_python_setup_extension_rpath target) 378 cmake_parse_arguments(ARG 379 "" 380 "RELATIVE_INSTALL_ROOT" 381 "" 382 ${ARGN}) 383 384 # RPATH handling. 385 # For the build tree, include the LLVM lib directory and the current 386 # directory for RPATH searching. For install, just the current directory 387 # (assumes that needed dependencies have been installed). 388 if(NOT APPLE AND NOT UNIX) 389 return() 390 endif() 391 392 set(_origin_prefix "\$ORIGIN") 393 if(APPLE) 394 set(_origin_prefix "@loader_path") 395 endif() 396 set_target_properties(${target} PROPERTIES 397 BUILD_WITH_INSTALL_RPATH OFF 398 BUILD_RPATH "${_origin_prefix}" 399 INSTALL_RPATH "${_origin_prefix}" 400 ) 401 402 # For static builds, that is all that is needed: all dependencies will be in 403 # the one directory. For shared builds, then we also need to add the global 404 # lib directory. This will be absolute for the build tree and relative for 405 # install. 406 # When we have access to CMake >= 3.20, there is a helper to calculate this. 407 if(BUILD_SHARED_LIBS AND ARG_RELATIVE_INSTALL_ROOT) 408 get_filename_component(_real_lib_dir "${LLVM_LIBRARY_OUTPUT_INTDIR}" REALPATH) 409 set_property(TARGET ${target} APPEND PROPERTY 410 BUILD_RPATH "${_real_lib_dir}") 411 set_property(TARGET ${target} APPEND PROPERTY 412 INSTALL_RPATH "${_origin_prefix}/${ARG_RELATIVE_INSTALL_ROOT}/lib${LLVM_LIBDIR_SUFFIX}") 413 endif() 414endfunction() 415 416# Function: add_mlir_python_common_capi_library 417# Adds a shared library which embeds dependent CAPI libraries needed to link 418# all extensions. 419# Arguments: 420# INSTALL_COMPONENT: Name of the install component. Typically same as the 421# target name passed to add_mlir_python_modules(). 422# INSTALL_DESTINATION: Prefix into the install tree in which to install the 423# library. 424# OUTPUT_DIRECTORY: Full path in the build tree in which to create the 425# library. Typically, this will be the common _mlir_libs directory where 426# all extensions are emitted. 427# RELATIVE_INSTALL_ROOT: See mlir_python_setup_extension_rpath(). 428# DECLARED_HEADERS: Source groups from which to discover headers that belong 429# to the library and should be installed with it. 430# DECLARED_SOURCES: Source groups from which to discover dependent 431# EMBED_CAPI_LINK_LIBS. 432# EMBED_LIBS: Additional libraries to embed (must be built with OBJECTS and 433# have an "obj.${name}" object library associated). 434function(add_mlir_python_common_capi_library name) 435 cmake_parse_arguments(ARG 436 "" 437 "INSTALL_COMPONENT;INSTALL_DESTINATION;OUTPUT_DIRECTORY;RELATIVE_INSTALL_ROOT" 438 "DECLARED_HEADERS;DECLARED_SOURCES;EMBED_LIBS" 439 ${ARGN}) 440 # Collect all explicit and transitive embed libs. 441 set(_embed_libs ${ARG_EMBED_LIBS}) 442 _flatten_mlir_python_targets(_all_source_targets ${ARG_DECLARED_SOURCES}) 443 foreach(t ${_all_source_targets}) 444 get_target_property(_local_embed_libs ${t} mlir_python_EMBED_CAPI_LINK_LIBS) 445 if(_local_embed_libs) 446 list(APPEND _embed_libs ${_local_embed_libs}) 447 endif() 448 endforeach() 449 list(REMOVE_DUPLICATES _embed_libs) 450 451 # Generate the aggregate .so that everything depends on. 452 add_mlir_aggregate(${name} 453 SHARED 454 DISABLE_INSTALL 455 EMBED_LIBS ${_embed_libs} 456 ) 457 458 # Process any headers associated with the library 459 _flatten_mlir_python_targets(_flat_header_targets ${ARG_DECLARED_HEADERS}) 460 set(_header_sources_target "${name}.sources") 461 add_mlir_python_sources_target(${_header_sources_target} 462 INSTALL_COMPONENT ${ARG_INSTALL_COMPONENT} 463 INSTALL_DIR "${ARG_INSTALL_DESTINATION}/include" 464 OUTPUT_DIRECTORY "${ARG_OUTPUT_DIRECTORY}/include" 465 SOURCES_TARGETS ${_flat_header_targets} 466 ) 467 add_dependencies(${name} ${_header_sources_target}) 468 469 if(MSVC) 470 set_property(TARGET ${name} PROPERTY WINDOWS_EXPORT_ALL_SYMBOLS ON) 471 endif() 472 set_target_properties(${name} PROPERTIES 473 LIBRARY_OUTPUT_DIRECTORY "${ARG_OUTPUT_DIRECTORY}" 474 BINARY_OUTPUT_DIRECTORY "${ARG_OUTPUT_DIRECTORY}" 475 # Needed for windows (and don't hurt others). 476 RUNTIME_OUTPUT_DIRECTORY "${ARG_OUTPUT_DIRECTORY}" 477 ARCHIVE_OUTPUT_DIRECTORY "${ARG_OUTPUT_DIRECTORY}" 478 ) 479 mlir_python_setup_extension_rpath(${name} 480 RELATIVE_INSTALL_ROOT "${ARG_RELATIVE_INSTALL_ROOT}" 481 ) 482 install(TARGETS ${name} 483 COMPONENT ${ARG_INSTALL_COMPONENT} 484 LIBRARY DESTINATION "${ARG_INSTALL_DESTINATION}" 485 RUNTIME DESTINATION "${ARG_INSTALL_DESTINATION}" 486 ) 487endfunction() 488 489function(_flatten_mlir_python_targets output_var) 490 set(_flattened) 491 foreach(t ${ARGN}) 492 get_target_property(_source_type ${t} mlir_python_SOURCES_TYPE) 493 get_target_property(_depends ${t} mlir_python_DEPENDS) 494 if(_source_type) 495 list(APPEND _flattened "${t}") 496 if(_depends) 497 _flatten_mlir_python_targets(_local_flattened ${_depends}) 498 list(APPEND _flattened ${_local_flattened}) 499 endif() 500 endif() 501 endforeach() 502 list(REMOVE_DUPLICATES _flattened) 503 set(${output_var} "${_flattened}" PARENT_SCOPE) 504endfunction() 505 506# Function: add_mlir_python_sources_target 507# Adds a target corresponding to an interface target that carries source 508# information. This target is responsible for "building" the sources by 509# placing them in the correct locations in the build and install trees. 510# Arguments: 511# INSTALL_COMPONENT: Name of the install component. Typically same as the 512# target name passed to add_mlir_python_modules(). 513# INSTALL_DESTINATION: Prefix into the install tree in which to install the 514# library. 515# OUTPUT_DIRECTORY: Full path in the build tree in which to create the 516# library. Typically, this will be the common _mlir_libs directory where 517# all extensions are emitted. 518# SOURCES_TARGETS: List of interface libraries that carry source information. 519function(add_mlir_python_sources_target name) 520 cmake_parse_arguments(ARG 521 "" 522 "INSTALL_COMPONENT;INSTALL_DIR;OUTPUT_DIRECTORY" 523 "SOURCES_TARGETS" 524 ${ARGN}) 525 526 if(ARG_UNPARSED_ARGUMENTS) 527 message(FATAL_ERROR "Unhandled arguments to add_mlir_python_sources_target(${name}, ... : ${ARG_UNPARSED_ARGUMENTS}") 528 endif() 529 530 add_custom_target(${name}) 531 532 # On Windows create_symlink requires special permissions. Use copy_if_different instead. 533 if(CMAKE_HOST_WIN32) 534 set(_link_or_copy copy_if_different) 535 else() 536 set(_link_or_copy create_symlink) 537 endif() 538 539 foreach(_sources_target ${ARG_SOURCES_TARGETS}) 540 add_dependencies(${name} ${_sources_target}) 541 542 get_target_property(_src_paths ${_sources_target} SOURCES) 543 if(NOT _src_paths) 544 get_target_property(_src_paths ${_sources_target} INTERFACE_SOURCES) 545 if(NOT _src_paths) 546 break() 547 endif() 548 endif() 549 550 get_target_property(_root_dir ${_sources_target} INCLUDE_DIRECTORIES) 551 if(NOT _root_dir) 552 get_target_property(_root_dir ${_sources_target} INTERFACE_INCLUDE_DIRECTORIES) 553 endif() 554 555 foreach(_src_path ${_src_paths}) 556 file(RELATIVE_PATH _source_relative_path "${_root_dir}" "${_src_path}") 557 set(_dest_path "${ARG_OUTPUT_DIRECTORY}/${_source_relative_path}") 558 559 get_filename_component(_dest_dir "${_dest_path}" DIRECTORY) 560 file(MAKE_DIRECTORY "${_dest_dir}") 561 562 add_custom_command( 563 TARGET ${name} PRE_BUILD 564 COMMENT "Copying python source ${_src_path} -> ${_dest_path}" 565 DEPENDS "${_src_path}" 566 BYPRODUCTS "${_dest_path}" 567 COMMAND "${CMAKE_COMMAND}" -E ${_link_or_copy} 568 "${_src_path}" "${_dest_path}" 569 ) 570 if(ARG_INSTALL_DIR) 571 # We have to install each file individually because we need to preserve 572 # the relative directory structure in the install destination. 573 # As an example, ${_source_relative_path} may be dialects/math.py 574 # which would be transformed to ${ARG_INSTALL_DIR}/dialects 575 # here. This could be moved outside of the loop and cleaned up 576 # if we used FILE_SETS (introduced in CMake 3.23). 577 get_filename_component(_install_destination "${ARG_INSTALL_DIR}/${_source_relative_path}" DIRECTORY) 578 install( 579 FILES ${_src_path} 580 DESTINATION "${_install_destination}" 581 COMPONENT ${ARG_INSTALL_COMPONENT} 582 ) 583 endif() 584 endforeach() 585 endforeach() 586endfunction() 587 588################################################################################ 589# Build python extension 590################################################################################ 591function(add_mlir_python_extension libname extname) 592 cmake_parse_arguments(ARG 593 "" 594 "INSTALL_COMPONENT;INSTALL_DIR;OUTPUT_DIRECTORY" 595 "SOURCES;LINK_LIBS" 596 ${ARGN}) 597 if(ARG_UNPARSED_ARGUMENTS) 598 message(FATAL_ERROR "Unhandled arguments to add_mlir_python_extension(${libname}, ... : ${ARG_UNPARSED_ARGUMENTS}") 599 endif() 600 601 # The actual extension library produces a shared-object or DLL and has 602 # sources that must be compiled in accordance with pybind11 needs (RTTI and 603 # exceptions). 604 pybind11_add_module(${libname} 605 ${ARG_SOURCES} 606 ) 607 608 # The extension itself must be compiled with RTTI and exceptions enabled. 609 # Also, some warning classes triggered by pybind11 are disabled. 610 target_compile_options(${libname} PRIVATE 611 $<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>: 612 # Enable RTTI and exceptions. 613 -frtti -fexceptions 614 > 615 $<$<CXX_COMPILER_ID:MSVC>: 616 # Enable RTTI and exceptions. 617 /EHsc /GR> 618 ) 619 620 # Configure the output to match python expectations. 621 set_target_properties( 622 ${libname} PROPERTIES 623 LIBRARY_OUTPUT_DIRECTORY ${ARG_OUTPUT_DIRECTORY} 624 OUTPUT_NAME "${extname}" 625 NO_SONAME ON 626 ) 627 628 if(WIN32) 629 # Need to also set the RUNTIME_OUTPUT_DIRECTORY on Windows in order to 630 # control where the .dll gets written. 631 set_target_properties( 632 ${libname} PROPERTIES 633 RUNTIME_OUTPUT_DIRECTORY ${ARG_OUTPUT_DIRECTORY} 634 ARCHIVE_OUTPUT_DIRECTORY ${ARG_OUTPUT_DIRECTORY} 635 ) 636 endif() 637 638 target_link_libraries(${libname} 639 PRIVATE 640 ${ARG_LINK_LIBS} 641 ) 642 643 target_link_options(${libname} 644 PRIVATE 645 # On Linux, disable re-export of any static linked libraries that 646 # came through. 647 $<$<PLATFORM_ID:Linux>:LINKER:--exclude-libs,ALL> 648 ) 649 650 if(WIN32) 651 # On Windows, pyconfig.h (and by extension python.h) hardcode the version of the 652 # python library which will be used for linkage depending on the flavor of the build. 653 # pybind11 has a workaround which depends on the definition of Py_DEBUG (if Py_DEBUG 654 # is not passed in as a compile definition, pybind11 undefs _DEBUG when including 655 # python.h, so that the release python library would be used). 656 # Since mlir uses pybind11, we can leverage their workaround by never directly 657 # pyconfig.h or python.h and instead relying on the pybind11 headers to include the 658 # necessary python headers. This results in mlir always linking against the 659 # release python library via the (undocumented) cmake property Python3_LIBRARY_RELEASE. 660 target_link_libraries(${libname} PRIVATE ${Python3_LIBRARY_RELEASE}) 661 endif() 662 663 ################################################################################ 664 # Install 665 ################################################################################ 666 if(ARG_INSTALL_DIR) 667 install(TARGETS ${libname} 668 COMPONENT ${ARG_INSTALL_COMPONENT} 669 LIBRARY DESTINATION ${ARG_INSTALL_DIR} 670 ARCHIVE DESTINATION ${ARG_INSTALL_DIR} 671 # NOTE: Even on DLL-platforms, extensions go in the lib directory tree. 672 RUNTIME DESTINATION ${ARG_INSTALL_DIR} 673 ) 674 endif() 675endfunction() 676