if(NOT BUILD_DOCS) return() endif() # Prefer the sphinx-build that lives in the conda env where myst-parser / # pydata-sphinx-theme / breathe / exhale are installed. Override on the # CMake command line: -DSPHINX_BUILD=/path/to/sphinx-build find_program(SPHINX_BUILD NAMES sphinx-build DOC "Path to sphinx-build tool") if(NOT SPHINX_BUILD) message(STATUS "docs_sphinx: sphinx-build not found; `sphinx` target disabled") return() endif() find_program(DOT_EXECUTABLE NAMES dot DOC "Path to graphviz `dot` (required for API collaboration diagrams)") if(NOT DOT_EXECUTABLE) message(WARNING "docs_sphinx: graphviz `dot` not found — API class-page collaboration " "diagrams will be skipped. Install graphviz (e.g. `sudo apt-get install " "graphviz`) and re-run cmake so the Doxygen HTML build enables HAVE_DOT.") else() message(STATUS "docs_sphinx: graphviz dot found (${DOT_EXECUTABLE}) — " "collaboration diagrams enabled") endif() set(_SPHINX_CONFDIR "${CMAKE_CURRENT_SOURCE_DIR}") set(_SPHINX_OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/html") set(_SPHINX_INPUT_ROOT "${CMAKE_CURRENT_BINARY_DIR}/docs_sphinx_input") set(_SPHINX_INPUT_TUTORIALS "${_SPHINX_INPUT_ROOT}/tutorials") set(_SPHINX_INPUT_CONTRIB "${_SPHINX_INPUT_ROOT}/tutorials_contrib") file(REMOVE_RECURSE "${_SPHINX_INPUT_ROOT}") file(MAKE_DIRECTORY "${_SPHINX_INPUT_TUTORIALS}") # Main tree: symlink the master file + each main module subtree. file(CREATE_LINK "${CMAKE_SOURCE_DIR}/doc/tutorials/tutorials.markdown" "${_SPHINX_INPUT_TUTORIALS}/tutorials.markdown" SYMBOLIC COPY_ON_ERROR) file(CREATE_LINK "${CMAKE_SOURCE_DIR}/doc/faq.markdown" "${_SPHINX_INPUT_ROOT}/faq.markdown" SYMBOLIC COPY_ON_ERROR) file(CREATE_LINK "${CMAKE_SOURCE_DIR}/modules/core/doc/intro.markdown" "${_SPHINX_INPUT_ROOT}/intro.markdown" SYMBOLIC COPY_ON_ERROR) file(GLOB _main_tutorial_children RELATIVE "${CMAKE_SOURCE_DIR}/doc/tutorials" "${CMAKE_SOURCE_DIR}/doc/tutorials/*") foreach(_d ${_main_tutorial_children}) if(IS_DIRECTORY "${CMAKE_SOURCE_DIR}/doc/tutorials/${_d}") file(CREATE_LINK "${CMAKE_SOURCE_DIR}/doc/tutorials/${_d}" "${_SPHINX_INPUT_TUTORIALS}/${_d}" SYMBOLIC COPY_ON_ERROR) endif() endforeach() # js_tutorials, py_tutorials, images/ sit directly under opencv/doc/. foreach(_root js_tutorials py_tutorials images) if(EXISTS "${CMAKE_SOURCE_DIR}/doc/${_root}") file(CREATE_LINK "${CMAKE_SOURCE_DIR}/doc/${_root}" "${_SPHINX_INPUT_ROOT}/${_root}" SYMBOLIC COPY_ON_ERROR) endif() endforeach() if(OPENCV_EXTRA_MODULES_PATH AND EXISTS "${OPENCV_EXTRA_MODULES_PATH}") file(MAKE_DIRECTORY "${_SPHINX_INPUT_CONTRIB}") set(_contrib_root_md "${_SPHINX_INPUT_CONTRIB}/contrib_root.markdown") file(WRITE "${_contrib_root_md}" "Tutorials for contrib modules {#tutorial_contrib_root}\n" "=============================\n\n") file(GLOB _contrib_subdirs RELATIVE "${OPENCV_EXTRA_MODULES_PATH}" "${OPENCV_EXTRA_MODULES_PATH}/*") foreach(_m ${_contrib_subdirs}) set(_tut_dir "${OPENCV_EXTRA_MODULES_PATH}/${_m}/tutorials") if(IS_DIRECTORY "${_tut_dir}") file(CREATE_LINK "${_tut_dir}" "${_SPHINX_INPUT_CONTRIB}/${_m}" SYMBOLIC COPY_ON_ERROR) file(GLOB _tocs RELATIVE "${_tut_dir}" "${_tut_dir}/*.markdown") foreach(_t ${_tocs}) file(STRINGS "${_tut_dir}/${_t}" _id LIMIT_COUNT 1 REGEX ".*\\{#[^}]+\\}") string(REGEX REPLACE ".*\\{#([^}]+)\\}.*" "\\1" _id "${_id}") if(_id) file(APPEND "${_contrib_root_md}" "- @subpage ${_id}\n") endif() endforeach() endif() endforeach() endif() set(_LEGACY_DOXYFILE "${CMAKE_BINARY_DIR}/doc/Doxyfile") set(_DOXYGEN_XML_DIR "${CMAKE_BINARY_DIR}/doc/doxygen/xml") if(NOT DEFINED SPHINX_API_MODULES) # Any module whose include tree declares an @defgroup, keyed on dir name; matlab excluded. set(SPHINX_API_MODULES "") set(_api_module_roots "${CMAKE_SOURCE_DIR}/modules") if(OPENCV_EXTRA_MODULES_PATH AND EXISTS "${OPENCV_EXTRA_MODULES_PATH}") list(APPEND _api_module_roots "${OPENCV_EXTRA_MODULES_PATH}") endif() foreach(_root ${_api_module_roots}) file(GLOB _api_module_dirs RELATIVE "${_root}" "${_root}/*") foreach(_m ${_api_module_dirs}) if(IS_DIRECTORY "${_root}/${_m}/include/opencv2" AND NOT _m STREQUAL "matlab") file(GLOB_RECURSE _api_hdrs "${_root}/${_m}/include/opencv2/*.hpp") foreach(_hdr ${_api_hdrs}) file(READ "${_hdr}" _hdr_contents) if(_hdr_contents MATCHES "@defgroup") list(APPEND SPHINX_API_MODULES "${_m}") break() endif() endforeach() endif() endforeach() endforeach() list(REMOVE_DUPLICATES SPHINX_API_MODULES) list(SORT SPHINX_API_MODULES) message(STATUS "docs_sphinx: discovered API modules: ${SPHINX_API_MODULES}") endif() set(_doxy_input "") foreach(_m ${SPHINX_API_MODULES}) foreach(_base "${CMAKE_SOURCE_DIR}/modules" "${OPENCV_EXTRA_MODULES_PATH}") if(EXISTS "${_base}/${_m}/include") string(APPEND _doxy_input " ${_base}/${_m}/include") break() endif() endforeach() endforeach() if(EXISTS "${_LEGACY_DOXYFILE}") set(_SPHINX_DOXYFILE "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile-xml") file(WRITE "${_SPHINX_DOXYFILE}" "@INCLUDE = ${_LEGACY_DOXYFILE}\n" "GENERATE_HTML = NO\n" "GENERATE_LATEX = NO\n" "GENERATE_XML = YES\n" "XML_OUTPUT = xml\n" "XML_PROGRAMLISTING = NO\n" "CREATE_SUBDIRS = NO\n" "CLASS_GRAPH = NO\n" "COLLABORATION_GRAPH = NO\n" "GROUP_GRAPHS = NO\n" "INCLUDE_GRAPH = NO\n" "INCLUDED_BY_GRAPH = NO\n" "DIRECTORY_GRAPH = NO\n" "INPUT =${_doxy_input}\n" "RECURSIVE = YES\n" "EXCLUDE_SYMBOLS = cv::DataType<*> int void CV__* T __CV* cv::gapi::detail*\n" "MACRO_EXPANSION = YES\n" "EXPAND_ONLY_PREDEF = YES\n" "PREDEFINED += __device__= __host__= __forceinline__= __global__= __constant__= __shared__= __restrict__=\n" ) find_program(DOXYGEN_EXE NAMES doxygen) if(DOXYGEN_EXE) # Stamp-based: Doxygen only re-runs when Doxyfile changes, not on every build. set(_SPHINX_XML_STAMP "${_DOXYGEN_XML_DIR}/.sphinx_xml.stamp") add_custom_command( OUTPUT "${_SPHINX_XML_STAMP}" COMMAND ${CMAKE_COMMAND} -E make_directory ${_DOXYGEN_XML_DIR} COMMAND ${DOXYGEN_EXE} ${_SPHINX_DOXYFILE} COMMAND ${CMAKE_COMMAND} -E touch "${_SPHINX_XML_STAMP}" DEPENDS "${_SPHINX_DOXYFILE}" WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/doc COMMENT "Doxygen XML (for Sphinx breathe) -> ${_DOXYGEN_XML_DIR}" VERBATIM) add_custom_target(sphinx-xml DEPENDS "${_SPHINX_XML_STAMP}") message(STATUS "docs_sphinx: sphinx-xml target enabled (doxygen: ${DOXYGEN_EXE})") else() message(STATUS "docs_sphinx: doxygen not found; `sphinx-xml` disabled") endif() else() message(STATUS "docs_sphinx: legacy Doxyfile not present at ${_LEGACY_DOXYFILE}; " "`sphinx-xml` disabled (re-run cmake after doc/ subdir configures)") endif() set(_SPHINX_ENV "OPENCV_SPHINX_INPUT_ROOT=${_SPHINX_INPUT_ROOT}" "OPENCV_CONTRIB_ROOT=${OPENCV_EXTRA_MODULES_PATH}" "OPENCV_DOXYGEN_XML_DIR=${_DOXYGEN_XML_DIR}" ) if(OPENCV_PYTHON_SIGNATURES_FILE) list(APPEND _SPHINX_ENV "OPENCV_PYTHON_SIGNATURES_FILE=${OPENCV_PYTHON_SIGNATURES_FILE}") endif() if(NOT SPHINX_JOBS) set(SPHINX_JOBS "auto") endif() set(_SPHINX_WARNINGS "${CMAKE_CURRENT_BINARY_DIR}/sphinx-warnings.log") # opencv.js (JS Try-it): download once into the build dir, bundled into html output. set(_OPENCV_JS "${CMAKE_CURRENT_BINARY_DIR}/opencv.js") if(NOT EXISTS "${_OPENCV_JS}") message(STATUS "docs_sphinx: downloading opencv.js (one-time)") file(DOWNLOAD "https://docs.opencv.org/5.x/opencv.js" "${_OPENCV_JS}" SHOW_PROGRESS STATUS _DL_STATUS) list(GET _DL_STATUS 0 _DL_OK) if(NOT _DL_OK EQUAL 0) message(WARNING "docs_sphinx: opencv.js download failed — Try-it won't run") file(REMOVE "${_OPENCV_JS}") set(_OPENCV_JS "") endif() endif() if(_OPENCV_JS) list(APPEND _SPHINX_ENV "OPENCV_JS_PATH=${_OPENCV_JS}") endif() add_custom_target(sphinx COMMAND ${CMAKE_COMMAND} -E make_directory ${_SPHINX_OUTDIR} COMMAND ${CMAKE_COMMAND} -E env ${_SPHINX_ENV} ${SPHINX_BUILD} -j ${SPHINX_JOBS} --keep-going -c ${_SPHINX_CONFDIR} ${_SPHINX_INPUT_ROOT} ${_SPHINX_OUTDIR} WORKING_DIRECTORY ${_SPHINX_CONFDIR} COMMENT "Building Sphinx HTML site -> ${_SPHINX_OUTDIR} (jobs=${SPHINX_JOBS})" VERBATIM) # Ensure breathe sees current XML on every `sphinx` build. if(TARGET sphinx-xml) add_dependencies(sphinx sphinx-xml) endif() # Auto-generate Python binding signatures (needed for Python names in enum tables). if(TARGET gen_opencv_python_source) add_dependencies(sphinx gen_opencv_python_source) endif() option(SPHINX_BUILD_DIAGRAMS "Rebuild the Doxygen HTML (collaboration diagrams) as part of the sphinx target" ON) if(SPHINX_BUILD_DIAGRAMS AND TARGET doxygen) add_dependencies(sphinx doxygen) endif() # Clean rebuild: `cmake --build --target sphinx-clean` add_custom_target(sphinx-clean COMMAND ${CMAKE_COMMAND} -E rm -rf ${_SPHINX_OUTDIR} COMMAND ${CMAKE_COMMAND} -E rm -rf ${CMAKE_CURRENT_BINARY_DIR}/.doctrees COMMENT "Removing ${_SPHINX_OUTDIR} and .doctrees env cache" VERBATIM) message(STATUS "docs_sphinx: sphinx target enabled (sphinx-build: ${SPHINX_BUILD})")