Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions .ci/all_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,9 @@ ml-dtypes==0.5.1 ; python_version < "3.13" \
--hash=sha256:d13755f8e8445b3870114e5b6240facaa7cb0c3361e54beba3e07fa912a6e12b \
--hash=sha256:fd918d4e6a4e0c110e2e05be7a7814d10dc1b95872accbf6512b80a109b71ae1
# via -r mlir/python/requirements.txt
nanobind==2.7.0 \
--hash=sha256:73b12d0e751d140d6c1bf4b215e18818a8debfdb374f08dc3776ad208d808e74 \
--hash=sha256:f9f1b160580c50dcf37b6495a0fd5ec61dc0d95dae5f8004f87dd9ad7eb46b34
nanobind==2.9.2 \
--hash=sha256:c37957ffd5eac7eda349cff3622ecd32e5ee1244ecc912c99b5bc8188bafd16e \
--hash=sha256:e7608472de99d375759814cab3e2c94aba3f9ec80e62cfef8ced495ca5c27d6e
# via -r mlir/python/requirements.txt
numpy==2.0.2 \
--hash=sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a \
Expand Down Expand Up @@ -383,6 +383,10 @@ swig==4.3.1 \
--hash=sha256:efec16327029f682f649a26da726bb0305be8800bd0f1fa3e81bf0769cf5b476 \
--hash=sha256:fc496c0d600cf1bb2d91e28d3d6eae9c4301e5ea7a0dec5a4281b5efed4245a8
# via -r lldb/test/requirements.txt
typing-extensions==4.15.0 \
--hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \
--hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548
# via -r mlir/python/requirements.txt
urllib3==2.5.0 \
--hash=sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760 \
--hash=sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc
Expand Down
169 changes: 149 additions & 20 deletions mlir/cmake/modules/AddMLIRPython.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,78 @@ function(declare_mlir_python_sources name)
endif()
endfunction()

# Function: generate_type_stubs
# Turns on automatic type stub generation (via nanobind's stubgen) for extension modules.
# Arguments:
# MODULE_NAME: The fully-qualified name of the extension module (used for importing in python).
# DEPENDS_TARGET: The dso target corresponding to the extension module
# (e.g., something like StandalonePythonModules.extension._standaloneDialectsNanobind.dso)
# CORE_MLIR_DEPENDS_TARGET: The dso target corresponding to the main/core extension module
# (e.g., something like StandalonePythonModules.extension._mlir.dso)
# OUTPUT_DIR: The root output directory to emit the type stubs into.
# OUTPUTS: List of expected outputs.
# DEPENDS_TARGET_SRC_DEPS: List of cpp sources for extension library (for generating a DEPFILE).
# IMPORT_PATH:
# Outputs:
# NB_STUBGEN_CUSTOM_TARGET: The target corresponding to generation which other targets can depend on.
function(generate_type_stubs)
cmake_parse_arguments(ARG
""
"MODULE_NAME;DEPENDS_TARGET;CORE_MLIR_DEPENDS_TARGET;OUTPUT_DIR;IMPORT_PATH"
"OUTPUTS;DEPENDS_TARGET_SRC_DEPS"
${ARGN})
# for people doing find_package(nanobind)
if(EXISTS ${nanobind_DIR}/../src/stubgen.py)
set(NB_STUBGEN "${nanobind_DIR}/../src/stubgen.py")
elseif(EXISTS ${nanobind_DIR}/../stubgen.py)
set(NB_STUBGEN "${nanobind_DIR}/../stubgen.py")
# for people using FetchContent_Declare and FetchContent_MakeAvailable
elseif(EXISTS ${nanobind_SOURCE_DIR}/src/stubgen.py)
set(NB_STUBGEN "${nanobind_SOURCE_DIR}/src/stubgen.py")
elseif(EXISTS ${nanobind_SOURCE_DIR}/stubgen.py)
set(NB_STUBGEN "${nanobind_SOURCE_DIR}/stubgen.py")
else()
message(FATAL_ERROR "generate_type_stubs(): could not locate 'stubgen.py'!")
endif()

