feat(build): add_glob_target runs only on changed files #19070

The general idea is that add_glob_targets creates a "touch file", a
dummy file that acts as a dependency in order to check which files are
outdated since the last time the target was run.

Remove RunUncrustify.cmake as it's no longer necessary. It was initially
introduced to silence its noisy output. The per-file targets will
suppress the noisy output from uncrustify, except for the very first
run.

Also remove DefCmdTarget.cmake since add_glob_target already
incorporates its functionality.
This commit is contained in:
dundargoc 2022-06-30 14:10:05 +02:00 committed by GitHub
parent f50135a32e
commit 37af46bb4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 190 additions and 74 deletions

View File

@ -21,6 +21,9 @@ list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
# We don't support building in-tree.
include(PreventInTreeBuilds)
include(Util)
set(TOUCHES_DIR ${PROJECT_BINARY_DIR}/touches)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
@ -611,34 +614,44 @@ find_program(STYLUA_PRG stylua)
find_program(FLAKE8_PRG flake8)
find_program(UNCRUSTIFY_PRG uncrustify)
find_program(SHELLCHECK_PRG shellcheck)
include(DefCmdTarget)
def_cmd_target(lintlua ${LUACHECK_PRG} LUACHECK_PRG true)
if(LUACHECK_PRG)
add_custom_command(TARGET lintlua
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMAND ${LUACHECK_PRG} -q runtime/ scripts/ src/ test/)
endif()
if(STYLUA_PRG)
add_custom_command(TARGET lintlua
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMAND ${STYLUA_PRG} --color=always --check runtime/)
else()
add_custom_command(TARGET lintlua COMMAND ${CMAKE_COMMAND} -E echo "STYLUA_PRG not found")
endif()
def_cmd_target(lintpy ${FLAKE8_PRG} FLAKE8_PRG false)
if(FLAKE8_PRG)
add_custom_command(TARGET lintpy
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMAND ${FLAKE8_PRG} contrib/ scripts/ src/ test/)
endif()
def_cmd_target(lintsh ${SHELLCHECK_PRG} SHELLCHECK_PRG false)
if(SHELLCHECK_PRG)
add_custom_command(TARGET lintsh
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMAND ${SHELLCHECK_PRG} scripts/vim-patch.sh)
endif()
add_glob_targets(
REQUIRED
TARGET lintlua-luacheck
COMMAND ${LUACHECK_PRG}
FLAGS -q
GLOB_DIRS runtime/ scripts/ src/ test/
GLOB_PAT *.lua
TOUCH_STRATEGY SINGLE
)
add_glob_targets(
TARGET lintlua-stylua
COMMAND ${STYLUA_PRG}
FLAGS --color=always --check
GLOB_DIRS runtime/
GLOB_PAT *.lua
TOUCH_STRATEGY SINGLE
)
add_custom_target(lintlua)
add_dependencies(lintlua lintlua-luacheck lintlua-stylua)
include(InstallHelpers)
add_glob_targets(
TARGET lintpy
COMMAND ${FLAKE8_PRG}
GLOB_DIRS contrib scripts src test
GLOB_PAT *.py
TOUCH_STRATEGY SINGLE
)
add_glob_targets(
TARGET lintsh
COMMAND ${SHELLCHECK_PRG}
FILES scripts/vim-patch.sh
TOUCH_STRATEGY SINGLE
)
install_helper(
FILES ${CMAKE_SOURCE_DIR}/src/man/nvim.1

View File

@ -1,19 +0,0 @@
# Defines a target named ${target}. If ${prg} is undefined the target prints
# "not found".
#
# - Use add_custom_command(TARGET <target_name> ...) to append a command to the
# target.
function(def_cmd_target target prg prg_name prg_fatal)
add_custom_target(${target})
if(NOT prg)
if(prg_fatal)
add_custom_command(TARGET ${target}
COMMAND ${CMAKE_COMMAND} -E echo "${target}: ${prg_name} not found"
COMMAND false)
else()
add_custom_command(TARGET ${target}
COMMAND ${CMAKE_COMMAND} -E echo "${target}: SKIP: ${prg_name} not found")
endif()
endif()
endfunction()

View File

@ -1,16 +0,0 @@
# HACK: This script is invoked with "cmake -P " as a workaround to silence uncrustify.
# Split space-separated string into a cmake list, so that execute_process()
# will pass each file as individual arg to uncrustify.
string(REPLACE " " ";" NVIM_SOURCES ${NVIM_SOURCES})
string(REPLACE " " ";" NVIM_HEADERS ${NVIM_HEADERS})
execute_process(
COMMAND ${UNCRUSTIFY_PRG} -c "${PROJECT_SOURCE_DIR}/src/uncrustify.cfg" -q --check ${NVIM_SOURCES} ${NVIM_HEADERS}
OUTPUT_VARIABLE crusty_out
ERROR_VARIABLE crusty_err
RESULT_VARIABLE crusty_res)
if(NOT crusty_res EQUAL 0)
message(FATAL_ERROR "crusty: ${crusty_res} ${crusty_err}")
endif()

145
cmake/Util.cmake Normal file
View File

@ -0,0 +1,145 @@
# Defines a target that depends on FILES and the files found by globbing
# when using GLOB_PAT and GLOB_DIRS. The target will rerun if any files it
# depends on has changed. Which files the target will run the command on
# depends on the value of TOUCH_STRATEGY.
#
# Options:
# REQUIRED - Abort if COMMAND doesn't exist.
#
# Single value arguments:
# TARGET - Name of the target
# COMMAND - Path of the command to be run
# GLOB_PAT - Glob pattern to use. Only used if GLOB_DIRS is specified
# TOUCH_STRATEGY - Specify touch strategy, meaning decide how to group files
# and connect them to a specific touch file.
#
# For example, let us say we have file A and B and that we create a touch file
# for each of them, TA and TB. This would essentially make file A and B
# independent of each other, meaning that if I change file A and run the
# target, then the target will only run its commands for file A and ignore
# file B.
#
# Another example: let's say we have file A and B, but now we create only a
# single touch file T for both of them. This would mean that if I change
# either file A or B, then the target will run its commands on both A and B.
# Meaning that even if I only change file A, the target will still run
# commands on both A and B.
#
# The more touch files we create for a target, the fewer commands we'll need
# to rerun, and by extension, the more time we'll save. Unfortunately, the
# more touch files we create the more intermediary targets will be created,
# one for each touch file. This makes listing all targets with
# `cmake --build build --target help` less useful since each touch file will
# be listed. The tradeoff that needs to be done here is between performance
# and "discoverability". As a general guideline: the more popular a target is
# and the more time it takes to run it, the more granular you want your touch
# files to be. Conversely, if a target rarely needs to be run or if it's fast,
# then you should create fewer targets.
#
# Possible values for TOUCH_STRATEGY:
# "SINGLE": create a single touch file for all files.
# "PER_FILE": create a touch file for each file. Defaults to this if
# TOUCH_STRATEGY isn't specified.
# "PER_DIR": create a touch file for each directory.
#
# List arguments:
# FLAGS - List of flags to use after COMMAND
# FILES - List of files to use COMMAND on. It's possible to combine this
# with GLOB_PAT and GLOB_DIRS; the files found by globbing will
# simple be added to FILES
# GLOB_DIRS - The directories to recursively search for files with extension
# GLOB_PAT
#
function(add_glob_targets)
cmake_parse_arguments(ARG
"REQUIRED"
"TARGET;COMMAND;GLOB_PAT;TOUCH_STRATEGY"
"FLAGS;FILES;GLOB_DIRS"
${ARGN}
)
if(NOT ARG_COMMAND)
add_custom_target(${ARG_TARGET})
if(ARG_REQUIRED)
add_custom_command(TARGET ${ARG_TARGET}
COMMAND ${CMAKE_COMMAND} -E echo "${ARG_TARGET}: ${ARG_COMMAND} not found"
COMMAND false)
else()
add_custom_command(TARGET ${ARG_TARGET}
COMMAND ${CMAKE_COMMAND} -E echo "${ARG_TARGET} SKIP: ${ARG_COMMAND} not found")
endif()
return()
endif()
foreach(gd ${ARG_GLOB_DIRS})
file(GLOB_RECURSE globfiles ${PROJECT_SOURCE_DIR}/${gd}/${ARG_GLOB_PAT})
list(APPEND ARG_FILES ${globfiles})
endforeach()
if(NOT ARG_TOUCH_STRATEGY)
set(ARG_TOUCH_STRATEGY PER_FILE)
endif()
set(POSSIBLE_TOUCH_STRATEGIES SINGLE PER_FILE PER_DIR)
if(NOT ARG_TOUCH_STRATEGY IN_LIST POSSIBLE_TOUCH_STRATEGIES)
message(FATAL_ERROR "Unrecognized value for TOUCH_STRATEGY: ${ARG_TOUCH_STRATEGY}")
endif()
if(ARG_TOUCH_STRATEGY STREQUAL SINGLE)
set(touch_file ${TOUCHES_DIR}/ran-${ARG_TARGET})
add_custom_command(
OUTPUT ${touch_file}
COMMAND ${CMAKE_COMMAND} -E touch ${touch_file}
COMMAND ${ARG_COMMAND} ${ARG_FLAGS} ${ARG_FILES}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
DEPENDS ${ARG_FILES})
list(APPEND touch_list ${touch_file})
elseif(ARG_TOUCH_STRATEGY STREQUAL PER_FILE)
set(touch_dir ${TOUCHES_DIR}/${ARG_TARGET})
file(MAKE_DIRECTORY ${touch_dir})
foreach(f ${ARG_FILES})
string(REGEX REPLACE "^${PROJECT_SOURCE_DIR}/" "" tf ${f})
string(REGEX REPLACE "[/.]" "-" tf ${tf})
set(touch_file ${touch_dir}/ran-${tf})
add_custom_command(
OUTPUT ${touch_file}
COMMAND ${CMAKE_COMMAND} -E touch ${touch_file}
COMMAND ${ARG_COMMAND} ${ARG_FLAGS} ${f}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
DEPENDS ${f})
list(APPEND touch_list ${touch_file})
endforeach()
elseif(ARG_TOUCH_STRATEGY STREQUAL PER_DIR)
set(touch_dirs)
foreach(f ${ARG_FILES})
get_filename_component(out ${f} DIRECTORY)
list(APPEND touch_dirs ${out})
endforeach()
list(REMOVE_DUPLICATES touch_dirs)
foreach(touch_dir ${touch_dirs})
set(relevant_files)
foreach(f ${ARG_FILES})
get_filename_component(out ${f} DIRECTORY)
if(${touch_dir} STREQUAL ${out})
list(APPEND relevant_files ${f})
endif()
endforeach()
set(td ${TOUCHES_DIR}/${ARG_TARGET})
file(MAKE_DIRECTORY ${td})
string(REGEX REPLACE "^${PROJECT_SOURCE_DIR}/" "" tf ${touch_dir})
string(REGEX REPLACE "[/.]" "-" tf ${tf})
set(touch_file ${td}/ran-${tf})
add_custom_command(
OUTPUT ${touch_file}
COMMAND ${CMAKE_COMMAND} -E touch ${touch_file}
COMMAND ${ARG_COMMAND} ${ARG_FLAGS} ${relevant_files}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
DEPENDS ${relevant_files})
list(APPEND touch_list ${touch_file})
endforeach()
endif()
add_custom_target(${ARG_TARGET} DEPENDS ${touch_list})
endfunction()

View File

@ -1,7 +1,5 @@
option(USE_GCOV "Enable gcov support" OFF)
include(DefCmdTarget)
if(USE_GCOV)
if(CLANG_TSAN)
# GCOV and TSAN results in false data race reports
@ -24,7 +22,6 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -framework CoreServices")
endif()
set(TOUCHES_DIR ${PROJECT_BINARY_DIR}/touches)
set(GENERATOR_DIR ${CMAKE_CURRENT_LIST_DIR}/generators)
set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/nvim/auto)
set(BINARY_LIB_DIR ${PROJECT_BINARY_DIR}/lib/nvim/)
@ -800,16 +797,12 @@ foreach(sfile ${LINT_NVIM_SOURCES})
endforeach()
add_custom_target(lintc DEPENDS ${LINT_TARGETS})
def_cmd_target(lintuncrustify ${UNCRUSTIFY_PRG} UNCRUSTIFY_PRG false)
if(UNCRUSTIFY_PRG)
add_custom_command(TARGET lintuncrustify
COMMAND ${CMAKE_COMMAND}
-D UNCRUSTIFY_PRG=${UNCRUSTIFY_PRG}
-D PROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}
-D NVIM_SOURCES="${NVIM_SOURCES}"
-D NVIM_HEADERS="${NVIM_HEADERS}"
-P ${PROJECT_SOURCE_DIR}/cmake/RunUncrustify.cmake)
endif()
add_glob_targets(
TARGET lintuncrustify
COMMAND ${UNCRUSTIFY_PRG}
FLAGS -c "${PROJECT_SOURCE_DIR}/src/uncrustify.cfg" -q --check
FILES ${LINT_NVIM_SOURCES}
)
add_custom_target(
lintcfull