123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- # 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.
- #
- # 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(defaultBuildTypes Debug Release MinSizeRel RelWithDebInfo)
- 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 defaultBuildTypes 0 ${BUILD_TYPE})
- list(REMOVE_DUPLICATES defaultBuildTypes)
- set(CMAKE_CONFIGURATION_TYPES ${defaultBuildTypes} 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 "${defaultBuildTypes}")
- 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)
- 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()
|