file(REAL_PATH "${NB_STUBGEN}" NB_STUBGEN)
file(REAL_PATH "${ARG_IMPORT_PATH}" _import_path)
set(_nb_stubgen_cmd
"${Python_EXECUTABLE}"
"${NB_STUBGEN}"
--module
"${ARG_MODULE_NAME}"
-i
"${_import_path}"
--recursive
--include-private
--output-dir
"${ARG_OUTPUT_DIR}"
--quiet)

list(TRANSFORM ARG_OUTPUTS PREPEND "${ARG_OUTPUT_DIR}/" OUTPUT_VARIABLE _generated_type_stubs)
set(_depfile "${ARG_OUTPUT_DIR}/${ARG_MODULE_NAME}.d")
if ((NOT EXISTS ${_depfile}) AND ARG_DEPENDS_TARGET_SRC_DEPS)
list(JOIN ARG_DEPENDS_TARGET_SRC_DEPS " " _depfiles)
list(TRANSFORM _generated_type_stubs APPEND ": ${_depfiles}" OUTPUT_VARIABLE _depfiles)
list(JOIN _depfiles "\n" _depfiles)
file(GENERATE OUTPUT "${_depfile}" CONTENT "${_depfiles}")
endif()
add_custom_command(
OUTPUT ${_generated_type_stubs}
COMMAND ${_nb_stubgen_cmd}
WORKING_DIRECTORY "${CMAKE_CURRENT_FUNCTION_LIST_DIR}"
DEPENDS
"${ARG_CORE_MLIR_DEPENDS_TARGET}.extension._mlir.dso"
"${ARG_CORE_MLIR_DEPENDS_TARGET}.sources.MLIRPythonSources.Core.Python"
"${ARG_DEPENDS_TARGET}"
DEPFILE "${_depfile}"
)
set(_name "${ARG_CORE_MLIR_DEPENDS_TARGET}.${ARG_DEPENDS_TARGET}.${ARG_MODULE_NAME}.type_stubs")
add_custom_target("${_name}" DEPENDS ${_generated_type_stubs})
set(NB_STUBGEN_CUSTOM_TARGET "${_name}" PARENT_SCOPE)
endfunction()

# Function: declare_mlir_python_extension
# Declares a buildable python extension from C++ source files. The built
# module is considered a python source file and included as everything else.
Expand All @@ -115,11 +187,15 @@ endfunction()
# on. These will be collected for all extensions and put into an
# aggregate dylib that is linked against.
# PYTHON_BINDINGS_LIBRARY: Either pybind11 or nanobind.
# GENERATE_TYPE_STUBS: Either
# 1. OFF (default)
# 2. ON if ${MODULE_NAME}.pyi is the only stub
# 3. A list of generated type stubs expected from stubgen (relative to _mlir_libs).
function(declare_mlir_python_extension name)
cmake_parse_arguments(ARG
""
"ROOT_DIR;MODULE_NAME;ADD_TO_PARENT;PYTHON_BINDINGS_LIBRARY"
"SOURCES;PRIVATE_LINK_LIBS;EMBED_CAPI_LINK_LIBS"
"SOURCES;PRIVATE_LINK_LIBS;EMBED_CAPI_LINK_LIBS;GENERATE_TYPE_STUBS"
${ARGN})

if(NOT ARG_ROOT_DIR)
Expand All @@ -132,15 +208,33 @@ function(declare_mlir_python_extension name)
endif()

add_library(${name} INTERFACE)

