From 4d38fbe331dfb7b5d30e96d2df9ae6dee05423d2 Mon Sep 17 00:00:00 2001 From: John Freeman Date: Thu, 23 Jul 2026 20:55:18 +0000 Subject: [PATCH 1/7] JCF: pure save-my-work commit --- cmake/DAQ.cmake | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/cmake/DAQ.cmake b/cmake/DAQ.cmake index c77bb31..fcc0596 100644 --- a/cmake/DAQ.cmake +++ b/cmake/DAQ.cmake @@ -913,23 +913,26 @@ function(daq_add_python_bindings) find_program(PYBIND11_STUBGEN pybind11-stubgen) if(PYBIND11_STUBGEN) - execute_process( - COMMAND ${PYBIND11_STUBGEN} -o ${PROJECT_NAME}/python ${PROJECT_NAME} - RESULT_VARIABLE retval - ERROR_VARIABLE errmsg - ) + set(PRIMARY_STUB_FILE ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME}/__init__.pyi) + add_custom_command( + OUTPUT + ${PRIMARY_STUB_FILE} + COMMAND PYTHONPATH ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME} ${PYBIND11_STUBGEN} -o ${PROJECT_NAME}/python ${DEFAULT_LINK_LIBRARY} + DEPENDS ${libname} + ) + + add_custom_target(${PROJECT_NAME}_pybind11_stubs DEPENDS ${PRIMARY_STUB_FILE}) + add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}_pybind11_stubs) + endif() + + +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME}/ DESTINATION ${CMAKE_INSTALL_PYTHONDIR} OPTIONAL FILES_MATCHING PATTERN "*.pyi" PATTERN "py.typed") - if(retval) - message(WARNING - "pybind11-stubgen failed for ${PROJECT_NAME}.\n" - "${errmsg}\n" - "The Python bindings were built successfully, but pybind11-stubgen was unable to generate stubs.") - endif() endif() + _daq_define_exportname() install(TARGETS ${libname} EXPORT ${DAQ_PROJECT_EXPORTNAME} DESTINATION ${destdir}) - install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME}/ DESTINATION ${destdir} OPTIONAL FILES_MATCHING PATTERN "*.pyi" PATTERN "py.typed") set(DAQ_PROJECT_INSTALLS_TARGETS true PARENT_SCOPE) endfunction() From f6d9425eb8c892c5b27e4835d9c7da2026b3411d Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 24 Jul 2026 12:22:17 -0500 Subject: [PATCH 2/7] JCF: Issue #174: repair broken state of code from the saved-my-work commit * Removed unnecessary / flawed attempt to forward PYTHONPATH to the pybind11-stubgen call * Added "ALL" to add_custom_target so the generation of *.pyi files will actually occur * Install the *.pyi files --- cmake/DAQ.cmake | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/cmake/DAQ.cmake b/cmake/DAQ.cmake index fcc0596..33e2a78 100644 --- a/cmake/DAQ.cmake +++ b/cmake/DAQ.cmake @@ -910,29 +910,27 @@ function(daq_add_python_bindings) message(FATAL_ERROR "ERROR: No source files found for python library: ${libname}.") endif() + _daq_define_exportname() + find_program(PYBIND11_STUBGEN pybind11-stubgen) if(PYBIND11_STUBGEN) - set(PRIMARY_STUB_FILE ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME}/__init__.pyi) - add_custom_command( - OUTPUT - ${PRIMARY_STUB_FILE} - COMMAND PYTHONPATH ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME} ${PYBIND11_STUBGEN} -o ${PROJECT_NAME}/python ${DEFAULT_LINK_LIBRARY} - DEPENDS ${libname} - ) - - add_custom_target(${PROJECT_NAME}_pybind11_stubs DEPENDS ${PRIMARY_STUB_FILE}) - add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}_pybind11_stubs) - endif() - + set(PRIMARY_STUB_FILE ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME}/__init__.pyi) + add_custom_command( + OUTPUT + ${PRIMARY_STUB_FILE} + COMMAND ${PYBIND11_STUBGEN} -o ${CMAKE_CURRENT_BINARY_DIR}/python ${DEFAULT_LINK_LIBRARY} + DEPENDS ${libname} + ) -install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME}/ DESTINATION ${CMAKE_INSTALL_PYTHONDIR} OPTIONAL FILES_MATCHING PATTERN "*.pyi" PATTERN "py.typed") + add_custom_target(${PROJECT_NAME}_pybind11_stubs ALL DEPENDS ${PRIMARY_STUB_FILE}) + add_dependencies(${PROJECT_NAME}_pybind11_stubs ${libname}) + install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME}/ DESTINATION ${destdir} FILES_MATCHING PATTERN "*.pyi" PATTERN "py.typed") endif() - - _daq_define_exportname() install(TARGETS ${libname} EXPORT ${DAQ_PROJECT_EXPORTNAME} DESTINATION ${destdir}) + set(DAQ_PROJECT_INSTALLS_TARGETS true PARENT_SCOPE) endfunction() From 09f89ad8e396387ba79af303bdbe18407a5ced8c Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 24 Jul 2026 12:54:39 -0500 Subject: [PATCH 3/7] JCF: Issue #174: since in Spack installations (as opposed to work area installations) pybind11-stubgen fails because it can't Python-import a package, copy the full Python code over to the build area --- cmake/DAQ.cmake | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cmake/DAQ.cmake b/cmake/DAQ.cmake index 33e2a78..988969c 100644 --- a/cmake/DAQ.cmake +++ b/cmake/DAQ.cmake @@ -915,6 +915,16 @@ function(daq_add_python_bindings) find_program(PYBIND11_STUBGEN pybind11-stubgen) if(PYBIND11_STUBGEN) + + # Usually we copy the Python code straight from the source area to + # the install area, but since pybind11-stubgen calls "import " we'll need this code available in the build area + + file(COPY + ${CMAKE_CURRENT_SOURCE_DIR}/python/${PROJECT_NAME} + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/python + ) + set(PRIMARY_STUB_FILE ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME}/__init__.pyi) add_custom_command( OUTPUT From 4ec41ccdf25afbdf7424df250f211bec30573fa0 Mon Sep 17 00:00:00 2001 From: Dune Date: Fri, 24 Jul 2026 13:06:17 -0500 Subject: [PATCH 4/7] JCF: Issue #174: also for the import to work, need to forward the standard PYTHONPATH + the local package Python directory --- cmake/DAQ.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/DAQ.cmake b/cmake/DAQ.cmake index 988969c..faee56d 100644 --- a/cmake/DAQ.cmake +++ b/cmake/DAQ.cmake @@ -929,7 +929,7 @@ function(daq_add_python_bindings) add_custom_command( OUTPUT ${PRIMARY_STUB_FILE} - COMMAND ${PYBIND11_STUBGEN} -o ${CMAKE_CURRENT_BINARY_DIR}/python ${DEFAULT_LINK_LIBRARY} + COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}/python:$ENV{PYTHONPATH} ${PYBIND11_STUBGEN} -o ${CMAKE_CURRENT_BINARY_DIR}/python ${DEFAULT_LINK_LIBRARY} DEPENDS ${libname} ) From 340262c1e9e08b75d7deb1c94fc7eee382380c44 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 24 Jul 2026 15:47:42 -0500 Subject: [PATCH 5/7] JCF: Issue #174: add logic so pybind11-stubgen stubs are also correctly generated for DAL libraries. The work in this commit has brought up ideas for improvement addressed by my TODOs --- cmake/DAQ.cmake | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/cmake/DAQ.cmake b/cmake/DAQ.cmake index faee56d..cb2d601 100644 --- a/cmake/DAQ.cmake +++ b/cmake/DAQ.cmake @@ -905,6 +905,9 @@ function(daq_add_python_bindings) add_dependencies( ${libname} ${PRE_BUILD_STAGE_DONE_TRGT}) + # JCF, Jul-24-2026, TODO: determine if we should have the output + # dir be python/${PROJECT_NAME}_DAL if it's a DAL library + _daq_set_target_output_dirs( ${libname} python/${PROJECT_NAME} ) else() message(FATAL_ERROR "ERROR: No source files found for python library: ${libname}.") @@ -921,22 +924,39 @@ function(daq_add_python_bindings) # of package>" we'll need this code available in the build area file(COPY - ${CMAKE_CURRENT_SOURCE_DIR}/python/${PROJECT_NAME} - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/python + ${srcdir}/ + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/python/${DEFAULT_LINK_LIBRARY} ) set(PRIMARY_STUB_FILE ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME}/__init__.pyi) - add_custom_command( - OUTPUT - ${PRIMARY_STUB_FILE} - COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}/python:$ENV{PYTHONPATH} ${PYBIND11_STUBGEN} -o ${CMAKE_CURRENT_BINARY_DIR}/python ${DEFAULT_LINK_LIBRARY} - DEPENDS ${libname} - ) + + # JCF, Jul-24-2026: see my TODO comment above, from this same day + if(NOT ${BINDOPTS_DAL}) + + add_custom_command( + OUTPUT + ${PRIMARY_STUB_FILE} + COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}/python:$ENV{PYTHONPATH} ${PYBIND11_STUBGEN} -o ${CMAKE_CURRENT_BINARY_DIR}/python ${DEFAULT_LINK_LIBRARY} + DEPENDS ${libname} + ) + + else() + + add_custom_command( + OUTPUT + ${PRIMARY_STUB_FILE} + COMMAND ln -s ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME}/${libname}.so ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME}_dal/${libname}.so + COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}/python:$ENV{PYTHONPATH} ${PYBIND11_STUBGEN} -o ${CMAKE_CURRENT_BINARY_DIR}/python ${DEFAULT_LINK_LIBRARY} + DEPENDS ${libname} + ) + endif() + + install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/python/${DEFAULT_LINK_LIBRARY}/ DESTINATION ${destdir} FILES_MATCHING PATTERN "*.pyi" PATTERN "py.typed") add_custom_target(${PROJECT_NAME}_pybind11_stubs ALL DEPENDS ${PRIMARY_STUB_FILE}) add_dependencies(${PROJECT_NAME}_pybind11_stubs ${libname}) - install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME}/ DESTINATION ${destdir} FILES_MATCHING PATTERN "*.pyi" PATTERN "py.typed") + endif() install(TARGETS ${libname} EXPORT ${DAQ_PROJECT_EXPORTNAME} DESTINATION ${destdir}) From 1f82a768e546935da82278f77d656f25626826d4 Mon Sep 17 00:00:00 2001 From: Dune Date: Fri, 24 Jul 2026 15:59:57 -0500 Subject: [PATCH 6/7] JCF: Issue #174: before doing the soft link, avoid collisions if a soft link is already there --- cmake/DAQ.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/DAQ.cmake b/cmake/DAQ.cmake index cb2d601..54b9560 100644 --- a/cmake/DAQ.cmake +++ b/cmake/DAQ.cmake @@ -945,7 +945,7 @@ function(daq_add_python_bindings) add_custom_command( OUTPUT ${PRIMARY_STUB_FILE} - COMMAND ln -s ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME}/${libname}.so ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME}_dal/${libname}.so + COMMAND ln -sf ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME}/${libname}.so ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME}_dal/${libname}.so COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}/python:$ENV{PYTHONPATH} ${PYBIND11_STUBGEN} -o ${CMAKE_CURRENT_BINARY_DIR}/python ${DEFAULT_LINK_LIBRARY} DEPENDS ${libname} ) From 45c4894ec904b5b192791d8625ec933b6df54c36 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 4 Aug 2026 11:01:57 -0500 Subject: [PATCH 7/7] JCF: Issue #174: add a GENERATE_STUBS option where you need to affirmatively indicate to daq_add_python_bindings that you wish to generate pybind11-stubgen stubs --- cmake/DAQ.cmake | 74 ++++++++++++++++++++++++++----------------------- docs/README.md | 7 ++++- 2 files changed, 46 insertions(+), 35 deletions(-) diff --git a/cmake/DAQ.cmake b/cmake/DAQ.cmake index 54b9560..5a10999 100644 --- a/cmake/DAQ.cmake +++ b/cmake/DAQ.cmake @@ -800,7 +800,7 @@ endfunction() #################################################################################################### # daq_add_python_bindings: # Usage: -# daq_add_python_bindings( ... [DAL] [LINK_LIBRARIES ...]) +# daq_add_python_bindings( ... [DAL] [GENERATE_STUBS] [LINK_LIBRARIES ...]) # # daq_add_python_bindings is designed to produce a library providing a @@ -832,9 +832,12 @@ endfunction() # python/${PROJECT_NAME}_dal/__init__.py file which imports # _daq_${PROJECT_NAME}_dal_py.so +# GENERATE_STUBS is used if you want daq_add_python_bindings to call +# pybind11-stubgen to generate *.pyi files off of the Python bindings + function(daq_add_python_bindings) - cmake_parse_arguments(BINDOPTS "DAL" "" "LINK_LIBRARIES" ${ARGN}) + cmake_parse_arguments(BINDOPTS "DAL;GENERATE_STUBS" "" "LINK_LIBRARIES" ${ARGN}) if (NOT ${BINDOPTS_DAL}) set(libname _daq_${PROJECT_NAME}_py) @@ -915,48 +918,51 @@ function(daq_add_python_bindings) _daq_define_exportname() - find_program(PYBIND11_STUBGEN pybind11-stubgen) - - if(PYBIND11_STUBGEN) + if(BINDOPTS_GENERATE_STUBS) - # Usually we copy the Python code straight from the source area to - # the install area, but since pybind11-stubgen calls "import " we'll need this code available in the build area - - file(COPY - ${srcdir}/ - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/python/${DEFAULT_LINK_LIBRARY} - ) + find_program(PYBIND11_STUBGEN pybind11-stubgen) - set(PRIMARY_STUB_FILE ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME}/__init__.pyi) - - # JCF, Jul-24-2026: see my TODO comment above, from this same day - if(NOT ${BINDOPTS_DAL}) + if (PYBIND11_STUBGEN) + + # Usually we copy the Python code straight from the source area to + # the install area, but since pybind11-stubgen calls "import " we'll need this code available in the build area - add_custom_command( - OUTPUT - ${PRIMARY_STUB_FILE} - COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}/python:$ENV{PYTHONPATH} ${PYBIND11_STUBGEN} -o ${CMAKE_CURRENT_BINARY_DIR}/python ${DEFAULT_LINK_LIBRARY} - DEPENDS ${libname} + file(COPY + ${srcdir}/ + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/python/${DEFAULT_LINK_LIBRARY} ) - else() + set(PRIMARY_STUB_FILE ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME}/__init__.pyi) - add_custom_command( - OUTPUT - ${PRIMARY_STUB_FILE} - COMMAND ln -sf ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME}/${libname}.so ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME}_dal/${libname}.so - COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}/python:$ENV{PYTHONPATH} ${PYBIND11_STUBGEN} -o ${CMAKE_CURRENT_BINARY_DIR}/python ${DEFAULT_LINK_LIBRARY} - DEPENDS ${libname} - ) - endif() + # JCF, Jul-24-2026: see my TODO comment above, from this same day + if(NOT ${BINDOPTS_DAL}) - install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/python/${DEFAULT_LINK_LIBRARY}/ DESTINATION ${destdir} FILES_MATCHING PATTERN "*.pyi" PATTERN "py.typed") + add_custom_command( + OUTPUT + ${PRIMARY_STUB_FILE} + COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}/python:$ENV{PYTHONPATH} ${PYBIND11_STUBGEN} -o ${CMAKE_CURRENT_BINARY_DIR}/python ${DEFAULT_LINK_LIBRARY} + DEPENDS ${libname} + ) - add_custom_target(${PROJECT_NAME}_pybind11_stubs ALL DEPENDS ${PRIMARY_STUB_FILE}) - add_dependencies(${PROJECT_NAME}_pybind11_stubs ${libname}) + else() + add_custom_command( + OUTPUT + ${PRIMARY_STUB_FILE} + COMMAND ln -sf ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME}/${libname}.so ${CMAKE_CURRENT_BINARY_DIR}/python/${PROJECT_NAME}_dal/${libname}.so + COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}/python:$ENV{PYTHONPATH} ${PYBIND11_STUBGEN} -o ${CMAKE_CURRENT_BINARY_DIR}/python ${DEFAULT_LINK_LIBRARY} + DEPENDS ${libname} + ) + endif() + install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/python/${DEFAULT_LINK_LIBRARY}/ DESTINATION ${destdir} FILES_MATCHING PATTERN "*.pyi" PATTERN "py.typed") + + add_custom_target(${PROJECT_NAME}_pybind11_stubs ALL DEPENDS ${PRIMARY_STUB_FILE}) + add_dependencies(${PROJECT_NAME}_pybind11_stubs ${libname}) + else() + message(FATAL_ERROR "GENERATE_STUBS passed as option, but pybind11-stubgen was not found") + endif() endif() install(TARGETS ${libname} EXPORT ${DAQ_PROJECT_EXPORTNAME} DESTINATION ${destdir}) diff --git a/docs/README.md b/docs/README.md index 5153c17..49b3074 100644 --- a/docs/README.md +++ b/docs/README.md @@ -273,7 +273,7 @@ Its compilation will be done automatically, i.e. there is no need to add `*.pb.c ### daq_add_python_bindings: Usage: ``` -daq_add_python_bindings( ... [DAL] [LINK_LIBRARIES ...]) +daq_add_python_bindings( ... [DAL] [GENERATE_STUBS] [LINK_LIBRARIES ...]) ``` `daq_add_python_bindings` is designed to produce a library providing a Python @@ -300,6 +300,11 @@ _With_ the `DAL` option, the library shared object will be `_daq_${PROJECT_NAME} and will be installed in the `python/${PROJECT_NAME}_dal` directory. Here, you need a `python/${PROJECT_NAME}_dal/__init__.py` file which imports `_daq_${PROJECT_NAME}_dal_py.so`. +`GENERATE_STUBS` is used if you want `daq_add_python_bindings` to call +`pybind11-stubgen` to generate `*.pyi` files off of the Python +bindings + + ### daq_add_plugin: Usage: ```