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. 26# DEST_PREFIX: Destination prefix to prepend to files in the python 27# package directory namespace. 28function(declare_mlir_python_sources name) 29 cmake_parse_arguments(ARG 30 "" 31 "ROOT_DIR;ADD_TO_PARENT;DEST_PREFIX" 32 "SOURCES;SOURCES_GLOB" 33 ${ARGN}) 34 35 if(NOT ARG_ROOT_DIR) 36 set(ARG_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") 37 endif() 38 set(_install_destination "src/python/${name}") 39 40 # Process the glob. 41 set(_glob_sources) 42 if(ARG_SOURCES_GLOB) 43 set(_glob_spec ${ARG_SOURCES_GLOB}) 44 list(TRANSFORM _glob_spec PREPEND "${ARG_ROOT_DIR}/") 45 file(GLOB_RECURSE _glob_sources 46 RELATIVE "${ARG_ROOT_DIR}" 47 ${_glob_spec} 48 ) 49 list(APPEND ARG_SOURCES ${_glob_sources}) 50 endif() 51 52 # We create a custom target to carry properties and dependencies for 53 # generated sources. 54 add_library(${name} INTERFACE) 55 set(_file_depends "${ARG_SOURCES}") 56 list(TRANSFORM _file_depends PREPEND "${ARG_ROOT_DIR}/") 57 set_target_properties(${name} PROPERTIES 58 # Yes: Leading-lowercase property names are load bearing and the recommended 59 # way to do this: https://gitlab.kitware.com/cmake/cmake/-/issues/19261 60 # Note that ROOT_DIR and FILE_DEPENDS are not exported because they are 61 # only relevant to in-tree uses. 62 EXPORT_PROPERTIES "mlir_python_SOURCES_TYPE;mlir_python_DEST_PREFIX;mlir_python_ROOT_DIR;mlir_python_SOURCES;mlir_python_DEPENDS" 63 mlir_python_SOURCES_TYPE pure 64 mlir_python_ROOT_DIR "${ARG_ROOT_DIR}" 65 mlir_python_DEST_PREFIX "${ARG_DEST_PREFIX}" 66 mlir_python_SOURCES "${ARG_SOURCES}" 67 mlir_python_FILE_DEPENDS "${_file_depends}" 68 mlir_python_DEPENDS "" 69 ) 70 # Note that an "include" directory has no meaning to such faux targets, 71 # but it is a CMake supported way to specify a directory search list in a 72 # way that works both in-tree and out. It has some super powers which are 73 # not possible to emulate with custom properties (because of the prohibition 74 # on using generator expressions in exported custom properties and the 75 # special dispensation for $<INSTALL_PREFIX>). 76 target_include_directories(${name} INTERFACE 77 "$<BUILD_INTERFACE:${ARG_ROOT_DIR}>" 78 "$<INSTALL_INTERFACE:${_install_destination}>" 79 ) 80 81 # Add to parent. 82 if(ARG_ADD_TO_PARENT) 83 set_property(TARGET ${ARG_ADD_TO_PARENT} APPEND PROPERTY mlir_python_DEPENDS ${name}) 84 endif() 85 86 # Install. 87 set_property(GLOBAL APPEND PROPERTY MLIR_EXPORTS ${name}) 88 if(NOT LLVM_INSTALL_TOOLCHAIN_ONLY) 89 _mlir_python_install_sources( 90 ${name} "${ARG_ROOT_DIR}" "${_install_destination}" 91 ${ARG_SOURCES} 92 ) 93 endif() 94endfunction() 95 96# Function: declare_mlir_python_extension 97# Declares a buildable python extension from C++ source files. The built 98# module is considered a python source file and included as everything else. 99# Arguments: 100# ROOT_DIR: Root directory where sources are interpreted relative to. 101# Defaults to CMAKE_CURRENT_SOURCE_DIR. 102# MODULE_NAME: Local import name of the module (i.e. "_mlir"). 103# ADD_TO_PARENT: Same as for declare_mlir_python_sources. 104# SOURCES: C++ sources making up the module. 105# PRIVATE_LINK_LIBS: List of libraries to link in privately to the module 106# regardless of how it is included in the project (generally should be 107# static libraries that can be included with hidden visibility). 108# EMBED_CAPI_LINK_LIBS: Dependent CAPI libraries that this extension depends 109# on. These will be collected for all extensions and put into an 110# aggregate dylib that is linked against. 111function(declare_mlir_python_extension name) 112 cmake_parse_arguments(ARG 113 "" 114 "ROOT_DIR;MODULE_NAME;ADD_TO_PARENT" 115 "SOURCES;PRIVATE_LINK_LIBS;EMBED_CAPI_LINK_LIBS" 116 ${ARGN}) 117 118 if(NOT ARG_ROOT_DIR) 119 set(ARG_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") 120 endif() 121 set(_install_destination "src/python/${name}") 122 123 add_library(${name} INTERFACE) 124 set_target_properties(${name} PROPERTIES 125 # Yes: Leading-lowercase property names are load bearing and the recommended 126 # way to do this: https://gitlab.kitware.com/cmake/cmake/-/issues/19261 127 # Note that ROOT_DIR and FILE_DEPENDS are not exported because they are 128 # only relevant to in-tree uses. 129 EXPORT_PROPERTIES "mlir_python_SOURCES_TYPE;mlir_python_ROOT_DIR;mlir_python_EXTENSION_MODULE_NAME;mlir_python_CPP_SOURCES;mlir_python_PRIVATE_LINK_LIBS;mlir_python_EMBED_CAPI_LINK_LIBS;mlir_python_DEPENDS" 130 mlir_python_SOURCES_TYPE extension 131 mlir_python_ROOT_DIR "${ARG_ROOT_DIR}" 132 mlir_python_EXTENSION_MODULE_NAME "${ARG_MODULE_NAME}" 133 mlir_python_CPP_SOURCES "${ARG_SOURCES}" 134 mlir_python_PRIVATE_LINK_LIBS "${ARG_PRIVATE_LINK_LIBS}" 135 mlir_python_EMBED_CAPI_LINK_LIBS "${ARG_EMBED_CAPI_LINK_LIBS}" 136 mlir_python_FILE_DEPENDS "" 137 mlir_python_DEPENDS "" 138 ) 139 # Note that an "include" directory has no meaning to such faux targets, 140 # but it is a CMake supported way to specify an install-prefix relative 141 # directory. It has some super powers which are not possible to emulate 142 # with custom properties (because of the prohibition on using generator 143 # expressions in exported custom properties and the special dispensation 144 # for $<INSTALL_PREFIX> and $<INSTALL_INTERFACE>). On imported targets, 145 # this is used as a single value, not as a list, so it must only have one 146 # item in it. 147 target_include_directories(${name} INTERFACE 148 "$<INSTALL_INTERFACE:${_install_destination}>" 149 ) 150 151 # Add to parent. 152 if(ARG_ADD_TO_PARENT) 153 set_property(TARGET ${ARG_ADD_TO_PARENT} APPEND PROPERTY mlir_python_DEPENDS ${name}) 154 endif() 155 156 # Install. 157 set_property(GLOBAL APPEND PROPERTY MLIR_EXPORTS ${name}) 158 if(NOT LLVM_INSTALL_TOOLCHAIN_ONLY) 159 _mlir_python_install_sources( 160 ${name} "${ARG_ROOT_DIR}" "src/python/${name}" 161 ${ARG_SOURCES} 162 ) 163 endif() 164endfunction() 165 166function(_mlir_python_install_sources name source_root_dir destination) 167 foreach(source_relative_path ${ARGN}) 168 # Transform "a/b/c.py" -> "${install_prefix}/a/b" for installation. 169 get_filename_component( 170 dest_relative_path "${source_relative_path}" DIRECTORY 171 BASE_DIR "${source_root_dir}" 172 ) 173 install( 174 FILES "${source_root_dir}/${source_relative_path}" 175 DESTINATION "${destination}/${dest_relative_path}" 176 COMPONENT "${name}" 177 ) 178 endforeach() 179 get_target_export_arg(${name} MLIR export_to_mlirtargets UMBRELLA mlir-libraries) 180 install(TARGETS ${name} 181 COMPONENT ${name} 182 ${export_to_mlirtargets} 183 ) 184endfunction() 185 186# Function: add_mlir_python_modules 187# Adds python modules to a project, building them from a list of declared 188# source groupings (see declare_mlir_python_sources and 189# declare_mlir_python_extension). One of these must be called for each 190# packaging root in use. 191# Arguments: 192# ROOT_PREFIX: The directory in the build tree to emit sources. This will 193# typically be something like ${MY_BINARY_DIR}/python_packages/foobar 194# for non-relocatable modules or a deeper directory tree for relocatable. 195# INSTALL_PREFIX: Prefix into the install tree for installing the package. 196# Typically mirrors the path above but without an absolute path. 197# DECLARED_SOURCES: List of declared source groups to include. The entire 198# DAG of source modules is included. 199# COMMON_CAPI_LINK_LIBS: List of dylibs (typically one) to make every 200# extension depend on (see mlir_python_add_common_capi_library). 201function(add_mlir_python_modules name) 202 cmake_parse_arguments(ARG 203 "" 204 "ROOT_PREFIX;INSTALL_PREFIX;COMMON_CAPI_LINK_LIBS" 205 "DECLARED_SOURCES" 206 ${ARGN}) 207 # Helper to process an individual target. 208 function(_process_target modules_target sources_target) 209 get_target_property(_source_type ${sources_target} mlir_python_SOURCES_TYPE) 210 211 get_target_property(_python_root_dir ${sources_target} mlir_python_ROOT_DIR) 212 if(NOT _python_root_dir) 213 message(FATAL_ERROR "Target ${sources_target} lacks mlir_python_ROOT_DIR property") 214 endif() 215 216 if(_source_type STREQUAL "pure") 217 # Pure python sources to link into the tree. 218 get_target_property(_python_sources ${sources_target} mlir_python_SOURCES) 219 get_target_property(_specified_dest_prefix ${sources_target} mlir_python_DEST_PREFIX) 220 foreach(_source_relative_path ${_python_sources}) 221 set(_dest_relative_path "${_source_relative_path}") 222 if(_specified_dest_prefix) 223 set(_dest_relative_path "${_specified_dest_prefix}/${_dest_relative_path}") 224 endif() 225 set(_src_path "${_python_root_dir}/${_source_relative_path}") 226 set(_dest_path "${ARG_ROOT_PREFIX}/${_dest_relative_path}") 227 228 get_filename_component(_dest_dir "${_dest_path}" DIRECTORY) 229 get_filename_component(_install_path "${ARG_INSTALL_PREFIX}/${_dest_relative_path}" DIRECTORY) 230 231 file(MAKE_DIRECTORY "${_dest_dir}") 232 233 # On Windows create_symlink requires special permissions. Use copy_if_different instead. 234 if(CMAKE_HOST_WIN32) 235 set(_link_or_copy copy_if_different) 236 else() 237 set(_link_or_copy create_symlink) 238 endif() 239 240 add_custom_command( 241 TARGET ${modules_target} PRE_BUILD 242 COMMENT "Copying python source ${_src_path} -> ${_dest_path}" 243 DEPENDS "${_src_path}" 244 BYPRODUCTS "${_dest_path}" 245 COMMAND "${CMAKE_COMMAND}" -E ${_link_or_copy} 246 "${_src_path}" "${_dest_path}" 247 ) 248 install( 249 FILES "${_src_path}" 250 DESTINATION "${_install_path}" 251 COMPONENT ${modules_target} 252 ) 253 endforeach() 254 elseif(_source_type STREQUAL "extension") 255 # Native CPP extension. 256 get_target_property(_module_name ${sources_target} mlir_python_EXTENSION_MODULE_NAME) 257 get_target_property(_cpp_sources ${sources_target} mlir_python_CPP_SOURCES) 258 get_target_property(_private_link_libs ${sources_target} mlir_python_PRIVATE_LINK_LIBS) 259 # Transform relative source to based on root dir. 260 list(TRANSFORM _cpp_sources PREPEND "${_python_root_dir}/") 261 set(_extension_target "${name}.extension.${_module_name}.dso") 262 add_mlir_python_extension(${_extension_target} "${_module_name}" 263 INSTALL_COMPONENT ${modules_target} 264 INSTALL_DIR "${ARG_INSTALL_PREFIX}/_mlir_libs" 265 OUTPUT_DIRECTORY "${ARG_ROOT_PREFIX}/_mlir_libs" 266 SOURCES ${_cpp_sources} 267 LINK_LIBS PRIVATE 268 ${_private_link_libs} 269 ${ARG_COMMON_CAPI_LINK_LIBS} 270 ) 271 add_dependencies(${name} ${_extension_target}) 272 mlir_python_setup_extension_rpath(${_extension_target}) 273 else() 274 message(SEND_ERROR "Unrecognized source type '${_source_type}' for python source target ${sources_target}") 275 return() 276 endif() 277 endfunction() 278 279 _flatten_mlir_python_targets(_flat_targets ${ARG_DECLARED_SOURCES}) 280 # Collect dependencies. 281 set(_depends) 282 foreach(sources_target ${_flat_targets}) 283 get_target_property(_local_depends ${sources_target} mlir_python_FILE_DEPENDS) 284 if(_local_depends) 285 list(APPEND _depends ${_local_depends}) 286 endif() 287 endforeach() 288 289 # Build the modules target. 290 add_custom_target(${name} ALL DEPENDS ${_depends}) 291 foreach(sources_target ${_flat_targets}) 292 _process_target(${name} ${sources_target}) 293 endforeach() 294 295 # Create an install target. 296 if(NOT LLVM_ENABLE_IDE) 297 add_llvm_install_targets( 298 install-${name} 299 DEPENDS ${name} 300 COMPONENT ${name}) 301 endif() 302endfunction() 303 304# Function: declare_mlir_dialect_python_bindings 305# Helper to generate source groups for dialects, including both static source 306# files and a TD_FILE to generate wrappers. 307# 308# This will generate a source group named ${ADD_TO_PARENT}.${DIALECT_NAME}. 309# 310# Arguments: 311# ROOT_DIR: Same as for declare_mlir_python_sources(). 312# ADD_TO_PARENT: Same as for declare_mlir_python_sources(). Unique names 313# for the subordinate source groups are derived from this. 314# TD_FILE: Tablegen file to generate source for (relative to ROOT_DIR). 315# DIALECT_NAME: Python name of the dialect. 316# SOURCES: Same as declare_mlir_python_sources(). 317# SOURCES_GLOB: Same as declare_mlir_python_sources(). 318# DEPENDS: Additional dependency targets. 319function(declare_mlir_dialect_python_bindings) 320 cmake_parse_arguments(ARG 321 "" 322 "ROOT_DIR;ADD_TO_PARENT;TD_FILE;DIALECT_NAME" 323 "SOURCES;SOURCES_GLOB;DEPENDS" 324 ${ARGN}) 325 # Sources. 326 set(_dialect_target "${ARG_ADD_TO_PARENT}.${ARG_DIALECT_NAME}") 327 declare_mlir_python_sources(${_dialect_target} 328 ROOT_DIR "${ARG_ROOT_DIR}" 329 ADD_TO_PARENT "${ARG_ADD_TO_PARENT}" 330 SOURCES "${ARG_SOURCES}" 331 SOURCES_GLOB "${ARG_SOURCES_GLOB}" 332 ) 333 334 # Tablegen 335 if(ARG_TD_FILE) 336 set(tblgen_target "${ARG_ADD_TO}.${ARG_DIALECT_NAME}.tablegen") 337 set(td_file "${ARG_ROOT_DIR}/${ARG_TD_FILE}") 338 get_filename_component(relative_td_directory "${ARG_TD_FILE}" DIRECTORY) 339 file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${relative_td_directory}") 340 set(dialect_filename "${relative_td_directory}/_${ARG_DIALECT_NAME}_ops_gen.py") 341 set(LLVM_TARGET_DEFINITIONS ${td_file}) 342 mlir_tablegen("${dialect_filename}" -gen-python-op-bindings 343 -bind-dialect=${ARG_DIALECT_NAME}) 344 add_public_tablegen_target(${tblgen_target}) 345 if(ARG_DEPENDS) 346 add_dependencies(${tblgen_target} ${ARG_DEPENDS}) 347 endif() 348 349 # Generated. 350 declare_mlir_python_sources("${ARG_ADD_TO_PARENT}.${ARG_DIALECT_NAME}.ops_gen" 351 ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}" 352 ADD_TO_PARENT "${_dialect_target}" 353 SOURCES "${dialect_filename}" 354 ) 355 endif() 356endfunction() 357 358# Function: declare_mlir_dialect_extension_python_bindings 359# Helper to generate source groups for dialect extensions, including both 360# static source files and a TD_FILE to generate wrappers. 361# 362# This will generate a source group named ${ADD_TO_PARENT}.${EXTENSION_NAME}. 363# 364# Arguments: 365# ROOT_DIR: Same as for declare_mlir_python_sources(). 366# ADD_TO_PARENT: Same as for declare_mlir_python_sources(). Unique names 367# for the subordinate source groups are derived from this. 368# TD_FILE: Tablegen file to generate source for (relative to ROOT_DIR). 369# DIALECT_NAME: Python name of the dialect. 370# EXTENSION_NAME: Python name of the dialect extension. 371# SOURCES: Same as declare_mlir_python_sources(). 372# SOURCES_GLOB: Same as declare_mlir_python_sources(). 373# DEPENDS: Additional dependency targets. 374function(declare_mlir_dialect_extension_python_bindings) 375 cmake_parse_arguments(ARG 376 "" 377 "ROOT_DIR;ADD_TO_PARENT;TD_FILE;DIALECT_NAME;EXTENSION_NAME" 378 "SOURCES;SOURCES_GLOB;DEPENDS" 379 ${ARGN}) 380 # Source files. 381 set(_extension_target "${ARG_ADD_TO_PARENT}.${ARG_EXTENSION_NAME}") 382 declare_mlir_python_sources(${_extension_target} 383 ROOT_DIR "${ARG_ROOT_DIR}" 384 ADD_TO_PARENT "${ARG_ADD_TO_PARENT}" 385 SOURCES "${ARG_SOURCES}" 386 SOURCES_GLOB "${ARG_SOURCES_GLOB}" 387 ) 388 389 # Tablegen 390 if(ARG_TD_FILE) 391 set(tblgen_target "${ARG_ADD_TO_PARENT}.${ARG_EXTENSION_NAME}.tablegen") 392 set(td_file "${ARG_ROOT_DIR}/${ARG_TD_FILE}") 393 get_filename_component(relative_td_directory "${ARG_TD_FILE}" DIRECTORY) 394 file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${relative_td_directory}") 395 set(output_filename "${relative_td_directory}/_${ARG_EXTENSION_NAME}_ops_gen.py") 396 set(LLVM_TARGET_DEFINITIONS ${td_file}) 397 mlir_tablegen("${output_filename}" -gen-python-op-bindings 398 -bind-dialect=${ARG_DIALECT_NAME} 399 -dialect-extension=${ARG_EXTENSION_NAME}) 400 add_public_tablegen_target(${tblgen_target}) 401 if(ARG_DEPENDS) 402 add_dependencies(${tblgen_target} ${ARG_DEPENDS}) 403 endif() 404 405 declare_mlir_python_sources("${_extension_target}.ops_gen" 406 ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}" 407 ADD_TO_PARENT "${_extension_target}" 408 SOURCES "${output_filename}" 409 ) 410 endif() 411endfunction() 412 413# Function: mlir_python_setup_extension_rpath 414# Sets RPATH properties on a target, assuming that it is being output to 415# an _mlir_libs directory with all other libraries. For static linkage, 416# the RPATH will just be the origin. If linking dynamically, then the LLVM 417# library directory will be added. 418# Arguments: 419# RELATIVE_INSTALL_ROOT: If building dynamically, an RPATH entry will be 420# added to the install tree lib/ directory by first traversing this 421# path relative to the installation location. Typically a number of ".." 422# entries, one for each level of the install path. 423function(mlir_python_setup_extension_rpath target) 424 cmake_parse_arguments(ARG 425 "" 426 "RELATIVE_INSTALL_ROOT" 427 "" 428 ${ARGN}) 429 430 # RPATH handling. 431 # For the build tree, include the LLVM lib directory and the current 432 # directory for RPATH searching. For install, just the current directory 433 # (assumes that needed dependencies have been installed). 434 if(NOT APPLE AND NOT UNIX) 435 return() 436 endif() 437 438 set(_origin_prefix "\$ORIGIN") 439 if(APPLE) 440 set(_origin_prefix "@loader_path") 441 endif() 442 set_target_properties(${target} PROPERTIES 443 BUILD_WITH_INSTALL_RPATH OFF 444 BUILD_RPATH "${_origin_prefix}" 445 INSTALL_RPATH "${_origin_prefix}" 446 ) 447 448 # For static builds, that is all that is needed: all dependencies will be in 449 # the one directory. For shared builds, then we also need to add the global 450 # lib directory. This will be absolute for the build tree and relative for 451 # install. 452 # When we have access to CMake >= 3.20, there is a helper to calculate this. 453 if(BUILD_SHARED_LIBS AND ARG_RELATIVE_INSTALL_ROOT) 454 get_filename_component(_real_lib_dir "${LLVM_LIBRARY_OUTPUT_INTDIR}" REALPATH) 455 set_property(TARGET ${target} APPEND PROPERTY 456 BUILD_RPATH "${_real_lib_dir}") 457 set_property(TARGET ${target} APPEND PROPERTY 458 INSTALL_RPATH "${_origin_prefix}/${ARG_RELATIVE_INSTALL_ROOT}/lib${LLVM_LIBDIR_SUFFIX}") 459 endif() 460endfunction() 461 462# Function: add_mlir_python_common_capi_library 463# Adds a shared library which embeds dependent CAPI libraries needed to link 464# all extensions. 465# Arguments: 466# INSTALL_COMPONENT: Name of the install component. Typically same as the 467# target name passed to add_mlir_python_modules(). 468# INSTALL_DESTINATION: Prefix into the install tree in which to install the 469# library. 470# OUTPUT_DIRECTORY: Full path in the build tree in which to create the 471# library. Typically, this will be the common _mlir_libs directory where 472# all extensions are emitted. 473# RELATIVE_INSTALL_ROOT: See mlir_python_setup_extension_rpath(). 474# DECLARED_SOURCES: Source groups from which to discover dependent 475# EMBED_CAPI_LINK_LIBS. 476# EMBED_LIBS: Additional libraries to embed (must be built with OBJECTS and 477# have an "obj.${name}" object library associated). 478function(add_mlir_python_common_capi_library name) 479 cmake_parse_arguments(ARG 480 "" 481 "INSTALL_COMPONENT;INSTALL_DESTINATION;OUTPUT_DIRECTORY;RELATIVE_INSTALL_ROOT" 482 "DECLARED_SOURCES;EMBED_LIBS" 483 ${ARGN}) 484 # Collect all explicit and transitive embed libs. 485 set(_embed_libs ${ARG_EMBED_LIBS}) 486 _flatten_mlir_python_targets(_all_source_targets ${ARG_DECLARED_SOURCES}) 487 foreach(t ${_all_source_targets}) 488 get_target_property(_local_embed_libs ${t} mlir_python_EMBED_CAPI_LINK_LIBS) 489 if(_local_embed_libs) 490 list(APPEND _embed_libs ${_local_embed_libs}) 491 endif() 492 endforeach() 493 list(REMOVE_DUPLICATES _embed_libs) 494 495 # Generate the aggregate .so that everything depends on. 496 add_mlir_aggregate(${name} 497 SHARED 498 DISABLE_INSTALL 499 EMBED_LIBS ${_embed_libs} 500 ) 501 502 if(MSVC) 503 set_property(TARGET ${name} PROPERTY WINDOWS_EXPORT_ALL_SYMBOLS ON) 504 endif() 505 set_target_properties(${name} PROPERTIES 506 LIBRARY_OUTPUT_DIRECTORY "${ARG_OUTPUT_DIRECTORY}" 507 BINARY_OUTPUT_DIRECTORY "${ARG_OUTPUT_DIRECTORY}" 508 # Needed for windows (and don't hurt others). 509 RUNTIME_OUTPUT_DIRECTORY "${ARG_OUTPUT_DIRECTORY}" 510 ARCHIVE_OUTPUT_DIRECTORY "${ARG_OUTPUT_DIRECTORY}" 511 ) 512 mlir_python_setup_extension_rpath(${name} 513 RELATIVE_INSTALL_ROOT "${ARG_RELATIVE_INSTALL_ROOT}" 514 ) 515 install(TARGETS ${name} 516 COMPONENT ${ARG_INSTALL_COMPONENT} 517 LIBRARY DESTINATION "${ARG_INSTALL_DESTINATION}" 518 RUNTIME DESTINATION "${ARG_INSTALL_DESTINATION}" 519 ) 520 521endfunction() 522 523function(_flatten_mlir_python_targets output_var) 524 set(_flattened) 525 foreach(t ${ARGN}) 526 get_target_property(_source_type ${t} mlir_python_SOURCES_TYPE) 527 get_target_property(_depends ${t} mlir_python_DEPENDS) 528 if(_source_type) 529 list(APPEND _flattened "${t}") 530 if(_depends) 531 _flatten_mlir_python_targets(_local_flattened ${_depends}) 532 list(APPEND _flattened ${_local_flattened}) 533 endif() 534 endif() 535 endforeach() 536 list(REMOVE_DUPLICATES _flattened) 537 set(${output_var} "${_flattened}" PARENT_SCOPE) 538endfunction() 539 540################################################################################ 541# Build python extension 542################################################################################ 543function(add_mlir_python_extension libname extname) 544 cmake_parse_arguments(ARG 545 "" 546 "INSTALL_COMPONENT;INSTALL_DIR;OUTPUT_DIRECTORY" 547 "SOURCES;LINK_LIBS" 548 ${ARGN}) 549 if(ARG_UNPARSED_ARGUMENTS) 550 message(FATAL_ERROR " Unhandled arguments to add_mlir_python_extension(${libname}, ... : ${ARG_UNPARSED_ARGUMENTS}") 551 endif() 552 if("${ARG_SOURCES}" STREQUAL "") 553 message(FATAL_ERROR " Missing SOURCES argument to add_mlir_python_extension(${libname}, ...") 554 endif() 555 556 # The actual extension library produces a shared-object or DLL and has 557 # sources that must be compiled in accordance with pybind11 needs (RTTI and 558 # exceptions). 559 pybind11_add_module(${libname} 560 ${ARG_SOURCES} 561 ) 562 563 # The extension itself must be compiled with RTTI and exceptions enabled. 564 # Also, some warning classes triggered by pybind11 are disabled. 565 target_compile_options(${libname} PRIVATE 566 $<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>: 567 # Enable RTTI and exceptions. 568 -frtti -fexceptions 569 > 570 $<$<CXX_COMPILER_ID:MSVC>: 571 # Enable RTTI and exceptions. 572 /EHsc /GR> 573 ) 574 575 # Configure the output to match python expectations. 576 set_target_properties( 577 ${libname} PROPERTIES 578 LIBRARY_OUTPUT_DIRECTORY ${ARG_OUTPUT_DIRECTORY} 579 OUTPUT_NAME "${extname}" 580 NO_SONAME ON 581 ) 582 583 if(WIN32) 584 # Need to also set the RUNTIME_OUTPUT_DIRECTORY on Windows in order to 585 # control where the .dll gets written. 586 set_target_properties( 587 ${libname} PROPERTIES 588 RUNTIME_OUTPUT_DIRECTORY ${ARG_OUTPUT_DIRECTORY} 589 ARCHIVE_OUTPUT_DIRECTORY ${ARG_OUTPUT_DIRECTORY} 590 ) 591 endif() 592 593 target_link_libraries(${libname} 594 PRIVATE 595 ${ARG_LINK_LIBS} 596 ) 597 598 target_link_options(${libname} 599 PRIVATE 600 # On Linux, disable re-export of any static linked libraries that 601 # came through. 602 $<$<PLATFORM_ID:Linux>:LINKER:--exclude-libs,ALL> 603 ) 604 605 if(WIN32) 606 # On Windows, pyconfig.h (and by extension python.h) hardcode the version of the 607 # python library which will be used for linkage depending on the flavor of the build. 608 # pybind11 has a workaround which depends on the definition of Py_DEBUG (if Py_DEBUG 609 # is not passed in as a compile definition, pybind11 undefs _DEBUG when including 610 # python.h, so that the release python library would be used). 611 # Since mlir uses pybind11, we can leverage their workaround by never directly 612 # pyconfig.h or python.h and instead relying on the pybind11 headers to include the 613 # necessary python headers. This results in mlir always linking against the 614 # release python library via the (undocumented) cmake property Python3_LIBRARY_RELEASE. 615 target_link_libraries(${libname} PRIVATE ${Python3_LIBRARY_RELEASE}) 616 endif() 617 618 ################################################################################ 619 # Install 620 ################################################################################ 621 if(ARG_INSTALL_DIR) 622 install(TARGETS ${libname} 623 COMPONENT ${ARG_INSTALL_COMPONENT} 624 LIBRARY DESTINATION ${ARG_INSTALL_DIR} 625 ARCHIVE DESTINATION ${ARG_INSTALL_DIR} 626 # NOTE: Even on DLL-platforms, extensions go in the lib directory tree. 627 RUNTIME DESTINATION ${ARG_INSTALL_DIR} 628 ) 629 endif() 630endfunction() 631