blob: 197a1552d6b4eba52569e7fb7263dd09966d3397 [file] [edit]
include(GNUInstallDirs)
# Create sphinx target
if (LLVM_ENABLE_SPHINX)
message(STATUS "Sphinx enabled.")
find_package(Sphinx REQUIRED)
# Sphinx has internal parallelism, so give it a custom job pool. Sphinx tends
# not to use all available CPU, so this is not enabled by default.
set(LLVM_PARALLEL_SPHINX_JOBS "" CACHE STRING
"Define the maximum number of concurrent sphinx-build invocations (Ninja only).")
if (LLVM_PARALLEL_SPHINX_JOBS)
if (CMAKE_GENERATOR MATCHES "Ninja")
get_property(_sphinx_job_pools GLOBAL PROPERTY JOB_POOLS)
if (NOT "sphinx_job_pool=${LLVM_PARALLEL_SPHINX_JOBS}" IN_LIST _sphinx_job_pools)
set_property(GLOBAL APPEND PROPERTY JOB_POOLS sphinx_job_pool=${LLVM_PARALLEL_SPHINX_JOBS})
endif()
set(sphinx_job_pool JOB_POOL sphinx_job_pool)
else()
message(WARNING "Job pooling is only available with Ninja generators.")
endif()
endif()
if (LLVM_BUILD_DOCS AND NOT TARGET sphinx)
add_custom_target(sphinx ALL)
set_target_properties(sphinx PROPERTIES FOLDER "LLVM/Docs")
endif()
else()
message(STATUS "Sphinx disabled.")
endif()
# Create a target that synchronizes checked-in Sphinx inputs into a build-tree
# source directory. The synchronization preserves mtimes for unchanged files,
# updates changed files, and removes stale .rst/.md files so incremental docs
# builds stay correct when documentation is renamed or deleted. The cheap scan
# target is always run to notice directory listing changes, but it writes the
# file list only when it changes; the sync command depends on that list and on a
# depfile of the listed source files. The scan also records the destination
# .rst/.md listing so destination-only stale files trigger cleanup. It also
# records source files that are missing from the destination so externally
# removed copies are restored. The actual copy step can still be skipped when
# both trees are unchanged. Use PRESERVE_DOCS for generated .rst/.md files that
# live in the destination directory but do not exist in source_dir. Use
# IGNORE_MISSING_FILES for files that are copied by the sync step but
# intentionally removed by a later build action.
function(add_sphinx_source_sync_target target source_dir destination_dir)
cmake_parse_arguments(ARG "" "" "PRESERVE_DOCS;IGNORE_MISSING_FILES" ${ARGN})
set(sync_dir "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${target}.sphinx-source-sync")
set(file_list "${sync_dir}/files.txt")
set(destination_doc_list "${sync_dir}/destination-docs.txt")
set(missing_file_list "${sync_dir}/missing-files.txt")
set(preserve_file "${sync_dir}/preserve.txt")
set(ignore_missing_file "${sync_dir}/ignore-missing.txt")
set(manifest_file "${sync_dir}/manifest.txt")
set(depfile "${sync_dir}/sync.d")
set(stamp_file "${sync_dir}/sync.stamp")
set(scan_target "${target}-scan")
set(scan_script "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/SphinxSourceScan.cmake")
set(sync_script "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/SphinxSourceSync.cmake")
set(utils_script "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/SphinxSourceUtils.cmake")
file(MAKE_DIRECTORY "${sync_dir}")
string(REPLACE ";" "\n" preserve_docs "${ARG_PRESERVE_DOCS}")
if (preserve_docs)
string(APPEND preserve_docs "\n")
endif()
if (EXISTS "${preserve_file}")
file(READ "${preserve_file}" old_preserve_docs)
else()
set(old_preserve_docs)
endif()
if (NOT preserve_docs STREQUAL old_preserve_docs)
file(WRITE "${preserve_file}" "${preserve_docs}")
endif()
string(REPLACE ";" "\n" ignore_missing_files "${ARG_IGNORE_MISSING_FILES}")
if (ignore_missing_files)
string(APPEND ignore_missing_files "\n")
endif()
if (EXISTS "${ignore_missing_file}")
file(READ "${ignore_missing_file}" old_ignore_missing_files)
else()
set(old_ignore_missing_files)
endif()
if (NOT ignore_missing_files STREQUAL old_ignore_missing_files)
file(WRITE "${ignore_missing_file}" "${ignore_missing_files}")
endif()
add_custom_target(${scan_target}
COMMAND "${CMAKE_COMMAND}"
"-DSOURCE_DIR=${source_dir}"
"-DDESTINATION_DIR=${destination_dir}"
"-DFILE_LIST=${file_list}"
"-DDESTINATION_DOC_LIST=${destination_doc_list}"
"-DMISSING_FILE_LIST=${missing_file_list}"
"-DIGNORE_MISSING_FILE=${ignore_missing_file}"
-P "${scan_script}"
BYPRODUCTS "${file_list}" "${destination_doc_list}"
"${missing_file_list}"
DEPENDS "${scan_script}" "${utils_script}"
COMMENT
"Scanning Sphinx sources in \"${source_dir}\""
VERBATIM)
add_custom_command(OUTPUT "${stamp_file}"
COMMAND "${CMAKE_COMMAND}"
"-DSOURCE_DIR=${source_dir}"
"-DDESTINATION_DIR=${destination_dir}"
"-DFILE_LIST=${file_list}"
"-DPRESERVE_FILE=${preserve_file}"
"-DMANIFEST_FILE=${manifest_file}"
"-DDEPFILE=${depfile}"
"-DSTAMP_FILE=${stamp_file}"
-P "${sync_script}"
DEPENDS "${file_list}" "${preserve_file}"
"${destination_doc_list}" "${missing_file_list}"
"${ignore_missing_file}" "${sync_script}"
"${utils_script}"
DEPFILE "${depfile}"
BYPRODUCTS "${manifest_file}"
COMMENT
"Copying Sphinx sources from \"${source_dir}\" to \"${destination_dir}\""
VERBATIM)
add_custom_target(${target} DEPENDS "${stamp_file}")
add_dependencies(${target} ${scan_target})
endfunction()
# Handy function for creating the different Sphinx targets.
#
# ``builder`` should be one of the supported builders used by
# the sphinx-build command.
#
# ``project`` should be the project name
#
# Named arguments:
# ``ENV_VARS`` should be a list of environment variables that should be set when
# running Sphinx. Each environment variable should be a string with
# the form KEY=VALUE.
function (add_sphinx_target builder project)
cmake_parse_arguments(ARG "" "SOURCE_DIR" "ENV_VARS" ${ARGN})
set(SPHINX_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/${builder}")
set(SPHINX_DOC_TREE_DIR "${CMAKE_CURRENT_BINARY_DIR}/_doctrees-${project}-${builder}")
set(SPHINX_TARGET_NAME docs-${project}-${builder})
if (SPHINX_WARNINGS_AS_ERRORS)
set(SPHINX_WARNINGS_AS_ERRORS_FLAG "-W")
else()
set(SPHINX_WARNINGS_AS_ERRORS_FLAG "")
endif()
if (NOT ARG_SOURCE_DIR)
set(ARG_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
endif()
# Give Sphinx some internal job parallelism, since it tends to be on the
# critical path at the end of the build. This can speed up doc builds by
# ~80%. Sphinx rarely consumes all cores available, so it's safe to
# overallocate a bit.
if (NOT DEFINED LLVM_SPHINX_THREADS)
cmake_host_system_information(RESULT number_of_logical_cores
QUERY NUMBER_OF_LOGICAL_CORES)
math(EXPR LLVM_SPHINX_THREADS
"(${number_of_logical_cores} + 1) / 2")
endif()
set(LLVM_SPHINX_THREADS "${LLVM_SPHINX_THREADS}"
CACHE STRING "Define the number of parallel jobs for each Sphinx build.")
if (LLVM_SPHINX_THREADS)
set(sphinx_jobs_flag -j ${LLVM_SPHINX_THREADS})
endif()
if ("${LLVM_VERSION_SUFFIX}" STREQUAL "git")
set(PreReleaseTag "-tPreRelease")
endif()
add_custom_target(${SPHINX_TARGET_NAME}
COMMAND ${CMAKE_COMMAND} -E env ${ARG_ENV_VARS}
--modify "PYTHONPATH=path_list_append:${LLVM_MAIN_SRC_DIR}/../utils/docs"
${SPHINX_EXECUTABLE}
-b ${builder}
-d "${SPHINX_DOC_TREE_DIR}"
${sphinx_jobs_flag}
-q # Quiet: no output other than errors and warnings.
-t builder-${builder} # tag for builder
-D version=${LLVM_VERSION_MAJOR}
-D release=${PACKAGE_VERSION}
${PreReleaseTag}
${SPHINX_WARNINGS_AS_ERRORS_FLAG} # Treat warnings as errors if requested
"${ARG_SOURCE_DIR}" # Source
"${SPHINX_BUILD_DIR}" # Output
${sphinx_job_pool}
COMMENT
"Generating ${builder} Sphinx documentation for ${project} into \"${SPHINX_BUILD_DIR}\"")
get_subproject_title(subproject_title)
set_target_properties(${SPHINX_TARGET_NAME} PROPERTIES FOLDER "${subproject_title}/Docs")
# When "clean" target is run, remove the Sphinx build directory
set_property(DIRECTORY APPEND PROPERTY
ADDITIONAL_MAKE_CLEAN_FILES
"${SPHINX_BUILD_DIR}")
# We need to remove ${SPHINX_DOC_TREE_DIR} when make clean is run
# but we should only add this path once
get_property(_CURRENT_MAKE_CLEAN_FILES
DIRECTORY PROPERTY ADDITIONAL_MAKE_CLEAN_FILES)
if (NOT "${SPHINX_DOC_TREE_DIR}" IN_LIST _CURRENT_MAKE_CLEAN_FILES)
set_property(DIRECTORY APPEND PROPERTY
ADDITIONAL_MAKE_CLEAN_FILES
"${SPHINX_DOC_TREE_DIR}")
endif()
if (LLVM_BUILD_DOCS)
add_dependencies(sphinx ${SPHINX_TARGET_NAME})
# Handle installation
if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
if (builder STREQUAL man)
# FIXME: We might not ship all the tools that these man pages describe
install(DIRECTORY "${SPHINX_BUILD_DIR}/" # Slash indicates contents of
COMPONENT "${project}-sphinx-man"
DESTINATION "${CMAKE_INSTALL_MANDIR}/man1")
if(NOT LLVM_ENABLE_IDE)
add_llvm_install_targets("install-${SPHINX_TARGET_NAME}"
DEPENDS ${SPHINX_TARGET_NAME}
COMPONENT "${project}-sphinx-man")
endif()
elseif (builder STREQUAL html)
string(TOUPPER "${project}" project_upper)
set(${project_upper}_INSTALL_SPHINX_HTML_DIR "${CMAKE_INSTALL_DOCDIR}/${project}/html"
CACHE STRING "HTML documentation install directory for ${project}")
# '/.' indicates: copy the contents of the directory directly into
# the specified destination, without recreating the last component
# of ${SPHINX_BUILD_DIR} implicitly.
install(DIRECTORY "${SPHINX_BUILD_DIR}/."
COMPONENT "${project}-sphinx-html"
DESTINATION "${${project_upper}_INSTALL_SPHINX_HTML_DIR}")
if(NOT LLVM_ENABLE_IDE)
add_llvm_install_targets("install-${SPHINX_TARGET_NAME}"
DEPENDS ${SPHINX_TARGET_NAME}
COMPONENT "${project}-sphinx-html")
endif()
else()
message(WARNING Installation of ${builder} not supported)
endif()
endif()
endif()
endfunction()