1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
|
# 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:
#
# 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
# EXCLUDE - List of paths to skip (regex). Works on both directories and
# files.
function(add_glob_target)
cmake_parse_arguments(ARG
""
"TARGET;COMMAND;GLOB_PAT;TOUCH_STRATEGY"
"FLAGS;FILES;GLOB_DIRS;EXCLUDE"
${ARGN}
)
if(NOT ARG_COMMAND)
add_custom_target(${ARG_TARGET})
add_custom_command(TARGET ${ARG_TARGET}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "${ARG_TARGET} SKIP: ${ARG_COMMAND} not found")
return()
endif()
foreach(gd ${ARG_GLOB_DIRS})
file(GLOB_RECURSE globfiles_unnormalized ${PROJECT_SOURCE_DIR}/${gd}/${ARG_GLOB_PAT})
set(globfiles)
foreach(f ${globfiles_unnormalized})
file(TO_CMAKE_PATH "${f}" f)
list(APPEND globfiles ${f})
endforeach()
list(APPEND ARG_FILES ${globfiles})
endforeach()
list(APPEND ARG_EXCLUDE runtime/lua/vim/_meta) # only generated files, always ignore
foreach(exclude_pattern ${ARG_EXCLUDE})
list(FILTER ARG_FILES EXCLUDE REGEX ${exclude_pattern})
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}/${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}/${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}/${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()
# A wrapper function that combines add_custom_command and add_custom_target. It
# essentially models the "make" dependency where a target is only rebuilt if
# any dependencies have been changed.
#
# Important to note is that `DEPENDS` is a bit misleading; it should not only
# specify dependencies but also the files that are being generated/output
# files in order to work correctly.
function(add_target)
cmake_parse_arguments(ARG
""
""
"COMMAND;DEPENDS;CUSTOM_COMMAND_ARGS"
${ARGN}
)
set(target ${ARGV0})
set(touch_file ${TOUCHES_DIR}/${target})
add_custom_command(
OUTPUT ${touch_file}
COMMAND ${CMAKE_COMMAND} -E touch ${touch_file}
COMMAND ${CMAKE_COMMAND} -E env "VIMRUNTIME=${NVIM_RUNTIME_DIR}" ${ARG_COMMAND}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
DEPENDS ${ARG_DEPENDS}
${ARG_CUSTOM_COMMAND_ARGS})
add_custom_target(${target} DEPENDS ${touch_file})
endfunction()
# Set default build type to BUILD_TYPE. Also limit the list of allowable build
# types to the ones defined in variable allowableBuildTypes.
#
# The correct way to specify build type (for example Release) for
# single-configuration generators (Make and Ninja) is to run
#
# cmake -B build -D CMAKE_BUILD_TYPE=Release
# cmake --build build
#
# while for multi-configuration generators (Visual Studio, Xcode and Ninja
# Multi-Config) is to run
#
# cmake -B build
# cmake --build build --config Release
#
# Passing CMAKE_BUILD_TYPE for multi-config generators will not only not be
# used, but also generate a warning for the user.
function(set_default_buildtype BUILD_TYPE)
set(allowableBuildTypes Debug Release MinSizeRel RelWithDebInfo)
if(NOT BUILD_TYPE IN_LIST allowableBuildTypes)
message(FATAL_ERROR "Invalid build type: ${BUILD_TYPE}")
endif()
get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(isMultiConfig)
# Multi-config generators use the first element in CMAKE_CONFIGURATION_TYPES as the default build type
list(INSERT allowableBuildTypes 0 ${BUILD_TYPE})
list(REMOVE_DUPLICATES allowableBuildTypes)
set(CMAKE_CONFIGURATION_TYPES ${allowableBuildTypes} PARENT_SCOPE)
if(CMAKE_BUILD_TYPE)
message(WARNING "CMAKE_BUILD_TYPE specified which is ignored on \
multi-configuration generators. Defaulting to ${BUILD_TYPE} build type.")
endif()
else()
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "${allowableBuildTypes}")
if(NOT CMAKE_BUILD_TYPE)
message(STATUS "CMAKE_BUILD_TYPE not specified, default is '${BUILD_TYPE}'")
set(CMAKE_BUILD_TYPE ${BUILD_TYPE} CACHE STRING "Choose the type of build" FORCE)
elseif(NOT CMAKE_BUILD_TYPE IN_LIST allowableBuildTypes)
message(FATAL_ERROR "Invalid build type: ${CMAKE_BUILD_TYPE}")
else()
message(STATUS "CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
endif()
endif()
endfunction()
# Check if a module is available in Lua
function(check_lua_module LUA_PRG_PATH MODULE RESULT_VAR)
execute_process(COMMAND ${LUA_PRG_PATH} -l "${MODULE}" -e ""
RESULT_VARIABLE module_missing)
if(module_missing)
set(${RESULT_VAR} FALSE PARENT_SCOPE)
else()
set(${RESULT_VAR} TRUE PARENT_SCOPE)
endif()
endfunction()
|