This is an automated email from the ASF dual-hosted git repository.
junrushao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-ffi.git
The following commit(s) were added to refs/heads/main by this push:
new ccd19f8 feat(cmake): Introduce `tvm_ffi_configure_target` and
`tvm_ffi_install` (#351)
ccd19f8 is described below
commit ccd19f8202a980bd03501a62600e338fa883f80c
Author: Junru Shao <[email protected]>
AuthorDate: Sat Dec 20 10:17:56 2025 -0800
feat(cmake): Introduce `tvm_ffi_configure_target` and `tvm_ffi_install`
(#351)
Similar to
[`pybind11-add-module`](https://pybind11.readthedocs.io/en/stable/compiling.html#pybind11-add-module)
and
[`nanobind_add_module`](https://nanobind.readthedocs.io/en/latest/api_cmake.html#command:nanobind_add_module),
this PR introduces two convenient CMake methods to help integrate CMake
targets with TVM-FFI.
---
cmake/Utils/Library.cmake | 262 ++++++++++++++++++++-
examples/packaging/CMakeLists.txt | 52 +---
.../packaging/python/my_ffi_extension/_ffi_api.py | 2 +-
3 files changed, 263 insertions(+), 53 deletions(-)
diff --git a/cmake/Utils/Library.cmake b/cmake/Utils/Library.cmake
index 73817e9..012b3ca 100644
--- a/cmake/Utils/Library.cmake
+++ b/cmake/Utils/Library.cmake
@@ -14,7 +14,6 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-
# ~~~
# tvm_ffi_add_prefix_map(target_name, prefix_path)
# Add a compile prefix map so absolute paths under `prefix_path` are remapped
to a stable,
@@ -34,14 +33,14 @@ endfunction ()
# ~~~
# tvm_ffi_add_apple_dsymutil(target_name)
# On Apple platforms, run `dsymutil` post-build to generate debug symbols for
better backtraces.
-# No-ops on non-Apple or when libbacktrace is disabled.
+# No-ops on non-Apple platforms.
#
# Parameters:
# target_name: CMake target to attach post-build step
# ~~~
function (tvm_ffi_add_apple_dsymutil target_name)
# running dsymutil on macos to generate debugging symbols for backtraces
- if (APPLE AND TVM_FFI_USE_LIBBACKTRACE)
+ if (APPLE)
find_program(DSYMUTIL dsymutil)
mark_as_advanced(DSYMUTIL)
add_custom_command(
@@ -137,3 +136,260 @@ function (tvm_ffi_add_target_from_obj target_name
obj_target_name)
tvm_ffi_add_apple_dsymutil(${target_name}_shared)
tvm_ffi_add_apple_dsymutil(${target_name}_testing)
endfunction ()
+
+# cmake-lint: disable=C0301,R0912,R0915
+# ~~~
+# tvm_ffi_configure_target(
+# target_name
+# [LINK_SHARED ON|OFF] [LINK_HEADER ON|OFF] [DEBUG_SYMBOL ON|OFF]
[MSVC_FLAGS ON|OFF]
+# [STUB_INIT ON|OFF] [STUB_DIR <dir>] [STUB_PKG <pkg>] [STUB_PREFIX <prefix>]
+# )
+# Configure a target to integrate with TVM-FFI CMake utilities:
+# - Optionally link against tvm_ffi_header and/or tvm_ffi_shared
+# - Always apply tvm_ffi_add_prefix_map(target_name <current source dir>)
+# - Optionally enable Apple dSYM generation via
tvm_ffi_add_apple_dsymutil(target_name)
+# - Optionally apply MSVC-specific flags via
tvm_ffi_add_msvc_flags(target_name)
+#
+# Parameters:
+# target_name: Existing CMake target to modify (positional, required)
+#
+# Keyword parameters:
+# LINK_SHARED: Whether to link tvm_ffi_shared into the target (default: ON;
ON/OFF-style)
+# LINK_HEADER: Whether to link tvm_ffi_header into the target (default: ON;
ON/OFF-style)
+# DEBUG_SYMBOL: Whether to enable debug symbol post-processing hooks.
+# On Apple this calls tvm_ffi_add_apple_dsymutil(target_name)
(default: ON; ON/OFF-style)
+# On non-Apple platforms this is currently a no-op unless you
extend it. (default: ON)
+# MSVC_FLAGS: Whether to call tvm_ffi_add_msvc_flags(target_name) to apply
MSVC-specific flags (default: ON; ON/OFF-style)
+# STUB_DIR: Stub generation runs when this is set. Directory to generate
Python stubs. Relative paths resolve against CMAKE_CURRENT_SOURCE_DIR.
+# STUB_INIT: Whether to allow generating new directives. Default: OFF
(ON/OFF-style)
+# STUB_PKG: Package name passed to stub generator (requires STUB_DIR and
STUB_INIT=ON; default: ${SKBUILD_PROJECT_NAME} if set, otherwise target name)
+# STUB_PREFIX: Module prefix passed to stub generator (requires STUB_DIR
and STUB_INIT=ON; default: "<STUB_PKG>.")
+# ~~~
+function (tvm_ffi_configure_target target)
+ if (NOT target)
+ message(
+ FATAL_ERROR
+ "tvm_ffi_configure_target: missing target name. "
+ "Usage: tvm_ffi_configure_target(<target> [LINK_SHARED ON|OFF]
[LINK_HEADER ON|OFF] [DEBUG_SYMBOL ON|OFF] [MSVC_FLAGS ON|OFF] [STUB_INIT
ON|OFF] [STUB_DIR <dir>] [STUB_PKG <pkg>] [STUB_PREFIX <prefix>])"
+ )
+ endif ()
+
+ if (NOT TARGET "${target}")
+ message(FATAL_ERROR "tvm_ffi_configure_target: '${target}' is not an
existing CMake target.")
+ endif ()
+
+ # Parse keyword args after the positional target name.
+ set(tvm_ffi_arg_options) # none; require explicit ON/OFF style values
+ set(tvm_ffi_arg_oneValueArgs
+ LINK_SHARED
+ LINK_HEADER
+ DEBUG_SYMBOL
+ MSVC_FLAGS
+ STUB_INIT
+ STUB_DIR
+ STUB_PKG
+ STUB_PREFIX
+ )
+ set(tvm_ffi_arg_multiValueArgs)
+
+ cmake_parse_arguments(
+ tvm_ffi_arg_ "${tvm_ffi_arg_options}" "${tvm_ffi_arg_oneValueArgs}"
+ "${tvm_ffi_arg_multiValueArgs}" ${ARGN}
+ )
+
+ # Defaults
+ foreach (arg IN ITEMS LINK_SHARED LINK_HEADER DEBUG_SYMBOL MSVC_FLAGS)
+ if (NOT DEFINED tvm_ffi_arg__${arg})
+ set(tvm_ffi_arg__${arg} ON)
+ endif ()
+ endforeach ()
+ if (NOT DEFINED tvm_ffi_arg__STUB_INIT)
+ set(tvm_ffi_arg__STUB_INIT OFF)
+ endif ()
+
+ # Validation
+ if ((NOT DEFINED tvm_ffi_arg__STUB_DIR) OR (NOT tvm_ffi_arg__STUB_DIR))
+ if (DEFINED tvm_ffi_arg__STUB_PKG OR DEFINED tvm_ffi_arg__STUB_PREFIX)
+ message(
+ FATAL_ERROR
+ "tvm_ffi_configure_target(${target}): STUB_PKG/STUB_PREFIX require
STUB_DIR to be set."
+ )
+ endif ()
+ endif ()
+ if (NOT tvm_ffi_arg__STUB_INIT)
+ if (DEFINED tvm_ffi_arg__STUB_PKG OR DEFINED tvm_ffi_arg__STUB_PREFIX)
+ message(
+ FATAL_ERROR
+ "tvm_ffi_configure_target(${target}): STUB_PKG/STUB_PREFIX cannot be
set when STUB_INIT is OFF."
+ )
+ endif ()
+ else ()
+ if (NOT DEFINED tvm_ffi_arg__STUB_DIR OR NOT tvm_ffi_arg__STUB_DIR)
+ message(
+ FATAL_ERROR "tvm_ffi_configure_target(${target}): STUB_INIT=ON
requires STUB_DIR to be set."
+ )
+ endif ()
+ endif ()
+
+ # STUB_PKG and STUB_PREFIX defaults
+ if (tvm_ffi_arg__STUB_INIT AND tvm_ffi_arg__STUB_DIR)
+ if (NOT DEFINED tvm_ffi_arg__STUB_PKG)
+ if (DEFINED SKBUILD_PROJECT_NAME AND SKBUILD_PROJECT_NAME)
+ set(tvm_ffi_arg__STUB_PKG "${SKBUILD_PROJECT_NAME}")
+ else ()
+ set(tvm_ffi_arg__STUB_PKG "${target}")
+ endif ()
+ endif ()
+ if (NOT DEFINED tvm_ffi_arg__STUB_PREFIX)
+ set(tvm_ffi_arg__STUB_PREFIX "${tvm_ffi_arg__STUB_PKG}.")
+ endif ()
+ endif ()
+
+ # Always-on prefix map
+ if (COMMAND tvm_ffi_add_prefix_map)
+ tvm_ffi_add_prefix_map("${target}" "${CMAKE_CURRENT_SOURCE_DIR}")
+ else ()
+ message(
+ FATAL_ERROR
+ "tvm_ffi_configure_target(${target}): required function
'tvm_ffi_add_prefix_map' is not defined/included."
+ )
+ endif ()
+
+ # LINK_HEADER
+ if (tvm_ffi_arg__LINK_HEADER)
+ if (TARGET tvm_ffi_header)
+ target_link_libraries("${target}" PRIVATE tvm_ffi_header)
+ else ()
+ message(
+ FATAL_ERROR
+ "tvm_ffi_configure_target(${target}): LINK_HEADER requested but
target 'tvm_ffi_header' does not exist."
+ )
+ endif ()
+ endif ()
+
+ # LINK_SHARED
+ if (tvm_ffi_arg__LINK_SHARED)
+ if (TARGET tvm_ffi_shared)
+ target_link_libraries("${target}" PRIVATE tvm_ffi_shared)
+ else ()
+ message(
+ FATAL_ERROR
+ "tvm_ffi_configure_target(${target}): LINK_SHARED requested but
target 'tvm_ffi_shared' does not exist."
+ )
+ endif ()
+ endif ()
+
+ # DEBUG_SYMBOL (default ON). Apple behavior only (hook only; installation
handled by
+ # tvm_ffi_install()).
+ if (tvm_ffi_arg__DEBUG_SYMBOL)
+ if (APPLE)
+ if (COMMAND tvm_ffi_add_apple_dsymutil)
+ tvm_ffi_add_apple_dsymutil("${target}")
+ else ()
+ message(
+ FATAL_ERROR
+ "tvm_ffi_configure_target(${target}): DEBUG_SYMBOL=ON but
'tvm_ffi_add_apple_dsymutil' is not defined/included."
+ )
+ endif ()
+ endif ()
+ endif ()
+
+ # Optional: MSVC flags
+ if (tvm_ffi_arg__MSVC_FLAGS)
+ if (COMMAND tvm_ffi_add_msvc_flags)
+ tvm_ffi_add_msvc_flags("${target}")
+ else ()
+ message(
+ FATAL_ERROR
+ "tvm_ffi_configure_target(${target}): MSVC_FLAGS=ON but
'tvm_ffi_add_msvc_flags' is not defined/included."
+ )
+ endif ()
+ endif ()
+
+ if (DEFINED tvm_ffi_arg__STUB_DIR AND tvm_ffi_arg__STUB_DIR)
+ get_filename_component(
+ tvm_ffi_arg__STUB_DIR_ABS "${tvm_ffi_arg__STUB_DIR}" ABSOLUTE BASE_DIR
+ "${CMAKE_CURRENT_SOURCE_DIR}"
+ )
+ find_package(
+ Python3
+ COMPONENTS Interpreter
+ REQUIRED
+ )
+ set(tvm_ffi_stub_cli_args "${tvm_ffi_arg__STUB_DIR_ABS}" --dlls
$<TARGET_FILE:${target}>)
+ if (tvm_ffi_arg__STUB_INIT)
+ list(
+ APPEND
+ tvm_ffi_stub_cli_args
+ --init-lib
+ ${target}
+ --init-pypkg
+ "${tvm_ffi_arg__STUB_PKG}"
+ --init-prefix
+ "${tvm_ffi_arg__STUB_PREFIX}"
+ )
+ endif ()
+ add_custom_command(
+ TARGET ${target}
+ POST_BUILD
+ COMMAND ${Python3_EXECUTABLE} -m tvm_ffi.stub.cli
${tvm_ffi_stub_cli_args}
+ COMMENT
+ "[COMMAND] Running: ${Python3_EXECUTABLE} -m tvm_ffi.stub.cli
${tvm_ffi_stub_cli_args}"
+ VERBATIM
+ )
+ endif ()
+endfunction ()
+
+# ~~~
+# tvm_ffi_install(target_name [DESTINATION <dir>])
+# Install TVM-FFI related artifacts for a configured target.
+#
+# Parameters:
+# target_name: Existing CMake target whose artifacts should be installed
+#
+# Keyword parameters:
+# DESTINATION: Install destination directory relative to
CMAKE_INSTALL_PREFIX (default: ".")
+#
+# Behavior:
+# - On Apple, installs the target's dSYM bundle if it exists.
+# This uses generator expressions and OPTIONAL so it does not fail if the
dSYM is absent.
+# - On non-Apple platforms, currently no-op (extend as needed for PDB/DWARF
packaging).
+#
+# Notes:
+# - This function does not create dSYMs; it only installs them if present.
+# Pair it with tvm_ffi_configure_target(... DEBUG_SYMBOL ON) to enable
dSYM generation hooks.
+# ~~~
+function (tvm_ffi_install target)
+ if (NOT target)
+ message(
+ FATAL_ERROR
+ "tvm_ffi_install: missing target name. Usage: tvm_ffi_install(<target>
[DESTINATION <dir>])"
+ )
+ endif ()
+
+ if (NOT TARGET "${target}")
+ message(FATAL_ERROR "tvm_ffi_install: '${target}' is not an existing CMake
target.")
+ endif ()
+
+ set(tvm_ffi_install_options) # none
+ set(tvm_ffi_install_oneValueArgs DESTINATION)
+ set(tvm_ffi_install_multiValueArgs)
+
+ cmake_parse_arguments(
+ tvm_ffi_install_ "${tvm_ffi_install_options}"
"${tvm_ffi_install_oneValueArgs}"
+ "${tvm_ffi_install_multiValueArgs}" ${ARGN}
+ )
+
+ if (NOT DEFINED tvm_ffi_install__DESTINATION)
+ set(tvm_ffi_install__DESTINATION ".")
+ endif ()
+
+ if (APPLE)
+ # Install target dSYM bundle if present.
+ install(
+ DIRECTORY "$<TARGET_FILE:${target}>.dSYM"
+ DESTINATION "${tvm_ffi_install__DESTINATION}"
+ OPTIONAL
+ )
+ endif ()
+endfunction ()
diff --git a/examples/packaging/CMakeLists.txt
b/examples/packaging/CMakeLists.txt
index 3648da6..9b9fa80 100644
--- a/examples/packaging/CMakeLists.txt
+++ b/examples/packaging/CMakeLists.txt
@@ -14,62 +14,16 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-
cmake_minimum_required(VERSION 3.18)
project(my_ffi_extension)
-option(TVM_FFI_EXT_FROM_SOURCE "Build tvm_ffi from source, useful for cross
compilation." ON)
-option(TVM_FFI_EXT_SHIP_DEBUG_SYMBOLS "Ship debug symbols" ON)
-
-# There are two ways to include tvm_ffi
-#
-# 1. Build tvm_ffi from source, which is reasonably cheap since tvm ffi is
small
-# 2. Use the pre-built tvm_ffi shipped from the pip
-#
-# This example shows both options, you only need to pick a specific one.
-#
-# * For common build cases, using pre-built and link tvm_ffi_shared is
sufficient.
-# * For cases where you may want to cross-compile or bundle part of
tvm_ffi_objects directly into
-# your project, opt for building tvm_ffi from source path. Note that it is
always safe to build
-# from source and extra cost of building tvm_ffi is small. So when in doubt,
you can always choose
-# to the building tvm_ffi from source route.
-#
-# In python or other cases when we dynamically load libtvm_ffi_shared. Even
when you build from
-# source, you do not need to ship libtvm_ffi.so built here as they are only
used to supply the
-# linking information. first find python related components
find_package(
Python
COMPONENTS Interpreter
REQUIRED
)
-if (TVM_FFI_BUILD_FROM_SOURCE)
- execute_process(
- COMMAND "${Python_EXECUTABLE}" -m tvm_ffi.config --sourcedir
- OUTPUT_STRIP_TRAILING_WHITESPACE
- OUTPUT_VARIABLE tvm_ffi_ROOT
- )
- message(STATUS "Building tvm_ffi from source: ${tvm_ffi_ROOT}")
- add_subdirectory(${tvm_ffi_ROOT} tvm_ffi)
-else ()
- # tvm_ffi is installed via pip. Try to find tvm_ffi automatically
- find_package(tvm_ffi CONFIG REQUIRED)
-endif ()
-
-# use the projects as usual
+find_package(tvm_ffi CONFIG REQUIRED)
add_library(my_ffi_extension SHARED src/extension.cc)
-target_link_libraries(my_ffi_extension tvm_ffi_header)
-target_link_libraries(my_ffi_extension tvm_ffi_shared)
-
-if (TVM_FFI_EXT_SHIP_DEBUG_SYMBOLS)
- # ship debugging symbols for backtrace on macos
- tvm_ffi_add_prefix_map(my_ffi_extension ${CMAKE_CURRENT_SOURCE_DIR})
- tvm_ffi_add_apple_dsymutil(my_ffi_extension)
- install(
- DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/
- DESTINATION .
- FILES_MATCHING
- PATTERN "*.dSYM"
- )
-endif ()
-
+tvm_ffi_configure_target(my_ffi_extension STUB_DIR "./python" STUB_INIT ON)
install(TARGETS my_ffi_extension DESTINATION .)
+tvm_ffi_install(my_ffi_extension)
diff --git a/examples/packaging/python/my_ffi_extension/_ffi_api.py
b/examples/packaging/python/my_ffi_extension/_ffi_api.py
index 887b991..be25187 100644
--- a/examples/packaging/python/my_ffi_extension/_ffi_api.py
+++ b/examples/packaging/python/my_ffi_extension/_ffi_api.py
@@ -28,7 +28,7 @@ if TYPE_CHECKING:
# fmt: on
# tvm-ffi-stubgen(end)
# tvm-ffi-stubgen(import-object):
tvm_ffi.libinfo.load_lib_module;False;_FFI_LOAD_LIB
-LIB = _FFI_LOAD_LIB("my-ffi-extension", "my_ffi_extension")
+LIB = _FFI_LOAD_LIB("my_ffi_extension", "my_ffi_extension")
# tvm-ffi-stubgen(begin): global/my_ffi_extension
# fmt: off
_FFI_INIT_FUNC("my_ffi_extension", __name__)