if(NOT ARG_GENERATE_TYPE_STUBS)
set(ARG_GENERATE_TYPE_STUBS OFF)
endif()
if("${ARG_GENERATE_TYPE_STUBS}" STREQUAL "ON")
set(ARG_GENERATE_TYPE_STUBS "${ARG_MODULE_NAME}.pyi")
endif()
if(ARG_GENERATE_TYPE_STUBS)
list(TRANSFORM ARG_GENERATE_TYPE_STUBS PREPEND "_mlir_libs/")
declare_mlir_python_sources(
"${name}.type_stub_gen"
ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}/type_stubs"
ADD_TO_PARENT "${ARG_ADD_TO_PARENT}"
SOURCES "${ARG_GENERATE_TYPE_STUBS}"
)
endif()

set_target_properties(${name} PROPERTIES
# Yes: Leading-lowercase property names are load bearing and the recommended
# way to do this: https://gitlab.kitware.com/cmake/cmake/-/issues/19261
EXPORT_PROPERTIES "mlir_python_SOURCES_TYPE;mlir_python_EXTENSION_MODULE_NAME;mlir_python_EMBED_CAPI_LINK_LIBS;mlir_python_DEPENDS;mlir_python_BINDINGS_LIBRARY"
EXPORT_PROPERTIES "mlir_python_SOURCES_TYPE;mlir_python_EXTENSION_MODULE_NAME;mlir_python_EMBED_CAPI_LINK_LIBS;mlir_python_DEPENDS;mlir_python_BINDINGS_LIBRARY;mlir_python_GENERATE_TYPE_STUBS"
mlir_python_SOURCES_TYPE extension
mlir_python_EXTENSION_MODULE_NAME "${ARG_MODULE_NAME}"
mlir_python_EMBED_CAPI_LINK_LIBS "${ARG_EMBED_CAPI_LINK_LIBS}"
mlir_python_DEPENDS ""
mlir_python_BINDINGS_LIBRARY "${ARG_PYTHON_BINDINGS_LIBRARY}"
mlir_python_GENERATE_TYPE_STUBS "${ARG_GENERATE_TYPE_STUBS}"
)

# Set the interface source and link_libs properties of the target
Expand Down Expand Up @@ -243,6 +337,39 @@ function(add_mlir_python_modules name)
)
add_dependencies(${modules_target} ${_extension_target})
mlir_python_setup_extension_rpath(${_extension_target})
# NOTE: `sources_target` (naturally) lists all the sources (it's the INTERFACE
# target defined above in declare_mlir_python_extension). It's also the name of the
# target that gets exported (i.e., is populated as an INTERFACE IMPORTED library in MLIRTargets.cmake).
# This is why all metadata is queried from `sources_target`. On the other hand
# `_extension_target` is the actual dylib target that's built just above with `add_mlir_python_extension`.
# That's why dependencies are in terms of `_extension_target`.
get_target_property(_generate_type_stubs ${sources_target} mlir_python_GENERATE_TYPE_STUBS)
if(_generate_type_stubs)
# TL;DR: all paths here are load bearing and annoyingly coupled. Changing paths here
# (or related code in declare_mlir_python_extension) will break either in-tree or out-of-tree generation.
#
# We remove _mlir_libs here because OUTPUT_DIR already includes it.
# Specifically OUTPUT_DIR already includes it because that's the actual directory
# where we want stubgen to dump the emitted sources. This is load bearing because up above
# (in declare_mlir_python_extension) we prefixed all the paths with _mlir_libs and
# we specified declare_mlir_python_sources with ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}/type_stubs".
#
# NOTE: INTERFACE_SOURCES is a genex in the build dir ($<BUILD_INTERFACE> $<INSTALL_INTERFACE>)
# which will be evaluated by file(GENERATE ...). In the install dir it's a conventional path
# (see install/lib/cmake/mlir/MLIRTargets.cmake).
get_target_property(_extension_srcs ${sources_target} INTERFACE_SOURCES)
list(TRANSFORM _generate_type_stubs REPLACE "_mlir_libs/" "")
generate_type_stubs(
MODULE_NAME "_mlir_libs.${_module_name}"
DEPENDS_TARGET ${_extension_target}
CORE_MLIR_DEPENDS_TARGET ${name}
OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/type_stubs/_mlir_libs"
OUTPUTS "${_generate_type_stubs}"
DEPENDS_TARGET_SRC_DEPS "${_extension_srcs}"
IMPORT_PATH "${ARG_ROOT_PREFIX}"
)
add_dependencies("${modules_target}" "${NB_STUBGEN_CUSTOM_TARGET}")
endif()
else()
message(SEND_ERROR "Unrecognized source type '${_source_type}' for python source target ${sources_target}")
return()
Expand Down Expand Up @@ -678,26 +805,28 @@ function(add_mlir_python_extension libname extname)
# the super project handle compile options as it wishes.
get_property(NB_LIBRARY_TARGET_NAME TARGET ${libname} PROPERTY LINK_LIBRARIES)
target_compile_options(${NB_LIBRARY_TARGET_NAME}
PRIVATE
-Wall -Wextra -Wpedantic
-Wno-c++98-compat-extra-semi
-Wno-cast-qual
-Wno-covered-switch-default
-Wno-nested-anon-types
-Wno-unused-parameter
-Wno-zero-length-array
${eh_rtti_enable})
PRIVATE
-Wall -Wextra -Wpedantic
-Wno-c++98-compat-extra-semi
-Wno-cast-qual
-Wno-covered-switch-default
-Wno-deprecated-literal-operator
-Wno-nested-anon-types
-Wno-unused-parameter
-Wno-zero-length-array
${eh_rtti_enable})

