aboutsummaryrefslogtreecommitdiff
path: root/thirdparty/ryml/ext/c4core/cmake/c4Project.cmake
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/ryml/ext/c4core/cmake/c4Project.cmake')
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/c4Project.cmake3691
1 files changed, 0 insertions, 3691 deletions
diff --git a/thirdparty/ryml/ext/c4core/cmake/c4Project.cmake b/thirdparty/ryml/ext/c4core/cmake/c4Project.cmake
deleted file mode 100644
index 60c8717fe..000000000
--- a/thirdparty/ryml/ext/c4core/cmake/c4Project.cmake
+++ /dev/null
@@ -1,3691 +0,0 @@
-if(NOT _c4_project_included)
-set(_c4_project_included ON)
-set(_c4_project_file ${CMAKE_CURRENT_LIST_FILE})
-set(_c4_project_dir ${CMAKE_CURRENT_LIST_DIR})
-
-
-# "I didn't have time to write a short letter, so I wrote a long one
-# instead." -- Mark Twain
-#
-# ... Eg, hopefully this code will be cleaned up. There's a lot of
-# code here that can be streamlined into a more intuitive arrangement.
-
-
-cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
-
-list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR})
-set_property(GLOBAL PROPERTY USE_FOLDERS ON)
-
-include(ConfigurationTypes)
-include(CreateSourceGroup)
-include(c4SanitizeTarget)
-include(c4StaticAnalysis)
-include(PrintVar)
-include(c4CatSources)
-include(c4Doxygen)
-include(PatchUtils)
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-# define c4 project settings
-
-set(C4_EXTERN_DIR "$ENV{C4_EXTERN_DIR}" CACHE PATH "the directory where imported projects should be looked for (or cloned in when not found)")
-set(C4_DBG_ENABLED OFF CACHE BOOL "enable detailed cmake logs in c4Project code")
-set(C4_LIBRARY_TYPE "" CACHE STRING "default library type: either \"\"(defer to BUILD_SHARED_LIBS),INTERFACE,STATIC,SHARED,MODULE")
-set(C4_SOURCE_TRANSFORM NONE CACHE STRING "global source transform method")
-set(C4_HDR_EXTS "h;hpp;hh;h++;hxx" CACHE STRING "list of header extensions for determining which files are headers")
-set(C4_SRC_EXTS "c;cpp;cc;c++;cxx;cu;" CACHE STRING "list of compilation unit extensions for determining which files are sources")
-set(C4_ADD_EXTS "natvis" CACHE STRING "list of additional file extensions that might be added as sources to targets")
-set(C4_GEN_SRC_EXT "cpp" CACHE STRING "the extension of the output source files resulting from concatenation")
-set(C4_GEN_HDR_EXT "hpp" CACHE STRING "the extension of the output header files resulting from concatenation")
-set(C4_CXX_STANDARDS "20;17;14;11" CACHE STRING "list of CXX standards")
-set(C4_CXX_STANDARD_DEFAULT "11" CACHE STRING "the default CXX standard for projects not specifying one")
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-
-macro(c4_log)
- message(STATUS "${_c4_prefix}: ${ARGN}")
-endmacro()
-
-
-macro(c4_err)
- message(FATAL_ERROR "${_c4_prefix}: ${ARGN}")
-endmacro()
-
-
-macro(c4_dbg)
- if(C4_DBG_ENABLED)
- message(STATUS "${_c4_prefix}: ${ARGN}")
- endif()
-endmacro()
-
-
-macro(c4_log_var varname)
- c4_log("${varname}=${${varname}} ${ARGN}")
-endmacro()
-macro(c4_log_vars)
- set(____s____)
- foreach(varname ${ARGN})
- set(____s____ "${____s____}${varname}=${${varname}} ")
- endforeach()
- c4_log("${____s____}")
-endmacro()
-macro(c4_dbg_var varname)
- c4_dbg("${varname}=${${varname}} ${ARGN}")
-endmacro()
-macro(c4_log_var_if varname)
- if(${varname})
- c4_log("${varname}=${${varname}} ${ARGN}")
- endif()
-endmacro()
-macro(c4_dbg_var_if varname)
- if(${varname})
- c4_dbg("${varname}=${${varname}} ${ARGN}")
- endif()
-endmacro()
-
-
-macro(_c4_show_pfx_vars)
- if(NOT ("${ARGN}" STREQUAL ""))
- c4_log("prefix vars: ${ARGN}")
- endif()
- print_var(_c4_prefix)
- print_var(_c4_ocprefix)
- print_var(_c4_ucprefix)
- print_var(_c4_lcprefix)
- print_var(_c4_oprefix)
- print_var(_c4_uprefix)
- print_var(_c4_lprefix)
-endmacro()
-
-
-function(c4_zero_pad padded size str)
- string(LENGTH "${str}" len)
- math(EXPR numchars "${size} - ${len}")
- if(numchars EQUAL 0)
- set(${padded} "${str}" PARENT_SCOPE)
- else()
- set(out "${str}")
- math(EXPR ncm1 "${numchars} - 1")
- foreach(z RANGE ${ncm1})
- set(out "0${out}")
- endforeach()
- set(${padded} "${out}" PARENT_SCOPE)
- endif()
-endfunction()
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-
-# handy macro for dealing with arguments in one single statement.
-# look for example usage cases below.
-macro(_c4_handle_args)
- set(opt0arg
- )
- set(opt1arg
- _PREFIX
- )
- set(optNarg
- _ARGS0
- _ARGS1
- _ARGSN
- _ARGS
- _DEPRECATE
- )
- # parse the arguments to this macro to find out the required arguments
- cmake_parse_arguments("__c4ha" "${opt0arg}" "${opt1arg}" "${optNarg}" ${ARGN})
- # now parse the required arguments
- cmake_parse_arguments("${__c4ha__PREFIX}" "${__c4ha__ARGS0}" "${__c4ha__ARGS1}" "${__c4ha__ARGSN}" ${__c4ha__ARGS})
- # raise an error on deprecated arguments
- foreach(a ${__c4ha__DEPRECATE})
- list(FIND __c4ha__ARGS ${a} contains)
- if(NOT (${contains} EQUAL -1))
- c4err("${a} is deprecated")
- endif()
- endforeach()
-endmacro()
-
-# fallback to provided default(s) if argument is not set
-macro(_c4_handle_arg argname)
- if("${_${argname}}" STREQUAL "")
- set(_${argname} "${ARGN}")
- else()
- set(_${argname} "${_${argname}}")
- endif()
-endmacro()
-macro(_c4_handle_arg_no_pfx argname)
- if("${${argname}}" STREQUAL "")
- set(${argname} "${ARGN}")
- else()
- set(${argname} "${${argname}}")
- endif()
-endmacro()
-
-
-# if ${_${argname}} is non empty, return it
-# otherwise, fallback to ${_c4_uprefix}${argname}
-# otherwise, fallback to C4_${argname}
-# otherwise, fallback to provided default through ${ARGN}
-macro(_c4_handle_arg_or_fallback argname)
- if(NOT ("${_${argname}}" STREQUAL ""))
- c4_dbg("handle arg ${argname}: picking explicit value _${argname}=${_${argname}}")
- else()
- foreach(_c4haf_varname "${_c4_uprefix}${argname}" "C4_${argname}" "${argname}" "CMAKE_${argname}")
- set(v ${${_c4haf_varname}})
- if("${v}" STREQUAL "")
- c4_dbg("handle arg ${argname}: ${_c4haf_varname}: empty, continuing")
- else()
- c4_dbg("handle arg ${argname}: ${_c4haf_varname}=${v} not empty!")
- c4_setg(_${argname} "${v}")
- break()
- endif()
- endforeach()
- if("${_${argname}}" STREQUAL "")
- c4_dbg("handle arg ${argname}: picking default: ${ARGN}")
- c4_setg(_${argname} "${ARGN}")
- endif()
- endif()
-endmacro()
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-
-function(c4_get_config var name)
- c4_dbg("get_config: ${var} ${name}")
- c4_get_from_first_of(config ${ARGN} VARS ${_c4_uprefix}${name} C4_${name} ${name})
- c4_dbg("get_config: ${var} ${name}=${config}")
- set(${var} ${config} PARENT_SCOPE)
-endfunction()
-
-
-function(c4_get_from_first_of var)
- _c4_handle_args(_ARGS ${ARGN}
- _ARGS0
- REQUIRED # raise an error if no set variable was found
- ENV # if none of the provided vars is given,
- # then search next on environment variables
- # of the same name, using the same sequence
- _ARGS1
- DEFAULT
- _ARGSN
- VARS
- )
- c4_dbg("get_from_first_of(): searching ${var}")
- foreach(_var ${_VARS})
- set(val ${${_var}})
- c4_dbg("${var}: searching ${_var}=${val}")
- if(NOT ("${val}" STREQUAL ""))
- set(${var} "${val}" PARENT_SCOPE)
- return()
- endif()
- endforeach()
- if(_ENV)
- foreach(_envvar ${_VARS})
- set(val $ENV{${_envvar}})
- c4_dbg("${var}: searching environment variable ${_envvar}=${val}")
- if(NOT ("${val}" STREQUAL ""))
- c4_dbg("${var}: picking ${val} from ${_envvar}")
- set(${var} "${val}" PARENT_SCOPE)
- return()
- endif()
- endforeach()
- endif()
- if(_REQUIRED)
- c4_err("could not find a value for the variable ${var}")
- endif()
- set(${var} ${_DEFAULT} PARENT_SCOPE)
-endfunction()
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-
-# assumes a prior call to project()
-function(c4_project)
- _c4_handle_args(_ARGS ${ARGN}
- _ARGS0 # zero-value macro arguments
- STANDALONE # Declare that targets from this project MAY be
- # compiled in standalone mode. In this mode, any
- # designated libraries on which a target depends
- # will be incorporated into the target instead of
- # being linked with it. The effect is to "flatten"
- # those libraries into the requesting library, with
- # their sources now becoming part of the requesting
- # library; their dependencies are transitively handled.
- # Note that requesting targets must explicitly
- # opt-in to this behavior via the INCORPORATE
- # argument to c4_add_library() or
- # c4_add_executable(). Note also that this behavior
- # is only enabled if this project's option
- # ${prefix}_STANDALONE or C4_STANDALONE is set to ON.
- _ARGS1 # one-value macro arguments
- AUTHOR # specify author(s); used in cpack
- VERSION # cmake does not accept semantic versioning so we provide
- # that here (see https://gitlab.kitware.com/cmake/cmake/-/issues/16716)
- CXX_STANDARD # one of latest;${C4_VALID_CXX_STANDARDS}
- # if this is not provided, falls back on
- # ${uprefix}CXX_STANDARD, then C4_CXX_STANDARD,
- # then CXX_STANDARD. if none are provided,
- # defaults to 11
- _ARGSN # multi-value macro arguments
- )
- # get the prefix from the call to project()
- set(prefix ${PROJECT_NAME})
- string(TOUPPER "${prefix}" ucprefix) # ucprefix := upper case prefix
- string(TOLOWER "${prefix}" lcprefix) # lcprefix := lower case prefix
- if(NOT _c4_prefix)
- c4_setg(_c4_is_root_proj ON)
- c4_setg(_c4_root_proj ${prefix})
- c4_setg(_c4_root_uproj ${ucprefix})
- c4_setg(_c4_root_lproj ${lcprefix})
- c4_setg(_c4_curr_path "")
- else()
- c4_setg(_c4_is_root_proj OFF)
- if(_c4_curr_path)
- c4_setg(_c4_curr_path "${_c4_curr_path}/${prefix}")
- else()
- c4_setg(_c4_curr_path "${prefix}")
- endif()
- endif()
- c4_setg(_c4_curr_subproject ${prefix})
- # get the several prefix flavors
- c4_setg(_c4_ucprefix ${ucprefix})
- c4_setg(_c4_lcprefix ${lcprefix})
- c4_setg(_c4_ocprefix ${prefix}) # ocprefix := original case prefix
- c4_setg(_c4_prefix ${prefix}) # prefix := original prefix
- c4_setg(_c4_oprefix ${prefix}) # oprefix := original prefix
- c4_setg(_c4_uprefix ${_c4_ucprefix}) # upper prefix: for variables
- c4_setg(_c4_lprefix ${_c4_lcprefix}) # lower prefix: for targets
- if(_c4_oprefix)
- c4_setg(_c4_oprefix "${_c4_oprefix}_")
- endif()
- if(_c4_uprefix)
- c4_setg(_c4_uprefix "${_c4_uprefix}_")
- endif()
- if(_c4_lprefix)
- c4_setg(_c4_lprefix "${_c4_lprefix}-")
- endif()
- #
- if(_STANDALONE)
- option(${_c4_uprefix}STANDALONE
- "Enable compilation of opting-in targets from ${_c4_lcprefix} in standalone mode (ie, incorporate subprojects as specified in the INCORPORATE clause to c4_add_library/c4_add_target)"
- ${_c4_is_root_proj})
- c4_setg(_c4_root_proj_standalone ${_c4_uprefix}STANDALONE)
- endif()
- _c4_handle_arg_or_fallback(CXX_STANDARD ${C4_CXX_STANDARD_DEFAULT})
- _c4_handle_arg(VERSION 0.0.0-pre0)
- _c4_handle_arg(AUTHOR "")
- _c4_handle_semantic_version(${_VERSION})
- #
- # make sure project-wide settings are defined -- see cmake's
- # documentation for project(), which defines these and other
- # variables
- if("${PROJECT_DESCRIPTION}" STREQUAL "")
- c4_setg(PROJECT_DESCRIPTION "${prefix}")
- c4_setg(${prefix}_DESCRIPTION "${prefix}")
- endif()
- if("${PROJECT_HOMEPAGE_URL}" STREQUAL "")
- c4_setg(PROJECT_HOMEPAGE_URL "")
- c4_setg(${prefix}_HOMEPAGE_URL "")
- endif()
- # other specific c4_project properties
- c4_setg(PROJECT_AUTHOR "${_AUTHOR}")
- c4_setg(${prefix}_AUTHOR "${_AUTHOR}")
-
- # CXX standard
- if("${_CXX_STANDARD}" STREQUAL "latest")
- _c4_find_latest_supported_cxx_standard(_CXX_STANDARD)
- endif()
- c4_log("using C++ standard: C++${_CXX_STANDARD}")
- c4_set_proj_prop(CXX_STANDARD "${_CXX_STANDARD}")
- c4_setg(${_c4_uprefix}CXX_STANDARD "${_CXX_STANDARD}")
- if(${_CXX_STANDARD})
- c4_set_cxx(${_CXX_STANDARD})
- endif()
-
- # we are opinionated with respect to directory structure
- c4_setg(${_c4_uprefix}SRC_DIR ${CMAKE_CURRENT_LIST_DIR}/src)
- c4_setg(${_c4_uprefix}EXT_DIR ${CMAKE_CURRENT_LIST_DIR}/ext)
- c4_setg(${_c4_uprefix}API_DIR ${CMAKE_CURRENT_LIST_DIR}/api)
- # opionionated also for directory test
- # opionionated also for directory bm (benchmarks)
-
- if("${C4_DEV}" STREQUAL "")
- option(C4_DEV "enable development targets for all c4 projects" OFF)
- endif()
- option(${_c4_uprefix}DEV "enable development targets: tests, benchmarks, sanitize, static analysis, coverage" ${C4_DEV})
-
- if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/test")
- cmake_dependent_option(${_c4_uprefix}BUILD_TESTS "build unit tests" ON ${_c4_uprefix}DEV OFF)
- else()
- c4_dbg("no tests: directory does not exist: ${CMAKE_CURRENT_LIST_DIR}/test")
- endif()
- if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/bm")
- cmake_dependent_option(${_c4_uprefix}BUILD_BENCHMARKS "build benchmarks" ON ${_c4_uprefix}DEV OFF)
- else()
- c4_dbg("no benchmarks: directory does not exist: ${CMAKE_CURRENT_LIST_DIR}/bm")
- endif()
- if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/api")
- cmake_dependent_option(${_c4_uprefix}BUILD_API "build API" OFF ${_c4_uprefix}DEV OFF)
- else()
- c4_dbg("no API generation: directory does not exist: ${CMAKE_CURRENT_LIST_DIR}/api")
- endif()
- if(_c4_is_root_proj)
- c4_setup_coverage()
- endif()
- c4_setup_valgrind(${_c4_uprefix}DEV)
- c4_setup_sanitize(${_c4_uprefix}DEV)
- c4_setup_static_analysis(${_c4_uprefix}DEV)
- c4_setup_doxygen(${_c4_uprefix}DEV)
-
- # option to use libc++
- option(${_c4_uprefix}USE_LIBCXX "use libc++ instead of the default standard library" OFF)
- if(${_c4_uprefix}USE_LIBCXX)
- if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
- c4_log("using libc++")
- list(APPEND CMAKE_CXX_FLAGS -stdlib=libc++)
- list(APPEND CMAKE_EXE_LINKER_FLAGS -lc++)
- list(APPEND CMAKE_MODULE_LINKER_FLAGS -lc++)
- list(APPEND CMAKE_SHARED_LINKER_FLAGS -lc++)
- list(APPEND CMAKE_STATIC_LINKER_FLAGS -lc++)
- else()
- c4_err("libc++ can only be used with clang")
- endif()
- endif()
-
- # default compilation flags
- set(${_c4_uprefix}CXX_FLAGS "${${_c4_uprefix}CXX_FLAGS_FWD}" CACHE STRING "compilation flags for ${_c4_prefix} targets")
- set(${_c4_uprefix}CXX_LINKER_FLAGS "${${_c4_uprefix}CXX_LINKER_FLAGS_FWD}" CACHE STRING "linker flags for ${_c4_prefix} targets")
- c4_dbg_var_if(${_c4_uprefix}CXX_LINKER_FLAGS_FWD)
- c4_dbg_var_if(${_c4_uprefix}CXX_FLAGS_FWD)
- c4_dbg_var_if(${_c4_uprefix}CXX_LINKER_FLAGS)
- c4_dbg_var_if(${_c4_uprefix}CXX_FLAGS)
-
- # Dev compilation flags, appended to the project's flags. They
- # are enabled when in dev mode, but provided as a (default-disabled)
- # option when not in dev mode
- c4_dbg_var_if(${_c4_uprefix}CXX_FLAGS_OPT_FWD)
- c4_setg(${_c4_uprefix}CXX_FLAGS_OPT "${${_c4_uprefix}CXX_FLAGS_OPT_FWD}")
- c4_optional_compile_flags_dev(WERROR "Compile with warnings as errors"
- GCC_CLANG -Werror -pedantic-errors
- MSVC /WX
- )
- c4_optional_compile_flags_dev(STRICT_ALIASING "Enable strict aliasing"
- GCC_CLANG -fstrict-aliasing
- MSVC # does it have this?
- )
- c4_optional_compile_flags_dev(PEDANTIC "Compile in pedantic mode"
- GCC ${_C4_PEDANTIC_FLAGS_GCC}
- CLANG ${_C4_PEDANTIC_FLAGS_CLANG}
- MSVC ${_C4_PEDANTIC_FLAGS_MSVC}
- )
- c4_dbg_var_if(${_c4_uprefix}CXX_FLAGS_OPT)
-endfunction(c4_project)
-
-
-# cmake: VERSION argument in project() does not accept semantic versioning
-# see: https://gitlab.kitware.com/cmake/cmake/-/issues/16716
-macro(_c4_handle_semantic_version version)
- # https://stackoverflow.com/questions/18658233/split-string-to-3-variables-in-cmake
- string(REPLACE "." ";" version_list ${version})
- list(GET version_list 0 _major)
- list(GET version_list 1 _minor)
- list(GET version_list 2 _patch)
- if("${_patch}" STREQUAL "")
- set(_patch 1)
- set(_tweak)
- else()
- string(REGEX REPLACE "([0-9]+)[-_.]?(.*)" "\\2" _tweak ${_patch}) # do this first
- string(REGEX REPLACE "([0-9]+)[-_.]?(.*)" "\\1" _patch ${_patch}) # ... because this replaces _patch
- endif()
- # because cmake handles only numeric tweak fields, make sure to skip our
- # semantic tweak field if it is not numeric
- if(${_tweak} MATCHES "^[0-9]+$")
- set(_safe_tweak ${_tweak})
- set(_safe_version ${_major}.${_minor}.${_patch}.${_tweak})
- else()
- set(_safe_tweak)
- set(_safe_version ${_major}.${_minor}.${_patch})
- endif()
- c4_setg(PROJECT_VERSION_FULL ${version})
- c4_setg(PROJECT_VERSION ${_safe_version})
- c4_setg(PROJECT_VERSION_MAJOR ${_major})
- c4_setg(PROJECT_VERSION_MINOR ${_minor})
- c4_setg(PROJECT_VERSION_PATCH ${_patch})
- c4_setg(PROJECT_VERSION_TWEAK "${_safe_tweak}")
- c4_setg(PROJECT_VERSION_TWEAK_FULL "${_tweak}")
- c4_setg(${prefix}_VERSION_FULL ${version})
- c4_setg(${prefix}_VERSION ${_safe_version})
- c4_setg(${prefix}_VERSION_MAJOR ${_major})
- c4_setg(${prefix}_VERSION_MINOR ${_minor})
- c4_setg(${prefix}_VERSION_PATCH ${_patch})
- c4_setg(${prefix}_VERSION_TWEAK "${_safe_tweak}")
- c4_setg(${prefix}_VERSION_TWEAK_FULL "${_tweak}")
-endmacro()
-
-
-# Add targets for testing (dir=./test), benchmark (dir=./bm) and API (dir=./api).
-# Call this macro towards the end of the project's main CMakeLists.txt.
-# Experimental feature: docs.
-function(c4_add_dev_targets)
- if(NOT CMAKE_CURRENT_LIST_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
- c4_err("this macro needs to be called on the project's main CMakeLists.txt file")
- endif()
- #
- if(${_c4_uprefix}BUILD_TESTS)
- if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/test")
- c4_dbg("adding tests: ${CMAKE_CURRENT_LIST_DIR}/test")
- enable_testing() # this must be done here (and not inside the
- # test dir) so that the cmake-generated test
- # targets are available at the top level
- add_subdirectory(test)
- endif()
- endif()
- #
- if(${_c4_uprefix}BUILD_BENCHMARKS)
- if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/bm")
- c4_dbg("adding benchmarks: ${CMAKE_CURRENT_LIST_DIR}/bm")
- add_subdirectory(bm)
- endif()
- endif()
- #
- if(${_c4_uprefix}BUILD_API)
- if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/api")
- c4_dbg("adding API: ${d}")
- add_subdirectory(api)
- endif()
- endif()
- #
- # FIXME
- c4_add_doxygen(doc DOXYFILE_IN ${_c4_project_dir}/Doxyfile.in
- PROJ c4core
- INPUT ${${_c4_uprefix}SRC_DIR}
- EXCLUDE ${${_c4_uprefix}EXT_DIR} ${${_c4_uprefix}SRC_DIR}/c4/ext
- STRIP_FROM_PATH ${${_c4_uprefix}SRC_DIR}
- STRIP_FROM_INC_PATH ${${_c4_uprefix}SRC_DIR}
- CLANG_DATABASE_PATH ${CMAKE_BINARY_DIR}
- )
- c4_add_doxygen(doc-full DOXYFILE_IN ${_c4_project_dir}/Doxyfile.full.in
- PROJ c4core
- INPUT ${${_c4_uprefix}SRC_DIR}
- EXCLUDE ${${_c4_uprefix}EXT_DIR} ${${_c4_uprefix}SRC_DIR}/c4/ext
- STRIP_FROM_PATH ${${_c4_uprefix}SRC_DIR}
- STRIP_FROM_INC_PATH ${${_c4_uprefix}SRC_DIR}
- CLANG_DATABASE_PATH ${CMAKE_BINARY_DIR}
- )
-endfunction()
-
-
-function(_c4_get_san_targets target result)
- _c4_get_tgt_prop(san_targets ${target} C4_SAN_TARGETS)
- if(NOT san_targets)
- #c4_err("${target} must have at least itself in its sanitized target list")
- set(${result} ${target} PARENT_SCOPE)
- else()
- set(${result} ${san_targets} PARENT_SCOPE)
- endif()
-endfunction()
-
-
-# -----------------------------------------------------------------------------
-# -----------------------------------------------------------------------------
-# -----------------------------------------------------------------------------
-
-# utilities for compilation flags and defines
-
-# flags enabled only on dev mode
-macro(c4_optional_compile_flags_dev tag desc)
- _c4_handle_args(_ARGS ${ARGN}
- _ARGS0
- _ARGS1
- _ARGSN
- MSVC # flags for Visual Studio compilers
- GCC # flags for gcc compilers
- CLANG # flags for clang compilers
- GCC_CLANG # flags common to gcc and clang
- _DEPRECATE
- )
- cmake_dependent_option(${_c4_uprefix}${tag} "${desc}" ON ${_c4_uprefix}DEV OFF)
- set(optname ${_c4_uprefix}${tag})
- if(${optname})
- c4_dbg("${optname} is enabled. Adding flags...")
- if(MSVC)
- set(flags ${_MSVC})
- elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
- set(flags ${_GCC_CLANG};${_CLANG})
- elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
- set(flags ${_GCC_CLANG};${_GCC})
- elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
- set(flags ${_ALL};${_GCC_CLANG};${_GCC}) # FIXME
- elseif(CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
- set(flags ${_ALL};${_GCC_CLANG};${_CLANG}) # FIXME
- else()
- c4_err("unknown compiler")
- endif()
- else()
- c4_dbg("${optname} is disabled.")
- endif()
- if(flags)
- c4_log("${tag} flags [${desc}]: ${flags}")
- c4_setg(${_c4_uprefix}CXX_FLAGS_OPT "${${_c4_uprefix}CXX_FLAGS_OPT};${flags}")
- endif()
-endmacro()
-
-
-function(c4_target_compile_flags target)
- _c4_handle_args(_ARGS ${ARGN}
- _ARGS0
- PUBLIC
- PRIVATE
- INTERFACE
- AFTER # this is the default
- BEFORE
- _ARGS1
- _ARGSN
- ALL # flags for all compilers
- MSVC # flags for Visual Studio compilers
- GCC # flags for gcc compilers
- CLANG # flags for clang compilers
- GCC_CLANG # flags common to gcc and clang
- _DEPRECATE
- )
- if(MSVC)
- set(flags ${_ALL};${_MSVC})
- elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
- set(flags ${_ALL};${_GCC_CLANG};${_CLANG})
- elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
- set(flags ${_ALL};${_GCC_CLANG};${_GCC})
- elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
- set(flags ${_ALL};${_GCC_CLANG};${_GCC}) # FIXME
- elseif(CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
- set(flags ${_ALL};${_GCC_CLANG};${_CLANG}) # FIXME
- else()
- c4_err("unknown compiler")
- endif()
- if(NOT flags)
- c4_dbg("no compile flags to be set")
- return()
- endif()
- if(_AFTER OR (NOT _BEFORE))
- set(mode)
- c4_log("${target}: adding compile flags AFTER: ${flags}")
- elseif(_BEFORE)
- set(mode BEFORE)
- c4_log("${target}: adding compile flags BEFORE: ${flags}")
- endif()
- _c4_get_san_targets(${target} san_targets)
- foreach(st ${san_targets})
- if(_PUBLIC)
- target_compile_options(${st} ${mode} PUBLIC ${flags})
- elseif(_PRIVATE)
- target_compile_options(${st} ${mode} PRIVATE ${flags})
- elseif(_INTERFACE)
- target_compile_options(${st} ${mode} INTERFACE ${flags})
- else()
- c4_err("${target}: must have one of PUBLIC, PRIVATE or INTERFACE")
- endif()
- endforeach()
-endfunction()
-
-
-function(c4_target_definitions target)
- _c4_handle_args(_ARGS ${ARGN}
- _ARGS0
- PUBLIC
- PRIVATE
- INTERFACE
- _ARGS1
- _ARGSN
- ALL # defines for all compilers
- MSVC # defines for Visual Studio compilers
- GCC # defines for gcc compilers
- CLANG # defines for clang compilers
- GCC_CLANG # defines common to gcc and clang
- _DEPRECATE
- )
- if(MSVC)
- set(flags ${_ALL};${_MSVC})
- elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
- set(flags ${_ALL};${_GCC_CLANG};${_CLANG})
- elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
- set(flags ${_ALL};${_GCC_CLANG};${_GCC})
- else()
- c4_err("unknown compiler")
- endif()
- if(NOT flags)
- c4_dbg("no compile flags to be set")
- return()
- endif()
- if(_AFTER OR (NOT _BEFORE))
- set(mode)
- c4_log("${target}: adding definitions AFTER: ${flags}")
- elseif(_BEFORE)
- set(mode BEFORE)
- c4_log("${target}: adding definitions BEFORE: ${flags}")
- endif()
- _c4_get_san_targets(${target} san_targets)
- foreach(st ${san_targets})
- if(_PUBLIC)
- target_compile_definitions(${st} ${mode} PUBLIC ${flags})
- elseif(_PRIVATE)
- target_compile_definitions(${st} ${mode} PRIVATE ${flags})
- elseif(_INTERFACE)
- target_compile_definitions(${st} ${mode} INTERFACE ${flags})
- else()
- c4_err("${target}: must have one of PUBLIC, PRIVATE or INTERFACE")
- endif()
- endforeach()
-endfunction()
-
-
-function(c4_target_remove_compile_flags target)
- _c4_handle_args(_ARGS ${ARGN}
- _ARGS0
- PUBLIC # remove only from public compile options
- INTERFACE # remove only from interface compile options
- _ARGS1
- _ARGSN
- MSVC # flags for Visual Studio compilers
- GCC # flags for gcc compilers
- CLANG # flags for clang compilers
- GCC_CLANG # flags common to gcc and clang
- _DEPRECATE
- )
- if(MSVC)
- set(flags ${_MSVC})
- elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
- set(flags ${_GCC_CLANG};${_CLANG})
- elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
- set(flags ${_GCC_CLANG};${_GCC})
- else()
- c4_err("unknown compiler")
- endif()
- if(NOT flags)
- return()
- endif()
- _c4_get_san_targets(${target} san_targets)
- foreach(st ${san_targets})
- if(_PUBLIC OR (NOT _INTERFACE))
- get_target_property(co ${st} COMPILE_OPTIONS)
- if(co)
- _c4_remove_entries_from_list("${flags}" co)
- set_target_properties(${st} PROPERTIES COMPILE_OPTIONS "${co}")
- endif()
- endif()
- if(_INTERFACE OR (NOT _PUBLIC))
- get_target_property(ico ${st} INTERFACE_COMPILE_OPTIONS)
- if(ico)
- _c4_remove_entries_from_list("${flags}" ico)
- set_target_properties(${st} PROPERTIES INTERFACE_COMPILE_OPTIONS "${ico}")
- endif()
- endif()
- endforeach()
-endfunction()
-
-
-function(_c4_remove_entries_from_list entries_to_remove list)
- set(str ${${list}})
- string(REPLACE ";" "==?==" str "${str}")
- foreach(entry ${entries_to_remove})
- string(REPLACE "${entry}" "" str "${str}")
- endforeach()
- string(REPLACE "==?==" ";" str "${str}")
- string(REPLACE ";;" ";" str "${str}")
- set(${list} "${str}" PARENT_SCOPE)
-endfunction()
-
-
-
-# pedantic flags...
-# default pedantic flags taken from:
-# https://github.com/lefticus/cpp_starter_project/blob/master/cmake/CompilerWarnings.cmake
-set(_C4_PEDANTIC_FLAGS_MSVC
- /W4 # Baseline reasonable warnings
- /w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss of data
- /w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
- /w14263 # 'function': member function does not override any base class virtual member function
- /w14265 # 'classname': class has virtual functions, but destructor is not virtual instances of this class may not
- # be destructed correctly
- /w14287 # 'operator': unsigned/negative constant mismatch
- /we4289 # nonstandard extension used: 'variable': loop control variable declared in the for-loop is used outside
- # the for-loop scope
- /w14296 # 'operator': expression is always 'boolean_value'
- /w14311 # 'variable': pointer truncation from 'type1' to 'type2'
- /w14545 # expression before comma evaluates to a function which is missing an argument list
- /w14546 # function call before comma missing argument list
- /w14547 # 'operator': operator before comma has no effect; expected operator with side-effect
- /w14549 # 'operator': operator before comma has no effect; did you intend 'operator'?
- /w14555 # expression has no effect; expected expression with side- effect
- /w14619 # pragma warning: there is no warning number 'number'
- /w14640 # Enable warning on thread un-safe static member initialization
- /w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may cause unexpected runtime behavior.
- /w14905 # wide string literal cast to 'LPSTR'
- /w14906 # string literal cast to 'LPWSTR'
- /w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied
- $<$<VERSION_GREATER:${MSVC_VERSION},1900>:/permissive-> # standards conformance mode for MSVC compiler (only vs2017+)
- )
-
-set(_C4_PEDANTIC_FLAGS_CLANG
- -Wall
- -Wextra
- -pedantic
- -Wshadow # warn the user if a variable declaration shadows one from a parent context
- -Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. This helps
- # catch hard to track down memory errors
- #-Wold-style-cast # warn for c-style casts
- -Wcast-align # warn for potential performance problem casts
- -Wunused # warn on anything being unused
- -Woverloaded-virtual # warn if you overload (not override) a virtual function
- -Wpedantic # warn if non-standard C++ is used
- -Wconversion # warn on type conversions that may lose data
- -Wsign-conversion # warn on sign conversions
- -Wdouble-promotion # warn if float is implicit promoted to double
- -Wfloat-equal # warn if comparing floats
- -Wformat=2 # warn on security issues around functions that format output (ie printf)
- )
-
-set(_C4_PEDANTIC_FLAGS_GCC ${_C4_PEDANTIC_FLAGS_CLANG}
- -Wlogical-op # where logical operations are used where bitwise were probably wanted
- -Wuseless-cast # where you perform a cast to the same type
- )
-
-if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 6.0))
- list(APPEND _C4_PEDANTIC_FLAGS_GCC
- -Wnull-dereference # warn if a null dereference is detected
- -Wmisleading-indentation # where indentation implies blocks where blocks do not exist
- -Wduplicated-cond # where if-else chain has duplicated conditions
- )
-endif()
-
-if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 7.0))
- list(APPEND _C4_PEDANTIC_FLAGS_GCC
- -Wduplicated-branches # where if-else branches have duplicated code
- )
-endif()
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-
-function(c4_pack_project)
- # if this is the top-level project... pack it.
- if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
- c4_log("packing the project: ${ARGN}")
- c4_set_default_pack_properties(${ARGN})
- include(CPack)
- endif()
-endfunction()
-
-
-# [WIP] set convenient defaults for the properties used by CPack
-function(c4_set_default_pack_properties)
- _c4_handle_args(_ARGS ${ARGN}
- _ARGS0 # zero-value macro arguments
- _ARGS1 # one-value macro arguments
- TYPE # one of LIBRARY, EXECUTABLE
- _ARGSN # multi-value macro arguments
- )
- set(pd "${PROJECT_SOURCE_DIR}")
- _c4_handle_arg(TYPE EXECUTABLE) # default to EXECUTABLE
- #
- _c4_get_platform_tag(platform_tag)
- if("${_TYPE}" STREQUAL "LIBRARY")
- if(BUILD_SHARED_LIBS)
- set(build_tag "-shared")
- else()
- set(build_tag "-static")
- endif()
- get_property(multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
- if(multi_config)
- # doesn't work because generators are not evaluated: set(build_tag "${build_tag}-$<CONFIG>")
- # doesn't work because generators are not evaluated: set(build_tag "${build_tag}$<$<CONFIG:Debug>:-Debug>$<$<CONFIG:MinSizeRel>:-MinSizeRel>$<$<CONFIG:Release>:-Release>$<$<CONFIG:RelWithDebInfo>:-RelWithDebInfo>")
- # see also https://stackoverflow.com/questions/44153730/how-to-change-cpack-package-file-name-based-on-configuration
- if(CMAKE_BUILD_TYPE) # in the off-chance it was explicitly set
- set(build_tag "${build_tag}-${CMAKE_BUILD_TYPE}")
- endif()
- else()
- set(build_tag "${build_tag}-${CMAKE_BUILD_TYPE}")
- endif()
- elseif("${_TYPE}" STREQUAL "EXECUTABLE")
- set(build_tag)
- elseif()
- c4_err("unknown TYPE: ${_TYPE}")
- endif()
- #
- c4_setg(CPACK_VERBATIM_VARIABLES true)
- c4_setg(CPACK_PACKAGE_VENDOR "${${_c4_prefix}_HOMEPAGE_URL}")
- c4_setg(CPACK_PACKAGE_CONTACT "${${_c4_prefix}_AUTHOR}")
- c4_setg(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${${_c4_prefix}_DESCRIPTION}")
- if(EXISTS "${pd}/README.md")
- c4_setg(CPACK_PACKAGE_DESCRIPTION_FILE "${pd}/README.md")
- c4_setg(CPACK_PACKAGE_DESCRIPTION_README "${pd}/README.md")
- c4_setg(CPACK_PACKAGE_DESCRIPTION_WELCOME "${pd}/README.md")
- elseif(EXISTS "${pd}/README.txt")
- c4_setg(CPACK_PACKAGE_DESCRIPTION_FILE "${pd}/README.txt")
- c4_setg(CPACK_PACKAGE_DESCRIPTION_README "${pd}/README.txt")
- c4_setg(CPACK_PACKAGE_DESCRIPTION_WELCOME "${pd}/README.txt")
- endif()
- if(EXISTS "${pd}/LICENSE.md")
- c4_setg(CPACK_RESOURCE_FILE_LICENSE "${pd}/LICENSE.md")
- elseif(EXISTS "${pd}/LICENSE.txt")
- c4_setg(CPACK_RESOURCE_FILE_LICENSE "${pd}/LICENSE.txt")
- endif()
- c4_proj_get_version("${pd}" version_tag full major minor patch tweak)
- c4_setg(CPACK_PACKAGE_VERSION "${full}")
- c4_setg(CPACK_PACKAGE_VERSION_MAJOR "${major}")
- c4_setg(CPACK_PACKAGE_VERSION_MINOR "${minor}")
- c4_setg(CPACK_PACKAGE_VERSION_PATCH "${patch}")
- c4_setg(CPACK_PACKAGE_VERSION_TWEAK "${tweak}")
- c4_setg(CPACK_PACKAGE_INSTALL_DIRECTORY "${_c4_prefix}-${version_tag}")
- c4_setg(CPACK_PACKAGE_FILE_NAME "${_c4_prefix}-${version_tag}-${platform_tag}${build_tag}")
- if(WIN32 AND NOT UNIX)
- # There is a bug in NSI that does not handle full UNIX paths properly.
- # Make sure there is at least one set of four backlashes.
- #c4_setg(CPACK_PACKAGE_ICON "${CMake_SOURCE_DIR}/Utilities/Release\\\\InstallIcon.bmp")
- #c4_setg(CPACK_NSIS_INSTALLED_ICON_NAME "bin\\\\MyExecutable.exe")
- c4_setg(CPACK_NSIS_DISPLAY_NAME "${_c4_prefix} ${version_tag}")
- c4_setg(CPACK_NSIS_HELP_LINK "${${_c4_prefix}_HOMEPAGE_URL}")
- c4_setg(CPACK_NSIS_URL_INFO_ABOUT "${${_c4_prefix}_HOMEPAGE_URL}")
- c4_setg(CPACK_NSIS_CONTACT "${${_c4_prefix}_AUTHOR}")
- c4_setg(CPACK_NSIS_MODIFY_PATH ON)
- else()
- #c4_setg(CPACK_STRIP_FILES "bin/MyExecutable")
- #c4_setg(CPACK_SOURCE_STRIP_FILES "")
- c4_setg(CPACK_DEBIAN_PACKAGE_MAINTAINER "${${_c4_prefix}_AUTHOR}")
- endif()
- #c4_setg(CPACK_PACKAGE_EXECUTABLES "MyExecutable" "My Executable")
-endfunction()
-
-
-function(_c4_get_platform_tag tag_)
- if(WIN32 AND NOT UNIX)
- set(tag win)
- elseif(APPLE)
- set(tag apple)
- elseif(UNIX)
- set(tag unix)
- else()
- set(tag ${CMAKE_SYSTEM_NAME})
- endif()
- if(CMAKE_SIZEOF_VOID_P EQUAL 8) # 64 bits
- set(tag ${tag}64)
- elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) # 32 bits
- set(tag ${tag}32)
- else()
- c4_err("not implemented")
- endif()
- set(${tag_} ${tag} PARENT_SCOPE)
-endfunction()
-
-
-function(_c4_extract_version_tag tag_)
- # git describe --tags <commit-id> for unannotated tags
- # git describe --contains <commit>
-endfunction()
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-
-# set project-wide property
-function(c4_set_proj_prop prop value)
- c4_dbg("set ${prop}=${value}")
- set(C4PROJ_${_c4_prefix}_${prop} ${value})
-endfunction()
-
-# set project-wide property
-function(c4_get_proj_prop prop var)
- c4_dbg("get ${prop}=${C4PROJ_${_c4_prefix}_${prop}}")
- set(${var} ${C4PROJ_${_c4_prefix}_${prop}} PARENT_SCOPE)
-endfunction()
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-
-# set target-wide c4 property
-function(c4_set_target_prop target prop value)
- _c4_set_tgt_prop(${target} C4_TGT_${prop} "${value}")
-endfunction()
-function(c4_append_target_prop target prop value)
- _c4_append_tgt_prop(${target} C4_TGT_${prop} "${value}")
-endfunction()
-
-# get target-wide c4 property
-function(c4_get_target_prop target prop var)
- _c4_get_tgt_prop(val ${target} C4_TGT_${prop})
- set(${var} ${val} PARENT_SCOPE)
-endfunction()
-
-
-# get target-wide property
-function(_c4_get_tgt_prop out tgt prop)
- get_target_property(target_type ${target} TYPE)
- if(target_type STREQUAL "INTERFACE_LIBRARY")
- get_property(val GLOBAL PROPERTY C4_TGT_${tgt}_${prop})
- else()
- get_target_property(val ${tgt} ${prop})
- endif()
- c4_dbg("target ${tgt}: get ${prop}=${val}")
- set(${out} "${val}" PARENT_SCOPE)
-endfunction()
-
-# set target-wide property
-function(_c4_set_tgt_prop tgt prop propval)
- c4_dbg("target ${tgt}: set ${prop}=${propval}")
- get_target_property(target_type ${target} TYPE)
- if(target_type STREQUAL "INTERFACE_LIBRARY")
- set_property(GLOBAL PROPERTY C4_TGT_${tgt}_${prop} "${propval}")
- else()
- set_target_properties(${tgt} PROPERTIES ${prop} "${propval}")
- endif()
-endfunction()
-function(_c4_append_tgt_prop tgt prop propval)
- c4_dbg("target ${tgt}: appending ${prop}=${propval}")
- _c4_get_tgt_prop(curr ${tgt} ${prop})
- if(curr)
- list(APPEND curr "${propval}")
- else()
- set(curr "${propval}")
- endif()
- _c4_set_tgt_prop(${tgt} ${prop} "${curr}")
-endfunction()
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-
-function(c4_set_var_tmp var value)
- c4_dbg("tmp-setting ${var} to ${value} (was ${${value}})")
- set(_c4_old_val_${var} ${${var}})
- set(${var} ${value} PARENT_SCOPE)
-endfunction()
-
-function(c4_clean_var_tmp var)
- c4_dbg("cleaning ${var} to ${_c4_old_val_${var}} (tmp was ${${var}})")
- set(${var} ${_c4_old_val_${var}} PARENT_SCOPE)
-endfunction()
-
-macro(c4_override opt val)
- set(${opt} ${val} CACHE BOOL "" FORCE)
-endmacro()
-
-
-macro(c4_setg var val)
- set(${var} ${val})
- set(${var} ${val} PARENT_SCOPE)
-endmacro()
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-
-function(c4_proj_get_version dir tag_o full_o major_o minor_o patch_o tweak_o)
- if("${dir}" STREQUAL "")
- set(dir ${CMAKE_CURRENT_LIST_DIR})
- endif()
- find_program(GIT git REQUIRED)
- function(_c4pgv_get_cmd outputvar)
- execute_process(COMMAND ${ARGN}
- WORKING_DIRECTORY ${dir}
- ERROR_VARIABLE error
- ERROR_STRIP_TRAILING_WHITESPACE
- OUTPUT_VARIABLE output
- OUTPUT_STRIP_TRAILING_WHITESPACE)
- c4_dbg("output of ${ARGN}: ${outputvar}=${output} [@${dir}]")
- set(${outputvar} ${output} PARENT_SCOPE)
- endfunction()
- # do we have any tags yet?
- _c4pgv_get_cmd(head_desc ${GIT} describe HEAD)
- _c4pgv_get_cmd(branch ${GIT} rev-parse --abbrev-ref HEAD)
- if(NOT head_desc)
- c4_dbg("the repo does not have any tags yet")
- _c4pgv_get_cmd(commit_hash ${GIT} rev-parse --short HEAD)
- set(otag "${commit_hash}-${branch}")
- else()
- c4_dbg("there are tags!")
- # is the current commit tagged?
- _c4pgv_get_cmd(commit_hash_full ${GIT} rev-parse HEAD)
- _c4pgv_get_cmd(commit_desc ${GIT} describe --exact-match ${commit_hash_full})
- if(commit_desc)
- c4_dbg("current commit is tagged")
- # is the tag a version tag?
- _c4_parse_version_tag(${commit_desc} is_version major minor patch tweak more)
- if(is_version)
- c4_dbg("current commit's tag is a version tag")
- # is the tag the current version tag?
- if("${is_version}" VERSION_EQUAL "${${_c4_prefix}_VERSION_FULL}")
- c4_dbg("this is the official version commit")
- else()
- c4_dbg("this is a different version")
- endif()
- set(otag "${commit_desc}")
- else()
- c4_dbg("this is a non-version tag")
- set(otag "${commit_desc}-${branch}")
- endif()
- else(commit_desc)
- # is the latest tag in the head_desc a version tag?
- string(REGEX REPLACE "(.*)-[0-9]+-[0-9a-f]+" "\\1" latest_tag "${head_desc}")
- c4_dbg("current commit is NOT tagged. latest tag=${latest_tag}")
- _c4_parse_version_tag(${latest_tag} latest_tag_is_a_version major minor patch tweak more)
- if(latest_tag_is_a_version)
- c4_dbg("latest tag is a version. stick to the head description")
- set(otag "${head_desc}-${branch}")
- set(full "${latest_tag_is_a_version}")
- else()
- c4_dbg("latest tag is NOT a version. Use the current project version from cmake + the output of git describe")
- set(otag "v${full}-${head_desc}-${branch}")
- set(full "${${_c4_prefix}_VERSION_FULL}")
- set(major "${${_c4_prefix}_VERSION_MAJOR}")
- set(minor "${${_c4_prefix}_VERSION_MINOR}")
- set(patch "${${_c4_prefix}_VERSION_PATCH}")
- set(tweak "${${_c4_prefix}_VERSION_TWEAK}")
- endif()
- endif(commit_desc)
- endif(NOT head_desc)
- c4_log("cpack tag: ${otag}")
- set(${tag_o} "${otag}" PARENT_SCOPE)
- set(${full_o} "${full}" PARENT_SCOPE)
- set(${major_o} "${major}" PARENT_SCOPE)
- set(${minor_o} "${minor}" PARENT_SCOPE)
- set(${patch_o} "${patch}" PARENT_SCOPE)
- set(${tweak_o} "${tweak}" PARENT_SCOPE)
- # also: dirty index?
- # https://stackoverflow.com/questions/2657935/checking-for-a-dirty-index-or-untracked-files-with-git
-endfunction()
-
-
-function(_c4_parse_version_tag tag is_version major minor patch tweak more)
- # does the tag match a four-part version?
- string(REGEX MATCH "v?([0-9]+)([\._][0-9]+)([\._][0-9]+)([\._][0-9]+)(.*)" match "${tag}")
- function(_triml arg out) # trim the leading [\._] from the left
- if("${arg}" STREQUAL "")
- set(${out} "" PARENT_SCOPE)
- else()
- string(REGEX REPLACE "[\._](.*)" "\\1" ret "${arg}")
- set("${out}" "${ret}" PARENT_SCOPE)
- endif()
- endfunction()
- if(match)
- set(${is_version} ${tag} PARENT_SCOPE)
- _triml("${CMAKE_MATCH_1}" major_v)
- _triml("${CMAKE_MATCH_2}" minor_v)
- _triml("${CMAKE_MATCH_3}" patch_v)
- _triml("${CMAKE_MATCH_4}" tweak_v)
- _triml("${CMAKE_MATCH_5}" more_v)
- else()
- # does the tag match a three-part version?
- string(REGEX MATCH "v?([0-9]+)([\._][0-9]+)([\._][0-9]+)(.*)" match "${tag}")
- if(match)
- set(${is_version} ${tag} PARENT_SCOPE)
- _triml("${CMAKE_MATCH_1}" major_v)
- _triml("${CMAKE_MATCH_2}" minor_v)
- _triml("${CMAKE_MATCH_3}" patch_v)
- _triml("${CMAKE_MATCH_4}" more_v)
- else()
- # does the tag match a two-part version?
- string(REGEX MATCH "v?([0-9]+)([\._][0-9]+)(.*)" match "${tag}")
- if(match)
- set(${is_version} ${tag} PARENT_SCOPE)
- _triml("${CMAKE_MATCH_1}" major_v)
- _triml("${CMAKE_MATCH_2}" minor_v)
- _triml("${CMAKE_MATCH_3}" more_v)
- else()
- # not a version!
- set(${is_version} FALSE PARENT_SCOPE)
- endif()
- endif()
- endif()
- set(${major} "${major_v}" PARENT_SCOPE)
- set(${minor} "${minor_v}" PARENT_SCOPE)
- set(${patch} "${patch_v}" PARENT_SCOPE)
- set(${tweak} "${tweak_v}" PARENT_SCOPE)
- set(${more} "${more_v}" PARENT_SCOPE)
-endfunction()
-
-
-#function(testvtag)
-# set(err FALSE)
-# function(cmp value expected)
-# if(NOT ("${${value}}" STREQUAL "${expected}"))
-# c4_log("${tag}: error: expected ${value}=='${expected}': '${${value}}'=='${expected}'")
-# set(err TRUE PARENT_SCOPE)
-# else()
-# c4_log("${tag}: ok: expected ${value}=='${expected}': '${${value}}'=='${expected}'")
-# endif()
-# endfunction()
-# function(verify tag is_version_e major_e minor_e patch_e tweak_e more_e)
-# _c4_parse_version_tag(${tag} is_version major minor patch tweak more)
-# cmp(is_version ${is_version_e})
-# cmp(major "${major_e}")
-# cmp(minor "${minor_e}")
-# cmp(patch "${patch_e}")
-# cmp(tweak "${tweak_e}")
-# cmp(more "${more_e}")
-# set(err ${err} PARENT_SCOPE)
-# endfunction()
-# verify(v12.34.567.89-rcfoo TRUE 12 34 567 89 -rcfoo)
-# verify(v12_34_567_89-rcfoo TRUE 12 34 567 89 -rcfoo)
-# verify(v12.34.567.89 TRUE 12 34 567 89 "")
-# verify(v12_34_567_89 TRUE 12 34 567 89 "")
-# verify(v12.34.567-rcfoo TRUE 12 34 567 "" -rcfoo)
-# verify(v12_34_567-rcfoo TRUE 12 34 567 "" -rcfoo)
-# verify(v12.34.567 TRUE 12 34 567 "" "")
-# verify(v12_34_567 TRUE 12 34 567 "" "")
-# verify(v12_34 TRUE 12 34 "" "" "")
-# verify(v12.34 TRUE 12 34 "" "" "")
-# if(err)
-# c4_err("test failed")
-# endif()
-#endfunction()
-#testvtag()
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-
-
-macro(_c4_handle_cxx_standard_args)
- # EXTENSIONS:
- # enable compiler extensions eg, prefer gnu++11 to c++11
- if(EXTENSIONS IN_LIST ARGN)
- set(_EXTENSIONS ON)
- else()
- c4_get_from_first_of(_EXTENSIONS
- ENV
- DEFAULT OFF
- VARS ${_c4_uprefix}CXX_EXTENSIONS C4_CXX_EXTENSIONS CMAKE_CXX_EXTENSIONS)
- endif()
- #
- # OPTIONAL
- if(OPTIONAL IN_LIST ARGN)
- set(_REQUIRED OFF)
- else()
- c4_get_from_first_of(_REQUIRED
- ENV
- DEFAULT ON
- VARS ${_c4_uprefix}CXX_STANDARD_REQUIRED C4_CXX_STANDARD_REQUIRED CMAKE_CXX_STANDARD_REQUIRED)
- endif()
-endmacro()
-
-
-# set the global cxx standard for the project.
-#
-# examples:
-# c4_set_cxx(latest) # find the latest standard supported by the compiler, and use that
-# c4_set_cxx(11) # required, no extensions (eg c++11)
-# c4_set_cxx(14) # required, no extensions (eg c++14)
-# c4_set_cxx(11 EXTENSIONS) # opt-in to extensions (eg, gnu++11)
-# c4_set_cxx(14 EXTENSIONS) # opt-in to extensions (eg, gnu++14)
-# c4_set_cxx(11 OPTIONAL) # not REQUIRED. no extensions
-# c4_set_cxx(11 OPTIONAL EXTENSIONS) # not REQUIRED. with extensions.
-macro(c4_set_cxx standard)
- _c4_handle_cxx_standard_args(${ARGN})
- if(NOT DEFINED CMAKE_CXX_STANDARD)
- c4_log("setting C++ standard: ${standard}")
- c4_setg(CMAKE_CXX_STANDARD ${standard})
- endif()
- if(NOT DEFINED CMAKE_CXX_STANDARD_REQUIRED)
- c4_log("setting C++ standard required: ${_REQUIRED}")
- c4_setg(CMAKE_CXX_STANDARD_REQUIRED ${_REQUIRED})
- endif()
- if(NOT DEFINED CMAKE_CXX_STANDARD_REQUIRED)
- c4_log("setting C++ standard extensions: ${_EXTENSIONS}")
- c4_setg(CMAKE_CXX_EXTENSIONS ${_EXTENSIONS})
- endif()
-endmacro()
-
-
-# set the cxx standard for a target.
-#
-# examples:
-# c4_target_set_cxx(target latest) # find the latest standard supported by the compiler, and use that
-# c4_target_set_cxx(target 11) # required, no extensions (eg c++11)
-# c4_target_set_cxx(target 14) # required, no extensions (eg c++14)
-# c4_target_set_cxx(target 11 EXTENSIONS) # opt-in to extensions (eg, gnu++11)
-# c4_target_set_cxx(target 14 EXTENSIONS) # opt-in to extensions (eg, gnu++14)
-# c4_target_set_cxx(target 11 OPTIONAL) # not REQUIRED. no extensions
-# c4_target_set_cxx(target 11 OPTIONAL EXTENSIONS)
-function(c4_target_set_cxx target standard)
- c4_dbg("setting C++ standard for target ${target}: ${standard}")
- _c4_handle_cxx_standard_args(${ARGN})
- set_target_properties(${target} PROPERTIES
- CXX_STANDARD ${standard}
- CXX_STANDARD_REQUIRED ${_REQUIRED}
- CXX_EXTENSIONS ${_EXTENSIONS})
- target_compile_features(${target} PUBLIC cxx_std_${standard})
-endfunction()
-
-
-# set the cxx standard for a target based on the global project settings
-function(c4_target_inherit_cxx_standard target)
- c4_dbg("inheriting C++ standard for target ${target}: ${CMAKE_CXX_STANDARD}")
- set_target_properties(${target} PROPERTIES
- CXX_STANDARD "${CMAKE_CXX_STANDARD}"
- CXX_STANDARD_REQUIRED "${CMAKE_CXX_STANDARD_REQUIRED}"
- CXX_EXTENSIONS "${CMAKE_CXX_EXTENSIONS}")
- target_compile_features(${target} PUBLIC cxx_std_${CMAKE_CXX_STANDARD})
-endfunction()
-
-
-function(_c4_find_latest_supported_cxx_standard out)
- if(NOT c4_latest_supported_cxx_standard)
- include(CheckCXXCompilerFlag)
- # make sure CMAKE_CXX_FLAGS is clean here
- # see https://cmake.org/cmake/help/v3.16/module/CheckCXXCompilerFlag.html
- # Note: since this is a function, we don't need to reset CMAKE_CXX_FLAGS
- # back to its previous value
- set(CMAKE_CXX_FLAGS)
- set(standard 11) # default to C++11 if everything fails
- foreach(s ${C4_CXX_STANDARDS})
- if(MSVC)
- set(flag /std:c++${s})
- else()
- # assume GNU-style compiler
- set(flag -std=c++${s})
- endif()
- c4_log("checking CXX standard: C++${s} flag=${flag}")
- check_cxx_compiler_flag(${flag} has${s})
- if(has${s})
- c4_log("checking CXX standard: C++${s} is supported! flag=${flag}")
- set(standard ${s})
- break()
- else()
- c4_log("checking CXX standard: C++${s}: no support for flag=${flag} no")
- endif()
- endforeach()
- set(c4_latest_supported_cxx_standard ${standard} CACHE INTERNAL "")
- endif()
- set(${out} ${c4_latest_supported_cxx_standard} PARENT_SCOPE)
-endfunction()
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-# examples:
-#
-# # require subproject c4core, as a subdirectory. c4core will be used
-# # as a separate library
-# c4_require_subproject(c4core SUBDIRECTORY ${C4OPT_EXT_DIR}/c4core)
-#
-# # require subproject c4core, as a remote proj
-# c4_require_subproject(c4core REMOTE
-# GIT_REPOSITORY https://github.com/biojppm/c4core
-# GIT_TAG master
-# )
-function(c4_require_subproject subproj)
- _c4_handle_args(_ARGS ${ARGN}
- _ARGS0
- INCORPORATE
- EXCLUDE_FROM_ALL
- _ARGS1
- SUBDIRECTORY # the subproject is located in the given directory name and
- # will be added via add_subdirectory()
- _ARGSN
- REMOTE # the subproject is located in a remote repo/url
- # and will be added via c4_import_remote_proj(),
- # forwarding all the arguments in here.
- OVERRIDE # a list of variable name+value pairs
- # these variables will be set with c4_override()
- # before calling add_subdirectory()
- SET_FOLDER_TARGETS # Set the folder of the given targets using
- # c4_set_folder_remote_project_targets().
- # The first expected argument is the folder,
- # and the remaining arguments are the targets
- # which we want to set the folder.
- _DEPRECATE
- INTERFACE
- )
- list(APPEND _${_c4_uprefix}_deps ${subproj})
- c4_setg(_${_c4_uprefix}_deps ${_${_c4_uprefix}_deps})
- c4_dbg("-----------------------------------------------")
- c4_dbg("requires subproject ${subproj}!")
- if(_INCORPORATE)
- c4_dbg("requires subproject ${subproj} in INCORPORATE mode!")
- c4_dbg_var(${_c4_root_uproj}_STANDALONE)
- if(${_c4_root_uproj}_STANDALONE)
- c4_dbg("${_c4_root_uproj} is STANDALONE: honoring INCORPORATE mode...")
- else()
- c4_dbg("${_c4_root_uproj} is not STANDALONE: ignoring INCORPORATE mode...")
- set(_INCORPORATE OFF)
- endif()
- endif()
- #
- _c4_get_subproject_property(${subproj} AVAILABLE _available)
- if(_available)
- c4_dbg("required subproject ${subproj} was already imported:")
- c4_dbg_subproject(${subproj})
- # TODO check version compatibility
- else() #elseif(NOT _${subproj}_available)
- c4_dbg("required subproject ${subproj} is unknown. Importing...")
- if(_EXCLUDE_FROM_ALL)
- set(excl EXCLUDE_FROM_ALL)
- endif()
- # forward c4 compile flags
- string(TOUPPER ${subproj} usubproj)
- c4_setg(${usubproj}_CXX_FLAGS_FWD "${${_c4_uprefix}CXX_FLAGS}")
- c4_setg(${usubproj}_CXX_FLAGS_OPT_FWD "${${_c4_uprefix}CXX_FLAGS_OPT}")
- c4_setg(${usubproj}_CXX_LINKER_FLAGS_FWD "${${_c4_uprefix}CXX_LINKER_FLAGS}")
- # root dir
- set(_r ${CMAKE_CURRENT_BINARY_DIR}/subprojects/${subproj})
- if(_REMOTE)
- c4_log("importing subproject ${subproj} (REMOTE)... ${_REMOTE}")
- _c4_mark_subproject_imported(${subproj} ${_r}/src ${_r}/build ${_INCORPORATE})
- c4_import_remote_proj(${subproj} ${_r} REMOTE ${_REMOTE} OVERRIDE ${_OVERRIDE} ${excl})
- _c4_get_subproject_property(${subproj} SRC_DIR _srcdir)
- c4_dbg("finished importing subproject ${subproj} (REMOTE, SRC_DIR=${_srcdir}).")
- elseif(_SUBDIRECTORY)
- c4_log("importing subproject ${subproj} (SUBDIRECTORY)... ${_SUBDIRECTORY}")
- _c4_mark_subproject_imported(${subproj} ${_SUBDIRECTORY} ${_r}/build ${_INCORPORATE})
- c4_add_subproj(${subproj} ${_SUBDIRECTORY} ${_r}/build OVERRIDE ${_OVERRIDE} ${excl})
- set(_srcdir ${_SUBDIRECTORY})
- c4_dbg("finished importing subproject ${subproj} (SUBDIRECTORY=${_SUBDIRECTORY}).")
- else()
- c4_err("subproject type must be either REMOTE or SUBDIRECTORY")
- endif()
- endif()
- #
- if(_SET_FOLDER_TARGETS)
- c4_set_folder_remote_project_targets(${_SET_FOLDER_TARGETS})
- endif()
-endfunction(c4_require_subproject)
-
-
-function(c4_add_subproj proj dir bindir)
- _c4_handle_args(_ARGS ${ARGN}
- _ARGS0
- EXCLUDE_FROM_ALL # forward to add_subdirectory()
- _ARGS1
- _ARGSN
- OVERRIDE # a list of variable name+value pairs
- # these variables will be set with c4_override()
- # before calling add_subdirectory()
- )
- # push the subproj into the current path
- set(prev_subproject ${_c4_curr_subproject})
- set(prev_path ${_c4_curr_path})
- set(_c4_curr_subproject ${proj})
- string(REGEX MATCH ".*/${proj}\$" pos "${_c4_curr_path}")
- if(pos EQUAL -1)
- string(REGEX MATCH "^${proj}\$" pos "${_c4_curr_path}")
- if(pos EQUAL -1)
- set(_c4_curr_path ${_c4_curr_path}/${proj})
- endif()
- endif()
- #
- while(_OVERRIDE)
- list(POP_FRONT _OVERRIDE varname)
- list(POP_FRONT _OVERRIDE varvalue)
- c4_override(${varname} ${varvalue})
- endwhile()
- #
- if(_EXCLUDE_FROM_ALL)
- set(excl EXCLUDE_FROM_ALL)
- endif()
- #
- c4_dbg("adding subproj: ${prev_subproject}->${_c4_curr_subproject}. path=${_c4_curr_path}")
- add_subdirectory(${dir} ${bindir} ${excl})
- # pop the subproj from the current path
- set(_c4_curr_subproject ${prev_subproject})
- set(_c4_curr_path ${prev_path})
-endfunction()
-
-
-function(_c4_mark_subproject_imported subproject_name subproject_src_dir subproject_bin_dir incorporate)
- c4_dbg("marking subproject imported: ${subproject_name} (imported by ${_c4_prefix}). src=${subproject_src_dir}")
- _c4_append_subproject_property(${_c4_prefix} DEPENDENCIES ${subproject_name})
- _c4_get_folder(folder ${_c4_prefix} ${subproject_name})
- _c4_set_subproject_property(${subproject_name} AVAILABLE ON)
- _c4_set_subproject_property(${subproject_name} IMPORTER "${_c4_prefix}")
- _c4_set_subproject_property(${subproject_name} SRC_DIR "${subproject_src_dir}")
- _c4_set_subproject_property(${subproject_name} BIN_DIR "${subproject_bin_dir}")
- _c4_set_subproject_property(${subproject_name} FOLDER "${folder}")
- _c4_set_subproject_property(${subproject_name} INCORPORATE "${incorporate}")
-endfunction()
-
-
-function(_c4_get_subproject_property subproject property var)
- get_property(v GLOBAL PROPERTY _c4_subproject-${subproject}-${property})
- set(${var} "${v}" PARENT_SCOPE)
-endfunction()
-
-
-function(_c4_set_subproject_property subproject property value)
- c4_dbg("setting subproj prop: ${subproject}: ${property}=${value}")
- set_property(GLOBAL PROPERTY _c4_subproject-${subproject}-${property} "${value}")
-endfunction()
-function(_c4_append_subproject_property subproject property value)
- _c4_get_subproject_property(${subproject} ${property} cval)
- if(cval)
- list(APPEND cval ${value})
- else()
- set(cval ${value})
- endif()
- _c4_set_subproject_property(${subproject} ${property} ${cval})
-endfunction()
-
-
-function(_c4_is_incorporated subproj out)
- if("${subproj}" STREQUAL "${_c4_root_proj}")
- c4_dbg("${subproj} is incorporated? root proj, no")
- set(${out} OFF PARENT_SCOPE)
- else()
- _c4_get_subproject_property(${subproj} INCORPORATE inc)
- c4_dbg("${subproj} is incorporated? not root proj, incorporate=${inc}")
- set(${out} ${inc} PARENT_SCOPE)
- endif()
-endfunction()
-
-
-function(c4_dbg_subproject subproject)
- set(props AVAILABLE IMPORTER SRC_DIR BIN_DIR DEPENDENCIES FOLDER INCORPORATE)
- foreach(p ${props})
- _c4_get_subproject_property(${subproject} ${p} pv)
- c4_dbg("${subproject}: ${p}=${pv}")
- endforeach()
-endfunction()
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-
-#
-#
-function(c4_import_remote_proj name dir)
- _c4_handle_args(_ARGS ${ARGN}
- _ARGS0
- EXCLUDE_FROM_ALL
- _ARGS1
- SUBDIR # path to the subdirectory where the CMakeLists file is to be found.
- _ARGSN
- OVERRIDE # a list of variable name+value pairs
- # these variables will be set with c4_override()
- # before calling add_subdirectory()
- REMOTE # to specify url, repo, tag, or branch,
- # pass the needed arguments after dir.
- # These arguments will be forwarded to ExternalProject_Add()
- SET_FOLDER_TARGETS # Set the folder of the given targets using
- # c4_set_folder_remote_project_targets().
- # The first expected argument is the folder,
- # and the remaining arguments are the targets
- # which we want to set the folder.
- )
- set(srcdir_in_out "${dir}")
- c4_download_remote_proj(${name} srcdir_in_out ${_REMOTE})
- if(_SUBDIR)
- set(srcdir_in_out "${srcdir_in_out}/${_SUBDIR}")
- endif()
- _c4_set_subproject_property(${name} SRC_DIR "${srcdir_in_out}")
- if(_EXCLUDE_FROM_ALL)
- set(excl EXCLUDE_FROM_ALL)
- endif()
- c4_add_subproj(${name} "${srcdir_in_out}" "${dir}/build" OVERRIDE ${_OVERRIDE} ${excl})
- #
- if(_SET_FOLDER_TARGETS)
- c4_set_folder_remote_project_targets(${_SET_FOLDER_TARGETS})
- endif()
-endfunction()
-
-
-# download remote projects while running cmake
-# to specify url, repo, tag, or branch,
-# pass the needed arguments after dir.
-# These arguments will be forwarded to ExternalProject_Add()
-function(c4_download_remote_proj name candidate_dir)
- # https://crascit.com/2015/07/25/cmake-gtest/
- # (via https://stackoverflow.com/questions/15175318/cmake-how-to-build-external-projects-and-include-their-targets)
- set(dir ${${candidate_dir}})
- if("${dir}" STREQUAL "")
- set(dir "${CMAKE_BINARY_DIR}/extern/${name}")
- endif()
- set(cvar _${_c4_uprefix}_DOWNLOAD_${name}_LOCATION)
- set(cval ${${cvar}})
- #
- # was it already downloaded in this project?
- if(NOT ("${cval}" STREQUAL ""))
- if(EXISTS "${cval}")
- c4_log("${name} was previously imported into this project - found at \"${cval}\"!")
- set(${candidate_dir} "${cval}" PARENT_SCOPE)
- return()
- else()
- c4_log("${name} was previously imported into this project - but was NOT found at \"${cval}\"!")
- endif()
- endif()
- #
- # try to find an existing version (downloaded by some other project)
- set(out "${dir}")
- _c4_find_cached_proj(${name} out)
- if(NOT ("${out}" STREQUAL "${dir}"))
- c4_log("using ${name} from \"${out}\"...")
- set(${cvar} "${out}" CACHE INTERNAL "")
- set(${candidate_dir} "${out}" PARENT_SCOPE)
- return()
- endif()
- #
- # no version was found; need to download.
- c4_log("downloading ${name}: not in cache...")
- # check for a global place to download into
- set(srcdir)
- _c4_get_cached_srcdir_global_extern(${name} srcdir)
- if("${srcdir}" STREQUAL "")
- # none found; default to the given dir
- set(srcdir "${dir}/src")
- endif()
- #
- # do it
- #if((EXISTS ${dir}/dl) AND (EXISTS ${dir}/dl/CMakeLists.txt))
- # return()
- #endif()
- c4_log("downloading remote project: ${name} -> \"${srcdir}\" (dir=${dir})...")
- #
- file(WRITE ${dir}/dl/CMakeLists.txt "
-cmake_minimum_required(VERSION 2.8.2)
-project(${_c4_lcprefix}-download-${name} NONE)
-
-# this project only downloads ${name}
-# (ie, no configure, build or install step)
-include(ExternalProject)
-
-ExternalProject_Add(${name}-dl
- ${ARGN}
- SOURCE_DIR \"${srcdir}\"
- BINARY_DIR \"${dir}/build\"
- CONFIGURE_COMMAND \"\"
- BUILD_COMMAND \"\"
- INSTALL_COMMAND \"\"
- TEST_COMMAND \"\"
-)
-")
- execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
- WORKING_DIRECTORY ${dir}/dl)
- execute_process(COMMAND ${CMAKE_COMMAND} --build .
- WORKING_DIRECTORY ${dir}/dl)
- #
- set(${candidate_dir} "${srcdir}" PARENT_SCOPE)
- set(_${_c4_uprefix}_DOWNLOAD_${name}_LOCATION "${srcdir}" CACHE INTERNAL "")
-endfunction()
-
-
-# checks if the project was already downloaded. If it was, then dir_in_out is
-# changed to the directory where the project was found at.
-function(_c4_find_cached_proj name dir_in_out)
- c4_log("downloading ${name}: searching cached project...")
- #
- # 1. search in the per-import variable, eg RYML_CACHE_DOWNLOAD_GTEST
- string(TOUPPER ${name} uname)
- set(var ${_c4_uprefix}CACHE_DOWNLOAD_${uname})
- set(val "${${var}}")
- if(NOT ("${val}" STREQUAL ""))
- c4_log("downloading ${name}: searching in ${var}=${val}")
- if(EXISTS "${val}")
- c4_log("downloading ${name}: picked ${sav} instead of ${${dir_in_out}}")
- set(${dir_in_out} ${sav} PARENT_SCOPE)
- endif()
- endif()
- #
- # 2. search in the global directory (if there is one)
- _c4_get_cached_srcdir_global_extern(${name} sav)
- if(NOT ("${sav}" STREQUAL ""))
- c4_log("downloading ${name}: searching in C4_EXTERN_DIR: ${sav}")
- if(EXISTS "${sav}")
- c4_log("downloading ${name}: picked ${sav} instead of ${${dir_in_out}}")
- set(${dir_in_out} ${sav} PARENT_SCOPE)
- endif()
- endif()
-endfunction()
-
-
-function(_c4_get_cached_srcdir_global_extern name out)
- set(${out} "" PARENT_SCOPE)
- if("${C4_EXTERN_DIR}" STREQUAL "")
- set(C4_EXTERN_DIR "$ENV{C4_EXTERN_DIR}")
- endif()
- if(NOT ("${C4_EXTERN_DIR}" STREQUAL ""))
- set(${out} "${C4_EXTERN_DIR}/${name}" PARENT_SCOPE)
- endif()
-endfunction()
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-
-function(_c4_get_folder output importer_subproject subproject_name)
- _c4_get_subproject_property(${importer_subproject} FOLDER importer_folder)
- if("${importer_folder}" STREQUAL "")
- set(folder ${importer_subproject})
- else()
- set(folder "${importer_folder}/deps/${subproject_name}")
- endif()
- set(${output} ${folder} PARENT_SCOPE)
-endfunction()
-
-
-function(_c4_set_target_folder target subfolder)
- string(FIND "${subfolder}" "/" pos)
- if(pos EQUAL 0)
- if("${_c4_curr_path}" STREQUAL "")
- string(SUBSTRING "${subfolder}" 1 -1 sf)
- set_target_properties(${target} PROPERTIES
- FOLDER "${sf}")
- else()
- set_target_properties(${target} PROPERTIES
- FOLDER "${subfolder}")
- endif()
- elseif("${subfolder}" STREQUAL "")
- set_target_properties(${target} PROPERTIES
- FOLDER "${_c4_curr_path}")
- else()
- if("${_c4_curr_path}" STREQUAL "")
- set_target_properties(${target} PROPERTIES
- FOLDER "${subfolder}")
- else()
- set_target_properties(${target} PROPERTIES
- FOLDER "${_c4_curr_path}/${subfolder}")
- endif()
- endif()
-endfunction()
-
-
-function(c4_set_folder_remote_project_targets subfolder)
- foreach(target ${ARGN})
- if(TARGET ${target})
- _c4_set_target_folder(${target} "${subfolder}")
- endif()
- endforeach()
-endfunction()
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-
-# a convenience alias to c4_add_target()
-function(c4_add_executable target)
- c4_add_target(${target} EXECUTABLE ${ARGN})
-endfunction(c4_add_executable)
-
-
-# a convenience alias to c4_add_target()
-function(c4_add_library target)
- c4_add_target(${target} LIBRARY ${ARGN})
-endfunction(c4_add_library)
-
-
-# example: c4_add_target(ryml LIBRARY SOURCES ${SRC})
-function(c4_add_target target)
- c4_dbg("adding target: ${target}: ${ARGN}")
- set(opt0arg
- LIBRARY # the target is a library
- EXECUTABLE # the target is an executable
- WIN32 # the executable is WIN32
- SANITIZE # deprecated
- )
- set(opt1arg
- LIBRARY_TYPE # override global setting for C4_LIBRARY_TYPE
- SHARED_MACRO # the name of the macro to turn on export/import symbols
- # for compiling the library as a windows DLL.
- # defaults to ${_c4_uprefix}SHARED.
- SHARED_EXPORTS # the name of the macro to turn on export of symbols
- # for compiling the library as a windows DLL.
- # defaults to ${_c4_uprefix}EXPORTS.
- SOURCE_ROOT # the directory where relative source paths
- # should be resolved. when empty,
- # use CMAKE_CURRENT_SOURCE_DIR
- FOLDER # IDE folder to group the target in
- SANITIZERS # outputs the list of sanitize targets in this var
- SOURCE_TRANSFORM # WIP
- )
- set(optnarg
- INCORPORATE # incorporate these libraries into this target,
- # subject to ${_c4_uprefix}STANDALONE and C4_STANDALONE
- SOURCES PUBLIC_SOURCES INTERFACE_SOURCES PRIVATE_SOURCES
- HEADERS PUBLIC_HEADERS INTERFACE_HEADERS PRIVATE_HEADERS
- INC_DIRS PUBLIC_INC_DIRS INTERFACE_INC_DIRS PRIVATE_INC_DIRS
- LIBS PUBLIC_LIBS INTERFACE_LIBS PRIVATE_LIBS
- DEFS PUBLIC_DEFS INTERFACE_DEFS PRIVATE_DEFS # defines
- CFLAGS PUBLIC_CFLAGS INTERFACE_CFLAGS PRIVATE_CFLAGS # compiler flags. TODO: linker flags
- DLLS # DLLs required by this target
- MORE_ARGS
- )
- cmake_parse_arguments("" "${opt0arg}" "${opt1arg}" "${optnarg}" ${ARGN})
- #
- if(_SANITIZE)
- c4_err("SANITIZE is deprecated")
- endif()
-
- if(${_LIBRARY})
- set(_what LIBRARY)
- elseif(${_EXECUTABLE})
- set(_what EXECUTABLE)
- else()
- c4_err("must be either LIBRARY or EXECUTABLE")
- endif()
-
- _c4_handle_arg(SHARED_MACRO ${_c4_uprefix}MACRO)
- _c4_handle_arg(SHARED_EXPORTS ${_c4_uprefix}EXPORTS)
- _c4_handle_arg_or_fallback(SOURCE_ROOT "${CMAKE_CURRENT_SOURCE_DIR}")
- function(_c4_transform_to_full_path list all)
- set(l)
- foreach(f ${${list}})
- if(NOT IS_ABSOLUTE "${f}")
- set(f "${_SOURCE_ROOT}/${f}")
- endif()
- list(APPEND l "${f}")
- endforeach()
- set(${list} "${l}" PARENT_SCOPE)
- set(cp ${${all}})
- list(APPEND cp ${l})
- set(${all} ${cp} PARENT_SCOPE)
- endfunction()
- _c4_transform_to_full_path( _SOURCES allsrc)
- _c4_transform_to_full_path( _HEADERS allsrc)
- _c4_transform_to_full_path( _PUBLIC_SOURCES allsrc)
- _c4_transform_to_full_path(_INTERFACE_SOURCES allsrc)
- _c4_transform_to_full_path( _PRIVATE_SOURCES allsrc)
- _c4_transform_to_full_path( _PUBLIC_HEADERS allsrc)
- _c4_transform_to_full_path(_INTERFACE_HEADERS allsrc)
- _c4_transform_to_full_path( _PRIVATE_HEADERS allsrc)
- create_source_group("" "${_SOURCE_ROOT}" "${allsrc}")
- # is the target name prefixed with the project prefix?
- string(REGEX MATCH "${_c4_prefix}::.*" target_is_prefixed "${target}")
- if(NOT ${_c4_uprefix}SANITIZE_ONLY)
- if(${_EXECUTABLE})
- c4_dbg("adding executable: ${target}")
- if(WIN32)
- if(${_WIN32})
- list(APPEND _MORE_ARGS WIN32)
- endif()
- endif()
- add_executable(${target} ${_MORE_ARGS})
- if(NOT target_is_prefixed)
- add_executable(${_c4_prefix}::${target} ALIAS ${target})
- endif()
- set(src_mode PRIVATE)
- set(tgt_type PUBLIC)
- set(compiled_target ON)
- set_target_properties(${target} PROPERTIES VERSION ${${_c4_prefix}_VERSION})
- elseif(${_LIBRARY})
- c4_dbg("adding library: ${target}")
- set(_blt ${C4_LIBRARY_TYPE}) # build library type
- if(NOT "${_LIBRARY_TYPE}" STREQUAL "")
- set(_blt ${_LIBRARY_TYPE})
- endif()
- if("${_blt}" STREQUAL "")
- endif()
- #
- if("${_blt}" STREQUAL "INTERFACE")
- c4_dbg("adding interface library ${target}")
- add_library(${target} INTERFACE)
- set(src_mode INTERFACE)
- set(tgt_type INTERFACE)
- set(compiled_target OFF)
- else()
- if("${_blt}" STREQUAL "")
- # obey BUILD_SHARED_LIBS (ie, either static or shared library)
- c4_dbg("adding library ${target} (defer to BUILD_SHARED_LIBS=${BUILD_SHARED_LIBS}) --- ${_MORE_ARGS}")
- add_library(${target} ${_MORE_ARGS})
- if(BUILD_SHARED_LIBS)
- set(_blt SHARED)
- else()
- set(_blt STATIC)
- endif()
- else()
- c4_dbg("adding library ${target} with type ${_blt}")
- add_library(${target} ${_blt} ${_MORE_ARGS})
- endif()
- # libraries
- set(src_mode PRIVATE)
- set(tgt_type PUBLIC)
- set(compiled_target ON)
- set_target_properties(${target} PROPERTIES VERSION ${${_c4_prefix}_VERSION})
- if("${_blt}" STREQUAL SHARED)
- set_target_properties(${target} PROPERTIES SOVERSION ${${_c4_prefix}_VERSION})
- endif()
- # exports for shared libraries
- if(WIN32)
- if("${_blt}" STREQUAL SHARED)
- set_target_properties(${target} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
- target_compile_definitions(${target} PUBLIC ${_SHARED_MACRO})
- target_compile_definitions(${target} PRIVATE $<BUILD_INTERFACE:${_SHARED_EXPORTS}>)
- # save the name of the macro for later use when(if) incorporating this library
- c4_set_target_prop(${target} SHARED_EXPORTS ${_SHARED_EXPORTS})
- endif() # shared lib
- endif() # win32
- endif() # interface or lib
- if(NOT target_is_prefixed)
- add_library(${_c4_prefix}::${target} ALIAS ${target})
- endif()
- endif(${_EXECUTABLE})
-
- if(src_mode STREQUAL "PUBLIC")
- c4_add_target_sources(${target}
- PUBLIC "${_SOURCES};${_HEADERS};${_PUBLIC_SOURCES};${_PUBLIC_HEADERS}"
- INTERFACE "${_INTERFACE_SOURCES};${_INTERFACE_HEADERS}"
- PRIVATE "${_PRIVATE_SOURCES};${_PRIVATE_HEADERS}")
- elseif(src_mode STREQUAL "INTERFACE")
- c4_add_target_sources(${target}
- PUBLIC "${_PUBLIC_SOURCES};${_PUBLIC_HEADERS}"
- INTERFACE "${_SOURCES};${_HEADERS};${_INTERFACE_SOURCES};${_INTERFACE_HEADERS}"
- PRIVATE "${_PRIVATE_SOURCES};${_PRIVATE_HEADERS}")
- elseif(src_mode STREQUAL "PRIVATE")
- c4_add_target_sources(${target}
- PUBLIC "${_PUBLIC_SOURCES};${_PUBLIC_HEADERS}"
- INTERFACE "${_INTERFACE_SOURCES};${_INTERFACE_HEADERS}"
- PRIVATE "${_SOURCES};${_HEADERS};${_PRIVATE_SOURCES};${_PRIVATE_HEADERS}")
- elseif()
- c4_err("${target}: adding sources: invalid source mode")
- endif()
- _c4_set_tgt_prop(${target} C4_SOURCE_ROOT "${_SOURCE_ROOT}")
-
- if(_INC_DIRS)
- c4_dbg("${target}: adding include dirs ${_INC_DIRS} [from target: ${tgt_type}]")
- target_include_directories(${target} "${tgt_type}" ${_INC_DIRS})
- endif()
- if(_PUBLIC_INC_DIRS)
- c4_dbg("${target}: adding PUBLIC include dirs ${_PUBLIC_INC_DIRS}")
- target_include_directories(${target} PUBLIC ${_PUBLIC_INC_DIRS})
- endif()
- if(_INTERFACE_INC_DIRS)
- c4_dbg("${target}: adding INTERFACE include dirs ${_INTERFACE_INC_DIRS}")
- target_include_directories(${target} INTERFACE ${_INTERFACE_INC_DIRS})
- endif()
- if(_PRIVATE_INC_DIRS)
- c4_dbg("${target}: adding PRIVATE include dirs ${_PRIVATE_INC_DIRS}")
- target_include_directories(${target} PRIVATE ${_PRIVATE_INC_DIRS})
- endif()
-
- if(_LIBS)
- _c4_link_with_libs(${target} "${tgt_type}" "${_LIBS}" "${_INCORPORATE}")
- endif()
- if(_PUBLIC_LIBS)
- _c4_link_with_libs(${target} PUBLIC "${_PUBLIC_LIBS}" "${_INCORPORATE}")
- endif()
- if(_INTERFACE_LIBS)
- _c4_link_with_libs(${target} INTERFACE "${_INTERFACE_LIBS}" "${_INCORPORATE}")
- endif()
- if(_PRIVATE_LIBS)
- _c4_link_with_libs(${target} PRIVATE "${_PRIVATE_LIBS}" "${_INCORPORATE}")
- endif()
-
- if(compiled_target)
- if(_FOLDER)
- _c4_set_target_folder(${target} "${_FOLDER}")
- else()
- _c4_set_target_folder(${target} "")
- endif()
- # cxx standard
- c4_target_inherit_cxx_standard(${target})
- # compile flags
- set(_more_flags
- ${${_c4_uprefix}CXX_FLAGS}
- ${${_c4_uprefix}C_FLAGS}
- ${${_c4_uprefix}CXX_FLAGS_OPT})
- if(_more_flags)
- get_target_property(_flags ${target} COMPILE_OPTIONS)
- if(_flags)
- set(_more_flags ${_flags};${_more_flags})
- endif()
- c4_dbg("${target}: COMPILE_FLAGS=${_more_flags}")
- target_compile_options(${target} PRIVATE "${_more_flags}")
- endif()
- # linker flags
- set(_link_flags ${${_c4_uprefix}CXX_LINKER_FLAGS})
- if(_link_flags)
- get_target_property(_flags ${target} LINK_OPTIONS)
- if(_flags)
- set(_link_flags ${_flags};${_more_flags})
- endif()
- c4_dbg("${target}: LINKER_FLAGS=${_link_flags}")
- target_link_options(${target} PUBLIC "${_link_flags}")
- endif()
- # static analysis
- if(${_c4_uprefix}LINT)
- c4_static_analysis_target(${target} "${_FOLDER}" lint_targets)
- endif()
- endif(compiled_target)
-
- if(_DEFS)
- target_compile_definitions(${target} "${tgt_type}" ${_DEFS})
- endif()
- if(_PUBLIC_DEFS)
- target_compile_definitions(${target} PUBLIC ${_PUBLIC_DEFS})
- endif()
- if(_INTERFACE_DEFS)
- target_compile_definitions(${target} INTERFACE ${_INTERFACE_DEFS})
- endif()
- if(_PRIVATE_DEFS)
- target_compile_definitions(${target} PRIVATE ${_PRIVATE_DEFS})
- endif()
-
- if(_CFLAGS)
- target_compile_options(${target} "${tgt_type}" ${_CFLAGS})
- endif()
- if(_PUBLIC_CFLAGS)
- target_compile_options(${target} PUBLIC ${_PUBLIC_CFLAGS})
- endif()
- if(_INTERFACE_CFLAGS)
- target_compile_options(${target} INTERFACE ${_INTERFACE_CFLAGS})
- endif()
- if(_PRIVATE_CFLAGS)
- target_compile_options(${target} PRIVATE ${_PRIVATE_CFLAGS})
- endif()
-
- if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND
- (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 4.8) AND
- (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0))
- c4_dbg("${target}: adding compat include path")
- target_include_directories(${target} PUBLIC $<BUILD_INTERFACE:${_c4_project_dir}/compat>)
- endif()
-
- endif(NOT ${_c4_uprefix}SANITIZE_ONLY)
-
- if(compiled_target)
- if(${_c4_uprefix}SANITIZE)
- c4_sanitize_target(${target}
- ${_what} # LIBRARY or EXECUTABLE
- SOURCES ${allsrc}
- INC_DIRS ${_INC_DIRS} ${_PUBLIC_INC_DIRS} ${_INTERFACE_INC_DIRS} ${_PRIVATE_INC_DIRS}
- LIBS ${_LIBS} ${_PUBLIC_LIBS} ${_INTERFACE_LIBS} ${_PRIVATE_LIBS}
- DEFS ${_DEFS} ${_PUBLIC_DEFS} ${_INTERFACE_DEFS} ${_PRIVATE_DEFS}
- CFLAGS ${_CFLAGS} ${_PUBLIC_CFLAGS} ${_INTERFACE_CFLAGS} ${_PRIVATE_CFLAGS}
- OUTPUT_TARGET_NAMES san_targets
- FOLDER "${_FOLDER}"
- )
- endif()
-
- if(NOT ${_c4_uprefix}SANITIZE_ONLY)
- list(INSERT san_targets 0 ${target})
- endif()
-
- if(_SANITIZERS)
- set(${_SANITIZERS} ${san_targets} PARENT_SCOPE)
- endif()
-
- _c4_set_tgt_prop(${target} C4_SAN_TARGETS "${san_targets}")
- else()
- _c4_set_tgt_prop(${target} C4_SAN_TARGETS "${target}")
- endif()
-
- # gather dlls so that they can be automatically copied to the target directory
- if(_DLLS)
- c4_append_transitive_property(${target} _C4_DLLS "${_DLLS}")
- endif()
-
- if(${_EXECUTABLE})
- if(WIN32)
- c4_get_transitive_property(${target} _C4_DLLS transitive_dlls)
- list(REMOVE_DUPLICATES transitive_dlls)
- foreach(_dll ${transitive_dlls})
- if(_dll)
- c4_dbg("enable copy of dll to target file dir: ${_dll} ---> $<TARGET_FILE_DIR:${target}>")
- add_custom_command(TARGET ${target} POST_BUILD
- COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_dll}" "$<TARGET_FILE_DIR:${target}>"
- )
- else()
- message(WARNING "dll required by ${_c4_prefix}/${target} was not found, so cannot copy: ${_dll}")
- endif()
- endforeach()
- endif()
- endif()
-endfunction() # add_target
-
-
-function(_c4_link_with_libs target link_type libs incorporate)
- foreach(lib ${libs})
- # add targets that are DLLs
- if(WIN32)
- if(TARGET ${lib})
- get_target_property(lib_type ${lib} TYPE)
- if(lib_type STREQUAL SHARED_LIBRARY)
- c4_append_transitive_property(${target} _C4_DLLS "$<TARGET_FILE:${lib}>")
- endif()
- endif()
- endif()
- _c4_lib_is_incorporated(${lib} isinc)
- if(isinc OR (incorporate AND ${_c4_uprefix}STANDALONE))
- c4_log("-----> target ${target} ${link_type} incorporating lib ${lib}")
- _c4_incorporate_lib(${target} ${link_type} ${lib})
- else()
- c4_dbg("${target} ${link_type} linking with lib ${lib}")
- target_link_libraries(${target} ${link_type} ${lib})
- endif()
- endforeach()
-endfunction()
-
-
-function(_c4_lib_is_incorporated lib ret)
- c4_dbg("${lib}: is incorporated?")
- if(NOT TARGET ${lib})
- c4_dbg("${lib}: no, not a target")
- set(${ret} OFF PARENT_SCOPE)
- else()
- c4_get_target_prop(${lib} INCORPORATING_TARGETS inc)
- if(inc)
- c4_dbg("${lib}: is incorporated!")
- set(${ret} ON PARENT_SCOPE)
- else()
- c4_dbg("${lib}: is not incorporated!")
- set(${ret} OFF PARENT_SCOPE)
- endif()
- endif()
-endfunction()
-
-
-function(_c4_incorporate_lib target link_type lib)
- c4_dbg("target ${target}: incorporating lib ${lib} [${link_type}]")
- _c4_get_tgt_prop(srcroot ${lib} C4_SOURCE_ROOT)
- #
- c4_append_target_prop(${lib} INCORPORATING_TARGETS ${target})
- c4_append_target_prop(${target} INCORPORATED_TARGETS ${lib})
- #
- _c4_get_tgt_prop(lib_src ${lib} SOURCES)
- if(lib_src)
- create_source_group("${lib}" "${srcroot}" "${lib_src}")
- c4_add_target_sources(${target} INCORPORATED_FROM ${lib} PRIVATE ${lib_src})
- endif()
- #
- _c4_get_tgt_prop(lib_isrc ${lib} INTERFACE_SOURCES)
- if(lib_isrc)
- create_source_group("${lib}" "${srcroot}" "${lib_isrc}")
- c4_add_target_sources(${target} INCORPORATED_FROM ${lib} INTERFACE ${lib_isrc})
- endif()
- #
- _c4_get_tgt_prop(lib_psrc ${lib} PRIVATE_SOURCES)
- if(lib_psrc)
- create_source_group("${lib}" "${srcroot}" "${lib_psrc}")
- c4_add_target_sources(${target} INCORPORATED_FROM ${lib} INTERFACE ${lib_psrc})
- endif()
- #
- #
- _c4_get_tgt_prop(lib_incs ${lib} INCLUDE_DIRECTORIES)
- if(lib_incs)
- target_include_directories(${target} PUBLIC ${lib_incs})
- endif()
- #
- _c4_get_tgt_prop(lib_iincs ${lib} INTERFACE_INCLUDE_DIRECTORIES)
- if(lib_iincs)
- target_include_directories(${target} INTERFACE ${lib_iincs})
- endif()
- #
- #
- _c4_get_tgt_prop(lib_lib ${lib} LINK_LIBRARIES)
- if(lib_lib)
- target_link_libraries(${target} PUBLIC ${lib_lib})
- endif()
- _c4_get_tgt_prop(lib_ilib ${lib} INTERFACE_LIBRARY)
- if(lib_ilib)
- target_link_libraries(${target} INTERFACE ${lib_ilib})
- endif()
- #
- #
- c4_get_target_prop(${lib} SHARED_EXPORTS lib_exports)
- if(lib_exports)
- target_compile_definitions(${target} PRIVATE $<BUILD_INTERFACE:${lib_exports}>)
- endif()
-endfunction()
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#
-#
-function(c4_add_target_sources target)
- # https://steveire.wordpress.com/2016/08/09/opt-in-header-only-libraries-with-cmake/
- _c4_handle_args(_ARGS ${ARGN}
- _ARGS1 # one-value macro arguments
- INCORPORATED_FROM
- TRANSFORM # Transform types:
- # * NONE - do not transform the sources
- # * UNITY
- # * UNITY_HDR
- # * SINGLE_HDR
- # * SINGLE_UNIT
- _ARGSN # multi-value macro arguments
- PUBLIC
- INTERFACE
- PRIVATE
- )
- if(("${_TRANSFORM}" STREQUAL "GLOBAL") OR ("${_TRANSFORM}" STREQUAL ""))
- set(_TRANSFORM ${C4_SOURCE_TRANSFORM})
- endif()
- if("${_TRANSFORM}" STREQUAL "")
- set(_TRANSFORM NONE)
- endif()
- #
- # is this target an interface?
- set(_is_iface FALSE)
- _c4_get_tgt_prop(target_type ${target} TYPE)
- if("${target_type}" STREQUAL "INTERFACE_LIBRARY")
- set(_is_iface TRUE)
- elseif("${prop_name}" STREQUAL "LINK_LIBRARIES")
- set(_is_iface FALSE)
- endif()
- #
- set(out)
- set(umbrella ${_c4_lprefix}transform-src)
- #
- if("${_TRANSFORM}" STREQUAL "NONE")
- c4_dbg("target ${target}: source transform: NONE!")
- #
- # do not transform the sources
- #
- if(_PUBLIC)
- c4_dbg("target=${target} PUBLIC sources: ${_PUBLIC}")
- c4_append_target_prop(${target} PUBLIC_SRC "${_PUBLIC}")
- if(_INCORPORATED_FROM)
- c4_append_target_prop(${target} PUBLIC_SRC_${_INCORPORATED_FROM} "${_PUBLIC}")
- else()
- c4_append_target_prop(${target} PUBLIC_SRC_${target} "${_PUBLIC}")
- endif()
- target_sources(${target} PUBLIC "${_PUBLIC}")
- endif()
- if(_INTERFACE)
- c4_dbg("target=${target} INTERFACE sources: ${_INTERFACE}")
- c4_append_target_prop(${target} INTERFACE_SRC "${_INTERFACE}")
- if(_INCORPORATED_FROM)
- c4_append_target_prop(${target} INTERFACE_SRC_${_INCORPORATED_FROM} "${_INTERFACE}")
- else()
- c4_append_target_prop(${target} INTERFACE_SRC_${target} "${_INTERFACE}")
- endif()
- target_sources(${target} INTERFACE "${_INTERFACE}")
- endif()
- if(_PRIVATE)
- c4_dbg("target=${target} PRIVATE sources: ${_PRIVATE}")
- c4_append_target_prop(${target} PRIVATE_SRC "${_PRIVATE}")
- if(_INCORPORATED_FROM)
- c4_append_target_prop(${target} PRIVATE_SRC_${_INCORPORATED_FROM} "${_PRIVATE}")
- else()
- c4_append_target_prop(${target} PRIVATE_SRC_${target} "${_PRIVATE}")
- endif()
- target_sources(${target} PRIVATE "${_PRIVATE}")
- endif()
- #
- elseif("${_TRANSFORM}" STREQUAL "UNITY")
- c4_dbg("target ${target}: source transform: UNITY!")
- c4_err("source transformation not implemented")
- #
- # concatenate all compilation unit files (excluding interface)
- # into a single compilation unit
- #
- _c4cat_filter_srcs("${_PUBLIC}" cpublic)
- _c4cat_filter_hdrs("${_PUBLIC}" hpublic)
- _c4cat_filter_srcs("${_INTERFACE}" cinterface)
- _c4cat_filter_hdrs("${_INTERFACE}" hinterface)
- _c4cat_filter_srcs("${_PRIVATE}" cprivate)
- _c4cat_filter_hdrs("${_PRIVATE}" hprivate)
- if(cpublic OR cinterface OR cprivate)
- _c4cat_get_outname(${target} "src" ${C4_GEN_SRC_EXT} out)
- c4_dbg("${target}: output unit: ${out}")
- c4_cat_sources("${cpublic};${cinterface};${cprivate}" "${out}" ${umbrella})
- add_dependencies(${target} ${out})
- endif()
- if(_PUBLIC)
- c4_append_target_prop(${target} PUBLIC_SRC
- $<BUILD_INTERFACE:${hpublic};${out}>
- $<INSTALL_INTERFACE:${hpublic};${out}>)
- target_sources(${target} PUBLIC
- $<BUILD_INTERFACE:${hpublic};${out}>
- $<INSTALL_INTERFACE:${hpublic};${out}>)
- endif()
- if(_INTERFACE)
- c4_append_target_prop(${target} INTERFACE_SRC
- $<BUILD_INTERFACE:${hinterface}>
- $<INSTALL_INTERFACE:${hinterface}>)
- target_sources(${target} INTERFACE
- $<BUILD_INTERFACE:${hinterface}>
- $<INSTALL_INTERFACE:${hinterface}>)
- endif()
- if(_PRIVATE)
- c4_append_target_prop(${target} PRIVATE_SRC
- $<BUILD_INTERFACE:${hprivate}>
- $<INSTALL_INTERFACE:${hprivate}>)
- target_sources(${target} PRIVATE
- $<BUILD_INTERFACE:${hprivate}>
- $<INSTALL_INTERFACE:${hprivate}>)
- endif()
- elseif("${_TRANSFORM}" STREQUAL "UNITY_HDR")
- c4_dbg("target ${target}: source transform: UNITY_HDR!")
- c4_err("target ${target}: source transformation not implemented")
- #
- # like unity, but concatenate compilation units into
- # a header file, leaving other header files untouched
- #
- _c4cat_filter_srcs("${_PUBLIC}" cpublic)
- _c4cat_filter_hdrs("${_PUBLIC}" hpublic)
- _c4cat_filter_srcs("${_INTERFACE}" cinterface)
- _c4cat_filter_hdrs("${_INTERFACE}" hinterface)
- _c4cat_filter_srcs("${_PRIVATE}" cprivate)
- _c4cat_filter_hdrs("${_PRIVATE}" hprivate)
- if(c)
- _c4cat_get_outname(${target} "src" ${C4_GEN_HDR_EXT} out)
- c4_dbg("${target}: output hdr: ${out}")
- _c4cat_filter_srcs_hdrs("${_PUBLIC}" c_h)
- c4_cat_sources("${c}" "${out}" ${umbrella})
- add_dependencies(${target} ${out})
- add_dependencies(${target} ${_c4_lprefix}cat)
- endif()
- set(${src} ${out} PARENT_SCOPE)
- set(${hdr} ${h} PARENT_SCOPE)
- #
- elseif("${_TRANSFORM}" STREQUAL "SINGLE_HDR")
- c4_dbg("target ${target}: source transform: SINGLE_HDR!")
- c4_err("target ${target}: source transformation not implemented")
- #
- # concatenate everything into a single header file
- #
- _c4cat_get_outname(${target} "all" ${C4_GEN_HDR_EXT} out)
- _c4cat_filter_srcs_hdrs("${_c4al_SOURCES}" ch)
- c4_cat_sources("${ch}" "${out}" ${umbrella})
- #
- elseif("${_TRANSFORM}" STREQUAL "SINGLE_UNIT")
- c4_dbg("target ${target}: source transform: SINGLE_UNIT!")
- c4_err("target ${target}: source transformation not implemented")
- #
- # concatenate:
- # * all compilation units into a single compilation unit
- # * all headers into a single header
- #
- _c4cat_get_outname(${target} "src" ${C4_GEN_SRC_EXT} out)
- _c4cat_get_outname(${target} "hdr" ${C4_GEN_SRC_EXT} out)
- _c4cat_filter_srcs_hdrs("${_c4al_SOURCES}" ch)
- c4_cat_sources("${ch}" "${out}" ${umbrella})
- else()
- c4_err("unknown transform type: ${transform_type}. Must be one of GLOBAL;NONE;UNITY;TO_HEADERS;SINGLE_HEADER")
- endif()
-endfunction()
-
-
-# -----------------------------------------------------------------------------
-# -----------------------------------------------------------------------------
-# -----------------------------------------------------------------------------
-# WIP, under construction (still incomplete)
-# see: https://github.com/pr0g/cmake-examples
-# see: https://cliutils.gitlab.io/modern-cmake/
-
-
-function(c4_install_target target)
- _c4_handle_args(_ARGS ${ARGN}
- _ARGS1 # one-value macro arguments
- EXPORT # the name of the export target. default: see below.
- )
- _c4_handle_arg(EXPORT "${_c4_prefix}-export")
- #
- c4_dbg("installing target: ${target} ${ARGN}")
- #_c4_is_incorporated(${_c4_prefix} inc)
- #if(inc)
- # c4_dbg("this project is INCORPORATEd. skipping install of targets")
- # return()
- #endif()
- #
- _c4_setup_install_vars()
- install(TARGETS ${target}
- EXPORT ${_EXPORT}
- RUNTIME DESTINATION ${_RUNTIME_INSTALL_DIR} #COMPONENT runtime
- BUNDLE DESTINATION ${_RUNTIME_INSTALL_DIR} #COMPONENT runtime
- LIBRARY DESTINATION ${_LIBRARY_INSTALL_DIR} #COMPONENT runtime
- ARCHIVE DESTINATION ${_ARCHIVE_INSTALL_DIR} #COMPONENT development
- OBJECTS DESTINATION ${_OBJECTS_INSTALL_DIR} #COMPONENT development
- INCLUDES DESTINATION ${_INCLUDE_INSTALL_DIR} #COMPONENT development
- PUBLIC_HEADER DESTINATION ${_INCLUDE_INSTALL_DIR} #COMPONENT development
- )
- c4_install_sources(${target} include)
- #
- # on windows, install also required DLLs
- if(WIN32)
- get_target_property(target_type ${target} TYPE)
- if("${target_type}" STREQUAL "EXECUTABLE")
- c4_get_transitive_property(${target} _C4_DLLS transitive_dlls)
- if(transitive_dlls)
- c4_dbg("${target}: installing dlls: ${transitive_dlls} to ${_RUNTIME_INSTALL_DIR}")
- list(REMOVE_DUPLICATES transitive_dlls)
- install(FILES ${transitive_dlls}
- DESTINATION ${_RUNTIME_INSTALL_DIR} # shouldn't it be _LIBRARY_INSTALL_DIR?
- #COMPONENT runtime
- )
- endif()
- endif()
- endif()
- #
- set(l ${${_c4_prefix}_TARGETS})
- list(APPEND l ${target})
- set(${_c4_prefix}_TARGETS ${l} PARENT_SCOPE)
- #
-# # pkgconfig (WIP)
-# set(pc ${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/${target}.pc)
-# file(WRITE ${pc} "# pkg-config: ${target}
-#
-#prefix=\"${CMAKE_INSTALL_PREFIX}\"
-#exec_prefix=\"\${_c4_prefix}\"
-#libdir=\"\${_c4_prefix}/${CMAKE_INSTALL_LIBDIR}\"
-#includedir=\"\${_c4_prefix}/include\"
-#
-#Name: ${target}
-#Description: A library for xyzzying frobnixes
-#URL: https://github.com/me/mylibrary
-#Version: 0.0.0
-#Requires: @PKGCONF_REQ_PUB@
-#Requires.private: @PKGCONF_REQ_PRIV@
-#Cflags: -I\"${includedir}\"
-#Libs: -L\"${libdir}\" -lmylibrary
-#Libs.private: -L\"${libdir}\" -lmylibrary @PKGCONF_LIBS_PRIV@
-#")
-# _c4_setup_install_vars()
-# install(FILES ${pc} DESTINATION "${_ARCHIVE_INSTALL_DIR}/pkgconfig/")
-endfunction()
-
-
-function(c4_install_sources target destination)
- c4_dbg("target ${target}: installing sources to ${destination}")
- # executables have no sources requiring install
- _c4_get_tgt_prop(target_type ${target} TYPE)
- if(target_type STREQUAL "EXECUTABLE")
- c4_dbg("target ${target}: is executable, skipping source install")
- return()
- endif()
- # install source from the target and incorporated targets
- c4_get_target_prop(${target} INCORPORATED_TARGETS inctargets)
- if(inctargets)
- set(targets "${inctargets};${target}")
- else()
- set(targets "${target}")
- endif()
- foreach(t ${targets})
- _c4_get_tgt_prop(srcroot ${t} C4_SOURCE_ROOT)
- # get the sources from the target
- #
- c4_get_target_prop(${t} PUBLIC_SRC_${t} src)
- if(src)
- _c4cat_filter_hdrs("${src}" srcf)
- _c4cat_filter_additional_exts("${src}" add)
- c4_install_files("${srcf}" "${destination}" "${srcroot}")
- c4_install_files("${add}" "${destination}" "${srcroot}")
- endif()
- #
- c4_get_target_prop(${t} PRIVATE_SRC_${t} psrc)
- if(psrc)
- _c4cat_filter_hdrs("${psrc}" psrcf)
- _c4cat_filter_additional_exts("${psrc}" add)
- c4_install_files("${psrcf}" "${destination}" "${srcroot}")
- c4_install_files("${add}" "${destination}" "${srcroot}")
- endif()
- #
- c4_get_target_prop(${t} INTERFACE_SRC_${t} isrc)
- if(isrc)
- _c4cat_filter_srcs_hdrs("${isrc}" isrcf)
- _c4cat_filter_additional_exts("${isrc}" add)
- c4_install_files("${isrcf}" "${destination}" "${srcroot}")
- c4_install_files("${add}" "${destination}" "${srcroot}")
- endif()
- #
- c4_get_target_prop(${t} ADDFILES addfiles)
- if(addfiles)
- foreach(af ${addfiles})
- string(REGEX REPLACE "(.*)!!(.*)!!(.*)" "\\1;\\2;\\3" li "${af}")
- list(GET li 0 files)
- list(GET li 1 dst)
- list(GET li 2 relative_to)
- string(REPLACE "%%%" ";" files "${files}")
- c4_install_files("${files}" "${dst}" "${relative_to}")
- endforeach()
- endif()
- #
- c4_get_target_prop(${t} ADDDIRS adddirs)
- if(adddirs)
- foreach(af ${adddirs})
- string(REGEX REPLACE "(.*)!!(.*)!!(.*)" "\\1;\\2;\\3" li "${af}")
- list(GET li 0 dirs)
- list(GET li 1 dst)
- list(GET li 2 relative_to)
- string(REPLACE "%%%" ";" dirs "${files}")
- c4_install_dirs("${dirs}" "${dst}" "${relative_to}")
- endforeach()
- endif()
- endforeach()
-endfunction()
-
-
-function(c4_install_target_add_files target files destination relative_to)
- c4_dbg("installing additional files for target ${target}, destination=${destination}: ${files}")
- string(REPLACE ";" "%%%" rfiles "${files}")
- c4_append_target_prop(${target} ADDFILES "${rfiles}!!${destination}!!${relative_to}")
- #
- _c4_is_incorporated(${_c4_prefix} inc)
- if(inc)
- c4_dbg("this project is INCORPORATEd. skipping install of targets")
- return()
- endif()
- c4_install_files("${files}" "${destination}" "${relative_to}")
-endfunction()
-
-
-function(c4_install_target_add_dirs target dirs destination relative_to)
- c4_dbg("installing additional dirs for target ${target}, destination=${destination}: ${dirs}")
- string(REPLACE ";" "%%%" rdirs "${dirs}")
- c4_append_target_prop(${target} ADDDIRS "${rdirs}!!${destination}!!${relative_to}")
- #
- _c4_is_incorporated(${_c4_prefix} inc)
- if(inc)
- c4_dbg("this project is INCORPORATEd. skipping install of targets")
- return()
- endif()
- c4_install_dirs("${dirs}" "${destination}" "${relative_to}")
-endfunction()
-
-
-function(c4_install_files files destination relative_to)
- c4_dbg("adding files to install list, destination ${destination}: ${files}")
- foreach(f ${files})
- file(RELATIVE_PATH rf "${relative_to}" ${f})
- get_filename_component(rd "${rf}" DIRECTORY)
- install(FILES ${f} DESTINATION "${destination}/${rd}" ${ARGN})
- endforeach()
-endfunction()
-
-
-function(c4_install_directories directories destination relative_to)
- c4_dbg("adding directories to install list, destination ${destination}: ${directories}")
- foreach(d ${directories})
- file(RELATIVE_PATH rf "${relative_to}" ${d})
- get_filename_component(rd "${rf}" DIRECTORY)
- install(DIRECTORY ${d} DESTINATION "${destination}/${rd}" ${ARGN})
- endforeach()
-endfunction()
-
-
-function(c4_install_exports)
- _c4_handle_args(_ARGS ${ARGN}
- _ARGS1 # one-value macro arguments
- PREFIX # override the c4 project-wide prefix. This will be used in the cmake
- TARGET # the name of the exports target
- NAMESPACE # the namespace for the targets
- _ARGSN # multi-value macro arguments
- DEPENDENCIES
- )
- #
- _c4_handle_arg(PREFIX "${_c4_prefix}")
- _c4_handle_arg(TARGET "${_c4_prefix}-export")
- _c4_handle_arg(NAMESPACE "${_c4_prefix}::")
- #
- c4_dbg("installing exports: ${ARGN}")
- #_c4_is_incorporated(${_c4_prefix} inc)
- #if(inc)
- # c4_dbg("this project is INCORPORATEd. skipping install of exports")
- # return()
- #endif()
- #
- _c4_setup_install_vars()
- #
- list(GET ${_c4_prefix}_TARGETS 0 target)
- set(exported_target "${_NAMESPACE}${target}")
- set(targets_file "${_PREFIX}Targets.cmake")
- #
- set(deps)
- if(_DEPENDENCIES)
- set(deps "#-----------------------------
-include(CMakeFindDependencyMacro)
-")
- foreach(d ${_DEPENDENCIES})
- _c4_is_incorporated(${d} inc)
- if(inc)
- c4_dbg("install: dependency ${d} is INCORPORATEd, skipping check")
- continue()
- endif()
- c4_dbg("install: adding dependency check for ${d}")
- set(deps "${deps}find_dependency(${d} REQUIRED)
-")
- endforeach()
- set(deps "${deps}#-----------------------------")
- endif()
- #
- # cfg_dst is the path relative to install root where the export
- # should be installed; cfg_dst_rel is the path from there to
- # the install root
- macro(__c4_install_exports cfg_dst cfg_dst_rel)
- # make sure that different exports are staged in different directories
- set(case ${CMAKE_CURRENT_BINARY_DIR}/export_cases/${cfg_dst})
- file(MAKE_DIRECTORY ${case})
- #
- install(EXPORT "${_TARGET}"
- FILE "${targets_file}"
- NAMESPACE "${_NAMESPACE}"
- DESTINATION "${cfg_dst}")
- export(EXPORT ${_TARGET}
- FILE "${targets_file}"
- NAMESPACE "${_NAMESPACE}")
- #
- # Config files
- # the module below has nice docs in it; do read them
- # to understand the macro calls below
- include(CMakePackageConfigHelpers)
- set(cfg ${case}/${_PREFIX}Config.cmake)
- set(cfg_ver ${case}/${_PREFIX}ConfigVersion.cmake)
- #
- file(WRITE ${cfg}.in "${deps}
-set(${_c4_uprefix}VERSION ${${_c4_uprefix}VERSION})
-
-@PACKAGE_INIT@
-
-if(NOT TARGET ${exported_target})
- include(\${PACKAGE_PREFIX_DIR}/${targets_file})
-endif()
-
-# HACK: PACKAGE_PREFIX_DIR is obtained from the PACKAGE_INIT macro above;
-# When used below in the calls to set_and_check(),
-# it points at the location of this file. So point it instead
-# to the CMAKE_INSTALL_PREFIX, in relative terms
-get_filename_component(PACKAGE_PREFIX_DIR
- \"\${PACKAGE_PREFIX_DIR}/${cfg_dst_rel}\" ABSOLUTE)
-
-set_and_check(${_c4_uprefix}INCLUDE_DIR \"@PACKAGE__INCLUDE_INSTALL_DIR@\")
-set_and_check(${_c4_uprefix}LIB_DIR \"@PACKAGE__LIBRARY_INSTALL_DIR@\")
-#set_and_check(${_c4_uprefix}SYSCONFIG_DIR \"@PACKAGE__SYSCONFIG_INSTALL_DIR@\")
-
-check_required_components(${_c4_lcprefix})
-")
- configure_package_config_file(${cfg}.in ${cfg}
- INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" # defaults to CMAKE_INSTALL_PREFIX
- INSTALL_DESTINATION "${CMAKE_INSTALL_PREFIX}"
- PATH_VARS
- _INCLUDE_INSTALL_DIR
- _LIBRARY_INSTALL_DIR
- _SYSCONFIG_INSTALL_DIR
- #NO_SET_AND_CHECK_MACRO
- #NO_CHECK_REQUIRED_COMPONENTS_MACRO
- )
- write_basic_package_version_file(
- ${cfg_ver}
- VERSION ${${_c4_uprefix}VERSION}
- COMPATIBILITY AnyNewerVersion
- )
- install(FILES ${cfg} ${cfg_ver} DESTINATION ${cfg_dst})
- endmacro(__c4_install_exports)
- #
- # To install the exports:
- #
- # Windows:
- # <prefix>/
- # <prefix>/(cmake|CMake)/
- # <prefix>/<name>*/
- # <prefix>/<name>*/(cmake|CMake)/
- #
- # Unix:
- # <prefix>/(lib/<arch>|lib|share)/cmake/<name>*/
- # <prefix>/(lib/<arch>|lib|share)/<name>*/
- # <prefix>/(lib/<arch>|lib|share)/<name>*/(cmake|CMake)/
- #
- # Apple:
- # <prefix>/<name>.framework/Resources/
- # <prefix>/<name>.framework/Resources/CMake/
- # <prefix>/<name>.framework/Versions/*/Resources/
- # <prefix>/<name>.framework/Versions/*/Resources/CMake/
- # <prefix>/<name>.app/Contents/Resources/
- # <prefix>/<name>.app/Contents/Resources/CMake/
- #
- # (This was taken from the find_package() documentation)
- if(WIN32)
- __c4_install_exports(cmake/ "..")
- elseif(APPLE)
- __c4_install_exports(${_ARCHIVE_INSTALL_DIR}/cmake/${_c4_prefix} "../../..")
- #__c4_install_exports(${_ARCHIVE_INSTALL_DIR}/${_c4_prefix}.framework/Resources/ "../../..")
- elseif(UNIX OR (CMAKE_SYSTEM_NAME STREQUAL UNIX) OR (CMAKE_SYSTEM_NAME STREQUAL Linux) OR (CMAKE_SYSTEM_NAME STREQUAL Generic))
- __c4_install_exports(${_ARCHIVE_INSTALL_DIR}/cmake/${_c4_prefix} "../../..")
- else()
- c4_err("unknown platform. CMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} CMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR}")
- endif()
-endfunction()
-
-
-macro(_c4_setup_install_vars)
- set(_RUNTIME_INSTALL_DIR bin/)
- set(_ARCHIVE_INSTALL_DIR lib/)
- set(_LIBRARY_INSTALL_DIR lib/) # TODO on Windows, ARCHIVE and LIBRARY dirs must be different to prevent name clashes
- set(_INCLUDE_INSTALL_DIR include/)
- set(_OBJECTS_INSTALL_DIR obj/)
- set(_SYSCONFIG_INSTALL_DIR etc/${_c4_lcprefix}/)
-endmacro()
-
-
-function(c4_get_target_installed_headers target out)
- c4_get_target_prop(${target} INCORPORATED_TARGETS inctargets)
- if(inctargets)
- set(targets "${inctargets};${target}")
- else()
- set(targets "${target}")
- endif()
- set(hdrs)
- foreach(t ${targets})
- _c4_get_tgt_prop(srcroot ${t} C4_SOURCE_ROOT)
- #
- c4_get_target_prop(${t} PUBLIC_SRC_${t} src)
- if(src)
- _c4cat_filter_hdrs("${src}" srcf)
- if(thdrs)
- set(thdrs "${thdrs};${srcf}")
- else()
- set(thdrs "${srcf}")
- endif()
- endif()
- #
- c4_get_target_prop(${t} PRIVATE_SRC_${t} psrc)
- if(src)
- _c4cat_filter_hdrs("${psrc}" psrcf)
- if(thdrs)
- set(thdrs "${thdrs};${psrcf}")
- else()
- set(thdrs "${psrcf}")
- endif()
- endif()
- #
- c4_get_target_prop(${t} INTERFACE_SRC_${t} isrc)
- if(src)
- _c4cat_filter_hdrs("${isrc}" isrcf)
- if(thdrs)
- set(thdrs "${thdrs};${isrcf}")
- else()
- set(thdrs "${isrcf}")
- endif()
- endif()
- #
- foreach(h ${thdrs})
- file(RELATIVE_PATH rf "${srcroot}" "${h}")
- list(APPEND hdrs "${rf}")
- endforeach()
- endforeach()
- set(${out} ${hdrs} PARENT_SCOPE)
-endfunction()
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-function(c4_setup_testing)
- _c4_handle_args(_ARGS ${ARGN}
- _ARGS0
- GTEST # download and import googletest
- DOCTEST # download and import doctest
- _ARGS1
- _ARGSN
- )
- #include(GoogleTest) # this module requires at least cmake 3.9
- c4_dbg("enabling tests")
- # umbrella target for building test binaries
- add_custom_target(${_c4_lprefix}test-build)
- _c4_set_target_folder(${_c4_lprefix}test-build test)
- # umbrella targets for running tests
- if(NOT TARGET test-build)
- add_custom_target(test-build)
- add_custom_target(test-verbose)
- _c4_set_target_folder(test-build "/test")
- _c4_set_target_folder(test-verbose "/test")
- endif()
- if(NOT TARGET test)
- # add a test target. To prevent a warning, we need to set up a policy,
- # and also suppress the resulting warning from suppressing the warning.
- set(_depr_old_val ${CMAKE_WARN_DEPRECATED})
- set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "" FORCE) # https://stackoverflow.com/questions/67432538/cannot-set-cmake-warn-deprecated-inside-the-cmakelists-txt
- cmake_policy(PUSH)
- cmake_policy(SET CMP0037 OLD) # target name "test" is reserved for CTesting
- add_custom_target(test)
- _c4_set_target_folder(test "/test")
- cmake_policy(POP)
- set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "${_depr_old_val}" FORCE)
- unset(_depr_old_val)
- endif()
- function(_def_runner runner)
- set(echo "
-CWD=${CMAKE_CURRENT_BINARY_DIR}
-----------------------------------
-${ARGN}
-----------------------------------
-")
- add_custom_target(${runner}
- #${CMAKE_COMMAND} -E echo "${echo}"
- COMMAND ${ARGN}
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
- DEPENDS ${_c4_lprefix}test-build
- )
- _c4_set_target_folder(${runner} test)
- endfunction()
- _def_runner(${_c4_lprefix}test-run ${CMAKE_CTEST_COMMAND} --output-on-failure ${${_c4_uprefix}CTEST_OPTIONS} -C $<CONFIG>)
- _def_runner(${_c4_lprefix}test-run-verbose ${CMAKE_CTEST_COMMAND} -VV ${${_c4_uprefix}CTEST_OPTIONS} -C $<CONFIG>)
- add_dependencies(test ${_c4_lprefix}test-run)
- add_dependencies(test-verbose ${_c4_lprefix}test-run-verbose)
- add_dependencies(test-build ${_c4_lprefix}test-build)
- #
- # import required libraries
- if(_GTEST)
- c4_log("testing requires googletest")
- if(NOT TARGET gtest)
- c4_import_remote_proj(gtest ${CMAKE_CURRENT_BINARY_DIR}/ext/gtest
- REMOTE
- GIT_REPOSITORY https://github.com/google/googletest.git
- GIT_TAG release-1.11.0 #GIT_SHALLOW ON
- OVERRIDE
- BUILD_GTEST ON
- BUILD_GMOCK OFF
- gtest_force_shared_crt ON
- gtest_build_samples OFF
- gtest_build_tests OFF
- SET_FOLDER_TARGETS ext gtest gtest_main
- EXCLUDE_FROM_ALL
- )
- # old gcc-4.8 support
- if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND
- (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 4.8) AND
- (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0))
- _c4_get_subproject_property(gtest SRC_DIR _gtest_patch_src_dir)
- apply_patch("${_c4_project_dir}/compat/gtest_gcc-4.8.patch"
- "${_gtest_patch_src_dir}"
- "${_gtest_patch_src_dir}/.gtest_gcc-4.8.patch")
- unset(_gtest_patch_src_dir)
- target_compile_options(gtest PUBLIC -include ${_c4_project_dir}/compat/c4/gcc-4.8.hpp)
- endif()
- endif()
- endif()
- if(_DOCTEST)
- c4_log("testing requires doctest")
- if(NOT TARGET doctest)
- c4_import_remote_proj(doctest ${CMAKE_CURRENT_BINARY_DIR}/ext/doctest
- REMOTE
- GIT_REPOSITORY https://github.com/onqtam/doctest.git
- GIT_TAG 2.4.6 #GIT_SHALLOW ON
- OVERRIDE
- DOCTEST_WITH_TESTS OFF
- DOCTEST_WITH_MAIN_IN_STATIC_LIB ON
- SET_FOLDER_TARGETS ext doctest_with_main
- EXCLUDE_FROM_ALL
- )
- endif()
- endif()
-endfunction(c4_setup_testing)
-
-
-function(c4_add_test target)
- _c4_handle_args(_ARGS ${ARGN}
- _ARGS0 # zero-value macro arguments
- _ARGS1 # one-value macro arguments
- WORKING_DIRECTORY
- _ARGSN # multi-value macro arguments
- ARGS
- )
- #
- if(_WORKING_DIRECTORY)
- set(_WORKING_DIRECTORY WORKING_DIRECTORY ${_WORKING_DIRECTORY})
- endif()
- set(cmd_pfx)
- if(CMAKE_CROSSCOMPILING)
- set(cmd_pfx ${CMAKE_CROSSCOMPILING_EMULATOR})
- endif()
- if(NOT ${uprefix}SANITIZE_ONLY)
- if(${CMAKE_VERSION} VERSION_LESS "3.16.0")
- add_test(NAME ${target}
- COMMAND ${cmd_pfx} "$<TARGET_FILE:${target}>" ${_ARGS}
- ${_WORKING_DIRECTORY})
- else()
- add_test(NAME ${target}
- COMMAND ${cmd_pfx} "$<TARGET_FILE:${target}>" ${_ARGS}
- ${_WORKING_DIRECTORY}
- COMMAND_EXPAND_LISTS)
- endif()
- endif()
- #
- if("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage")
- add_dependencies(${_c4_lprefix}test-build ${target})
- return()
- endif()
- #
- set(sanitized_targets)
- foreach(s asan msan tsan ubsan)
- set(t ${target}-${s})
- if(TARGET ${t})
- list(APPEND sanitized_targets ${s})
- endif()
- endforeach()
- if(sanitized_targets)
- add_custom_target(${target}-all)
- add_dependencies(${target}-all ${target})
- add_dependencies(${_c4_lprefix}test-build ${target}-all)
- _c4_set_target_folder(${target}-all test/${target})
- else()
- add_dependencies(${_c4_lprefix}test-build ${target})
- endif()
- if(sanitized_targets)
- foreach(s asan msan tsan ubsan)
- set(t ${target}-${s})
- if(TARGET ${t})
- add_dependencies(${target}-all ${t})
- c4_sanitize_get_target_command("${cmd_pfx};$<TARGET_FILE:${t}>" ${s} cmd)
- #c4_log("adding test: ${t}")
- add_test(NAME ${t}
- COMMAND ${cmd} ${_ARGS}
- ${_WORKING_DIRECTORY}
- COMMAND_EXPAND_LISTS)
- endif()
- endforeach()
- endif()
- if(NOT CMAKE_CROSSCOMPILING)
- if(NOT ${_c4_uprefix}SANITIZE_ONLY)
- c4_add_valgrind(${target} ${ARGN})
- endif()
- endif()
- if(${_c4_uprefix}LINT)
- c4_static_analysis_add_tests(${target}) # this will not actually run the executable
- endif()
-endfunction(c4_add_test)
-
-
-# every excess argument is passed on to set_target_properties()
-function(c4_add_test_fail_build name srccontent_or_srcfilename)
- #
- set(sdir ${CMAKE_CURRENT_BINARY_DIR}/test_fail_build)
- set(src ${srccontent_or_srcfilename})
- if("${src}" STREQUAL "")
- c4_err("must be given an existing source file name or a non-empty string")
- endif()
- #
- if(EXISTS ${src})
- set(fn ${src})
- else()
- if(NOT EXISTS ${sdir})
- file(MAKE_DIRECTORY ${sdir})
- endif()
- set(fn ${sdir}/${name}.cpp)
- file(WRITE ${fn} "${src}")
- endif()
- #
- # https://stackoverflow.com/questions/30155619/expected-build-failure-tests-in-cmake
- add_executable(${name} ${fn})
- # don't build this target
- set_target_properties(${name} PROPERTIES
- EXCLUDE_FROM_ALL TRUE
- EXCLUDE_FROM_DEFAULT_BUILD TRUE
- # and pass on further properties given by the caller
- ${ARGN})
- add_test(NAME ${name}
- COMMAND ${CMAKE_COMMAND} --build . --target ${name} --config $<CONFIGURATION>
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
- set_tests_properties(${name} PROPERTIES WILL_FAIL TRUE)
-endfunction()
-
-
-# add a test ensuring that a target linking and using code from a library
-# successfully compiles and runs against the installed library
-function(c4_add_install_link_test library namespace exe_source_code)
- if(CMAKE_CROSSCOMPILING)
- c4_log("cross-compiling: skip install link test")
- return()
- endif()
- if("${library}" STREQUAL "${_c4_prefix}")
- set(testname ${_c4_lprefix}test-install-link)
- else()
- set(testname ${_c4_lprefix}test-install-link-${library})
- endif()
- _c4_add_library_client_test(${library} "${namespace}" "${testname}" "${exe_source_code}")
-endfunction()
-
-
-# add a test ensuring that a target consuming every header in a library
-# successfully compiles and runs against the installed library
-function(c4_add_install_include_test library namespace)
- if(CMAKE_CROSSCOMPILING)
- c4_log("cross-compiling: skip install include test")
- return()
- endif()
- c4_get_target_installed_headers(${library} incfiles)
- set(incblock)
- foreach(i ${incfiles})
- set(incblock "${incblock}
-#include <${i}>")
- endforeach()
- set(src "${incblock}
-
-int main()
-{
- return 0;
-}
-")
- if("${library}" STREQUAL "${_c4_prefix}")
- set(testname ${_c4_lprefix}test-install-include)
- else()
- set(testname ${_c4_lprefix}test-install-include-${library})
- endif()
- _c4_add_library_client_test(${library} "${namespace}" "${testname}" "${src}")
-endfunction()
-
-
-function(_c4_add_library_client_test library namespace pname source_code)
- if("${CMAKE_BUILD_TYPE}" STREQUAL Coverage)
- add_test(NAME ${pname}
- COMMAND ${CMAKE_COMMAND} -E echo "skipping this test in coverage builds"
- )
- return()
- endif()
- set(pdir "${CMAKE_CURRENT_BINARY_DIR}/${pname}")
- set(bdir "${pdir}/build")
- if(NOT EXISTS "${pdir}")
- file(MAKE_DIRECTORY "${pdir}")
- endif()
- if(NOT EXISTS "${bdir}/build")
- file(MAKE_DIRECTORY "${bdir}/build")
- endif()
- set(psrc "${pdir}/${pname}.cpp")
- set(tsrc "${pdir}/${pname}-run.cmake")
- set(tout "${pdir}/${pname}-run-out")
- # generate the source file
- file(WRITE "${psrc}" "${source_code}")
- # generate the cmake project consuming this library
- file(WRITE "${pdir}/CMakeLists.txt" "
-cmake_minimum_required(VERSION 3.12)
-project(${pname} LANGUAGES CXX)
-
-find_package(${library} REQUIRED)
-
-message(STATUS \"
-found ${library}:
- ${_c4_uprefix}INCLUDE_DIR=\${${_c4_uprefix}INCLUDE_DIR}
- ${_c4_uprefix}LIB_DIR=\${${_c4_uprefix}LIB_DIR}
-\")
-
-add_executable(${pname} ${pname}.cpp)
-# this must be the only required setup to link with ${library}
-target_link_libraries(${pname} PUBLIC ${namespace}${library})
-
-get_target_property(lib_type ${namespace}${library} TYPE)
-if(WIN32 AND (lib_type STREQUAL SHARED_LIBRARY))
- # add the directory containing the DLL to the path
- get_target_property(imported_configs ${namespace}${library} IMPORTED_CONFIGURATIONS)
- message(STATUS \"${namespace}${library}: it's a shared library. imported configs: \${imported_configs}\")
- foreach(cfg \${imported_configs})
- get_target_property(implib ${namespace}${library} IMPORTED_IMPLIB_\${cfg})
- get_target_property(location ${namespace}${library} IMPORTED_LOCATION_\${cfg})
- message(STATUS \"${namespace}${library}: implib_\${cfg}=\${implib}\")
- message(STATUS \"${namespace}${library}: location_\${cfg}=\${location}\")
- break()
- endforeach()
- get_filename_component(dlldir \"\${location}\" DIRECTORY)
- message(STATUS \"${namespace}${library}: dlldir=\${dlldir}\")
- add_custom_target(${pname}-run
- COMMAND \${CMAKE_COMMAND} -E echo \"cd \${dlldir} && \$<TARGET_FILE:${pname}>\"
- COMMAND \$<TARGET_FILE:${pname}>
- DEPENDS ${pname}
- WORKING_DIRECTORY \${dlldir})
-else()
- add_custom_target(${pname}-run
- COMMAND \$<TARGET_FILE:${pname}>
- DEPENDS ${pname})
-endif()
-")
- # The test consists in running the script generated below.
- # We force evaluation of the configuration generator expression
- # by receiving its result via the command line.
- add_test(NAME ${pname}
- COMMAND ${CMAKE_COMMAND} -DCFG_IN=$<CONFIG> -P "${tsrc}"
- )
- # NOTE: in the cmake configure command, be sure to NOT use quotes
- # in -DCMAKE_PREFIX_PATH=\"${CMAKE_INSTALL_PREFIX}\". Use
- # -DCMAKE_PREFIX_PATH=${CMAKE_INSTALL_PREFIX} instead.
- # So here we add a check to make sure the install path has no spaces
- string(FIND "${CMAKE_INSTALL_PREFIX}" " " has_spaces)
- if(NOT (has_spaces EQUAL -1))
- c4_err("install tests will fail if the install path has spaces: '${CMAKE_INSTALL_PREFIX}' : ... ${has_spaces}")
- endif()
- # make sure the test project uses the same architecture
- # CMAKE_VS_PLATFORM_NAME is available only since cmake 3.9
- # see https://cmake.org/cmake/help/v3.9/variable/CMAKE_GENERATOR_PLATFORM.html
- if(WIN32)
- set(cfg_opt "--config \${cfg}")
- if(CMAKE_GENERATOR_PLATFORM OR CMAKE_VS_PLATFORM_NAME)
- set(arch "-DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}" "-DCMAKE_VS_PLATFORM_NAME=${CMAKE_VS_PLATFORM_NAME}")
- else()
- if(CMAKE_SIZEOF_VOID_P EQUAL 8)
- set(arch -A x64)
- elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
- set(arch -A Win32)
- else()
- c4_err("not implemented")
- endif()
- endif()
- elseif(ANDROID OR IOS OR WINCE OR WINDOWS_PHONE)
- c4_err("not implemented")
- elseif(IOS)
- c4_err("not implemented")
- elseif(UNIX)
- if(CMAKE_GENERATOR_PLATFORM OR CMAKE_VS_PLATFORM_NAME)
- set(arch "-DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}" "-DCMAKE_VS_PLATFORM_NAME=${CMAKE_VS_PLATFORM_NAME}")
- else()
- if(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64)
- else()
- if(CMAKE_SIZEOF_VOID_P EQUAL 8)
- set(arch "-DCMAKE_CXX_FLAGS=-m64")
- elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
- set(arch "-DCMAKE_CXX_FLAGS=-m32")
- else()
- c4_err("not implemented")
- endif()
- endif()
- endif()
- endif()
- # generate the cmake script with the test content
- file(WRITE "${tsrc}" "
-# run a command and check its return status
-function(runcmd id)
- set(cmdout \"${tout}-\${id}.log\")
- message(STATUS \"Running command: \${ARGN}\")
- message(STATUS \"Running command: output goes to \${cmdout}\")
- execute_process(
- COMMAND \${ARGN}
- RESULT_VARIABLE retval
- OUTPUT_FILE \"\${cmdout}\"
- ERROR_FILE \"\${cmdout}\"
- # COMMAND_ECHO STDOUT # only available from cmake-3.15
- )
- message(STATUS \"Running command: exit status was \${retval}\")
- file(READ \"\${cmdout}\" output)
- if(\"\${cmdout}\" STREQUAL \"\")
- message(STATUS \"Running command: no output\")
- else()
- message(STATUS \"Running command: output:
---------------------
-\${output}--------------------\")
- endif()
- if(NOT (\${retval} EQUAL 0))
- message(FATAL_ERROR \"Command failed with exit status \${retval}: \${ARGN}\")
- endif()
-endfunction()
-
-set(cmk \"${CMAKE_COMMAND}\")
-set(pfx \"${CMAKE_INSTALL_PREFIX}\")
-set(idir \"${CMAKE_BINARY_DIR}\")
-set(pdir \"${pdir}\")
-set(bdir \"${bdir}\")
-
-# force evaluation of the configuration generator expression
-# by receiving its result via the command line
-set(cfg \${CFG_IN})
-
-# remove any existing library install
-if(EXISTS \"\${pfx}\")
- runcmd(0_rmdir \"\${cmk}\" -E remove_directory \"\${pfx}\")
-else()
- message(STATUS \"does not exist: \${pfx}\")
-endif()
-
-# install the library
-#runcmd(1_install_lib \"\${cmk}\" --install \"\${idir}\" ${cfg_opt}) # requires cmake>3.13 (at least)
-runcmd(1_install_lib \"\${cmk}\" --build \"\${idir}\" ${cfg_opt} --target install)
-
-# configure the client project
-runcmd(2_config \"\${cmk}\" -S \"\${pdir}\" -B \"\${bdir}\" \"-DCMAKE_PREFIX_PATH=\${pfx}\" \"-DCMAKE_GENERATOR=${CMAKE_GENERATOR}\" ${arch} \"-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}\" \"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}\")
-
-# build the client project
-runcmd(3_build \"\${cmk}\" --build \"\${bdir}\" ${cfg_opt})
-
-# run the client executable
-runcmd(4_install \"\${cmk}\" --build \"\${bdir}\" --target \"${pname}-run\" ${cfg_opt})
-")
-endfunction()
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-function(c4_setup_valgrind umbrella_option)
- if(UNIX AND (NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Coverage"))
- if("${C4_VALGRIND}" STREQUAL "")
- option(C4_VALGRIND "enable valgrind tests (all subprojects)" ON)
- endif()
- if("${C4_VALGRIND_SGCHECK}" STREQUAL "")
- option(C4_VALGRIND_SGCHECK "enable valgrind tests with the exp-sgcheck tool (all subprojects)" OFF)
- endif()
- cmake_dependent_option(${_c4_uprefix}VALGRIND "enable valgrind tests" ${C4_VALGRIND} ${umbrella_option} OFF)
- cmake_dependent_option(${_c4_uprefix}VALGRIND_SGCHECK "enable valgrind tests with the exp-sgcheck tool" ${C4_VALGRIND_SGCHECK} ${umbrella_option} OFF)
- set(${_c4_uprefix}VALGRIND_OPTIONS "--gen-suppressions=all --error-exitcode=10101" CACHE STRING "options for valgrind tests")
- endif()
-endfunction(c4_setup_valgrind)
-
-
-function(c4_add_valgrind target_name)
- _c4_handle_args(_ARGS ${ARGN}
- _ARGS0 # zero-value macro arguments
- _ARGS1 # one-value macro arguments
- WORKING_DIRECTORY
- _ARGSN # multi-value macro arguments
- ARGS
- )
- #
- if(_WORKING_DIRECTORY)
- set(_WORKING_DIRECTORY WORKING_DIRECTORY ${_WORKING_DIRECTORY})
- endif()
- # @todo: consider doing this for valgrind:
- # http://stackoverflow.com/questions/40325957/how-do-i-add-valgrind-tests-to-my-cmake-test-target
- # for now we explicitly run it:
- if(${_c4_uprefix}VALGRIND)
- separate_arguments(_vg_opts UNIX_COMMAND "${${_c4_uprefix}VALGRIND_OPTIONS}")
- add_test(NAME ${target_name}-valgrind
- COMMAND valgrind ${_vg_opts} $<TARGET_FILE:${target_name}> ${_ARGS}
- ${_WORKING_DIRECTORY}
- COMMAND_EXPAND_LISTS)
- endif()
- if(${_c4_uprefix}VALGRIND_SGCHECK)
- # stack and global array overrun detector
- # http://valgrind.org/docs/manual/sg-manual.html
- separate_arguments(_sg_opts UNIX_COMMAND "--tool=exp-sgcheck ${${_c4_uprefix}VALGRIND_OPTIONS}")
- add_test(NAME ${target_name}-sgcheck
- COMMAND valgrind ${_sg_opts} $<TARGET_FILE:${target_name}> ${_ARGS}
- ${_WORKING_DIRECTORY}
- COMMAND_EXPAND_LISTS)
- endif()
-endfunction(c4_add_valgrind)
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-
-function(c4_setup_coverage)
- if(NOT ("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage"))
- return()
- endif()
- #
- _c4_handle_args(_ARGS ${ARGN}
- _ARGS0 # zero-value macro arguments
- _ARGS1 # one-value macro arguments
- _ARGSN # multi-value macro arguments
- COVFLAGS # coverage compilation flags
- INCLUDE # patterns to include in the coverage, relative to CMAKE_SOURCE_DIR
- EXCLUDE # patterns to exclude in the coverage, relative to CMAKE_SOURCE_DIR
- EXCLUDE_ABS # absolute paths to exclude in the coverage
- GENHTML_ARGS # options to pass to genhtml
- )
- # defaults for the macro arguments
- set(_genhtml_args "--title ${_c4_lcprefix} --demangle-cpp --sort --function-coverage --branch-coverage --prefix '${CMAKE_SOURCE_DIR}' --prefix '${CMAKE_BINARY_DIR}'")
- set(covflags "-g -O0 --coverage") #set(covflags "-g -O0 -fprofile-arcs -ftest-coverage")
- if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
- set(covflags "${covflags} -fprofile-arcs -ftest-coverage -fno-inline -fno-inline-small-functions -fno-default-inline")
- endif()
- set(${_c4_uprefix}COVERAGE_FLAGS "${covflags}" CACHE STRING "coverage compilation flags")
- set(${_c4_uprefix}COVERAGE_GENHTML_ARGS "${_genhtml_args}" CACHE STRING "arguments to pass to genhtml")
- set(${_c4_uprefix}COVERAGE_INCLUDE src CACHE STRING "relative paths to include in the coverage, relative to CMAKE_SOURCE_DIR")
- set(${_c4_uprefix}COVERAGE_EXCLUDE bm;build;extern;ext;src/c4/ext;test CACHE STRING "relative paths to exclude from the coverage, relative to CMAKE_SOURCE_DIR")
- set(${_c4_uprefix}COVERAGE_EXCLUDE_ABS /usr CACHE STRING "absolute paths to exclude from the coverage")
- _c4_handle_arg(COVFLAGS ${${_c4_uprefix}COVERAGE_FLAGS})
- _c4_handle_arg(INCLUDE ${${_c4_uprefix}COVERAGE_INCLUDE})
- _c4_handle_arg(EXCLUDE ${${_c4_uprefix}COVERAGE_EXCLUDE})
- _c4_handle_arg(EXCLUDE_ABS ${${_c4_uprefix}COVERAGE_EXCLUDE_ABS} "${CMAKE_BINARY_DIR}")
- _c4_handle_arg(GENHTML_ARGS ${${_c4_uprefix}COVERAGE_GENHTML_ARGS})
- #
- function(_c4cov_transform_filters var reldir)
- set(_filters)
- foreach(pat ${${var}})
- list(APPEND _filters "'${reldir}${pat}/*'")
- endforeach()
- set(${var} ${_filters} PARENT_SCOPE)
- endfunction()
- _c4cov_transform_filters(_INCLUDE "${CMAKE_SOURCE_DIR}/")
- _c4cov_transform_filters(_EXCLUDE "${CMAKE_SOURCE_DIR}/")
- _c4cov_transform_filters(_EXCLUDE_ABS "")
- #
- if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
- if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3)
- c4_err("coverage: clang version must be 3.0.0 or greater")
- endif()
- elseif(NOT CMAKE_COMPILER_IS_GNUCXX)
- c4_err("coverage: compiler is not GNUCXX")
- endif()
- #
- find_program(GCOV gcov)
- find_program(LCOV lcov)
- find_program(GENHTML genhtml)
- find_program(CTEST ctest)
- if(NOT (GCOV AND LCOV AND GENHTML AND CTEST))
- c4_err("Coverage tools not available:
- gcov: ${GCOV}
- lcov: ${LCOV}
- genhtml: ${GENHTML}
- ctest: ${CTEST}
- --coverage flags: ${_COVFLAGS}")
- endif()
- #
- add_configuration_type(Coverage
- DEFAULT_FROM DEBUG
- C_FLAGS ${_COVFLAGS}
- CXX_FLAGS ${_COVFLAGS}
- )
- #
- option(${_c4_uprefix}COVERAGE_CODECOV "enable target to submit coverage to codecov.io" OFF)
- option(${_c4_uprefix}COVERAGE_COVERALLS "enable target to submit coverage to coveralls.io" OFF)
- #
- c4_dbg("adding coverage targets")
- #
- set(sd "${CMAKE_SOURCE_DIR}")
- set(bd "${CMAKE_BINARY_DIR}")
- set(coverage_result ${bd}/lcov/index.html)
- set(lcov_result ${bd}/coverage3-final_filtered.lcov)
- separate_arguments(_GENHTML_ARGS NATIVE_COMMAND ${_GENHTML_ARGS})
- add_custom_command(OUTPUT ${coverage_result} ${lcov_result}
- COMMAND echo "cd ${CMAKE_BINARY_DIR}"
- COMMAND ${LCOV} -q --zerocounters --directory .
- COMMAND ${LCOV} -q --no-external --capture --base-directory "${sd}" --directory . --output-file ${bd}/coverage0-before.lcov --initial
- COMMAND ${CMAKE_COMMAND} --build . --target ${_c4_lprefix}test-run || echo "Failed running the tests. Proceeding with coverage, but results may be affected or even empty."
- COMMAND ${LCOV} -q --no-external --capture --base-directory "${sd}" --directory . --output-file ${bd}/coverage1-after.lcov
- COMMAND ${LCOV} -q --add-tracefile ${bd}/coverage0-before.lcov --add-tracefile ${bd}/coverage1-after.lcov --output-file ${bd}/coverage2-final.lcov
- COMMAND ${LCOV} -q --remove ${bd}/coverage2-final.lcov ${_EXCLUDE} ${EXCLUDE_ABS} --output-file ${bd}/coverage3-final_filtered.lcov
- COMMAND ${GENHTML} ${bd}/coverage3-final_filtered.lcov -o ${bd}/lcov ${_GENHTML_ARGS}
- COMMAND echo "Coverage report: ${coverage_result}"
- DEPENDS ${_c4_lprefix}test-build
- WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
- COMMENT "${_c4_prefix} coverage: LCOV report at ${coverage_result}"
- #VERBATIM
- )
- add_custom_target(${_c4_lprefix}coverage SOURCES ${coverage_result} ${lcov_result})
- #
- if(${_c4_uprefix}COVERAGE_CODECOV)
- set(_subm ${_c4_lprefix}coverage-submit-codecov)
- _c4cov_get_service_token(codecov _token)
- if(NOT ("${_token}" STREQUAL ""))
- set(_token -t "${_token}")
- endif()
- set(_silent_codecov)
- if(${_c4_uprefix}COVERAGE_CODECOV_SILENT)
- set(_silent_codecov >${CMAKE_BINARY_DIR}/codecov.log 2>&1)
- endif()
- #
- c4_log("coverage: enabling submission of results to https://codecov.io: ${_subm}")
- set(submitcc "${CMAKE_BINARY_DIR}/submit_codecov.sh")
- c4_download_file("https://codecov.io/bash" "${submitcc}")
- set(submit_cmd bash ${submitcc} -Z ${_token} -X gcov -X gcovout -p ${CMAKE_SOURCE_DIR} -f ${lcov_result} ${_silent_codecov})
- string(REPLACE ";" " " submit_cmd_str "${submit_cmd}")
- add_custom_target(${_subm}
- SOURCES ${lcov_result}
- WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
- COMMAND echo "cd ${CMAKE_BINARY_DIR} && ${submit_cmd_str}"
- COMMAND ${submit_cmd}
- VERBATIM
- COMMENT "${_c4_lcprefix} coverage: submit to codecov"
- )
- c4_add_umbrella_target(coverage-submit-codecov coverage-submit) # uses the current prefix
- endif()
- #
- if(${_c4_uprefix}COVERAGE_COVERALLS)
- set(_subm ${_c4_lprefix}coverage-submit-coveralls)
- _c4cov_get_service_token(coveralls _token)
- if(NOT ("${_token}" STREQUAL ""))
- set(_token --repo-token "${_token}")
- endif()
- set(_silent_coveralls)
- if(${_c4_uprefix}COVERAGE_COVERALLS_SILENT)
- set(_silent_coveralls >${CMAKE_BINARY_DIR}/coveralls.log 2>&1)
- endif()
- #
- c4_log("coverage: enabling submission of results to https://coveralls.io: ${_subm}")
- set(submit_cmd coveralls ${_token} --build-root ${CMAKE_BINARY_DIR} --root ${CMAKE_SOURCE_DIR} --no-gcov --lcov-file ${lcov_result} ${_silent_coveralls})
- string(REPLACE ";" " " submit_cmd_str "${submit_cmd}")
- add_custom_target(${_subm}
- SOURCES ${lcov_result}
- WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
- COMMAND echo "cd ${CMAKE_BINARY_DIR} && ${submit_cmd_str}"
- COMMAND ${submit_cmd}
- VERBATIM
- COMMENT "${_c4_lcprefix} coverage: submit to coveralls"
- )
- c4_add_umbrella_target(coverage-submit-coveralls coverage-submit) # uses the current prefix
- endif()
-endfunction(c4_setup_coverage)
-
-
-# 1. try cmake or environment variables
-# 2. try local file
-function(_c4cov_get_service_token service out)
- # try cmake var
- string(TOUPPER ${service} uservice)
- c4_get_from_first_of(token COVERAGE_${uservice}_TOKEN ENV)
- if(NOT ("${token}" STREQUAL ""))
- c4_dbg("${service}: found token from variable: ${token}")
- else()
- # try local file
- set(service_token_file ${CMAKE_SOURCE_DIR}/.ci/${service}.token)
- if(EXISTS ${service_token_file})
- file(READ ${service_token_file} token)
- c4_dbg("found token file for ${service} coverage report: ${service_token_file}")
- else()
- c4_dbg("could not find token for ${service} coverage report")
- endif()
- endif()
- set(${out} ${token} PARENT_SCOPE)
-endfunction()
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-
-function(c4_add_umbrella_target target umbrella_target)
- _c4_handle_args(_ARGS ${ARGN}
- # zero-value macro arguments
- _ARGS0
- ALWAYS # Add the umbrella target even if this is the only one under it.
- # The default behavior is to add the umbrella target only if
- # there is more than one target under it.
- # one-value macro arguments
- _ARGS1
- PREFIX # The project prefix. Defaults to ${_c4_lprefix}
- # multi-value macro arguments
- _ARGSN
- ARGS # more args to add_custom_target()
- )
- if(NOT _PREFIX)
- set(_PREFIX "${_c4_lprefix}")
- endif()
- set(t ${_PREFIX}${target})
- set(ut ${_PREFIX}${umbrella_target})
- # if the umbrella target already exists, just add the dependency
- if(TARGET ${ut})
- add_dependencies(${ut} ${t})
- else()
- if(_ALWAYS)
- add_custom_target(${ut} ${_ARGS})
- add_dependencies(${ut} ${t})
- else()
- # check if there is more than one under the same umbrella
- c4_get_proj_prop(${ut}_subtargets sub)
- if(sub)
- add_custom_target(${ut} ${_ARGS})
- add_dependencies(${ut} ${sub})
- add_dependencies(${ut} ${t})
- else()
- c4_set_proj_prop(${ut}_subtargets ${t})
- endif()
- endif()
- endif()
-endfunction()
-
-
-
-function(c4_download_file url dstpath)
- c4_dbg("downloading file: ${url} ---> ${dstpath}")
- get_filename_component(abspath ${dstpath} ABSOLUTE)
- if(NOT EXISTS ${abspath})
- c4_dbg("downloading file: does not exist: ${dstpath}")
- file(DOWNLOAD ${url} ${abspath} LOG dl_log STATUS status ${ARGN})
- if((NOT (status EQUAL 0)) OR (NOT EXISTS ${abspath}))
- c4_err("error downloading file: ${url} -> ${abspath}:\n${dl_log}")
- endif()
- endif()
-endfunction()
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-function(c4_setup_benchmarking)
- c4_log("enabling benchmarks: to build, ${_c4_lprefix}bm-build")
- c4_log("enabling benchmarks: to run, ${_c4_lprefix}bm-run")
- # umbrella target for building test binaries
- add_custom_target(${_c4_lprefix}bm-build)
- # umbrella target for running benchmarks
- add_custom_target(${_c4_lprefix}bm-run
- ${CMAKE_COMMAND} -E echo CWD=${CMAKE_CURRENT_BINARY_DIR}
- DEPENDS ${_c4_lprefix}bm-build
- )
- if(NOT TARGET bm-run)
- add_custom_target(bm-run)
- add_custom_target(bm-build)
- endif()
- add_dependencies(bm-run ${_c4_lprefix}bm-run)
- add_dependencies(bm-build ${_c4_lprefix}bm-build)
- _c4_set_target_folder(${_c4_lprefix}bm-run bm)
- _c4_set_target_folder(${_c4_lprefix}bm-build bm)
- _c4_set_target_folder(bm-build "/bm")
- _c4_set_target_folder(bm-run "/bm")
- # download google benchmark
- if(NOT TARGET benchmark)
- c4_import_remote_proj(googlebenchmark ${CMAKE_CURRENT_BINARY_DIR}/ext/googlebenchmark
- REMOTE
- GIT_REPOSITORY https://github.com/google/benchmark.git
- GIT_TAG main GIT_SHALLOW ON
- OVERRIDE
- BENCHMARK_ENABLE_TESTING OFF
- BENCHMARK_ENABLE_EXCEPTIONS OFF
- BENCHMARK_ENABLE_LTO OFF
- SET_FOLDER_TARGETS ext benchmark benchmark_main
- EXCLUDE_FROM_ALL
- )
- #
- if((CMAKE_CXX_COMPILER_ID STREQUAL GNU) OR (CMAKE_COMPILER_IS_GNUCC))
- target_compile_options(benchmark PRIVATE -Wno-deprecated-declarations)
- target_compile_options(benchmark PRIVATE -Wno-restrict)
- endif()
- #
- if(NOT WIN32)
- option(${_c4_uprefix}BENCHMARK_CPUPOWER
- "set the cpu mode to performance before / powersave after the benchmark" OFF)
- if(${_c4_uprefix}BENCHMARK_CPUPOWER)
- find_program(C4_SUDO sudo)
- find_program(C4_CPUPOWER cpupower)
- endif()
- endif()
- endif()
-endfunction()
-
-
-function(c4_add_benchmark_cmd casename)
- add_custom_target(${casename}
- COMMAND ${ARGN}
- VERBATIM
- COMMENT "${_c4_prefix}: running benchmark ${casename}: ${ARGN}")
- add_dependencies(${_c4_lprefix}bm-build ${casename})
- _c4_set_target_folder(${casename} bm)
-endfunction()
-
-
-# assumes this is a googlebenchmark target, and that multiple
-# benchmarks are defined from it
-function(c4_add_target_benchmark target casename)
- set(opt0arg
- )
- set(opt1arg
- WORKDIR # working directory
- FILTER # benchmark patterns to filter
- UMBRELLA_TARGET
- RESULTS_FILE
- )
- set(optnarg
- ARGS
- )
- cmake_parse_arguments("" "${opt0arg}" "${opt1arg}" "${optnarg}" ${ARGN})
- #
- set(name "${target}-${casename}")
- set(rdir "${CMAKE_CURRENT_BINARY_DIR}/bm-results")
- set(rfile "${rdir}/${name}.json")
- if(_RESULTS_FILE)
- set(${_RESULTS_FILE} "${rfile}" PARENT_SCOPE)
- endif()
- if(NOT EXISTS "${rdir}")
- file(MAKE_DIRECTORY "${rdir}")
- endif()
- set(filter)
- if(NOT ("${_FILTER}" STREQUAL ""))
- set(filter "--benchmark_filter=${_FILTER}")
- endif()
- set(args_fwd ${filter} --benchmark_out_format=json --benchmark_out=${rfile} ${_ARGS})
- c4_add_benchmark(${target}
- "${name}"
- "${_WORKDIR}"
- "saving results in ${rfile}"
- ${args_fwd}
- OUTPUT_FILE ${rfile})
- if(_UMBRELLA_TARGET)
- add_dependencies(${_UMBRELLA_TARGET} "${name}")
- endif()
-endfunction()
-
-
-function(c4_add_benchmark target casename work_dir comment)
- set(opt0arg
- )
- set(opt1arg
- OUTPUT_FILE
- )
- set(optnarg
- )
- cmake_parse_arguments("" "${opt0arg}" "${opt1arg}" "${optnarg}" ${ARGN})
- if(NOT TARGET ${target})
- c4_err("target ${target} does not exist...")
- endif()
- if(NOT ("${work_dir}" STREQUAL ""))
- if(NOT EXISTS "${work_dir}")
- file(MAKE_DIRECTORY "${work_dir}")
- endif()
- endif()
- set(exe $<TARGET_FILE:${target}>)
- if(${_c4_uprefix}BENCHMARK_CPUPOWER)
- if(C4_BM_SUDO AND C4_BM_CPUPOWER)
- set(c ${C4_SUDO} ${C4_CPUPOWER} frequency-set --governor performance)
- set(cpupow_before
- COMMAND echo ${c}
- COMMAND ${c})
- set(c ${C4_SUDO} ${C4_CPUPOWER} frequency-set --governor powersave)
- set(cpupow_after
- COMMAND echo ${c}
- COMMAND ${c})
- endif()
- endif()
- if(_OUTPUT_FILE)
- set(_OUTPUT_FILE BYPRODUCTS ${_OUTPUT_FILE})
- set(_OUTPUT_FILE) # otherwise the benchmarks run everytime when building depending targets
- endif()
- add_custom_target(${casename}
- ${cpupow_before}
- # this is useful to show the target file (you cannot echo generator variables)
- #COMMAND ${CMAKE_COMMAND} -E echo "target file = $<TARGET_FILE:${target}>"
- COMMAND ${CMAKE_COMMAND} -E echo "${exe} ${ARGN}"
- COMMAND "${exe}" ${ARGN}
- ${cpupow_after}
- VERBATIM
- ${_OUTPUT_FILE}
- WORKING_DIRECTORY "${work_dir}"
- DEPENDS ${target}
- COMMENT "${_c4_lcprefix}: running benchmark ${target}, case ${casename}: ${comment}"
- )
- add_dependencies(${_c4_lprefix}bm-build ${target})
- add_dependencies(${_c4_lprefix}bm-run ${casename})
- _c4_set_target_folder(${casename} bm/run)
-endfunction()
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-
-function(_c4cat_get_outname target id ext out)
- if("${_c4_lcprefix}" STREQUAL "${target}")
- set(p "${target}")
- else()
- set(p "${_c4_lcprefix}.${target}")
- endif()
- set(${out} "${CMAKE_CURRENT_BINARY_DIR}/${p}.${id}.${ext}" PARENT_SCOPE)
-endfunction()
-
-function(_c4cat_filter_srcs in out)
- _c4cat_filter_extensions("${in}" "${C4_SRC_EXTS}" l)
- set(${out} ${l} PARENT_SCOPE)
-endfunction()
-
-function(_c4cat_filter_hdrs in out)
- _c4cat_filter_extensions("${in}" "${C4_HDR_EXTS}" l)
- set(${out} ${l} PARENT_SCOPE)
-endfunction()
-
-function(_c4cat_filter_srcs_hdrs in out)
- _c4cat_filter_extensions("${in}" "${C4_HDR_EXTS};${C4_SRC_EXTS}" l)
- set(${out} ${l} PARENT_SCOPE)
-endfunction()
-
-function(_c4cat_filter_additional_exts in out)
- _c4cat_filter_extensions("${in}" "${C4_ADD_EXTS}" l)
- set(${out} ${l} PARENT_SCOPE)
-endfunction()
-
-function(_c4cat_filter_extensions in filter out)
- set(l)
- foreach(fn ${in}) # don't quote the list here
- _c4cat_get_file_ext("${fn}" ext)
- _c4cat_one_of("${ext}" "${filter}" yes)
- if(${yes})
- list(APPEND l "${fn}")
- endif()
- endforeach()
- set(${out} "${l}" PARENT_SCOPE)
-endfunction()
-
-function(_c4cat_get_file_ext in out)
- # https://stackoverflow.com/questions/30049180/strip-filename-shortest-extension-by-cmake-get-filename-removing-the-last-ext
- string(REGEX MATCH "^.*\\.([^.]*)$" dummy ${in})
- set(${out} ${CMAKE_MATCH_1} PARENT_SCOPE)
-endfunction()
-
-function(_c4cat_one_of ext candidates out)
- foreach(e ${candidates})
- if("${ext}" STREQUAL "${e}")
- set(${out} TRUE PARENT_SCOPE)
- return()
- endif()
- endforeach()
- set(${out} FALSE PARENT_SCOPE)
-endfunction()
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-
-# given a list of source files, return a list with full paths
-function(c4_to_full_path source_list source_list_with_full_paths)
- set(l)
- foreach(f ${source_list})
- if(IS_ABSOLUTE "${f}")
- list(APPEND l "${f}")
- else()
- list(APPEND l "${CMAKE_CURRENT_SOURCE_DIR}/${f}")
- endif()
- endforeach()
- set(${source_list_with_full_paths} ${l} PARENT_SCOPE)
-endfunction()
-
-
-# convert a list to a string separated with spaces
-function(c4_separate_list input_list output_string)
- set(s)
- foreach(e ${input_list})
- set(s "${s} ${e}")
- endforeach()
- set(${output_string} ${s} PARENT_SCOPE)
-endfunction()
-
-
-
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-#------------------------------------------------------------------------------
-endif(NOT _c4_project_included)