@@ -172,7 +172,8 @@ endfunction ()
172172# target_name
173173# [LINK_SHARED ON|OFF] [LINK_HEADER ON|OFF] [DEBUG_SYMBOL ON|OFF] [MSVC_FLAGS ON|OFF]
174174# [STUB_INIT ON|OFF] [STUB_DIR <dir>] [STUB_PKG <pkg>] [STUB_PREFIX <prefix>]
175- # [STUB_TARGET python|rust]
175+ # [STUB_TARGET <backend>...] # one or more of: python rust
176+ # [STUB_DIR_PYTHON <dir>] [STUB_DIR_RUST <dir>]
176177# )
177178# Configure a target to integrate with TVM-FFI CMake utilities:
178179# - Link against tvm_ffi::header and/or tvm_ffi::shared
@@ -195,11 +196,20 @@ endfunction ()
195196# STUB_INIT: Whether to allow generating new directives. Default: OFF (ON/OFF-style)
196197# STUB_PKG: Package name passed to stub generator (requires STUB_DIR and STUB_INIT=ON; default: ${SKBUILD_PROJECT_NAME} if set, otherwise target name)
197198# STUB_PREFIX: Module prefix passed to stub generator (requires STUB_DIR and STUB_INIT=ON; default: "<STUB_PKG>.")
198- # STUB_TARGET: Code generator backend: "python" (default) or "rust". Passed to the stub
199- # generator as --target. With "rust", object bindings are emitted into a Rust
200- # module tree under STUB_DIR (global functions are not generated for Rust).
201- # To generate both Python and Rust stubs for one target, call this function
202- # twice with different STUB_DIR/STUB_TARGET values.
199+ # STUB_TARGET: Code generator backend(s): a list of one or more of "python" (default) and
200+ # "rust" (e.g. STUB_TARGET python rust). Each listed backend is passed to the
201+ # stub generator as --target. With "rust", object bindings are emitted into a
202+ # Rust module tree under its STUB_DIR (global functions are not generated for Rust).
203+ # STUB_TARGET with multiple backends (e.g. STUB_TARGET python rust):
204+ # Generates stubs for each listed backend. Because each backend writes a
205+ # different file tree, supply per-backend STUB_DIR_PYTHON / STUB_DIR_RUST. One
206+ # post-build stub command is emitted per backend; the shared STUB_PKG / STUB_PREFIX
207+ # apply to every backend.
208+ # STUB_DIR_PYTHON / STUB_DIR_RUST:
209+ # Per-backend output directories, for listing several backends that each write a
210+ # different file tree. A listed backend with no directory (no STUB_DIR_<BACKEND>,
211+ # and no STUB_DIR for a single backend) is skipped. Relative paths resolve against
212+ # CMAKE_CURRENT_SOURCE_DIR, same as STUB_DIR.
203213# ~~~
204214function (tvm_ffi_configure_target target )
205215 if (NOT target )
@@ -225,9 +235,11 @@ function (tvm_ffi_configure_target target)
225235 STUB_DIR
226236 STUB_PKG
227237 STUB_PREFIX
228- STUB_TARGET
238+ STUB_DIR_PYTHON
239+ STUB_DIR_RUST
229240 )
230- set (tvm_ffi_arg_multiValueArgs)
241+ # STUB_TARGET is a list: one or more of `python` / `rust`.
242+ set (tvm_ffi_arg_multiValueArgs STUB_TARGET)
231243
232244 cmake_parse_arguments (
233245 tvm_ffi_arg_ "${tvm_ffi_arg_options} " "${tvm_ffi_arg_oneValueArgs} "
@@ -243,25 +255,35 @@ function (tvm_ffi_configure_target target)
243255 if (NOT DEFINED tvm_ffi_arg__STUB_INIT)
244256 set (tvm_ffi_arg__STUB_INIT OFF )
245257 endif ()
246- if (NOT DEFINED tvm_ffi_arg__STUB_TARGET)
258+ if (NOT DEFINED tvm_ffi_arg__STUB_TARGET OR NOT tvm_ffi_arg__STUB_TARGET )
247259 set (tvm_ffi_arg__STUB_TARGET "python" )
248260 endif ()
249261
250- # Validation
251- if (NOT tvm_ffi_arg__STUB_TARGET MATCHES "^(python|rust)$" )
252- message (
253- FATAL_ERROR
254- "tvm_ffi_configure_target(${target} ): STUB_TARGET must be 'python' or 'rust', got '${tvm_ffi_arg__STUB_TARGET} '."
255- )
256- endif ()
257- if ((NOT DEFINED tvm_ffi_arg__STUB_DIR) OR (NOT tvm_ffi_arg__STUB_DIR))
258- if (DEFINED tvm_ffi_arg__STUB_PKG OR DEFINED tvm_ffi_arg__STUB_PREFIX)
262+ list (LENGTH tvm_ffi_arg__STUB_TARGET tvm_ffi_stub_target_count)
263+
264+ # Validation: every requested backend must be 'python' or 'rust'.
265+ foreach (tvm_ffi_b IN LISTS tvm_ffi_arg__STUB_TARGET)
266+ if (NOT tvm_ffi_b MATCHES "^(python|rust)$" )
259267 message (
260268 FATAL_ERROR
261- "tvm_ffi_configure_target(${target} ): STUB_PKG/STUB_PREFIX require STUB_DIR to be set ."
269+ "tvm_ffi_configure_target(${target} ): STUB_TARGET entries must be 'python' or 'rust', got ' ${tvm_ffi_b} ' ."
262270 )
263271 endif ()
272+ endforeach ()
273+
274+ # A stub directory may be given globally (STUB_DIR) or, when STUB_TARGET lists several backends
275+ # that each write a different file tree, per backend (STUB_DIR_PYTHON / STUB_DIR_RUST).
276+ set (tvm_ffi_has_stub_dir OFF )
277+ if (DEFINED tvm_ffi_arg__STUB_DIR AND tvm_ffi_arg__STUB_DIR)
278+ set (tvm_ffi_has_stub_dir ON )
264279 endif ()
280+ foreach (tvm_ffi_B IN ITEMS PYTHON RUST)
281+ if (DEFINED tvm_ffi_arg__STUB_DIR_${tvm_ffi_B} AND tvm_ffi_arg__STUB_DIR_${tvm_ffi_B} )
282+ set (tvm_ffi_has_stub_dir ON )
283+ endif ()
284+ endforeach ()
285+
286+ # Validation
265287 if (NOT tvm_ffi_arg__STUB_INIT)
266288 if (DEFINED tvm_ffi_arg__STUB_PKG OR DEFINED tvm_ffi_arg__STUB_PREFIX)
267289 message (
@@ -270,15 +292,16 @@ function (tvm_ffi_configure_target target)
270292 )
271293 endif ()
272294 else ()
273- if (NOT DEFINED tvm_ffi_arg__STUB_DIR OR NOT tvm_ffi_arg__STUB_DIR )
295+ if (NOT tvm_ffi_has_stub_dir )
274296 message (
275- FATAL_ERROR "tvm_ffi_configure_target(${target} ): STUB_INIT=ON requires STUB_DIR to be set."
297+ FATAL_ERROR
298+ "tvm_ffi_configure_target(${target} ): STUB_INIT=ON requires a stub directory (STUB_DIR, or STUB_DIR_PYTHON/STUB_DIR_RUST)."
276299 )
277300 endif ()
278301 endif ()
279302
280- # STUB_PKG and STUB_PREFIX defaults
281- if (tvm_ffi_arg__STUB_INIT AND tvm_ffi_arg__STUB_DIR )
303+ # STUB_PKG and STUB_PREFIX defaults (shared across all listed backends)
304+ if (tvm_ffi_arg__STUB_INIT AND tvm_ffi_has_stub_dir )
282305 if (NOT DEFINED tvm_ffi_arg__STUB_PKG)
283306 if (DEFINED SKBUILD_PROJECT_NAME AND SKBUILD_PROJECT_NAME)
284307 set (tvm_ffi_arg__STUB_PKG "${SKBUILD_PROJECT_NAME} " )
@@ -352,39 +375,53 @@ function (tvm_ffi_configure_target target)
352375 endif ()
353376 endif ()
354377
355- if (DEFINED tvm_ffi_arg__STUB_DIR AND tvm_ffi_arg__STUB_DIR)
356- get_filename_component (
357- tvm_ffi_arg__STUB_DIR_ABS "${tvm_ffi_arg__STUB_DIR} " ABSOLUTE BASE_DIR
358- "${CMAKE_CURRENT_SOURCE_DIR } "
359- )
378+ if (tvm_ffi_has_stub_dir)
360379 find_package (
361380 Python3
362381 COMPONENTS Interpreter
363382 REQUIRED
364383 )
365- set (tvm_ffi_stub_cli_args "${tvm_ffi_arg__STUB_DIR_ABS} " --dlls $<TARGET_FILE :${target} >
366- --target "${tvm_ffi_arg__STUB_TARGET} "
367- )
368- if (tvm_ffi_arg__STUB_INIT)
369- list (
370- APPEND
371- tvm_ffi_stub_cli_args
372- --init-lib
373- ${target}
374- --init-pypkg
375- "${tvm_ffi_arg__STUB_PKG} "
376- --init-prefix
377- "${tvm_ffi_arg__STUB_PREFIX} "
378- )
379- endif ()
380- add_custom_command (
381- TARGET ${target}
382- POST_BUILD
383- COMMAND ${Python3_EXECUTABLE} -m tvm_ffi.stub.cli ${tvm_ffi_stub_cli_args}
384- COMMENT
385- "[COMMAND] Running: ${Python3_EXECUTABLE} -m tvm_ffi.stub.cli ${tvm_ffi_stub_cli_args} "
386- VERBATIM
387- )
384+ # One post-build stub command per backend. Each backend writes to its own STUB_DIR_<BACKEND>; a
385+ # single backend may instead use the shared STUB_DIR. A listed backend with no directory is
386+ # skipped.
387+ foreach (tvm_ffi_b IN LISTS tvm_ffi_arg__STUB_TARGET)
388+ string (TOUPPER "${tvm_ffi_b} " tvm_ffi_B)
389+ set (tvm_ffi_stub_dir "" )
390+ if (DEFINED tvm_ffi_arg__STUB_DIR_${tvm_ffi_B} AND tvm_ffi_arg__STUB_DIR_${tvm_ffi_B} )
391+ set (tvm_ffi_stub_dir "${tvm_ffi_arg__STUB_DIR_${tvm_ffi_B} }" )
392+ elseif (tvm_ffi_stub_target_count EQUAL 1)
393+ set (tvm_ffi_stub_dir "${tvm_ffi_arg__STUB_DIR} " )
394+ endif ()
395+ if (tvm_ffi_stub_dir)
396+ get_filename_component (
397+ tvm_ffi_arg__STUB_DIR_ABS "${tvm_ffi_stub_dir} " ABSOLUTE BASE_DIR
398+ "${CMAKE_CURRENT_SOURCE_DIR } "
399+ )
400+ set (tvm_ffi_stub_cli_args "${tvm_ffi_arg__STUB_DIR_ABS} " --dlls $<TARGET_FILE :${target} >
401+ --target "${tvm_ffi_b} "
402+ )
403+ if (tvm_ffi_arg__STUB_INIT)
404+ list (
405+ APPEND
406+ tvm_ffi_stub_cli_args
407+ --init-lib
408+ ${target}
409+ --init-pypkg
410+ "${tvm_ffi_arg__STUB_PKG} "
411+ --init-prefix
412+ "${tvm_ffi_arg__STUB_PREFIX} "
413+ )
414+ endif ()
415+ add_custom_command (
416+ TARGET ${target}
417+ POST_BUILD
418+ COMMAND ${Python3_EXECUTABLE} -m tvm_ffi.stub.cli ${tvm_ffi_stub_cli_args}
419+ COMMENT
420+ "[COMMAND] Running: ${Python3_EXECUTABLE} -m tvm_ffi.stub.cli ${tvm_ffi_stub_cli_args} "
421+ VERBATIM
422+ )
423+ endif ()
424+ endforeach ()
388425 endif ()
389426endfunction ()
390427
0 commit comments