target_compile_options(${libname}
PRIVATE
-Wall -Wextra -Wpedantic
-Wno-c++98-compat-extra-semi
-Wno-cast-qual
-Wno-covered-switch-default
-Wno-nested-anon-types
-Wno-unused-parameter
-Wno-zero-length-array
${eh_rtti_enable})
PRIVATE
-Wall -Wextra -Wpedantic
-Wno-c++98-compat-extra-semi
-Wno-cast-qual
-Wno-covered-switch-default
-Wno-deprecated-literal-operator
-Wno-nested-anon-types
-Wno-unused-parameter
-Wno-zero-length-array
${eh_rtti_enable})
endif()

if(APPLE)
Expand Down
2 changes: 1 addition & 1 deletion mlir/examples/standalone/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ if(MLIR_ENABLE_BINDINGS_PYTHON)
include(MLIRDetectPythonEnv)
mlir_configure_python_dev_packages()
set(MLIR_PYTHON_PACKAGE_PREFIX "mlir_standalone" CACHE STRING "" FORCE)
set(MLIR_BINDINGS_PYTHON_INSTALL_PREFIX "python_packages/standalone/mlir_standalone" CACHE STRING "" FORCE)
set(MLIR_BINDINGS_PYTHON_INSTALL_PREFIX "python_packages/standalone/${MLIR_PYTHON_PACKAGE_PREFIX}" CACHE STRING "" FORCE)
add_subdirectory(python)
endif()
add_subdirectory(test)
Expand Down
6 changes: 5 additions & 1 deletion mlir/examples/standalone/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ declare_mlir_python_extension(StandalonePythonSources.NanobindExtension
EMBED_CAPI_LINK_LIBS
StandaloneCAPI
PYTHON_BINDINGS_LIBRARY nanobind
# GENERATE_TYPE_STUBS
# "_standaloneDialectsNanobind/__init__.pyi"
# "_standaloneDialectsNanobind/standalone.pyi"
GENERATE_TYPE_STUBS OFF
)


Expand Down Expand Up @@ -76,4 +80,4 @@ add_mlir_python_modules(StandalonePythonModules
MLIRPythonSources.Dialects.builtin
COMMON_CAPI_LINK_LIBS
StandalonePythonCAPI
)
)
Loading