diff --git a/CMakeLists.txt b/CMakeLists.txt index 4dabb596..63792cf5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,7 +33,7 @@ ensure_entry_point() # use ccache if available include(ccache) -enable_ccache_if_possible() +enable_ccache() # set restrictive compilation warnings include(warnings) diff --git a/cmake/ccache.cmake b/cmake/ccache.cmake index 247ec0af..513b36a3 100644 --- a/cmake/ccache.cmake +++ b/cmake/ccache.cmake @@ -22,24 +22,54 @@ cmake_minimum_required(VERSION 3.4) -include(modern_project_structure) - -macro(_enable_ccache) - set(_options ACCOUNT_FOR_COMPILE_TIME_HEADER_CHANGES ACCOUNT_FOR_PCH ACCOUNT_FOR_MODULES) - set(_one_value_args MODE BASE_DIR) - set(_multi_value_args PREFIXES) - cmake_parse_arguments(PARSE_ARGV 0 _enable_ccache "${_options}" "${_one_value_args}" "${_multi_value_args}") +macro(_enable_ccache_failed) + if(NOT _enable_ccache_QUIET) + message(STATUS "Enabling ccache - failed") + endif() + return() +endmacro() +# +# enable_ccache([PROGRAM] # ccache by default +# [QUIET] [REQUIRED] +# [MODE DIRECT_PREPROCESSOR|DIRECT_DEPEND|PREPROCESSOR|DEPEND] # DIRECT_PREPROCESSOR by default +# [BASE_DIR dir] # ${CMAKE_SOURCE_DIR} by default +# [ACCOUNT_FOR_COMPILE_TIME_HEADER_CHANGES] +# [ACCOUNT_FOR_PCH] +# [ACCOUNT_FOR_MODULES] +# [PREFIXES prefixes...] +# ) +# +# BASE_DIR +# Set this option to ${CMAKE_BINARY_DIR} if you use FetchContent a lot for many projects with the same build options. +# Otherwise, if most of the sources come from the project itself then the default ${CMAKE_SOURCE_DIR} may be +# a better choice. +# +# ACCOUNT_FOR_COMPILE_TIME_HEADER_CHANGES +# Use it if some header files are being generated by the compilation process. +# +# ACCOUNT_FOR_PCH +# Use it if precompiled headers are enabled in your project. Automatically includes uses +# ACCOUNT_FOR_COMPILE_TIME_HEADER_CHANGES as well. +# See here for details: https://ccache.dev/manual/4.2.1.html#_precompiled_headers +# +# ACCOUNT_FOR_MODULES +# Use it for projects with C++20 modules. Requires DIRECT_DEPEND mode. +# +# PREFIXES +# A list of other tools that should be used together with ccache as a compiler launcher +# (i.e. distcc, icecc, sccache-dist, ...). +# +function(enable_ccache) if(NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) message(FATAL_ERROR "'enable_ccache' function should be called from the top-level CMakeLists.txt file!") # otherwise, it will not work for XCode - return() endif() - set(_ccacheEnv - CCACHE_CPP2=1 # avoids spurious warnings with some compilers for ccache older than 3.3 - # CCACHE_ABSSTDERR=1 # reverts absolute paths after applying CCACHE_BASEDIR - ) + set(_options QUIET REQUIRED ACCOUNT_FOR_COMPILE_TIME_HEADER_CHANGES ACCOUNT_FOR_PCH ACCOUNT_FOR_MODULES) + set(_one_value_args PROGRAM MODE BASE_DIR) + set(_multi_value_args PREFIXES) + cmake_parse_arguments(PARSE_ARGV 0 _enable_ccache "${_options}" "${_one_value_args}" "${_multi_value_args}") # validate and process arguments if(_enable_ccache_UNPARSED_ARGUMENTS) @@ -57,6 +87,56 @@ macro(_enable_ccache) endif() endif() + if(NOT _enable_ccache_QUIET) + message(STATUS "Enabling ccache") + endif() + + if(${_enable_ccache_REQUIRED}) + set(_error_log_level FATAL_ERROR) + elseif(NOT _enable_ccache_QUIET) + set(_error_log_level STATUS) + endif() + + if(NOT CMAKE_GENERATOR MATCHES "Ninja|Makefiles|Xcode") + if(DEFINED _error_log_level) + message(${_error_log_level} "ccache support not enabled: unsupported generator '${CMAKE_GENERATOR}'") + endif() + _enable_ccache_failed() + endif() + + if(NOT DEFINED _enable_ccache_PROGRAM) + set(_enable_ccache_PROGRAM ccache) + endif() + + find_program(CCACHE_PATH ${_enable_ccache_PROGRAM}) + if(CCACHE_PATH) + if(NOT _enable_ccache_QUIET) + message(STATUS " Executable: ${CCACHE_PATH}") + endif() + + # get version number + execute_process(COMMAND "${CCACHE_PATH}" --version OUTPUT_VARIABLE _output) + string(REPLACE "\n" ";" _output "${_output}") + foreach(_line ${_output}) + string(REGEX REPLACE "ccache version ([\\.0-9]+)$" "\\1" _ccache_version "${_line}") + if(_ccache_version) + if(NOT _enable_ccache_QUIET) + message(STATUS " Version: ${_ccache_version}") + endif() + break() + endif() + endforeach() + else() + if(DEFINED _error_log_level) + message(${_error_log_level} " '${_enable_ccache_PROGRAM}' executable was not found") + endif() + _enable_ccache_failed() + endif() + + if("${_ccache_version}" VERSION_LESS 3.3.0) + list(APPEND _ccacheEnv CCACHE_CPP2=1) # avoids spurious warnings with some compilers for ccache older than 3.3 + endif() + if(_enable_ccache_MODE STREQUAL DIRECT_DEPEND) list(APPEND _ccacheEnv CCACHE_DIRECT=1 CCACHE_DEPEND=1) elseif(_enable_ccache_MODE STREQUAL PREPROCESSOR) @@ -69,6 +149,7 @@ macro(_enable_ccache) endif() if(_enable_ccache_BASE_DIR) + # CCACHE_ABSSTDERR=1 # reverts absolute paths after applying CCACHE_BASEDIR if(NOT EXISTS ${_enable_ccache_BASE_DIR}) message(FATAL_ERROR "Base directory '${_enable_ccache_BASE_DIR}' does not exist") endif() @@ -103,13 +184,15 @@ macro(_enable_ccache) list(APPEND _ccacheEnv "CCACHE_SLOPPINESS=${_sloppiness_txt}") endif() - message(STATUS "Enabling ccache with '${_ccacheEnv}'") + if(NOT _enable_ccache_QUIET) + message(STATUS " Environment: ${_ccacheEnv}") + endif() if(CMAKE_GENERATOR MATCHES "Ninja|Makefiles") foreach(_lang IN ITEMS C CXX OBJC OBJCXX CUDA) set(CMAKE_${_lang}_COMPILER_LAUNCHER ${CMAKE_COMMAND} -E env - ${_ccacheEnv} ${_ccache_path} + ${_ccacheEnv} ${CCACHE_PATH} PARENT_SCOPE ) endforeach() @@ -132,62 +215,9 @@ macro(_enable_ccache) set(CMAKE_XCODE_ATTRIBUTE_CXX ${launchCXX} PARENT_SCOPE) set(CMAKE_XCODE_ATTRIBUTE_LD ${launchC} PARENT_SCOPE) set(CMAKE_XCODE_ATTRIBUTE_LDPLUSPLUS ${launchCXX} PARENT_SCOPE) - else() - message(WARNING "'${CMAKE_GENERATOR}' generator is not supported by ccache!") - return() - endif() -endmacro() - - -# -# enable_ccache([MODE DIRECT_PREPROCESSOR|DIRECT_DEPEND|PREPROCESSOR|DEPEND] # DIRECT_PREPROCESSOR by default -# [BASE_DIR dir] # ${CMAKE_SOURCE_DIR} by default -# [ACCOUNT_FOR_COMPILE_TIME_HEADER_CHANGES] -# [ACCOUNT_FOR_PCH] -# [ACCOUNT_FOR_MODULES] -# [PREFIXES prefixes...]) -# -# BASE_DIR -# Set this option to ${CMAKE_BINARY_DIR} if you use FetchContent a lot for many projects with the same build options. -# Otherwise, if most of the sources come from the project itself then the default ${CMAKE_SOURCE_DIR} may be -# a better choice. -# -# ACCOUNT_FOR_COMPILE_TIME_HEADER_CHANGES -# Use it if some header files are being generated by the compilation process. -# -# ACCOUNT_FOR_PCH -# Use it if precompiled headers are enabled in your project. Automatically includes uses -# ACCOUNT_FOR_COMPILE_TIME_HEADER_CHANGES as well. -# See here for details: https://ccache.dev/manual/4.2.1.html#_precompiled_headers -# -# ACCOUNT_FOR_MODULES -# Use it for projects with C++20 modules. Requires DIRECT_DEPEND mode. -# -# PREFIXES -# A list of other tools that should be used together with ccache as a compiler launcher -# (i.e. distcc, icecc, sccache-dist, ...). -# -function(enable_ccache) - find_program(_ccache_path NAMES "ccache") - if(NOT _ccache_path) - message(FATAL_ERROR "'ccache' executable not found!") - return() endif() - _enable_ccache() -endfunction() - -function(enable_ccache_if_possible) - find_program(_ccache_path NAMES "ccache") - if(NOT _ccache_path) - message(STATUS "ccache support not enabled: the executable was not found") - return() - endif() - - if(NOT CMAKE_GENERATOR MATCHES "Ninja|Makefiles|Xcode") - message(STATUS "ccache support not enabled: unsupported generator '${CMAKE_GENERATOR}'") - return() - endif() - - _enable_ccache() + if(NOT _enable_ccache_QUIET) + message(STATUS "Enabling ccache - done") + endif() endfunction()