diff --git a/Modules/WriteCompilerDetectionHeader.cmake b/Modules/WriteCompilerDetectionHeader.cmake index 86137e2d6..6d0bfc776 100644 --- a/Modules/WriteCompilerDetectionHeader.cmake +++ b/Modules/WriteCompilerDetectionHeader.cmake @@ -11,6 +11,7 @@ # write_compiler_detection_header( # FILE # PREFIX +# [OUTPUT_FILES_VAR OUTPUT_DIR ] # COMPILERS [...] # FEATURES [...] # [VERSION ] @@ -21,6 +22,33 @@ # The ``write_compiler_detection_header`` function generates the # file ```` with macros which all have the prefix ````. # +# By default, all content is written directly to the ````. The +# ``OUTPUT_FILES_VAR`` may be specified to cause the compiler-specific +# content to be written to separate files. The separate files are then +# available in the ```` and may be consumed by the caller +# for installation for example. The ``OUTPUT_DIR`` specifies a relative +# path from the main ```` to the compiler-specific files. For example: +# +# .. code-block:: cmake +# +# write_compiler_detection_header( +# FILE climbingstats_compiler_detection.h +# PREFIX ClimbingStats +# OUTPUT_FILES_VAR support_files +# OUTPUT_DIR compilers +# COMPILERS GNU Clang +# FEATURES cxx_variadic_templates +# ) +# install(FILES +# ${CMAKE_CURRENT_BINARY_DIR}/climbingstats_compiler_detection.h +# DESTINATION include +# ) +# install(FILES +# ${support_files} +# DESTINATION include/compilers +# ) +# +# # ``VERSION`` may be used to specify the API version to be generated. # Future versions of CMake may introduce alternative APIs. A given # API is selected by any ```` value greater than or equal @@ -220,7 +248,7 @@ function(write_compiler_detection_header message(FATAL_ERROR "write_compiler_detection_header: PREFIX parameter missing.") endif() set(options) - set(oneValueArgs VERSION EPILOG PROLOG) + set(oneValueArgs VERSION EPILOG PROLOG OUTPUT_FILES_VAR OUTPUT_DIR) set(multiValueArgs COMPILERS FEATURES) cmake_parse_arguments(_WCD "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) @@ -255,6 +283,35 @@ function(write_compiler_detection_header message(FATAL_ERROR "${err}") endif() + if(_WCD_OUTPUT_FILES_VAR) + if(NOT _WCD_OUTPUT_DIR) + message(FATAL_ERROR "If OUTPUT_FILES_VAR is specified, then OUTPUT_DIR must also be specified.") + endif() + endif() + if(_WCD_OUTPUT_DIR) + if(NOT _WCD_OUTPUT_FILES_VAR) + message(FATAL_ERROR "If OUTPUT_DIR is specified, then OUTPUT_FILES_VAR must also be specified.") + endif() + get_filename_component(main_file_dir ${file_arg} DIRECTORY) + if (NOT IS_ABSOLUTE ${main_file_dir}) + set(main_file_dir "${CMAKE_CURRENT_BINARY_DIR}/${main_file_dir}") + endif() + if (NOT IS_ABSOLUTE ${_WCD_OUTPUT_DIR}) + set(_WCD_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/${_WCD_OUTPUT_DIR}") + endif() + get_filename_component(out_file_dir ${_WCD_OUTPUT_DIR} ABSOLUTE) + string(FIND ${out_file_dir} ${main_file_dir} idx) + if (NOT idx EQUAL 0) + message(FATAL_ERROR "The compiler-specific output directory must be within the same directory as the main file.") + endif() + + if (main_file_dir STREQUAL out_file_dir) + unset(_WCD_OUTPUT_DIR) + else() + string(REPLACE "${main_file_dir}/" "" _WCD_OUTPUT_DIR "${out_file_dir}/") + endif() + endif() + set(compilers GNU Clang @@ -314,6 +371,14 @@ function(write_compiler_detection_header endforeach() list(REMOVE_DUPLICATES _langs) + if(_WCD_OUTPUT_FILES_VAR) + get_filename_component(main_file_name ${file_arg} NAME) + set(compiler_file_content_ +"#ifndef ${prefix_arg}_COMPILER_DETECTION_H +# error This file may only be included from ${main_file_name} +#endif\n") + endif() + foreach(_lang ${_langs}) get_property(known_features GLOBAL PROPERTY CMAKE_${_lang}_KNOWN_FEATURES) @@ -340,7 +405,19 @@ function(write_compiler_detection_header foreach(compiler ${_WCD_COMPILERS}) _load_compiler_variables(${compiler} ${_lang} ${${_lang}_features}) set(file_content "${file_content}\n# ${pp_if} ${prefix_arg}_COMPILER_IS_${compiler}\n") - set(file_content "${file_content} + + if(_WCD_OUTPUT_FILES_VAR) + set(compile_file_name "${_WCD_OUTPUT_DIR}${prefix_arg}_COMPILER_INFO_${compiler}.h") + set(file_content "${file_content}\n# include \"${compile_file_name}\"\n") + endif() + + if(_WCD_OUTPUT_FILES_VAR) + set(compiler_file_content compiler_file_content_${compiler}) + else() + set(compiler_file_content file_content) + endif() + + set(${compiler_file_content} "${${compiler_file_content}} # if !(${_cmake_oldestSupported_${compiler}}) # error Unsupported compiler version # endif\n") @@ -354,7 +431,7 @@ function(write_compiler_detection_header set(MACRO_HEX) endif() string(CONFIGURE "${_compiler_id_version_compute_${compiler}}" VERSION_BLOCK @ONLY) - set(file_content "${file_content}${VERSION_BLOCK}\n") + set(${compiler_file_content} "${${compiler_file_content}}${VERSION_BLOCK}\n") set(PREFIX) set(MACRO_DEC) set(MACRO_HEX) @@ -370,7 +447,7 @@ function(write_compiler_detection_header set(_define_item "\n# define ${prefix_arg}_${feature_PP} 0\n") set(_define_item "\n# if ${_cmake_feature_test_${compiler}_${feature}}\n# define ${prefix_arg}_${feature_PP} 1\n# else${_define_item}# endif\n") endif() - set(file_content "${file_content}${_define_item}") + set(${compiler_file_content} "${${compiler_file_content}}${_define_item}") endforeach() endforeach() if(pp_if STREQUAL "elif") @@ -522,6 +599,22 @@ function(write_compiler_detection_header endforeach() + if(_WCD_OUTPUT_FILES_VAR) + foreach(compiler ${_WCD_COMPILERS}) + set(CMAKE_CONFIGURABLE_FILE_CONTENT "${compiler_file_content_}") + set(CMAKE_CONFIGURABLE_FILE_CONTENT "${CMAKE_CONFIGURABLE_FILE_CONTENT}${compiler_file_content_${compiler}}") + + set(compile_file_name "${_WCD_OUTPUT_DIR}${prefix_arg}_COMPILER_INFO_${compiler}.h") + set(full_path "${main_file_dir}/${compile_file_name}") + list(APPEND ${_WCD_OUTPUT_FILES_VAR} ${full_path}) + configure_file("${CMAKE_ROOT}/Modules/CMakeConfigurableFile.in" + "${full_path}" + @ONLY + ) + endforeach() + set(${_WCD_OUTPUT_FILES_VAR} ${${_WCD_OUTPUT_FILES_VAR}} PARENT_SCOPE) + endif() + if (_WCD_EPILOG) set(file_content "${file_content}\n${_WCD_EPILOG}\n") endif() diff --git a/Tests/Module/WriteCompilerDetectionHeader/CMakeLists.txt b/Tests/Module/WriteCompilerDetectionHeader/CMakeLists.txt index 645cc6524..b1bc82230 100644 --- a/Tests/Module/WriteCompilerDetectionHeader/CMakeLists.txt +++ b/Tests/Module/WriteCompilerDetectionHeader/CMakeLists.txt @@ -67,6 +67,22 @@ add_executable(WriteCompilerDetectionHeader main.cpp) set_property(TARGET WriteCompilerDetectionHeader PROPERTY CXX_STANDARD 98) set_defines(WriteCompilerDetectionHeader "${true_defs}" "${false_defs}") +write_compiler_detection_header( + FILE "${CMAKE_CURRENT_BINARY_DIR}/compiler_multi_files/multi_file_compiler_detection.h" + PREFIX MULTI + OUTPUT_FILES_VAR multi_files + OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/compiler_multi_files/compiler_support" + COMPILERS GNU Clang + VERSION 3.1 + FEATURES + ${cxx_known_features} ${c_known_features} +) + +add_executable(multi_files multi_files.cpp) +set_property(TARGET multi_files PROPERTY CXX_STANDARD 98) +target_include_directories(multi_files PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/compiler_multi_files) +set_defines(multi_files "${true_defs}" "${false_defs}") + if(MSVC) return() # MSVC has only one mode. endif() @@ -86,3 +102,8 @@ endif() add_executable(WriteCompilerDetectionHeader_11 main.cpp) set_property(TARGET WriteCompilerDetectionHeader_11 PROPERTY CXX_STANDARD 11) set_defines(WriteCompilerDetectionHeader_11 "${true_defs}" "${false_defs}") + +add_executable(multi_files_11 multi_files.cpp) +set_property(TARGET multi_files_11 PROPERTY CXX_STANDARD 11) +target_include_directories(multi_files_11 PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/compiler_multi_files) +set_defines(multi_files_11 "${true_defs}" "${false_defs}") diff --git a/Tests/Module/WriteCompilerDetectionHeader/compile_tests.h b/Tests/Module/WriteCompilerDetectionHeader/compile_tests.h new file mode 100644 index 000000000..8b547d846 --- /dev/null +++ b/Tests/Module/WriteCompilerDetectionHeader/compile_tests.h @@ -0,0 +1,25 @@ + +#define JOIN_IMPL(A, B) A ## B +#define JOIN(A, B) JOIN_IMPL(A, B) + +#define CHECK(FEATURE) (JOIN(PREFIX, JOIN(_COMPILER_, FEATURE)) == JOIN(EXPECTED_COMPILER_, FEATURE)) + +#if !CHECK(CXX_DELEGATING_CONSTRUCTORS) +#error cxx_delegating_constructors expected availability did not match. +#endif + +#if !CHECK(CXX_VARIADIC_TEMPLATES) +#error cxx_variadic_templates expected availability did not match. +#endif + +#if !CHECK(VERSION_MAJOR) +#error Compiler major version did not match. +#endif + +#if !CHECK(VERSION_MINOR) +#error Compiler minor version did not match. +#endif + +#if !CHECK(VERSION_PATCH) +#error Compiler patch version did not match. +#endif diff --git a/Tests/Module/WriteCompilerDetectionHeader/main.cpp b/Tests/Module/WriteCompilerDetectionHeader/main.cpp index b807ad5eb..82b219163 100644 --- a/Tests/Module/WriteCompilerDetectionHeader/main.cpp +++ b/Tests/Module/WriteCompilerDetectionHeader/main.cpp @@ -1,29 +1,8 @@ #include "test_compiler_detection.h" -#define JOIN_IMPL(A, B) A ## B -#define JOIN(A, B) JOIN_IMPL(A, B) -#define CHECK(FEATURE) (JOIN(TEST_COMPILER_, FEATURE) == JOIN(EXPECTED_COMPILER_, FEATURE)) - -#if !CHECK(CXX_DELEGATING_CONSTRUCTORS) -#error cxx_delegating_constructors expected availability did not match. -#endif - -#if !CHECK(CXX_VARIADIC_TEMPLATES) -#error cxx_variadic_templates expected availability did not match. -#endif - -#if !CHECK(VERSION_MAJOR) -#error Compiler major version did not match. -#endif - -#if !CHECK(VERSION_MINOR) -#error Compiler minor version did not match. -#endif - -#if !CHECK(VERSION_PATCH) -#error Compiler patch version did not match. -#endif +#define PREFIX TEST +#include "compile_tests.h" int main() { diff --git a/Tests/Module/WriteCompilerDetectionHeader/multi_files.cpp b/Tests/Module/WriteCompilerDetectionHeader/multi_files.cpp new file mode 100644 index 000000000..ca298231d --- /dev/null +++ b/Tests/Module/WriteCompilerDetectionHeader/multi_files.cpp @@ -0,0 +1,10 @@ + +#include "multi_file_compiler_detection.h" + +#define PREFIX MULTI +#include "compile_tests.h" + +int main() +{ + return 0; +} diff --git a/Tests/RunCMake/WriteCompilerDetectionHeader/MultiBadOutDir-result.txt b/Tests/RunCMake/WriteCompilerDetectionHeader/MultiBadOutDir-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/WriteCompilerDetectionHeader/MultiBadOutDir-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/WriteCompilerDetectionHeader/MultiBadOutDir-stderr.txt b/Tests/RunCMake/WriteCompilerDetectionHeader/MultiBadOutDir-stderr.txt new file mode 100644 index 000000000..6658d5db1 --- /dev/null +++ b/Tests/RunCMake/WriteCompilerDetectionHeader/MultiBadOutDir-stderr.txt @@ -0,0 +1,6 @@ +CMake Error at .*Modules/WriteCompilerDetectionHeader.cmake:[0-9]+ \(message\): + The compiler-specific output directory must be within the same directory as + the main file. +Call Stack \(most recent call first\): + MultiBadOutDir.cmake:4 \(write_compiler_detection_header\) + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/WriteCompilerDetectionHeader/MultiBadOutDir.cmake b/Tests/RunCMake/WriteCompilerDetectionHeader/MultiBadOutDir.cmake new file mode 100644 index 000000000..b545beef6 --- /dev/null +++ b/Tests/RunCMake/WriteCompilerDetectionHeader/MultiBadOutDir.cmake @@ -0,0 +1,12 @@ + +include(WriteCompilerDetectionHeader) + +write_compiler_detection_header( + FILE "${CMAKE_CURRENT_BINARY_DIR}/dir/somefile" + PREFIX Pref + OUTPUT_FILES_VAR outfiles + OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}" + COMPILERS GNU + VERSION 3.1 + FEATURES cxx_auto_type +) diff --git a/Tests/RunCMake/WriteCompilerDetectionHeader/MultiNoOutFileVar-result.txt b/Tests/RunCMake/WriteCompilerDetectionHeader/MultiNoOutFileVar-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/WriteCompilerDetectionHeader/MultiNoOutFileVar-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/WriteCompilerDetectionHeader/MultiNoOutFileVar-stderr.txt b/Tests/RunCMake/WriteCompilerDetectionHeader/MultiNoOutFileVar-stderr.txt new file mode 100644 index 000000000..50f9b6ff1 --- /dev/null +++ b/Tests/RunCMake/WriteCompilerDetectionHeader/MultiNoOutFileVar-stderr.txt @@ -0,0 +1,5 @@ +CMake Error at .*Modules/WriteCompilerDetectionHeader.cmake:[0-9]+ \(message\): + If OUTPUT_DIR is specified, then OUTPUT_FILES_VAR must also be specified. +Call Stack \(most recent call first\): + MultiNoOutFileVar.cmake:4 \(write_compiler_detection_header\) + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/WriteCompilerDetectionHeader/MultiNoOutFileVar.cmake b/Tests/RunCMake/WriteCompilerDetectionHeader/MultiNoOutFileVar.cmake new file mode 100644 index 000000000..e42b0ed68 --- /dev/null +++ b/Tests/RunCMake/WriteCompilerDetectionHeader/MultiNoOutFileVar.cmake @@ -0,0 +1,11 @@ + +include(WriteCompilerDetectionHeader) + +write_compiler_detection_header( + FILE "${CMAKE_CURRENT_BINARY_DIR}/somefile" + PREFIX Pref + OUTPUT_DIR outfiles + COMPILERS GNU + VERSION 3.1 + FEATURES cxx_auto_type +) diff --git a/Tests/RunCMake/WriteCompilerDetectionHeader/MultiNoOutdir-result.txt b/Tests/RunCMake/WriteCompilerDetectionHeader/MultiNoOutdir-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/WriteCompilerDetectionHeader/MultiNoOutdir-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/WriteCompilerDetectionHeader/MultiNoOutdir-stderr.txt b/Tests/RunCMake/WriteCompilerDetectionHeader/MultiNoOutdir-stderr.txt new file mode 100644 index 000000000..1c83a1a01 --- /dev/null +++ b/Tests/RunCMake/WriteCompilerDetectionHeader/MultiNoOutdir-stderr.txt @@ -0,0 +1,5 @@ +CMake Error at .*Modules/WriteCompilerDetectionHeader.cmake:[0-9]+ \(message\): + If OUTPUT_FILES_VAR is specified, then OUTPUT_DIR must also be specified. +Call Stack \(most recent call first\): + MultiNoOutdir.cmake:4 \(write_compiler_detection_header\) + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/WriteCompilerDetectionHeader/MultiNoOutdir.cmake b/Tests/RunCMake/WriteCompilerDetectionHeader/MultiNoOutdir.cmake new file mode 100644 index 000000000..e8c2ae14f --- /dev/null +++ b/Tests/RunCMake/WriteCompilerDetectionHeader/MultiNoOutdir.cmake @@ -0,0 +1,11 @@ + +include(WriteCompilerDetectionHeader) + +write_compiler_detection_header( + FILE "${CMAKE_CURRENT_BINARY_DIR}/somefile" + PREFIX Pref + OUTPUT_FILES_VAR outfiles + COMPILERS GNU + VERSION 3.1 + FEATURES cxx_auto_type +) diff --git a/Tests/RunCMake/WriteCompilerDetectionHeader/RunCMakeTest.cmake b/Tests/RunCMake/WriteCompilerDetectionHeader/RunCMakeTest.cmake index 6dded44a6..a834e6d45 100644 --- a/Tests/RunCMake/WriteCompilerDetectionHeader/RunCMakeTest.cmake +++ b/Tests/RunCMake/WriteCompilerDetectionHeader/RunCMakeTest.cmake @@ -12,3 +12,7 @@ run_cmake(InvalidFeature) run_cmake(InvalidCXXFeature) run_cmake(EmptyPrefix) run_cmake(InvalidPrefix) +run_cmake(MultiNoOutdir) +run_cmake(MultiNoOutFileVar) +set(NO_CACHE TRUE) +run_cmake(MultiBadOutDir)