Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0aa3cae
Adding cmake function. See https://github.com/pybind/pybind11_mkdoc/i…
leakec Feb 22, 2026
1019bb5
Adding files to create config and versions.
leakec Feb 22, 2026
9e2fd54
Adding build artifacts to .gitignore.
leakec Feb 22, 2026
a66967e
Adding makefile to make it easy to re-generate when necessary.
leakec Feb 22, 2026
e1e6dd2
Adding these files to version control, since users will often install…
leakec Feb 22, 2026
d82c114
Moving things around to make installing easier.
leakec Feb 22, 2026
5702898
Adding method to get the cmake directory.
leakec Feb 22, 2026
baf6322
Some minor tweaks to get this working in more cases.
leakec Feb 22, 2026
3d83ce0
Adding simple unit test to ensure cmake is working.
leakec Feb 22, 2026
25a4d66
Adding test for EXTRA_ARGS as well.
leakec Feb 22, 2026
9e1ae61
Ran ruff and fixed errors.
leakec Feb 22, 2026
a918886
Fixing name.
leakec Feb 22, 2026
c458953
Adding docs.
leakec Feb 22, 2026
b09b362
Trying to sync test group first to ensure dependencies are installed.
leakec Feb 22, 2026
c96a17c
Trying again, but just adding pybind11 to --with.
leakec Feb 22, 2026
8b70ad0
Trying to pass in Python version directly to help cmake in the runners.
leakec Feb 22, 2026
b718498
Trying removing the --with now that I've got the Python path sorted i…
leakec Feb 22, 2026
1c39ee5
Update cmake/CMakeLists.txt
leakec Apr 9, 2026
5a9c4d8
Resolving some MR comments.
leakec Apr 9, 2026
268248e
Removing this unecessary line (as of pybind11 3.0).
leakec Apr 9, 2026
58a885a
chore: add a presets file
henryiii Apr 9, 2026
6a65608
fix: install target
henryiii Apr 9, 2026
bac011c
Removed Makefile, added README.md, and updated pybind11_mkdoc_functions.
leakec Apr 10, 2026
af84a4b
Changing this to use workflow.
leakec Apr 10, 2026
fe5a0e3
Update cmake/CMakePresets.json
leakec Apr 10, 2026
4a66658
Update cmake/CMakePresets.json
leakec Apr 10, 2026
e9fd2d6
Added back in default-install.
leakec Apr 10, 2026
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...4.3)
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
)
31 changes: 31 additions & 0 deletions cmake/CMakePresets.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"version": 6,
"configurePresets": [
{
"name": "default",
"displayName": "Default configure",
"binaryDir": "build",
"cacheVariables": {
"CMAKE_INSTALL_PREFIX": "../pybind11_mkdoc/share/cmake"
}
}
],
"buildPresets": [
{
"name": "default-install",
"displayName": "Default install",
"configurePreset": "default",
"targets": ["install"]
}
],
"workflowPresets": [
{
"name": "default",
"displayName": "Configure, build and install",
"steps": [
{ "type": "configure", "name": "default" },
{ "type": "build", "name": "default-install" }
]
}
]
}
5 changes: 5 additions & 0 deletions cmake/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Run
```bash
cmake --workflow default
```
to build and install the cmake files. This should be done whenever `pybind11_mkdoc_functions.cmake` is modified.
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() -> Path:
"""
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 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,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()
Loading