[flang-commits] [clang] [flang] [libc] [llvm] [cmake][docs] Fix incremental docs builds under deletion and renames (PR #211411)
Reid Kleckner via flang-commits
flang-commits at lists.llvm.org
Wed Jul 22 20:07:44 PDT 2026
https://github.com/rnk updated https://github.com/llvm/llvm-project/pull/211411
>From bdefa15bd3a7101217f95a4c3eba4a9946b9e31f Mon Sep 17 00:00:00 2001
From: Reid Kleckner <rkleckner at nvidia.com>
Date: Tue, 21 Jul 2026 23:38:44 +0000
Subject: [PATCH] [cmake][docs] Fix incremental docs builds under deletion and
renames
Currently clang, flang, and libc all copy documentation from the source
tree into the build tree in preparation to build it, usually to create a
combined tree that includes generated documentation files, like
AttributeReference.md. However, renaming a document and rebuilding
without cleaning the docs tree leaves behind stale documentation files
that can accumulate.
This patch fixes the problem with two actions:
1. List source documentation files. Always out of date, always
regenerates on every doc build.
2. Copy all source documents to the output, and delete any file that
neither originates from the source directory nor is mentioned as a
generated source to preserve.
These actions are implemented as CMake script files (`cmake -P`) since
they do things not covered by the builtin tools (`cmake -E
copy_if_different`). They could be simplified if we were willing to
tolerate more process launch overhead, but for something that runs on
the critical path to every doc rebuild, I decided it was worth spending
lines of CMake script on it.
Assisted-by: a coding tool
---
clang/docs/CMakeLists.txt | 19 ++--
flang/docs/CMakeLists.txt | 26 +++--
libc/docs/CMakeLists.txt | 67 ++++++------
llvm/cmake/modules/AddSphinxTarget.cmake | 93 +++++++++++++++++
llvm/cmake/modules/SphinxSourceScan.cmake | 97 +++++++++++++++++
llvm/cmake/modules/SphinxSourceSync.cmake | 121 ++++++++++++++++++++++
6 files changed, 375 insertions(+), 48 deletions(-)
create mode 100644 llvm/cmake/modules/SphinxSourceScan.cmake
create mode 100644 llvm/cmake/modules/SphinxSourceSync.cmake
diff --git a/clang/docs/CMakeLists.txt b/clang/docs/CMakeLists.txt
index 10992eea61c28..f4e9547ad37ce 100644
--- a/clang/docs/CMakeLists.txt
+++ b/clang/docs/CMakeLists.txt
@@ -87,6 +87,7 @@ function (gen_rst_file_from_td output_file td_option source docs_targets)
get_filename_component(TABLEGEN_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${source}" DIRECTORY)
list(APPEND LLVM_TABLEGEN_FLAGS "-I${TABLEGEN_INCLUDE_DIR}")
clang_tablegen(${output_file} ${td_option} SOURCE ${source} TARGET "gen-${output_file}")
+ add_dependencies("gen-${output_file}" copy-clang-rst-docs)
foreach(target ${docs_targets})
add_dependencies(${target} gen-${output_file})
endforeach()
@@ -95,16 +96,22 @@ endfunction()
if (LLVM_ENABLE_SPHINX)
include(AddSphinxTarget)
if (SPHINX_FOUND AND (${SPHINX_OUTPUT_HTML} OR ${SPHINX_OUTPUT_MAN}))
- # Copy rst files to build directory before generating the html
- # documentation. Some of the rst files are generated, so they
+ # Copy documentation sources to the build directory before generating
+ # documentation. Some of the files are generated, so they
# only exist in the build directory. Sphinx needs all files in
# the same directory in order to generate the html, so we need to
- # copy all the non-gnerated rst files from the source to the build
+ # copy all the non-generated source files from the source to the build
# directory before we run sphinx.
- add_custom_target(copy-clang-rst-docs
- COMMAND "${CMAKE_COMMAND}" -E copy_directory
+ set(clang_generated_docs
+ AttributeReference.rst
+ DiagnosticsReference.rst
+ AMDGPUBuiltinReference.rst
+ ClangCommandLineReference.rst
+ analyzer/user-docs/Options.rst)
+ add_sphinx_source_sync_target(copy-clang-rst-docs
"${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}"
-
+ PRESERVE_DOCS ${clang_generated_docs})
+ add_custom_command(TARGET copy-clang-rst-docs POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/../Maintainers.md"
"${CMAKE_CURRENT_BINARY_DIR}"
diff --git a/flang/docs/CMakeLists.txt b/flang/docs/CMakeLists.txt
index ddf3fb4543b6f..89fd219ef88cf 100644
--- a/flang/docs/CMakeLists.txt
+++ b/flang/docs/CMakeLists.txt
@@ -115,16 +115,22 @@ if (LLVM_ENABLE_SPHINX)
# Copy the entire flang/docs directory to the build Source dir,
# then remove the CommandGuide index.md file, to avoid clash with index.md
# which is used for the HTML build.
- add_custom_target(copy-flang-src-docs-html
- COMMAND "${CMAKE_COMMAND}" -E copy_directory
- "${CMAKE_CURRENT_SOURCE_DIR}"
- "${FLANG_DOCS_HTML_DIR}"
+ add_sphinx_source_sync_target(copy-flang-src-docs-html
+ "${CMAKE_CURRENT_SOURCE_DIR}"
+ "${FLANG_DOCS_HTML_DIR}"
+ PRESERVE_DOCS
+ FlangCommandLineReference.rst
+ FIRLangRef.md
+ IGNORE_MISSING_FILES
+ CommandGuide/index.md)
+ add_custom_command(TARGET copy-flang-src-docs-html POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E remove
"${FLANG_DOCS_HTML_DIR}/CommandGuide/index.md"
- COMMAND "${CMAKE_COMMAND}" -E copy
+ COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${CMAKE_CURRENT_BINARY_DIR}/Source/FlangCommandLineReference.rst"
- "${FLANG_DOCS_HTML_DIR}/FlangCommandLineReference.rst"
- DEPENDS flang-doc gen-FlangCommandLineReference.rst)
+ "${FLANG_DOCS_HTML_DIR}/FlangCommandLineReference.rst")
+ add_dependencies(copy-flang-src-docs-html
+ flang-doc gen-FlangCommandLineReference.rst)
# ${CMAKE_CURRENT_BINARY_DIR}/Dialect/FIRLangRef.md is generated by
# mlir-tblgen. The script executed in the command below adds some text to
@@ -154,13 +160,13 @@ if (LLVM_ENABLE_SPHINX)
add_custom_target(copy-flang-src-docs-man
COMMAND "${CMAKE_COMMAND}" -E make_directory
"${FLANG_DOCS_MAN_DIR}"
- COMMAND "${CMAKE_COMMAND}" -E copy
+ COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/conf.py"
"${FLANG_DOCS_MAN_DIR}/conf.py"
- COMMAND "${CMAKE_COMMAND}" -E copy
+ COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${CMAKE_CURRENT_BINARY_DIR}/Source/FlangCommandLineOptions.rst"
"${FLANG_DOCS_MAN_DIR}/FlangCommandLineOptions.rst"
- COMMAND "${CMAKE_COMMAND}" -E copy
+ COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/CommandGuide/index.md"
"${FLANG_DOCS_MAN_DIR}/index.md"
DEPENDS flang-doc gen-FlangCommandLineOptions.rst)
diff --git a/libc/docs/CMakeLists.txt b/libc/docs/CMakeLists.txt
index 261addc0006ff..08a56dfc9a819 100644
--- a/libc/docs/CMakeLists.txt
+++ b/libc/docs/CMakeLists.txt
@@ -2,38 +2,10 @@ if (LLVM_ENABLE_SPHINX)
include(AddSphinxTarget)
if (SPHINX_FOUND)
if (${SPHINX_OUTPUT_HTML})
- # Similar to clang, we copy our static .rst files from libc/docs/ to the
- # $build_dir/libc/docs/. That way, we can have a mix of both static
- # (committed) .rst files, and dynamically generated .rst files. We don't
- # want the dynamically generated .rst files to pollute the source tree.
- add_custom_target(copy-libc-rst-docs
- COMMAND "${CMAKE_COMMAND}" -E copy_directory
- "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}"
-
- COMMAND "${CMAKE_COMMAND}" -E copy_if_different
- "${CMAKE_CURRENT_SOURCE_DIR}/../Maintainers.md"
- "${CMAKE_CURRENT_BINARY_DIR}"
- )
-
- # For headers that are nested in directories, we need to
- # `mkdir $build_dir/libc/docs/headers/$dir` since the above copy_directory
- # command does not create such copies. Otherwise, the invocation of docgen
- # below will fail since the output file would be placed in a directory that
- # does not exist, leading to a `No such file or directory` error from the
- # shell.
- file(MAKE_DIRECTORY
- "${CMAKE_CURRENT_BINARY_DIR}/headers/arpa/"
- "${CMAKE_CURRENT_BINARY_DIR}/headers/net/"
- "${CMAKE_CURRENT_BINARY_DIR}/headers/netinet/"
- "${CMAKE_CURRENT_BINARY_DIR}/headers/sys/"
- )
-
- # Change sphinx to build from $build_dir/libc/docs/ rather than
- # llvm-project/libc/docs/.
- add_sphinx_target(html libc SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
- # Depend on the copy target.
- add_dependencies(docs-libc-html copy-libc-rst-docs)
-
+ # Similar to clang, we copy our static documentation files from libc/docs/
+ # to the $build_dir/libc/docs/. That way, we can have a mix of both static
+ # committed files and dynamically generated .rst files. We don't want the
+ # dynamically generated .rst files to pollute the source tree.
# Maintain a list of headers for which we dynamically generate html docs
# for via docgen. For more complex docs (such as per arch support, a la
# math.h), those should be omitted and exist statically in
@@ -92,6 +64,36 @@ if (SPHINX_FOUND)
wchar
wctype
)
+ foreach(stem IN LISTS docgen_list)
+ list(APPEND libc_generated_docs "headers/${stem}.rst")
+ endforeach()
+ list(APPEND libc_generated_docs configure.rst)
+ add_sphinx_source_sync_target(copy-libc-rst-docs
+ "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}"
+ PRESERVE_DOCS ${libc_generated_docs})
+ add_custom_command(TARGET copy-libc-rst-docs POST_BUILD
+ COMMAND "${CMAKE_COMMAND}" -E copy_if_different
+ "${CMAKE_CURRENT_SOURCE_DIR}/../Maintainers.md"
+ "${CMAKE_CURRENT_BINARY_DIR}"
+ )
+
+ # For headers that are nested in directories, we need to
+ # `mkdir $build_dir/libc/docs/headers/$dir`. Otherwise, the invocation of
+ # docgen below will fail since the output file would be placed in a
+ # directory that does not exist, leading to a `No such file or directory`
+ # error from the shell.
+ file(MAKE_DIRECTORY
+ "${CMAKE_CURRENT_BINARY_DIR}/headers/arpa/"
+ "${CMAKE_CURRENT_BINARY_DIR}/headers/net/"
+ "${CMAKE_CURRENT_BINARY_DIR}/headers/netinet/"
+ "${CMAKE_CURRENT_BINARY_DIR}/headers/sys/"
+ )
+
+ # Change sphinx to build from $build_dir/libc/docs/ rather than
+ # llvm-project/libc/docs/.
+ add_sphinx_target(html libc SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
+ # Depend on the copy target.
+ add_dependencies(docs-libc-html copy-libc-rst-docs)
foreach(stem IN LISTS docgen_list)
# It is an error in cmake to have a target name that contains a "/", but
@@ -105,6 +107,7 @@ if (SPHINX_FOUND)
add_custom_target(${docgen_target_name}
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/../utils/docgen/docgen.py ${stem}.h >
${CMAKE_CURRENT_BINARY_DIR}/headers/${stem}.rst)
+ add_dependencies(${docgen_target_name} copy-libc-rst-docs)
# depend on the docgen invocation.
add_dependencies(docs-libc-html ${docgen_target_name})
endforeach()
diff --git a/llvm/cmake/modules/AddSphinxTarget.cmake b/llvm/cmake/modules/AddSphinxTarget.cmake
index 379e36db48a25..f4301adf108d5 100644
--- a/llvm/cmake/modules/AddSphinxTarget.cmake
+++ b/llvm/cmake/modules/AddSphinxTarget.cmake
@@ -12,6 +12,99 @@ 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")
+
+ 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}"
+ 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}"
+ 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.
#
diff --git a/llvm/cmake/modules/SphinxSourceScan.cmake b/llvm/cmake/modules/SphinxSourceScan.cmake
new file mode 100644
index 0000000000000..633a329eb124f
--- /dev/null
+++ b/llvm/cmake/modules/SphinxSourceScan.cmake
@@ -0,0 +1,97 @@
+# Enumerate a documentation source tree for SphinxSourceSync.cmake.
+#
+# Usage:
+#
+# cmake -DSOURCE_DIR=/path/to/project/docs \
+# -DDESTINATION_DIR=/path/to/build/project/docs \
+# -DFILE_LIST=/path/to/build/file-list.txt \
+# -DDESTINATION_DOC_LIST=/path/to/build/destination-docs.txt \
+# -DMISSING_FILE_LIST=/path/to/build/missing-files.txt \
+# -DIGNORE_MISSING_FILE=/path/to/build/ignore-missing.txt \
+# -P /path/to/SphinxSourceScan.cmake
+#
+# The output file contains source-relative paths, one per line. It is written
+# only when the directory listing changes, allowing downstream custom commands
+# to skip work when the always-run scan observes no additions, removals, or
+# renames. DESTINATION_DOC_LIST similarly records destination-relative .rst and
+# .md files so destination-only stale docs can trigger cleanup.
+# MISSING_FILE_LIST records source files that do not currently exist in the
+# destination tree, excluding paths listed in IGNORE_MISSING_FILE.
+
+if (NOT DEFINED SOURCE_DIR)
+ message(FATAL_ERROR "SOURCE_DIR must be set")
+endif()
+
+if (NOT DEFINED FILE_LIST)
+ message(FATAL_ERROR "FILE_LIST must be set")
+endif()
+
+if (NOT DEFINED DESTINATION_DIR)
+ message(FATAL_ERROR "DESTINATION_DIR must be set")
+endif()
+
+if (NOT DEFINED DESTINATION_DOC_LIST)
+ message(FATAL_ERROR "DESTINATION_DOC_LIST must be set")
+endif()
+
+if (NOT DEFINED MISSING_FILE_LIST)
+ message(FATAL_ERROR "MISSING_FILE_LIST must be set")
+endif()
+
+if (NOT DEFINED IGNORE_MISSING_FILE)
+ message(FATAL_ERROR "IGNORE_MISSING_FILE must be set")
+endif()
+
+function(write_if_changed output_file contents)
+ if (EXISTS "${output_file}")
+ file(READ "${output_file}" old_contents)
+ else()
+ set(old_contents)
+ endif()
+
+ if (NOT contents STREQUAL old_contents)
+ get_filename_component(output_dir "${output_file}" DIRECTORY)
+ file(MAKE_DIRECTORY "${output_dir}")
+ file(WRITE "${output_file}" "${contents}")
+ endif()
+endfunction()
+
+file(GLOB_RECURSE source_files
+ LIST_DIRECTORIES false
+ RELATIVE "${SOURCE_DIR}"
+ "${SOURCE_DIR}/*")
+list(SORT source_files)
+
+set(contents)
+foreach(relative_path IN LISTS source_files)
+ string(APPEND contents "${relative_path}\n")
+endforeach()
+write_if_changed("${FILE_LIST}" "${contents}")
+
+file(STRINGS "${IGNORE_MISSING_FILE}" ignore_missing_files)
+set(contents)
+foreach(relative_path IN LISTS source_files)
+ list(FIND ignore_missing_files "${relative_path}" ignore_index)
+ if (ignore_index EQUAL -1 AND
+ NOT EXISTS "${DESTINATION_DIR}/${relative_path}")
+ string(APPEND contents "${relative_path}\n")
+ endif()
+endforeach()
+write_if_changed("${MISSING_FILE_LIST}" "${contents}")
+
+if (EXISTS "${DESTINATION_DIR}")
+ file(GLOB_RECURSE destination_docs
+ LIST_DIRECTORIES false
+ RELATIVE "${DESTINATION_DIR}"
+ "${DESTINATION_DIR}/*.rst"
+ "${DESTINATION_DIR}/*.md")
+ list(SORT destination_docs)
+else()
+ set(destination_docs)
+endif()
+
+set(contents)
+foreach(relative_path IN LISTS destination_docs)
+ string(APPEND contents "${relative_path}\n")
+endforeach()
+write_if_changed("${DESTINATION_DOC_LIST}" "${contents}")
diff --git a/llvm/cmake/modules/SphinxSourceSync.cmake b/llvm/cmake/modules/SphinxSourceSync.cmake
new file mode 100644
index 0000000000000..51f6eece26fa8
--- /dev/null
+++ b/llvm/cmake/modules/SphinxSourceSync.cmake
@@ -0,0 +1,121 @@
+# Synchronize a documentation source tree into an intermediate Sphinx source
+# tree.
+#
+# Usage:
+#
+# cmake -DSOURCE_DIR=/path/to/project/docs \
+# -DDESTINATION_DIR=/path/to/build/project/docs \
+# -DFILE_LIST=/path/to/build/file-list.txt \
+# -DPRESERVE_FILE=/path/to/build/preserve.txt \
+# -DMANIFEST_FILE=/path/to/build/manifest.txt \
+# -DDEPFILE=/path/to/build/sync.d \
+# -DSTAMP_FILE=/path/to/build/sync.stamp \
+# -P /path/to/SphinxSourceSync.cmake
+#
+# The sync copies all source files into DESTINATION_DIR, avoids rewriting files
+# whose contents are unchanged, and removes stale .rst and .md files that no
+# longer exist under SOURCE_DIR unless they are listed in PRESERVE_FILE. This
+# is intended for Sphinx builds that merge checked-in docs with generated docs
+# in a build-tree source directory.
+
+if (NOT DEFINED SOURCE_DIR)
+ message(FATAL_ERROR "SOURCE_DIR must be set")
+endif()
+
+if (NOT DEFINED DESTINATION_DIR)
+ message(FATAL_ERROR "DESTINATION_DIR must be set")
+endif()
+
+if (NOT DEFINED FILE_LIST)
+ message(FATAL_ERROR "FILE_LIST must be set")
+endif()
+
+if (NOT DEFINED PRESERVE_FILE)
+ message(FATAL_ERROR "PRESERVE_FILE must be set")
+endif()
+
+if (NOT DEFINED MANIFEST_FILE)
+ message(FATAL_ERROR "MANIFEST_FILE must be set")
+endif()
+
+if (NOT DEFINED DEPFILE)
+ message(FATAL_ERROR "DEPFILE must be set")
+endif()
+
+if (NOT DEFINED STAMP_FILE)
+ message(FATAL_ERROR "STAMP_FILE must be set")
+endif()
+
+function(escape_depfile_path input output)
+ set(path "${input}")
+ string(REPLACE "\\" "/" path "${path}")
+ string(REPLACE "$" "$$" path "${path}")
+ string(REPLACE "#" "\\#" path "${path}")
+ string(REPLACE " " "\\ " path "${path}")
+ set(${output} "${path}" PARENT_SCOPE)
+endfunction()
+
+file(MAKE_DIRECTORY "${DESTINATION_DIR}")
+file(STRINGS "${FILE_LIST}" source_files)
+file(STRINGS "${PRESERVE_FILE}" preserve_docs)
+
+set(depfile_dependencies)
+set(source_docs)
+foreach(relative_path IN LISTS source_files)
+ set(source_path "${SOURCE_DIR}/${relative_path}")
+ set(destination_path "${DESTINATION_DIR}/${relative_path}")
+ get_filename_component(destination_parent "${destination_path}" DIRECTORY)
+ file(MAKE_DIRECTORY "${destination_parent}")
+ # configure_file(COPYONLY) preserves mtimes for unchanged files and avoids
+ # the subprocess overhead of cmake -E copy_if_different.
+ configure_file("${source_path}" "${destination_path}" COPYONLY)
+ list(APPEND depfile_dependencies "${source_path}")
+
+ string(TOLOWER "${relative_path}" lower_relative_path)
+ if (lower_relative_path MATCHES "\\.(rst|md)$")
+ list(APPEND source_docs "${relative_path}")
+ endif()
+endforeach()
+
+file(GLOB_RECURSE destination_docs
+ LIST_DIRECTORIES false
+ RELATIVE "${DESTINATION_DIR}"
+ "${DESTINATION_DIR}/*.rst"
+ "${DESTINATION_DIR}/*.md")
+foreach(relative_path IN LISTS destination_docs)
+ list(FIND source_docs "${relative_path}" source_index)
+ list(FIND preserve_docs "${relative_path}" preserve_index)
+ # Generated docs are absent from SOURCE_DIR, but callers list them in
+ # PRESERVE_FILE so stale-source cleanup does not remove build outputs.
+ if (source_index EQUAL -1 AND preserve_index EQUAL -1)
+ file(REMOVE "${DESTINATION_DIR}/${relative_path}")
+ endif()
+endforeach()
+
+set(manifest_contents)
+foreach(relative_path IN LISTS source_docs)
+ string(APPEND manifest_contents "${relative_path}\n")
+endforeach()
+if (EXISTS "${MANIFEST_FILE}")
+ file(READ "${MANIFEST_FILE}" old_manifest_contents)
+else()
+ set(old_manifest_contents)
+endif()
+if (NOT manifest_contents STREQUAL old_manifest_contents)
+ get_filename_component(manifest_dir "${MANIFEST_FILE}" DIRECTORY)
+ file(MAKE_DIRECTORY "${manifest_dir}")
+ file(WRITE "${MANIFEST_FILE}" "${manifest_contents}")
+endif()
+
+escape_depfile_path("${STAMP_FILE}" escaped_stamp)
+set(depfile_contents "${escaped_stamp}:")
+foreach(source_path IN LISTS depfile_dependencies)
+ escape_depfile_path("${source_path}" escaped_source_path)
+ string(APPEND depfile_contents " ${escaped_source_path}")
+endforeach()
+string(APPEND depfile_contents "\n")
+file(WRITE "${DEPFILE}" "${depfile_contents}")
+
+get_filename_component(stamp_dir "${STAMP_FILE}" DIRECTORY)
+file(MAKE_DIRECTORY "${stamp_dir}")
+file(TOUCH "${STAMP_FILE}")
More information about the flang-commits
mailing list