neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

Util.cmake (8861B)


      1 # Defines a target that depends on FILES and the files found by globbing
      2 # when using GLOB_PAT and GLOB_DIRS. The target will rerun if any files it
      3 # depends on has changed. Which files the target will run the command on
      4 # depends on the value of TOUCH_STRATEGY.
      5 #
      6 # Options:
      7 #
      8 # Single value arguments:
      9 # TARGET         - Name of the target
     10 # COMMAND        - Path of the command to be run
     11 # GLOB_PAT       - Glob pattern to use. Only used if GLOB_DIRS is specified
     12 # TOUCH_STRATEGY - Specify touch strategy, meaning decide how to group files
     13 #                  and connect them to a specific touch file.
     14 #
     15 # For example, let us say we have file A and B and that we create a touch file
     16 # for each of them, TA and TB. This would essentially make file A and B
     17 # independent of each other, meaning that if I change file A and run the
     18 # target, then the target will only run its commands for file A and ignore
     19 # file B.
     20 #
     21 # Another example: let's say we have file A and B, but now we create only a
     22 # single touch file T for both of them. This would mean that if I change
     23 # either file A or B, then the target will run its commands on both A and B.
     24 # Meaning that even if I only change file A, the target will still run
     25 # commands on both A and B.
     26 #
     27 # The more touch files we create for a target, the fewer commands we'll need
     28 # to rerun, and by extension, the more time we'll save. Unfortunately, the
     29 # more touch files we create the more intermediary targets will be created,
     30 # one for each touch file. This makes listing all targets with
     31 # `cmake --build build --target help` less useful since each touch file will
     32 # be listed. The tradeoff that needs to be done here is between performance
     33 # and "discoverability". As a general guideline: the more popular a target is
     34 # and the more time it takes to run it, the more granular you want your touch
     35 # files to be. Conversely, if a target rarely needs to be run or if it's fast,
     36 # then you should create fewer targets.
     37 #
     38 # Possible values for TOUCH_STRATEGY:
     39 # "SINGLE":   create a single touch file for all files.
     40 # "PER_FILE": create a touch file for each file. Defaults to this if
     41 #             TOUCH_STRATEGY isn't specified.
     42 # "PER_DIR":  create a touch file for each directory.
     43 #
     44 # List arguments:
     45 # FLAGS     - List of flags to use after COMMAND
     46 # FILES     - List of files to use COMMAND on. It's possible to combine this
     47 #             with GLOB_PAT and GLOB_DIRS; the files found by globbing will
     48 #             simple be added to FILES
     49 # GLOB_DIRS - The directories to recursively search for files with extension
     50 #             GLOB_PAT
     51 # EXCLUDE   - List of paths to skip (regex). Works on both directories and
     52 #             files.
     53 function(add_glob_target)
     54  cmake_parse_arguments(ARG
     55    ""
     56    "TARGET;COMMAND;GLOB_PAT;TOUCH_STRATEGY"
     57    "FLAGS;FILES;GLOB_DIRS;EXCLUDE"
     58    ${ARGN}
     59  )
     60 
     61  if(NOT ARG_COMMAND)
     62    add_custom_target(${ARG_TARGET})
     63    add_custom_command(TARGET ${ARG_TARGET}
     64      POST_BUILD
     65      COMMAND ${CMAKE_COMMAND} -E echo "${ARG_TARGET} SKIP: ${ARG_COMMAND} not found")
     66    return()
     67  endif()
     68 
     69  foreach(gd ${ARG_GLOB_DIRS})
     70    file(GLOB_RECURSE globfiles_unnormalized ${PROJECT_SOURCE_DIR}/${gd}/${ARG_GLOB_PAT})
     71    set(globfiles)
     72    foreach(f ${globfiles_unnormalized})
     73      file(TO_CMAKE_PATH "${f}" f)
     74      list(APPEND globfiles ${f})
     75    endforeach()
     76    list(APPEND ARG_FILES ${globfiles})
     77  endforeach()
     78 
     79  list(APPEND ARG_EXCLUDE runtime/lua/vim/_meta) # only generated files, always ignore
     80  foreach(exclude_pattern ${ARG_EXCLUDE})
     81    list(FILTER ARG_FILES EXCLUDE REGEX ${exclude_pattern})
     82  endforeach()
     83 
     84  if(NOT ARG_TOUCH_STRATEGY)
     85    set(ARG_TOUCH_STRATEGY PER_FILE)
     86  endif()
     87  set(POSSIBLE_TOUCH_STRATEGIES SINGLE PER_FILE PER_DIR)
     88  if(NOT ARG_TOUCH_STRATEGY IN_LIST POSSIBLE_TOUCH_STRATEGIES)
     89    message(FATAL_ERROR "Unrecognized value for TOUCH_STRATEGY: ${ARG_TOUCH_STRATEGY}")
     90  endif()
     91 
     92  if(ARG_TOUCH_STRATEGY STREQUAL SINGLE)
     93    set(touch_file ${TOUCHES_DIR}/${ARG_TARGET})
     94    add_custom_command(
     95      OUTPUT ${touch_file}
     96      COMMAND ${CMAKE_COMMAND} -E touch ${touch_file}
     97      COMMAND ${ARG_COMMAND} ${ARG_FLAGS} ${ARG_FILES}
     98      WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
     99      DEPENDS ${ARG_FILES})
    100    list(APPEND touch_list ${touch_file})
    101  elseif(ARG_TOUCH_STRATEGY STREQUAL PER_FILE)
    102    set(touch_dir ${TOUCHES_DIR}/${ARG_TARGET})
    103    file(MAKE_DIRECTORY ${touch_dir})
    104    foreach(f ${ARG_FILES})
    105      string(REGEX REPLACE "^${PROJECT_SOURCE_DIR}/" "" tf ${f})
    106      string(REGEX REPLACE "[/.]" "-" tf ${tf})
    107      set(touch_file ${touch_dir}/${tf})
    108      add_custom_command(
    109        OUTPUT ${touch_file}
    110        COMMAND ${CMAKE_COMMAND} -E touch ${touch_file}
    111        COMMAND ${ARG_COMMAND} ${ARG_FLAGS} ${f}
    112        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
    113        DEPENDS ${f})
    114      list(APPEND touch_list ${touch_file})
    115    endforeach()
    116  elseif(ARG_TOUCH_STRATEGY STREQUAL PER_DIR)
    117    set(touch_dirs)
    118    foreach(f ${ARG_FILES})
    119      get_filename_component(out ${f} DIRECTORY)
    120      list(APPEND touch_dirs ${out})
    121    endforeach()
    122    list(REMOVE_DUPLICATES touch_dirs)
    123 
    124    foreach(touch_dir ${touch_dirs})
    125      set(relevant_files)
    126      foreach(f ${ARG_FILES})
    127        get_filename_component(out ${f} DIRECTORY)
    128        if(${touch_dir} STREQUAL ${out})
    129          list(APPEND relevant_files ${f})
    130        endif()
    131      endforeach()
    132 
    133      set(td ${TOUCHES_DIR}/${ARG_TARGET})
    134      file(MAKE_DIRECTORY ${td})
    135      string(REGEX REPLACE "^${PROJECT_SOURCE_DIR}/" "" tf ${touch_dir})
    136      string(REGEX REPLACE "[/.]" "-" tf ${tf})
    137      set(touch_file ${td}/${tf})
    138 
    139      add_custom_command(
    140        OUTPUT ${touch_file}
    141        COMMAND ${CMAKE_COMMAND} -E touch ${touch_file}
    142        COMMAND ${ARG_COMMAND} ${ARG_FLAGS} ${relevant_files}
    143        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
    144        DEPENDS ${relevant_files})
    145      list(APPEND touch_list ${touch_file})
    146    endforeach()
    147  endif()
    148 
    149  add_custom_target(${ARG_TARGET} DEPENDS ${touch_list})
    150 endfunction()
    151 
    152 # A wrapper function that combines add_custom_command and add_custom_target. It
    153 # essentially models the "make" dependency where a target is only rebuilt if
    154 # any dependencies have been changed.
    155 #
    156 # Important to note is that `DEPENDS` is a bit misleading; it should not only
    157 # specify dependencies but also the files that are being generated/output
    158 # files in order to work correctly.
    159 function(add_target)
    160  cmake_parse_arguments(ARG
    161    ""
    162    ""
    163    "COMMAND;DEPENDS;CUSTOM_COMMAND_ARGS"
    164    ${ARGN}
    165  )
    166  set(target ${ARGV0})
    167 
    168  set(touch_file ${TOUCHES_DIR}/${target})
    169  add_custom_command(
    170    OUTPUT ${touch_file}
    171    COMMAND ${CMAKE_COMMAND} -E touch ${touch_file}
    172    COMMAND ${CMAKE_COMMAND} -E env "VIMRUNTIME=${NVIM_RUNTIME_DIR}" ${ARG_COMMAND}
    173    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
    174    DEPENDS ${ARG_DEPENDS}
    175    ${ARG_CUSTOM_COMMAND_ARGS})
    176  add_custom_target(${target} DEPENDS ${touch_file})
    177 endfunction()
    178 
    179 # Set default build type to BUILD_TYPE.
    180 #
    181 # The correct way to specify build type (for example Release) for
    182 # single-configuration generators (Make and Ninja) is to run
    183 #
    184 # cmake -B build -D CMAKE_BUILD_TYPE=Release
    185 # cmake --build build
    186 #
    187 # while for multi-configuration generators (Visual Studio, Xcode and Ninja
    188 # Multi-Config) is to run
    189 #
    190 # cmake -B build
    191 # cmake --build build --config Release
    192 #
    193 # Passing CMAKE_BUILD_TYPE for multi-config generators will not only not be
    194 # used, but also generate a warning for the user.
    195 function(set_default_buildtype BUILD_TYPE)
    196  set(defaultBuildTypes Debug Release MinSizeRel RelWithDebInfo)
    197 
    198  get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
    199  if(isMultiConfig)
    200    # Multi-config generators use the first element in
    201    # CMAKE_CONFIGURATION_TYPES as the default build type
    202    list(INSERT defaultBuildTypes 0 ${BUILD_TYPE})
    203    list(REMOVE_DUPLICATES defaultBuildTypes)
    204    set(CMAKE_CONFIGURATION_TYPES ${defaultBuildTypes} PARENT_SCOPE)
    205    if(CMAKE_BUILD_TYPE)
    206      message(WARNING "CMAKE_BUILD_TYPE specified which is ignored on \
    207      multi-configuration generators. Defaulting to ${BUILD_TYPE} build type.")
    208    endif()
    209  else()
    210    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "${defaultBuildTypes}")
    211    if(NOT CMAKE_BUILD_TYPE)
    212      message(STATUS "CMAKE_BUILD_TYPE not specified, default is '${BUILD_TYPE}'")
    213      set(CMAKE_BUILD_TYPE ${BUILD_TYPE} CACHE STRING "Choose the type of build" FORCE)
    214    else()
    215      message(STATUS "CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
    216    endif()
    217  endif()
    218 endfunction()
    219 
    220 # Check if a module is available in Lua
    221 function(check_lua_module LUA_PRG_PATH MODULE RESULT_VAR)
    222  execute_process(COMMAND ${LUA_PRG_PATH} -l "${MODULE}" -e ""
    223    RESULT_VARIABLE module_missing)
    224  if(module_missing)
    225    set(${RESULT_VAR} FALSE PARENT_SCOPE)
    226  else()
    227    set(${RESULT_VAR} TRUE PARENT_SCOPE)
    228  endif()
    229 endfunction()