From 412fa5f3ca8a5699d884712d29c1da41b1cf41d6 Mon Sep 17 00:00:00 2001 From: Stephan Kantelberg Date: Mon, 15 May 2023 20:22:45 +0200 Subject: [PATCH] cleanup cmake - cleaned up main CMakeLists.txt - created new cmake files - adapted CMake structure to new CMake template from jason turner --- CMakeLists.txt | 57 ++-------- README.md | 2 +- cmake/BuildingConfig.cmake | 23 ++++ cmake/CompilerWarnings.cmake | 2 +- cmake/Doxygen.cmake | 50 ++++++++- cmake/GitInformation.cmake | 10 ++ cmake/InterproceduralOptimization.cmake | 11 ++ cmake/Options.cmake | 42 +++---- cmake/PrecompiledHeader.cmake | 14 +++ cmake/StandardProjectSettings.cmake | 37 ++++--- cmake/Utilities.cmake | 139 ++++++++++++++++++++++++ cmake/VCEnvironment.cmake | 71 ++++++++++++ 12 files changed, 370 insertions(+), 88 deletions(-) create mode 100644 cmake/BuildingConfig.cmake create mode 100644 cmake/GitInformation.cmake create mode 100644 cmake/InterproceduralOptimization.cmake create mode 100644 cmake/PrecompiledHeader.cmake create mode 100644 cmake/Utilities.cmake create mode 100644 cmake/VCEnvironment.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 8bfbfc7..a672dfd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 3.16...3.23) +CMAKE_MINIMUM_REQUIRED(VERSION 3.21) # strongly encouraged to enable this globally to avoid conflicts between # -Wpedantic being enabled and -std=c++20 and -std=gnu++20 for example @@ -18,38 +18,18 @@ ENDIF() SET(CMAKE_CXX_STANDARD ${CXX_STANDARD}) -GET_PROPERTY(BUILDING_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -IF(BUILDING_MULTI_CONFIG) - IF(NOT CMAKE_BUILD_TYPE) - # Make sure that all supported configuration types have their - # associated conan packages available. You can reduce this - # list to only the configuration types you use, but only if one - # is not forced-set on the command line for VS - MESSAGE(TRACE "Setting up multi-config build types") - SET(CMAKE_CONFIGURATION_TYPES - Debug - Release - RelWithDebInfo - MinSizeRel - CACHE STRING "Enabled build types" FORCE) - ELSE() - MESSAGE(TRACE "User chose a specific build type, so we are using that") - SET(CMAKE_CONFIGURATION_TYPES - ${CMAKE_BUILD_TYPE} - CACHE STRING "Enabled build types" FORCE) - ENDIF() -ENDIF() +INCLUDE(cmake/BuildingConfig.cmake) +SETUP_MULTI_CONFIG() INCLUDE(cmake/StandardProjectSettings.cmake) INCLUDE(cmake/PreventInSourceBuilds.cmake) INCLUDE(cmake/CodeFormat.cmake) +INCLUDE(cmake/InterproceduralOptimization.cmake) +ENABLE_IPO() -EXECUTE_PROCESS( - COMMAND git log -1 --format=%h - WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} - OUTPUT_VARIABLE GIT_HASH - OUTPUT_STRIP_TRAILING_WHITESPACE -) +# fetch git hash information for configure version template file +INCLUDE(cmake/GitInformation.cmake) +GET_GIT_HASH() CONFIGURE_FILE("templates/version.hpp.in" "${CMAKE_BINARY_DIR}/generated/include/version.hpp" ESCAPE_QUOTES) @@ -57,12 +37,6 @@ CONFIGURE_FILE("templates/version.hpp.in" "${CMAKE_BINARY_DIR}/generated/include ADD_LIBRARY(project_options INTERFACE) TARGET_COMPILE_FEATURES(project_options INTERFACE cxx_std_${CXX_STANDARD}) -IF(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") - IF(ENABLE_BUILD_WITH_TIME_TRACE) - TARGET_COMPILE_OPTIONS(project_options INTERFACE -ftime-trace) - ENDIF() -ENDIF() - # Link this 'library' to use the warnings specified in CompilerWarnings.cmake ADD_LIBRARY(project_warnings INTERFACE) @@ -84,18 +58,9 @@ ENABLE_DOXYGEN() # allow for static analysis options INCLUDE(cmake/StaticAnalyzers.cmake) -IF(ENABLE_PCH) - # This sets a global PCH parameter, each project will build its own PCH, which is a good idea if any #define's change - # - # consider breaking this out per project as necessary - TARGET_PRECOMPILE_HEADERS( - project_options - INTERFACE - - - - ) -ENDIF() +# enabled precompiled headers +INCLUDE(cmake/PrecompiledHeader.cmake) +ENABLE_PCH() INCLUDE(cmake/Conan.cmake) RUN_CONAN() diff --git a/README.md b/README.md index c28208e..25c5e3b 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Ongoing project of the Zühlke Germany **Modern C++ Topic Group**. With this project, we want to provide an example and starting point for C++ projects (embedded and otherwise), especially regarding tooling. The project has initially been forked/copied from [Jason Turner's cpp_starter_project](https://github.com/lefticus/cpp_starter_project) and is customised by Zühlke members and adapted to [Jason Turner's cmake_conan_boilerplate_template](https://github.com/cpp-best-practices/cmake_conan_boilerplate_template). - +It also uses CMake structure from [Jason Turner's cmake_template](https://github.com/cpp-best-practices/cmake_template) repository. ## Build Status diff --git a/cmake/BuildingConfig.cmake b/cmake/BuildingConfig.cmake new file mode 100644 index 0000000..8818792 --- /dev/null +++ b/cmake/BuildingConfig.cmake @@ -0,0 +1,23 @@ +FUNCTION(SETUP_MULTI_CONFIG) + GET_PROPERTY(BUILDING_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + IF(BUILDING_MULTI_CONFIG) + IF(NOT CMAKE_BUILD_TYPE) + # Make sure that all supported configuration types have their + # associated conan packages available. You can reduce this + # list to only the configuration types you use, but only if one + # is not forced-set on the command line for VS + MESSAGE(TRACE "Setting up multi-config build types") + SET(CMAKE_CONFIGURATION_TYPES + Debug + Release + RelWithDebInfo + MinSizeRel + CACHE STRING "Enabled build types" FORCE) + ELSE() + MESSAGE(TRACE "User chose a specific build type, so we are using that") + SET(CMAKE_CONFIGURATION_TYPES + ${CMAKE_BUILD_TYPE} + CACHE STRING "Enabled build types" FORCE) + ENDIF() + ENDIF() +ENDFUNCTION() diff --git a/cmake/CompilerWarnings.cmake b/cmake/CompilerWarnings.cmake index 76f7de5..9fab3dd 100644 --- a/cmake/CompilerWarnings.cmake +++ b/cmake/CompilerWarnings.cmake @@ -3,7 +3,6 @@ # https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md FUNCTION(SET_PROJECT_WARNINGS project_name) - OPTION(WARNINGS_AS_ERRORS "Treat compiler warnings as errors" TRUE) SET(MSVC_WARNINGS /W4 # Baseline reasonable warnings @@ -47,6 +46,7 @@ FUNCTION(SET_PROJECT_WARNINGS project_name) -Wnull-dereference # warn if a null dereference is detected -Wdouble-promotion # warn if float is implicit promoted to double -Wformat=2 # warn on security issues around functions that format output (ie printf) + -Wimplicit-fallthrough # warn on statements that fallthrough without an explicit annotation ) IF(WARNINGS_AS_ERRORS) diff --git a/cmake/Doxygen.cmake b/cmake/Doxygen.cmake index fe784c0..9628624 100644 --- a/cmake/Doxygen.cmake +++ b/cmake/Doxygen.cmake @@ -1,10 +1,56 @@ FUNCTION(ENABLE_DOXYGEN) IF(ENABLE_DOXYGEN) + # If not specified, use the top readme file as the first page + IF((NOT DOXYGEN_USE_MDFILE_AS_MAINPAGE) AND EXISTS "${PROJECT_SOURCE_DIR}/README.md") + SET(DOXYGEN_USE_MDFILE_AS_MAINPAGE "${PROJECT_SOURCE_DIR}/README.md") + ENDIF() + + # set better defaults for doxygen + IS_VERBOSE(_is_verbose) + IF(NOT ${_is_verbose}) + SET(DOXYGEN_QUIET YES) + ENDIF() SET(DOXYGEN_CALLER_GRAPH YES) SET(DOXYGEN_CALL_GRAPH YES) SET(DOXYGEN_EXTRACT_ALL YES) - FIND_PACKAGE(Doxygen REQUIRED dot) - DOXYGEN_ADD_DOCS(doxygen-docs ${PROJECT_SOURCE_DIR}) + SET(DOXYGEN_GENERATE_TREEVIEW YES) + # svg files are much smaller than jpeg and png, and yet they have higher quality + SET(DOXYGEN_DOT_IMAGE_FORMAT svg) + SET(DOXYGEN_DOT_TRANSPARENT YES) + + # If not specified, exclude the vcpkg files and the files CMake downloads under _deps (like project_options) + IF(NOT DOXYGEN_EXCLUDE_PATTERNS) + SET(DOXYGEN_EXCLUDE_PATTERNS "${CMAKE_CURRENT_BINARY_DIR}/vcpkg_installed/*" "${CMAKE_CURRENT_BINARY_DIR}/_deps/*") + ENDIF() + + IF("${DOXYGEN_THEME}" STREQUAL "") + SET(DOXYGEN_THEME "awesome-sidebar") + ENDIF() + + IF("${DOXYGEN_THEME}" STREQUAL "awesome" OR "${DOXYGEN_THEME}" STREQUAL "awesome-sidebar") + # use a modern doxygen theme + # https://github.com/jothepro/doxygen-awesome-css v2.0.0 + FETCHCONTENT_DECLARE(_doxygen_theme + URL https://github.com/jothepro/doxygen-awesome-css/archive/refs/tags/v2.2.0.zip) + FETCHCONTENT_MAKEAVAILABLE(_doxygen_theme) + IF("${DOXYGEN_THEME}" STREQUAL "awesome" OR "${DOXYGEN_THEME}" STREQUAL "awesome-sidebar") + SET(DOXYGEN_HTML_EXTRA_STYLESHEET "${_doxygen_theme_SOURCE_DIR}/doxygen-awesome.css") + ENDIF() + IF("${DOXYGEN_THEME}" STREQUAL "awesome-sidebar") + SET(DOXYGEN_HTML_EXTRA_STYLESHEET ${DOXYGEN_HTML_EXTRA_STYLESHEET} + "${_doxygen_theme_SOURCE_DIR}/doxygen-awesome-sidebar-only.css") + ENDIF() + ELSE() + # use the original doxygen theme + ENDIF() + + # find doxygen and dot if available + FIND_PACKAGE(Doxygen REQUIRED OPTIONAL_COMPONENTS dot) + + # add doxygen-docs target + MESSAGE(STATUS "Adding `doxygen-docs` target that builds the documentation.") + DOXYGEN_ADD_DOCS(doxygen-docs ALL ${PROJECT_SOURCE_DIR} + COMMENT "Generating documentation - entry file: ${CMAKE_CURRENT_BINARY_DIR}/html/index.html") ENDIF() ENDFUNCTION() diff --git a/cmake/GitInformation.cmake b/cmake/GitInformation.cmake new file mode 100644 index 0000000..0e62696 --- /dev/null +++ b/cmake/GitInformation.cmake @@ -0,0 +1,10 @@ +FUNCTION(GET_GIT_HASH) + EXECUTE_PROCESS( + COMMAND git log -1 --format=%h + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + OUTPUT_VARIABLE HASH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + SET(GIT_HASH ${HASH} PARENT_SCOPE) +ENDFUNCTION() diff --git a/cmake/InterproceduralOptimization.cmake b/cmake/InterproceduralOptimization.cmake new file mode 100644 index 0000000..c1b4504 --- /dev/null +++ b/cmake/InterproceduralOptimization.cmake @@ -0,0 +1,11 @@ +FUNCTION(ENABLE_IPO) + IF(ENABLE_IPO) + INCLUDE(CheckIPOSupported) + CHECK_IPO_SUPPORTED(RESULT result OUTPUT output) + IF(result) + SET(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) + ELSE() + MESSAGE(SEND_ERROR "IPO is not supported: ${output}") + ENDIF() + ENDIF() +ENDFUNCTION() diff --git a/cmake/Options.cmake b/cmake/Options.cmake index 3128aad..b950e72 100644 --- a/cmake/Options.cmake +++ b/cmake/Options.cmake @@ -2,30 +2,32 @@ IF(NOT MSVC) OPTION(ENABLE_BUILD_WITH_TIME_TRACE "Enable -ftime-trace to generate time tracing .json files on clang" OFF) ENDIF() -OPTION(BUILD_SHARED_LIBS "Enable compilation of shared libraries" OFF) -OPTION(ENABLE_TESTING "Enable Test Builds" ON) -OPTION(ENABLE_FUZZING "Enable Fuzzing Builds" OFF) - # Very basic PCH example OPTION(ENABLE_PCH "Enable Precompiled Headers" OFF) -option(ENABLE_CPPCHECK "Enable static analysis with cppcheck" OFF) -option(ENABLE_CLANG_TIDY "Enable static analysis with clang-tidy" OFF) -option(ENABLE_INCLUDE_WHAT_YOU_USE "Enable static analysis with include-what-you-use" OFF) +# static analyzers +OPTION(ENABLE_CPPCHECK "Enable static analysis with cppcheck" OFF) +OPTION(ENABLE_CLANG_TIDY "Enable static analysis with clang-tidy" OFF) +OPTION(ENABLE_INCLUDE_WHAT_YOU_USE "Enable static analysis with include-what-you-use" OFF) +# tooling OPTION(ENABLE_CACHE "Enable cache if available" ON) -option(ENABLE_DOXYGEN "Enable doxygen doc builds of source" OFF) +OPTION(ENABLE_DOXYGEN "Enable doxygen doc builds of source" OFF) # Sanitizers -OPTION(ENABLE_SANITIZER_UNDEFINED_BEHAVIOR "Enable undefined behavior sanitizer" FALSE) -OPTION(ENABLE_SANITIZER_THREAD "Enable thread sanitizer" FALSE) -OPTION(ENABLE_SANITIZER_MEMORY "Enable memory sanitizer" FALSE) -OPTION(ENABLE_SANITIZER_ADDRESS "Enable address sanitizer" FALSE) -OPTION(ENABLE_SANITIZER_LEAK "Enable leak sanitizer" FALSE) - -OPTION(ENABLE_COVERAGE "Enable coverage reporting for gcc/clang" FALSE) - +OPTION(ENABLE_SANITIZER_UNDEFINED_BEHAVIOR "Enable undefined behavior sanitizer" OFF) +OPTION(ENABLE_SANITIZER_THREAD "Enable thread sanitizer" OFF) +OPTION(ENABLE_SANITIZER_MEMORY "Enable memory sanitizer" OFF) +OPTION(ENABLE_SANITIZER_ADDRESS "Enable address sanitizer" OFF) +OPTION(ENABLE_SANITIZER_LEAK "Enable leak sanitizer" OFF) + +# others +OPTION(ENABLE_COVERAGE "Enable coverage reporting for gcc/clang" OFF) OPTION(ENABLE_IPO "Enable Interprocedural Optimization, aka Link Time Optimization (LTO)" OFF) +OPTION(WARNINGS_AS_ERRORS "Treat compiler warnings as errors" ON) +OPTION(BUILD_SHARED_LIBS "Enable compilation of shared libraries" OFF) +OPTION(ENABLE_TESTING "Enable Test Builds" ON) +OPTION(ENABLE_FUZZING "Enable Fuzzing Builds" OFF) # examples OPTION(CPP_STARTER_USE_SML "Enable compilation of SML sample" OFF) @@ -33,11 +35,3 @@ OPTION(CPP_STARTER_USE_BOOST_BEAST "Enable compilation of boost beast sample" OF OPTION(CPP_STARTER_USE_CROW "Enable compilation of crow sample" OFF) OPTION(CPP_STARTER_USE_CPPZMQ_PROTO "Enable compilation of protobuf and cppzmq sample" OFF) OPTION(CPP_STARTER_USE_EMBEDDED_TOOLCHAIN "Enable compilation of an example cortex m4 project" OFF) - -# Note: by default ENABLE_DEVELOPER_MODE is True -# This means that all analysis (sanitizers, static analysis) -# is enabled and all warnings are treated as errors -# if you want to switch this behavior, change TRUE to FALSE -SET(ENABLE_DEVELOPER_MODE - TRUE - CACHE BOOL "Enable 'developer mode'") diff --git a/cmake/PrecompiledHeader.cmake b/cmake/PrecompiledHeader.cmake new file mode 100644 index 0000000..16c74a7 --- /dev/null +++ b/cmake/PrecompiledHeader.cmake @@ -0,0 +1,14 @@ +FUNCTION(ENABLE_PCH) + IF(ENABLE_PCH) + # This sets a global PCH parameter, each project will build its own PCH, which is a good idea if any #define's change + # + # consider breaking this out per project as necessary + TARGET_PRECOMPILE_HEADERS( + project_options + INTERFACE + + + + ) + ENDIF() +ENDFUNCTION() diff --git a/cmake/StandardProjectSettings.cmake b/cmake/StandardProjectSettings.cmake index d869af2..13c2aa2 100644 --- a/cmake/StandardProjectSettings.cmake +++ b/cmake/StandardProjectSettings.cmake @@ -17,23 +17,32 @@ ENDIF() # Generate compile_commands.json to make it easier to work with clang based tools SET(CMAKE_EXPORT_COMPILE_COMMANDS ON) -IF(ENABLE_IPO) - INCLUDE(CheckIPOSupported) - CHECK_IPO_SUPPORTED( - RESULT - result - OUTPUT - output) - IF(result) - SET(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) +# Enhance error reporting and compiler messages +IF(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + IF(ENABLE_BUILD_WITH_TIME_TRACE) + TARGET_COMPILE_OPTIONS(project_options INTERFACE -ftime-trace) + ENDIF() + + IF(WIN32) + # On Windows cuda nvcc uses cl and not clang + ADD_COMPILE_OPTIONS($<$:-fcolor-diagnostics> $<$:-fcolor-diagnostics>) ELSE() - MESSAGE(SEND_ERROR "IPO is not supported: ${output}") + ADD_COMPILE_OPTIONS(-fcolor-diagnostics) ENDIF() -ENDIF() -IF(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") - ADD_COMPILE_OPTIONS(-fcolor-diagnostics) ELSEIF(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - ADD_COMPILE_OPTIONS(-fdiagnostics-color=always) + IF(WIN32) + # On Windows cuda nvcc uses cl and not gcc + ADD_COMPILE_OPTIONS($<$:-fdiagnostics-color=always> + $<$:-fdiagnostics-color=always>) + ELSE() + ADD_COMPILE_OPTIONS(-fdiagnostics-color=always) + ENDIF() +ELSEIF(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND MSVC_VERSION GREATER 1900) + ADD_COMPILE_OPTIONS(/diagnostics:column) ELSE() MESSAGE(STATUS "No colored compiler diagnostic set for '${CMAKE_CXX_COMPILER_ID}' compiler.") ENDIF() + +# run vcvarsall when msvc is used +INCLUDE("${CMAKE_CURRENT_LIST_DIR}/VCEnvironment.cmake") +RUN_VCVARSALL() diff --git a/cmake/Utilities.cmake b/cmake/Utilities.cmake new file mode 100644 index 0000000..9070d01 --- /dev/null +++ b/cmake/Utilities.cmake @@ -0,0 +1,139 @@ +# find a subtring from a string by a given prefix such as VCVARSALL_ENV_START +FUNCTION( + find_substring_by_prefix + output + prefix + input) + # find the prefix + STRING(FIND "${input}" "${prefix}" prefix_index) + IF("${prefix_index}" STREQUAL "-1") + MESSAGE(SEND_ERROR "Could not find ${prefix} in ${input}") + ENDIF() + # find the start index + STRING(LENGTH "${prefix}" prefix_length) + MATH(EXPR start_index "${prefix_index} + ${prefix_length}") + + STRING( + SUBSTRING "${input}" + "${start_index}" + "-1" + _output) + SET("${output}" + "${_output}" + PARENT_SCOPE) +ENDFUNCTION() + +# A function to set environment variables of CMake from the output of `cmd /c set` +FUNCTION(SET_ENV_FROM_STRING env_string) + # replace ; in paths with __sep__ so we can split on ; + STRING( + REGEX + REPLACE ";" + "__sep__" + env_string_sep_added + "${env_string}") + + # the variables are separated by \r?\n + STRING( + REGEX + REPLACE "\r?\n" + ";" + env_list + "${env_string_sep_added}") + + FOREACH(env_var ${env_list}) + # split by = + STRING( + REGEX + REPLACE "=" + ";" + env_parts + "${env_var}") + + LIST(LENGTH env_parts env_parts_length) + IF("${env_parts_length}" EQUAL "2") + # get the variable name and value + LIST( + GET + env_parts + 0 + env_name) + LIST( + GET + env_parts + 1 + env_value) + + # recover ; in paths + STRING( + REGEX + REPLACE "__sep__" + ";" + env_value + "${env_value}") + + # set env_name to env_value + SET(ENV{${env_name}} "${env_value}") + + # update cmake program path + IF("${env_name}" EQUAL "PATH") + LIST(APPEND CMAKE_PROGRAM_PATH ${env_value}) + ENDIF() + ENDIF() + ENDFOREACH() +ENDFUNCTION() + +FUNCTION(GET_ALL_TARGETS var) + SET(targets) + GET_ALL_TARGETS_RECURSIVE(targets ${CMAKE_CURRENT_SOURCE_DIR}) + SET(${var} + ${targets} + PARENT_SCOPE) +ENDFUNCTION() + +FUNCTION(GET_ALL_INSTALLABLE_TARGETS var) + SET(targets) + GET_ALL_TARGETS(targets) + FOREACH(_target ${targets}) + GET_TARGET_PROPERTY(_target_type ${_target} TYPE) + IF(NOT + ${_target_type} + MATCHES + ".*LIBRARY|EXECUTABLE") + LIST(REMOVE_ITEM targets ${_target}) + ENDIF() + ENDFOREACH() + SET(${var} + ${targets} + PARENT_SCOPE) +ENDFUNCTION() + +MACRO(GET_ALL_TARGETS_RECURSIVE targets dir) + GET_PROPERTY( + subdirectories + DIRECTORY ${dir} + PROPERTY SUBDIRECTORIES) + FOREACH(subdir ${subdirectories}) + GET_ALL_TARGETS_RECURSIVE(${targets} ${subdir}) + ENDFOREACH() + + GET_PROPERTY( + current_targets + DIRECTORY ${dir} + PROPERTY BUILDSYSTEM_TARGETS) + LIST(APPEND ${targets} ${current_targets}) +ENDMACRO() + +FUNCTION(IS_VERBOSE var) + IF("CMAKE_MESSAGE_LOG_LEVEL" STREQUAL "VERBOSE" + OR "CMAKE_MESSAGE_LOG_LEVEL" STREQUAL "DEBUG" + OR "CMAKE_MESSAGE_LOG_LEVEL" STREQUAL "TRACE") + SET(${var} + ON + PARENT_SCOPE) + ELSE() + SET(${var} + OFF + PARENT_SCOPE) + ENDIF() +ENDFUNCTION() diff --git a/cmake/VCEnvironment.cmake b/cmake/VCEnvironment.cmake new file mode 100644 index 0000000..523e1e2 --- /dev/null +++ b/cmake/VCEnvironment.cmake @@ -0,0 +1,71 @@ +INCLUDE("${CMAKE_CURRENT_LIST_DIR}/Utilities.cmake") + +MACRO(DETECT_ARCHITECTURE) + # detect the architecture + STRING(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" CMAKE_SYSTEM_PROCESSOR_LOWER) + IF(CMAKE_SYSTEM_PROCESSOR_LOWER STREQUAL x86 OR CMAKE_SYSTEM_PROCESSOR_LOWER MATCHES "^i[3456]86$") + SET(VCVARSALL_ARCH x86) + ELSEIF( + CMAKE_SYSTEM_PROCESSOR_LOWER STREQUAL x64 + OR CMAKE_SYSTEM_PROCESSOR_LOWER STREQUAL x86_64 + OR CMAKE_SYSTEM_PROCESSOR_LOWER STREQUAL amd64) + SET(VCVARSALL_ARCH x64) + ELSEIF(CMAKE_SYSTEM_PROCESSOR_LOWER STREQUAL arm) + SET(VCVARSALL_ARCH arm) + ELSEIF(CMAKE_SYSTEM_PROCESSOR_LOWER STREQUAL arm64 OR CMAKE_SYSTEM_PROCESSOR_LOWER STREQUAL aarch64) + SET(VCVARSALL_ARCH arm64) + ELSE() + IF(CMAKE_HOST_SYSTEM_PROCESSOR) + SET(VCVARSALL_ARCH ${CMAKE_HOST_SYSTEM_PROCESSOR}) + ELSE() + SET(VCVARSALL_ARCH x64) + MESSAGE(STATUS "Unkown architecture CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR_LOWER} - using x64") + ENDIF() + ENDIF() +ENDMACRO() + +# Run vcvarsall.bat and set CMake environment variables +FUNCTION(RUN_VCVARSALL) + # if MSVC but VSCMD_VER is not set, which means vcvarsall has not run + IF(MSVC AND "$ENV{VSCMD_VER}" STREQUAL "") + + # find vcvarsall.bat + GET_FILENAME_COMPONENT(MSVC_DIR ${CMAKE_CXX_COMPILER} DIRECTORY) + FIND_FILE( + VCVARSALL_FILE + NAMES vcvarsall.bat + PATHS "${MSVC_DIR}" + "${MSVC_DIR}/.." + "${MSVC_DIR}/../.." + "${MSVC_DIR}/../../../../../../../.." + "${MSVC_DIR}/../../../../../../.." + PATH_SUFFIXES "VC/Auxiliary/Build" "Common7/Tools" "Tools") + + IF(EXISTS ${VCVARSALL_FILE}) + # detect the architecture + DETECT_ARCHITECTURE() + + # run vcvarsall and print the environment variables + MESSAGE(STATUS "Running `${VCVARSALL_FILE} ${VCVARSALL_ARCH}` to set up the MSVC environment") + EXECUTE_PROCESS( + COMMAND + "cmd" "/c" ${VCVARSALL_FILE} ${VCVARSALL_ARCH} # + "&&" "call" "echo" "VCVARSALL_ENV_START" # + "&" "set" # + OUTPUT_VARIABLE VCVARSALL_OUTPUT + OUTPUT_STRIP_TRAILING_WHITESPACE) + + # parse the output and get the environment variables string + FIND_SUBSTRING_BY_PREFIX(VCVARSALL_ENV "VCVARSALL_ENV_START" "${VCVARSALL_OUTPUT}") + + # set the environment variables + SET_ENV_FROM_STRING("${VCVARSALL_ENV}") + + ELSE() + MESSAGE( + WARNING + "Could not find `vcvarsall.bat` for automatic MSVC environment preparation. Please manually open the MSVC command prompt and rebuild the project. + ") + ENDIF() + ENDIF() +ENDFUNCTION()