Skip to content
Open
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Build artifacts from generating cmake files
cmake/build

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,43 @@ py::class_<MyClass>(m, "MyClass", DOC(MyClass))
...
```

### CMake
The `pybind11_mkdoc` CMake function is included to easily generate header for a pybind11 module when
compiling said module in CMake. The function generates the headers based on the arguments provided.
In addition, it add target dependencies so the pybind11-mkdoc header file is generated before
the pybind11 module. Also, it will automatically add the current binary directory to the pybind11
module's includes, so it can easily be included when compiling the module.

The required parameters are:
* OUTPUT - The name of the output file.
* PYBIND11_MODULE - The pybind11 module target that these docs will be used for.
* HEADERS - The header files to create docs for. These can be absoulte paths or relative to the
current source directory.

The optional parameters are:
* EXTRA_ARGS - This string argument will be added verbatim to the pybind11-mkdoc command.

Below is an exmaple of how it is used:
```cmake
# Find pybind11-mkdoc
# This assumes you have already run a find_package for Python.
execute_process(
COMMAND ${Python_EXECUTABLE} -c "import pybind11_mkdoc; print(pybind11_mkdoc.get_cmake_dir())"
OUTPUT_VARIABLE pybind11_mkdoc_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
find_package(pybind11_mkdoc REQUIRED CONFIG)

# Add the pybind11 module
pybind11_add_module(my_pybind11_module my_src_files.cc)
pybind11_mkdoc(
OUTPUT my_pybind11_module_docs.h
PYBIND11_MODULE my_pybind11_module
HEADERS
header_1.h
/absolute/path/to/header_2.h
)
```
## Limitations

This tool supports Linux and macOS for Python versions 3.8 to 3.11. Also, it
Expand Down
31 changes: 31 additions & 0 deletions cmake/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
cmake_minimum_required(VERSION 3.25)
project(pybind11_mkdoc)

include(CMakePackageConfigHelpers)

# Generate version file
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/pybind11_mkdocConfigVersion.cmake"
VERSION 2.6.2
COMPATIBILITY SameMajorVersion
)

# Create config file
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/pybind11_mkdocConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/pybind11_mkdocConfig.cmake"
INSTALL_DESTINATION pybind11_mkdoc
)

# Install config + version
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/pybind11_mkdocConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/pybind11_mkdocConfigVersion.cmake"
DESTINATION pybind11_mkdoc
)

# Install functions
install(FILES
"${CMAKE_CURRENT_SOURCE_DIR}/pybind11_mkdoc_functions.cmake"
DESTINATION pybind11_mkdoc
)
10 changes: 10 additions & 0 deletions cmake/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.PHONY: all

all : ../pybind11_mkdoc/share/cmake/pybind11_mkdoc

build: CMakeLists.txt
cmake -B build -S . -DCMAKE_INSTALL_PREFIX=../pybind11_mkdoc/share/cmake

../pybind11_mkdoc/share/cmake/pybind11_mkdoc: pybind11_mkdocConfig.cmake.in pybind11_mkdoc_functions.cmake build
cmake --build build
cmake --install build
6 changes: 6 additions & 0 deletions cmake/pybind11_mkdocConfig.cmake.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@PACKAGE_INIT@

include("${CMAKE_CURRENT_LIST_DIR}/pybind11_mkdoc_functions.cmake")

# If you export targets:
# include("${CMAKE_CURRENT_LIST_DIR}/my_pkgTargets.cmake")
68 changes: 68 additions & 0 deletions cmake/pybind11_mkdoc_functions.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# This function is used to run pybind11-mkdoc for the headers of a pybind11 module.
# In addition, this will also add target dependencies so the pybind11-mkdoc header
# file is generated before the pybind11 module. Also, this will automatically add
# the current binary directory to the pybind11 module's includes, so it can
# easily be included when compiling the module.
#
# The required parameters are:
# * OUTPUT - The name of the output file.
# * PYBIND11_MODULE - The pybind11 module target that these docs will be used for.
# * HEADERS - The header files to create docs for. These can be absoulte paths or relative to the
# current source directory.
#
# The optional parameters are:
# * EXTRA_ARGS - This string argument will be added verbatim to the pybind11-mkdoc command.
#
# Example usage:
# pybind11_add_module(my_pybind11_module src/my_pybind11_module.cc)
# pybind11_mkdoc(
# OUTPUT my_pybind11_module_doc.h
# PYBIND11_MODULE my_pybind11_module
# HEADERS
# include/my_header_1.h
# /absolute/path/to/header.h
# )
function (pybind11_mkdoc)
set(options)
set(oneValueArgs OUTPUT PYBIND11_MODULE EXTRA_ARGS)
set(multiValueArgs HEADERS)
cmake_parse_arguments(PARSE_ARGV 0 arg
"${options}" "${oneValueArgs}" "${multiValueArgs}"
)

# Include directories for the pybind11 module
set(prop "$<TARGET_PROPERTY:${arg_PYBIND11_MODULE},INCLUDE_DIRECTORIES>")

# Remove the header file from the list
set(HEADERS "")
# Run through all the other arguments.
foreach(header ${arg_HEADERS})
if(IS_ABSOLUTE ${header})
# If it is an absolute path, then add it as is.
list(APPEND HEADERS ${header})
else()
# Otherwise, assume it is relative to the current source directory.
list(APPEND HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/${header}")
endif()
endforeach()

# Add a custom target and command for the full header file location that runs pybind11-mkdoc
# We automatically include the source directory and build/include
add_custom_target(
pybind11_mkdoc_${arg_OUTPUT}
DEPENDS ${arg_OUTPUT}
)

add_custom_command(
OUTPUT ${arg_OUTPUT}
COMMAND ${Python_EXECUTABLE} -m pybind11_mkdoc ${arg_EXTRA_ARGS} -o ${arg_OUTPUT} "$<$<BOOL:${prop}>:-I$<JOIN:${prop},;-I>>" ${HEADERS}
DEPENDS ${HEADERS}
COMMAND_EXPAND_LISTS
)

# Add a dependency so that the pybind11-mkdoc command runs before we try to compile the pybind11 module
add_dependencies(${arg_PYBIND11_MODULE} pybind11_mkdoc_${arg_OUTPUT})

# Add the current binary directory to the pybind11 module so it can be included easily
target_include_directories(${arg_PYBIND11_MODULE} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
endfunction()
13 changes: 13 additions & 0 deletions pybind11_mkdoc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import argparse
import os
import re
from pathlib import Path

from pybind11_mkdoc.mkdoc_lib import mkdoc

Expand Down Expand Up @@ -73,6 +74,18 @@ def _append_definition(args: list, definition: str):
pass


def get_cmake_dir() -> str:
"""
Return the path to the pybind11_mkdoc CMake module directory.
"""
cmake_installed_path = Path(__file__).parent / "share" / "cmake" / "pybind11_mkdoc"
if cmake_installed_path.exists():
return str(cmake_installed_path)

msg = "pybind11_mkdoc cmake files not found."
raise ImportError(msg)


def main():
"""
Entry point for the `pybind11_mkdoc` console script.
Expand Down
2 changes: 1 addition & 1 deletion pybind11_mkdoc/mkdoc_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ def process_comment(comment):
rm_lines.append(k)
add_to = (t_params, name)
elif m := return_re.match(line):
text, = m.groups()
(text,) = m.groups()
ret.append(text.strip())
add_to = (ret, len(ret) - 1)
rm_lines.append(k)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() #######
####### Any changes to this file will be overwritten by the next CMake run ####
####### The input file was pybind11_mkdocConfig.cmake.in ########

get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../" ABSOLUTE)

macro(set_and_check _var _file)
set(${_var} "${_file}")
if(NOT EXISTS "${_file}")
message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !")
endif()
endmacro()

macro(check_required_components _NAME)
foreach(comp ${${_NAME}_FIND_COMPONENTS})
if(NOT ${_NAME}_${comp}_FOUND)
if(${_NAME}_FIND_REQUIRED_${comp})
set(${_NAME}_FOUND FALSE)
endif()
endif()
endforeach()
endmacro()

####################################################################################

include("${CMAKE_CURRENT_LIST_DIR}/pybind11_mkdoc_functions.cmake")

# If you export targets:
# include("${CMAKE_CURRENT_LIST_DIR}/my_pkgTargets.cmake")
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# This is a basic version file for the Config-mode of find_package().
# It is used by write_basic_package_version_file() as input file for configure_file()
# to create a version-file which can be installed along a config.cmake file.
#
# The created file sets PACKAGE_VERSION_EXACT if the current version string and
# the requested version string are exactly the same and it sets
# PACKAGE_VERSION_COMPATIBLE if the current version is >= requested version,
# but only if the requested major version is the same as the current one.
# The variable CVF_VERSION must be set before calling configure_file().


set(PACKAGE_VERSION "2.6.2")

if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()

if("2.6.2" MATCHES "^([0-9]+)\\.")
set(CVF_VERSION_MAJOR "${CMAKE_MATCH_1}")
if(NOT CVF_VERSION_MAJOR VERSION_EQUAL 0)
string(REGEX REPLACE "^0+" "" CVF_VERSION_MAJOR "${CVF_VERSION_MAJOR}")
endif()
else()
set(CVF_VERSION_MAJOR "2.6.2")
endif()

if(PACKAGE_FIND_VERSION_RANGE)
# both endpoints of the range must have the expected major version
math (EXPR CVF_VERSION_MAJOR_NEXT "${CVF_VERSION_MAJOR} + 1")
if (NOT PACKAGE_FIND_VERSION_MIN_MAJOR STREQUAL CVF_VERSION_MAJOR
OR ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND NOT PACKAGE_FIND_VERSION_MAX_MAJOR STREQUAL CVF_VERSION_MAJOR)
OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND NOT PACKAGE_FIND_VERSION_MAX VERSION_LESS_EQUAL CVF_VERSION_MAJOR_NEXT)))
set(PACKAGE_VERSION_COMPATIBLE FALSE)
elseif(PACKAGE_FIND_VERSION_MIN_MAJOR STREQUAL CVF_VERSION_MAJOR
AND ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND PACKAGE_VERSION VERSION_LESS_EQUAL PACKAGE_FIND_VERSION_MAX)
OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION_MAX)))
set(PACKAGE_VERSION_COMPATIBLE TRUE)
else()
set(PACKAGE_VERSION_COMPATIBLE FALSE)
endif()
else()
if(PACKAGE_FIND_VERSION_MAJOR STREQUAL CVF_VERSION_MAJOR)
set(PACKAGE_VERSION_COMPATIBLE TRUE)
else()
set(PACKAGE_VERSION_COMPATIBLE FALSE)
endif()

if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION)
set(PACKAGE_VERSION_EXACT TRUE)
endif()
endif()
endif()


# if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it:
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "" OR "8" STREQUAL "")
return()
endif()

# check that the installed version has the same 32/64bit-ness as the one which is currently searching:
if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "8")
math(EXPR installedBits "8 * 8")
set(PACKAGE_VERSION "${PACKAGE_VERSION} (${installedBits}bit)")
set(PACKAGE_VERSION_UNSUITABLE TRUE)
endif()
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# This function is used to run pybind11-mkdoc for the headers of a pybind11 module.
# In addition, this will also add target dependencies so the pybind11-mkdoc header
# file is generated before the pybind11 module. Also, this will automatically add
# the current binary directory to the pybind11 module's includes, so it can
# easily be included when compiling the module.
#
# The required parameters are:
# * OUTPUT - The name of the output file.
# * PYBIND11_MODULE - The pybind11 module target that these docs will be used for.
# * HEADERS - The header files to create docs for. These can be absoulte paths or relative to the
# current source directory.
#
# The optional parameters are:
# * EXTRA_ARGS - This string argument will be added verbatim to the pybind11-mkdoc command.
#
# Example usage:
# pybind11_add_module(my_pybind11_module src/my_pybind11_module.cc)
# pybind11_mkdoc(
# OUTPUT my_pybind11_module_doc.h
# PYBIND11_MODULE my_pybind11_module
# HEADERS
# include/my_header_1.h
# /absolute/path/to/header.h
# )
function (pybind11_mkdoc)
set(options)
set(oneValueArgs OUTPUT PYBIND11_MODULE EXTRA_ARGS)
set(multiValueArgs HEADERS)
cmake_parse_arguments(PARSE_ARGV 0 arg
"${options}" "${oneValueArgs}" "${multiValueArgs}"
)

# Include directories for the pybind11 module
set(prop "$<TARGET_PROPERTY:${arg_PYBIND11_MODULE},INCLUDE_DIRECTORIES>")

# Remove the header file from the list
set(HEADERS "")
# Run through all the other arguments.
foreach(header ${arg_HEADERS})
if(IS_ABSOLUTE ${header})
# If it is an absolute path, then add it as is.
list(APPEND HEADERS ${header})
else()
# Otherwise, assume it is relative to the current source directory.
list(APPEND HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/${header}")
endif()
endforeach()

# Add a custom target and command for the full header file location that runs pybind11-mkdoc
# We automatically include the source directory and build/include
add_custom_target(
pybind11_mkdoc_${arg_OUTPUT}
DEPENDS ${arg_OUTPUT}
)

add_custom_command(
OUTPUT ${arg_OUTPUT}
COMMAND ${Python_EXECUTABLE} -m pybind11_mkdoc ${arg_EXTRA_ARGS} -o ${arg_OUTPUT} "$<$<BOOL:${prop}>:-I$<JOIN:${prop},;-I>>" ${HEADERS}
DEPENDS ${HEADERS}
COMMAND_EXPAND_LISTS
)

add_dependencies(${arg_PYBIND11_MODULE} pybind11_mkdoc_${arg_OUTPUT})

# Add the current binary directory to the pybind11 module so it can be included easily
target_include_directories(${arg_PYBIND11_MODULE} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
endfunction()
Loading