diff --git a/cmake/nanobind-config.cmake b/cmake/nanobind-config.cmake index bc680e57..fbf52227 100644 --- a/cmake/nanobind-config.cmake +++ b/cmake/nanobind-config.cmake @@ -590,7 +590,7 @@ endfunction() # --------------------------------------------------------------------------- function (nanobind_add_stub name) - cmake_parse_arguments(PARSE_ARGV 1 ARG "VERBOSE;INCLUDE_PRIVATE;EXCLUDE_DOCSTRINGS;INSTALL_TIME;RECURSIVE;EXCLUDE_FROM_ALL" "MODULE;COMPONENT;PATTERN_FILE;OUTPUT_PATH" "PYTHON_PATH;DEPENDS;MARKER_FILE;OUTPUT") + cmake_parse_arguments(PARSE_ARGV 1 ARG "VERBOSE;INCLUDE_PRIVATE;EXCLUDE_DOCSTRINGS;INSTALL_TIME;RECURSIVE;EXCLUDE_FROM_ALL" "MODULE;COMPONENT;PATTERN_FILE;OUTPUT_PATH" "PYTHON_PATH;LIB_PATH;DEPENDS;MARKER_FILE;OUTPUT") if (EXISTS ${NB_DIR}/src/stubgen.py) set(NB_STUBGEN "${NB_DIR}/src/stubgen.py") @@ -622,6 +622,10 @@ function (nanobind_add_stub name) list(APPEND NB_STUBGEN_ARGS -i "${PYTHON_PATH}") endforeach() + foreach (LIB_PATH IN LISTS ARG_LIB_PATH) + list(APPEND NB_STUBGEN_ARGS -L "${LIB_PATH}") + endforeach() + if (ARG_PATTERN_FILE) list(APPEND NB_STUBGEN_ARGS -p "${ARG_PATTERN_FILE}") endif() diff --git a/docs/api_cmake.rst b/docs/api_cmake.rst index 13b492e5..5af7f2ea 100644 --- a/docs/api_cmake.rst +++ b/docs/api_cmake.rst @@ -492,6 +492,14 @@ Nanobind's CMake tooling includes a convenience command to interface with the generation. Otherwise, generator expressions should not be used. Optional. + * - ``LIB_PATH`` + - List of search paths that should be considered when searching for + shared libraries. This can be useful when the python module being + imported depends on shared libraries that are not in the default + search path. The paths are relative to ``CMAKE_CURRENT_BINARY_DIR`` + for build-time stub generation and relative to + ``CMAKE_INSTALL_PREFIX`` for install-time stub generation. Optional. + * - ``DEPENDS`` - Any targets listed here will be marked as a dependencies. This should generally be used to list the target names of one or more prior diff --git a/docs/typing.rst b/docs/typing.rst index 92b5af55..e41ac3c6 100644 --- a/docs/typing.rst +++ b/docs/typing.rst @@ -539,8 +539,9 @@ The program has the following command line options: .. code-block:: text - usage: python -m nanobind.stubgen [-h] [-o FILE] [-O PATH] [-i PATH] [-m MODULE] - [-r] [-M FILE] [-P] [-D] [-q] + usage: python -m nanobind.stubgen [-h] [-o FILE] [-O PATH] [-i PATH] + [-L PATH] [-m MODULE] [-r] [-M FILE] [-P] + [-D] [-q] Generate stubs for nanobind-based extensions. @@ -550,6 +551,8 @@ The program has the following command line options: -O PATH, --output-dir PATH write generated stubs to the specified directory -i PATH, --import PATH add the directory to the Python import path (can specify multiple times) + -L PATH, --lib-path PATH add directory to shared library search path (can + specify multiple times) -m MODULE, --module MODULE generate a stub for the specified module (can specify multiple times) -r, --recursive recursively process submodules diff --git a/src/stubgen.py b/src/stubgen.py index 61a650f3..25745504 100755 --- a/src/stubgen.py +++ b/src/stubgen.py @@ -1263,6 +1263,16 @@ def parse_options(args: List[str]) -> argparse.Namespace: help="add the directory to the Python import path (can specify multiple times)", ) + parser.add_argument( + "-L", + "--lib-path", + action="append", + metavar="PATH", + dest="lib_paths", + default=[], + help="add directory to shared library search path (can specify multiple times)" + ) + parser.add_argument( "-m", "--module", @@ -1396,6 +1406,7 @@ def add_pattern(query: str, lines: List[str]): def main(args: Optional[List[str]] = None) -> None: import sys + import os # Ensure that the current directory is on the path if "" not in sys.path and "." not in sys.path: @@ -1416,6 +1427,16 @@ def main(args: Optional[List[str]] = None) -> None: for i in opt.imports: sys.path.insert(0, i) + if os.name == 'nt': + for lib_path in opt.lib_paths: + os.add_dll_directory(lib_path) + else: + lib_env = "DYLD_LIBRARY_PATH" if sys.platform == "darwin" else "LD_LIBRARY_PATH" + old_value = os.environ.get(lib_env, "") + paths_str = ":".join(opt.lib_paths) + if paths_str: + os.environ[lib_env] = f"{paths_str}:{old_value}" if old_value else paths_str + for i, mod in enumerate(opt.modules): if not opt.quiet: if i > 0: