Features: Extend concept to C language.

Add properties and variables corresponding to CXX equivalents.

Add features for c_function_prototypes (C90), c_restrict (C99),
c_variadic_macros (C99) and c_static_assert (C11). This feature
set can be extended later.

Add a <PREFIX>_RESTRICT symbol define to WriteCompilerDetectionHeader
to conditionally represent the c_restrict feature.
This commit is contained in:
Stephen Kelly 2013-11-04 01:15:43 +01:00
parent 775458dede
commit e0890d03a4
57 changed files with 630 additions and 27 deletions

View File

@ -8,7 +8,8 @@ Add expected compiler features to a target.
target_compile_features(<target> <PRIVATE|PUBLIC|INTERFACE> <feature> [...]) target_compile_features(<target> <PRIVATE|PUBLIC|INTERFACE> <feature> [...])
Specify compiler features required when compiling a given target. If the Specify compiler features required when compiling a given target. If the
feature is not listed in the :variable:`CMAKE_CXX_COMPILE_FEATURES` variable, feature is not listed in the :variable:`CMAKE_C_COMPILE_FEATURES` variable
or :variable:`CMAKE_CXX_COMPILE_FEATURES` variable,
then an error will be reported by CMake. If the use of the feature requires then an error will be reported by CMake. If the use of the feature requires
an additional compiler flag, such as ``-std=c++11``, the flag will be added an additional compiler flag, such as ``-std=c++11``, the flag will be added
automatically. automatically.

View File

@ -16,6 +16,7 @@ Properties of Global Scope
/prop_gbl/ALLOW_DUPLICATE_CUSTOM_TARGETS /prop_gbl/ALLOW_DUPLICATE_CUSTOM_TARGETS
/prop_gbl/AUTOGEN_TARGETS_FOLDER /prop_gbl/AUTOGEN_TARGETS_FOLDER
/prop_gbl/AUTOMOC_TARGETS_FOLDER /prop_gbl/AUTOMOC_TARGETS_FOLDER
/prop_gbl/CMAKE_C_KNOWN_FEATURES
/prop_gbl/CMAKE_CXX_KNOWN_FEATURES /prop_gbl/CMAKE_CXX_KNOWN_FEATURES
/prop_gbl/DEBUG_CONFIGURATIONS /prop_gbl/DEBUG_CONFIGURATIONS
/prop_gbl/DISABLED_FEATURES /prop_gbl/DISABLED_FEATURES
@ -93,6 +94,9 @@ Properties on Targets
/prop_tgt/BUILD_WITH_INSTALL_RPATH /prop_tgt/BUILD_WITH_INSTALL_RPATH
/prop_tgt/BUNDLE_EXTENSION /prop_tgt/BUNDLE_EXTENSION
/prop_tgt/BUNDLE /prop_tgt/BUNDLE
/prop_tgt/C_EXTENSIONS
/prop_tgt/C_STANDARD
/prop_tgt/C_STANDARD_REQUIRED
/prop_tgt/COMPATIBLE_INTERFACE_BOOL /prop_tgt/COMPATIBLE_INTERFACE_BOOL
/prop_tgt/COMPATIBLE_INTERFACE_NUMBER_MAX /prop_tgt/COMPATIBLE_INTERFACE_NUMBER_MAX
/prop_tgt/COMPATIBLE_INTERFACE_NUMBER_MIN /prop_tgt/COMPATIBLE_INTERFACE_NUMBER_MIN

View File

@ -260,6 +260,10 @@ Variables for Languages
:maxdepth: 1 :maxdepth: 1
/variable/CMAKE_COMPILER_IS_GNULANG /variable/CMAKE_COMPILER_IS_GNULANG
/variable/CMAKE_C_COMPILE_FEATURES
/variable/CMAKE_C_EXTENSIONS
/variable/CMAKE_C_STANDARD
/variable/CMAKE_C_STANDARD_REQUIRED
/variable/CMAKE_CXX_COMPILE_FEATURES /variable/CMAKE_CXX_COMPILE_FEATURES
/variable/CMAKE_CXX_EXTENSIONS /variable/CMAKE_CXX_EXTENSIONS
/variable/CMAKE_CXX_STANDARD /variable/CMAKE_CXX_STANDARD

View File

@ -0,0 +1,25 @@
CMAKE_C_KNOWN_FEATURES
----------------------
List of C features known to this version of CMake.
The features listed in this global property may be known to be available to the
C compiler. If the feature is available with the C compiler, it will
be listed in the :variable:`CMAKE_C_COMPILE_FEATURES` variable.
The features listed here may be used with the :command:`target_compile_features`
command.
The features known to this version of CMake are:
``c_function_prototypes``
Function prototypes, as defined in ``ISO/IEC 9899:1990``.
``c_restrict``
``restrict`` keyword, as defined in ``ISO/IEC 9899:1999``.
``c_static_assert``
Static assert, as defined in ``ISO/IEC 9899:2011``.
``c_variadic_macros``
Variadic macros, as defined in ``ISO/IEC 9899:1999``.

View File

@ -0,0 +1,8 @@
C_EXTENSIONS
------------
Boolean specifying whether compiler specific extensions are requested.
This property specifies whether compiler specific extensions should be
used. For some compilers, this results in adding a flag such
as ``-std=gnu11`` instead of ``-std=c11`` to the compile line.

View File

@ -0,0 +1,27 @@
C_STANDARD
----------
The C standard whose features are requested to build this target.
This property specifies the C standard whose features are requested
to build this target. For some compilers, this results in adding a
flag such as ``-std=c11`` to the compile line.
Supported values are ``90``, ``99`` and ``11``.
If the value requested does not result in a compile flag being added for
the compiler in use, a previous standard flag will be added instead. This
means that using:
.. code-block:: cmake
set_property(TARGET tgt PROPERTY C_STANDARD 11)
with a compiler which does not support ``-std=c11`` or an equivalent
flag will not result in an error or warning, but will instead add the
``-std=c99`` or ``-std=c90`` flag if supported. This "decay" behavior may
be controlled with the :prop_tgt:`C_STANDARD_REQUIRED` target property.
This property is initialized by the value of
the :variable:`CMAKE_C_STANDARD` variable if it is set when a target
is created.

View File

@ -0,0 +1,14 @@
C_STANDARD_REQUIRED
-------------------
Boolean describing whether the value of :prop_tgt:`C_STANDARD` is a requirement.
If this property is set to ``ON``, then the value of the
:prop_tgt:`C_STANDARD` target property is treated as a requirement. If this
property is ``OFF`` or unset, the :prop_tgt:`C_STANDARD` target property is
treated as optional and may "decay" to a previous standard if the requested is
not available.
This property is initialized by the value of
the :variable:`CMAKE_C_STANDARD_REQUIRED` variable if it is set when a
target is created.

View File

@ -7,6 +7,12 @@ target-language-features
:variable:`CMAKE_CXX_STANDARD` and :variable:`CMAKE_CXX_EXTENSIONS` :variable:`CMAKE_CXX_STANDARD` and :variable:`CMAKE_CXX_EXTENSIONS`
variables may be set to initialize the target properties. variables may be set to initialize the target properties.
* New :prop_tgt:`C_STANDARD` and :prop_tgt:`C_EXTENSIONS` target
properties may specify values which CMake uses to compute required
compile options such as ``-std=c11`` or ``-std=gnu11``. The
:variable:`CMAKE_C_STANDARD` and :variable:`CMAKE_C_EXTENSIONS`
variables may be set to initialize the target properties.
* New :prop_tgt:`COMPILE_FEATURES` target property may contain a list * New :prop_tgt:`COMPILE_FEATURES` target property may contain a list
of features required to compile a target. CMake uses this of features required to compile a target. CMake uses this
information to ensure that the compiler in use is capable of building information to ensure that the compiler in use is capable of building

View File

@ -0,0 +1,8 @@
CMAKE_C_COMPILE_FEATURES
------------------------
List of features known to the C compiler
These features are known to be available for use with the C compiler. This
list is a subset of the features listed in the :prop_gbl:`CMAKE_C_KNOWN_FEATURES`
global property.

View File

@ -0,0 +1,8 @@
CMAKE_C_EXTENSIONS
------------------
Default value for ``C_EXTENSIONS`` property of targets.
This variable is used to initialize the :prop_tgt:`C_EXTENSIONS`
property on all targets. See that target property for additional
information.

View File

@ -0,0 +1,8 @@
CMAKE_C_STANDARD
----------------
Default value for ``C_STANDARD`` property of targets.
This variable is used to initialize the :prop_tgt:`C_STANDARD`
property on all targets. See that target property for additional
information.

View File

@ -0,0 +1,8 @@
CMAKE_C_STANDARD_REQUIRED
-------------------------
Default value for ``C_STANDARD_REQUIRED`` property of targets.
This variable is used to initialize the :prop_tgt:`C_STANDARD_REQUIRED`
property on all targets. See that target property for additional
information.

View File

@ -2,6 +2,11 @@ set(CMAKE_C_COMPILER "@CMAKE_C_COMPILER@")
set(CMAKE_C_COMPILER_ARG1 "@CMAKE_C_COMPILER_ARG1@") set(CMAKE_C_COMPILER_ARG1 "@CMAKE_C_COMPILER_ARG1@")
set(CMAKE_C_COMPILER_ID "@CMAKE_C_COMPILER_ID@") set(CMAKE_C_COMPILER_ID "@CMAKE_C_COMPILER_ID@")
set(CMAKE_C_COMPILER_VERSION "@CMAKE_C_COMPILER_VERSION@") set(CMAKE_C_COMPILER_VERSION "@CMAKE_C_COMPILER_VERSION@")
set(CMAKE_C_COMPILE_FEATURES "@CMAKE_C_COMPILE_FEATURES@")
set(CMAKE_C90_COMPILE_FEATURES "@CMAKE_C90_COMPILE_FEATURES@")
set(CMAKE_C99_COMPILE_FEATURES "@CMAKE_C99_COMPILE_FEATURES@")
set(CMAKE_C11_COMPILE_FEATURES "@CMAKE_C11_COMPILE_FEATURES@")
set(CMAKE_C_PLATFORM_ID "@CMAKE_C_PLATFORM_ID@") set(CMAKE_C_PLATFORM_ID "@CMAKE_C_PLATFORM_ID@")
set(CMAKE_C_SIMULATE_ID "@CMAKE_C_SIMULATE_ID@") set(CMAKE_C_SIMULATE_ID "@CMAKE_C_SIMULATE_ID@")
set(CMAKE_C_SIMULATE_VERSION "@CMAKE_C_SIMULATE_VERSION@") set(CMAKE_C_SIMULATE_VERSION "@CMAKE_C_SIMULATE_VERSION@")

View File

@ -14,7 +14,45 @@
function(cmake_determine_compile_features lang) function(cmake_determine_compile_features lang)
if(lang STREQUAL CXX AND COMMAND cmake_record_cxx_compile_features) if(lang STREQUAL C AND COMMAND cmake_record_c_compile_features)
message(STATUS "Detecting ${lang} compile features")
set(CMAKE_C90_COMPILE_FEATURES)
set(CMAKE_C99_COMPILE_FEATURES)
set(CMAKE_C11_COMPILE_FEATURES)
include("${CMAKE_ROOT}/Modules/Internal/FeatureTesting.cmake")
cmake_record_c_compile_features()
if(NOT _result EQUAL 0)
message(STATUS "Detecting ${lang} compile features - failed")
return()
endif()
if (CMAKE_C99_COMPILE_FEATURES AND CMAKE_C11_COMPILE_FEATURES)
list(REMOVE_ITEM CMAKE_C11_COMPILE_FEATURES ${CMAKE_C99_COMPILE_FEATURES})
endif()
if (CMAKE_C90_COMPILE_FEATURES AND CMAKE_C99_COMPILE_FEATURES)
list(REMOVE_ITEM CMAKE_C99_COMPILE_FEATURES ${CMAKE_C90_COMPILE_FEATURES})
endif()
if(NOT CMAKE_C_COMPILE_FEATURES)
set(CMAKE_C_COMPILE_FEATURES
${CMAKE_C90_COMPILE_FEATURES}
${CMAKE_C99_COMPILE_FEATURES}
${CMAKE_C11_COMPILE_FEATURES}
)
endif()
set(CMAKE_C_COMPILE_FEATURES ${CMAKE_C_COMPILE_FEATURES} PARENT_SCOPE)
set(CMAKE_C90_COMPILE_FEATURES ${CMAKE_C90_COMPILE_FEATURES} PARENT_SCOPE)
set(CMAKE_C99_COMPILE_FEATURES ${CMAKE_C99_COMPILE_FEATURES} PARENT_SCOPE)
set(CMAKE_C11_COMPILE_FEATURES ${CMAKE_C11_COMPILE_FEATURES} PARENT_SCOPE)
message(STATUS "Detecting ${lang} compile features - done")
elseif(lang STREQUAL CXX AND COMMAND cmake_record_cxx_compile_features)
message(STATUS "Detecting ${lang} compile features") message(STATUS "Detecting ${lang} compile features")
set(CMAKE_CXX98_COMPILE_FEATURES) set(CMAKE_CXX98_COMPILE_FEATURES)

View File

@ -73,6 +73,9 @@ else()
# Try to identify the ABI and configure it into CMakeCCompiler.cmake # Try to identify the ABI and configure it into CMakeCCompiler.cmake
include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerABI.cmake) include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerABI.cmake)
CMAKE_DETERMINE_COMPILER_ABI(C ${CMAKE_ROOT}/Modules/CMakeCCompilerABI.c) CMAKE_DETERMINE_COMPILER_ABI(C ${CMAKE_ROOT}/Modules/CMakeCCompilerABI.c)
# Try to identify the compiler features
include(${CMAKE_ROOT}/Modules/CMakeDetermineCompileFeatures.cmake)
CMAKE_DETERMINE_COMPILE_FEATURES(C)
# Re-configure to save learned information. # Re-configure to save learned information.
configure_file( configure_file(

View File

@ -0,0 +1,12 @@
set(_cmake_oldestSupported "(__GNUC__ * 100 + __GNUC_MINOR__) >= 407")
set(GNU46_C11 "${_cmake_oldestSupported} && __STDC_VERSION__ >= 201112L")
set(_cmake_feature_test_c_static_assert "${GNU46_C11}")
# Since 4.4 at least:
set(GNU44_C99 "${_cmake_oldestSupported} && __STDC_VERSION__ >= 199901L")
set(_cmake_feature_test_c_restrict "${GNU44_C99}")
set(_cmake_feature_test_c_variadic_macros "${GNU44_C99}")
set(GNU_C90 "${_cmake_oldestSupported} && !defined(__STDC_VERSION__)")
set(_cmake_feature_test_c_function_prototypes "${GNU_C90}")

View File

@ -1,2 +1,34 @@
include(Compiler/GNU) include(Compiler/GNU)
__compiler_gnu(C) __compiler_gnu(C)
if (NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.7)
set(CMAKE_C90_STANDARD_COMPILE_OPTION "-std=c90")
set(CMAKE_C90_EXTENSION_COMPILE_OPTION "-std=gnu90")
set(CMAKE_C99_STANDARD_COMPILE_OPTION "-std=c99")
set(CMAKE_C99_EXTENSION_COMPILE_OPTION "-std=gnu99")
set(CMAKE_C11_STANDARD_COMPILE_OPTION "-std=c11")
set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-std=gnu11")
endif()
# This may change in a future GNU version.
set(CMAKE_C_STANDARD_DEFAULT 90)
macro(cmake_record_c_compile_features)
macro(_get_gcc_features std_version list)
record_compiler_features(C "-std=${std_version}" ${list})
endmacro()
if (UNIX AND NOT APPLE AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.7)
_get_gcc_features(c90 CMAKE_C90_COMPILE_FEATURES)
if (_result EQUAL 0)
_get_gcc_features(c99 CMAKE_C99_COMPILE_FEATURES)
endif()
if (_result EQUAL 0)
_get_gcc_features(c11 CMAKE_C11_COMPILE_FEATURES)
endif()
else()
set(_result 0)
endif()
endmacro()

View File

@ -43,7 +43,8 @@
# Possible compiler identifiers are documented with the # Possible compiler identifiers are documented with the
# :variable:`CMAKE_<LANG>_COMPILER_ID` variable. # :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
# Available features in this version of CMake are listed in the # Available features in this version of CMake are listed in the
# :prop_gbl:`CMAKE_CXX_KNOWN_FEATURES` global property. # :prop_gbl:`CMAKE_C_KNOWN_FEATURES` and
# :prop_gbl:`CMAKE_CXX_KNOWN_FEATURES` global properties.
# #
# Feature Test Macros # Feature Test Macros
# =================== # ===================
@ -102,6 +103,7 @@
# ========================== =================================== ================= # ========================== =================================== =================
# Feature Define Symbol # Feature Define Symbol
# ========================== =================================== ================= # ========================== =================================== =================
# ``c_restrict`` ``<PREFIX>_RESTRICT`` ``restrict``
# ``cxx_constexpr`` ``<PREFIX>_CONSTEXPR`` ``constexpr`` # ``cxx_constexpr`` ``<PREFIX>_CONSTEXPR`` ``constexpr``
# ``cxx_deleted_functions`` ``<PREFIX>_DELETED_FUNCTION`` ``= delete`` # ``cxx_deleted_functions`` ``<PREFIX>_DELETED_FUNCTION`` ``= delete``
# ``cxx_extern_templates`` ``<PREFIX>_EXTERN_TEMPLATE`` ``extern`` # ``cxx_extern_templates`` ``<PREFIX>_EXTERN_TEMPLATE`` ``extern``
@ -221,6 +223,9 @@ function(write_compiler_detection_header
if (feature MATCHES "^cxx_") if (feature MATCHES "^cxx_")
list(APPEND _langs CXX) list(APPEND _langs CXX)
list(APPEND CXX_features ${feature}) list(APPEND CXX_features ${feature})
elseif (feature MATCHES "^c_")
list(APPEND _langs C)
list(APPEND C_features ${feature})
else() else()
message(FATAL_ERROR "Unsupported feature ${feature}.") message(FATAL_ERROR "Unsupported feature ${feature}.")
endif() endif()
@ -239,6 +244,8 @@ function(write_compiler_detection_header
if(_lang STREQUAL CXX) if(_lang STREQUAL CXX)
set(file_content "${file_content}\n#ifdef __cplusplus\n") set(file_content "${file_content}\n#ifdef __cplusplus\n")
else()
set(file_content "${file_content}\n#ifndef __cplusplus\n")
endif() endif()
compiler_id_detection(ID_CONTENT ${_lang} PREFIX ${prefix_arg}_ compiler_id_detection(ID_CONTENT ${_lang} PREFIX ${prefix_arg}_
@ -279,6 +286,16 @@ function(write_compiler_detection_header
string(TOUPPER ${feature} feature_upper) string(TOUPPER ${feature} feature_upper)
set(feature_PP "COMPILER_${feature_upper}") set(feature_PP "COMPILER_${feature_upper}")
set(def_name ${prefix_arg}_${feature_PP}) set(def_name ${prefix_arg}_${feature_PP})
if (feature STREQUAL c_restrict)
set(def_value "${prefix_arg}_RESTRICT")
set(file_content "${file_content}
# if ${def_name}
# define ${def_value} restrict
# else
# define ${def_value}
# endif
\n")
endif()
if (feature STREQUAL cxx_constexpr) if (feature STREQUAL cxx_constexpr)
set(def_value "${prefix_arg}_DECL_${feature_upper}") set(def_value "${prefix_arg}_DECL_${feature_upper}")
set(file_content "${file_content} set(file_content "${file_content}
@ -382,9 +399,8 @@ function(write_compiler_detection_header
\n") \n")
endif() endif()
endforeach() endforeach()
if(_lang STREQUAL CXX)
set(file_content "${file_content}#endif\n") set(file_content "${file_content}#endif\n")
endif()
endforeach() endforeach()

View File

@ -2201,6 +2201,10 @@ AddCompilerRequirementFlag(std::string &flags, cmTarget* target,
// Maintain sorted order, most recent first. // Maintain sorted order, most recent first.
langStdMap["CXX"].push_back("11"); langStdMap["CXX"].push_back("11");
langStdMap["CXX"].push_back("98"); langStdMap["CXX"].push_back("98");
langStdMap["C"].push_back("11");
langStdMap["C"].push_back("99");
langStdMap["C"].push_back("90");
} }
std::string standard(standardProp); std::string standard(standardProp);

View File

@ -4973,6 +4973,10 @@ void cmMakefile::RecordPolicies(cmPolicies::PolicyMap& pm)
} }
#define FEATURE_STRING(F) , #F #define FEATURE_STRING(F) , #F
static const char * const C_FEATURES[] = {
0
FOR_EACH_C_FEATURE(FEATURE_STRING)
};
static const char * const CXX_FEATURES[] = { static const char * const CXX_FEATURES[] = {
0 0
@ -4980,6 +4984,11 @@ static const char * const CXX_FEATURES[] = {
}; };
#undef FEATURE_STRING #undef FEATURE_STRING
static const char * const C_STANDARDS[] = {
"90"
, "99"
, "11"
};
static const char * const CXX_STANDARDS[] = { static const char * const CXX_STANDARDS[] = {
"98" "98"
, "11" , "11"
@ -4995,10 +5004,13 @@ AddRequiredTargetFeature(cmTarget *target, const std::string& feature,
target->AppendProperty("COMPILE_FEATURES", feature.c_str()); target->AppendProperty("COMPILE_FEATURES", feature.c_str());
return true; return true;
} }
bool isCFeature = std::find_if(cmArrayBegin(C_FEATURES) + 1,
cmArrayEnd(C_FEATURES), cmStrCmp(feature))
!= cmArrayEnd(C_FEATURES);
bool isCxxFeature = std::find_if(cmArrayBegin(CXX_FEATURES) + 1, bool isCxxFeature = std::find_if(cmArrayBegin(CXX_FEATURES) + 1,
cmArrayEnd(CXX_FEATURES), cmStrCmp(feature)) cmArrayEnd(CXX_FEATURES), cmStrCmp(feature))
!= cmArrayEnd(CXX_FEATURES); != cmArrayEnd(CXX_FEATURES);
if (!isCxxFeature) if (!isCFeature && !isCxxFeature)
{ {
cmOStringStream e; cmOStringStream e;
if (error) if (error)
@ -5022,7 +5034,7 @@ AddRequiredTargetFeature(cmTarget *target, const std::string& feature,
return false; return false;
} }
std::string lang = "CXX"; std::string lang = isCFeature ? "C" : "CXX";
const char* featuresKnown = const char* featuresKnown =
this->GetDefinition("CMAKE_" + lang + "_COMPILE_FEATURES"); this->GetDefinition("CMAKE_" + lang + "_COMPILE_FEATURES");
@ -5071,6 +5083,16 @@ AddRequiredTargetFeature(cmTarget *target, const std::string& feature,
target->AppendProperty("COMPILE_FEATURES", feature.c_str()); target->AppendProperty("COMPILE_FEATURES", feature.c_str());
return isCFeature
? this->AddRequiredTargetCFeature(target, feature)
: this->AddRequiredTargetCxxFeature(target, feature);
}
//----------------------------------------------------------------------------
bool cmMakefile::
AddRequiredTargetCxxFeature(cmTarget *target,
const std::string& feature) const
{
bool needCxx98 = false; bool needCxx98 = false;
bool needCxx11 = false; bool needCxx11 = false;
@ -5136,3 +5158,93 @@ AddRequiredTargetFeature(cmTarget *target, const std::string& feature,
} }
return true; return true;
} }
//----------------------------------------------------------------------------
bool cmMakefile::
AddRequiredTargetCFeature(cmTarget *target, const std::string& feature) const
{
bool needC90 = false;
bool needC99 = false;
bool needC11 = false;
if (const char *propC90 =
this->GetDefinition("CMAKE_C90_COMPILE_FEATURES"))
{
std::vector<std::string> props;
cmSystemTools::ExpandListArgument(propC90, props);
needC90 = std::find(props.begin(), props.end(), feature) != props.end();
}
if (const char *propC99 =
this->GetDefinition("CMAKE_C99_COMPILE_FEATURES"))
{
std::vector<std::string> props;
cmSystemTools::ExpandListArgument(propC99, props);
needC99 = std::find(props.begin(), props.end(), feature) != props.end();
}
if (const char *propC11 =
this->GetDefinition("CMAKE_C11_COMPILE_FEATURES"))
{
std::vector<std::string> props;
cmSystemTools::ExpandListArgument(propC11, props);
needC11 = std::find(props.begin(), props.end(), feature) != props.end();
}
const char *existingCStandard = target->GetProperty("C_STANDARD");
if (existingCStandard)
{
if (std::find_if(cmArrayBegin(C_STANDARDS), cmArrayEnd(C_STANDARDS),
cmStrCmp(existingCStandard)) == cmArrayEnd(C_STANDARDS))
{
cmOStringStream e;
e << "The C_STANDARD property on target \"" << target->GetName()
<< "\" contained an invalid value: \"" << existingCStandard << "\".";
this->IssueMessage(cmake::FATAL_ERROR, e.str().c_str());
return false;
}
}
const char * const *existingCIt = existingCStandard
? std::find_if(cmArrayBegin(C_STANDARDS),
cmArrayEnd(C_STANDARDS),
cmStrCmp(existingCStandard))
: cmArrayEnd(C_STANDARDS);
bool setC90 = needC90 && !existingCStandard;
bool setC99 = needC99 && !existingCStandard;
bool setC11 = needC11 && !existingCStandard;
if (needC11 && existingCStandard && existingCIt <
std::find_if(cmArrayBegin(C_STANDARDS),
cmArrayEnd(C_STANDARDS),
cmStrCmp("11")))
{
setC11 = true;
}
else if(needC99 && existingCStandard && existingCIt <
std::find_if(cmArrayBegin(C_STANDARDS),
cmArrayEnd(C_STANDARDS),
cmStrCmp("99")))
{
setC99 = true;
}
else if(needC90 && existingCStandard && existingCIt <
std::find_if(cmArrayBegin(C_STANDARDS),
cmArrayEnd(C_STANDARDS),
cmStrCmp("90")))
{
setC90 = true;
}
if (setC11)
{
target->SetProperty("C_STANDARD", "11");
}
else if (setC99)
{
target->SetProperty("C_STANDARD", "99");
}
else if (setC90)
{
target->SetProperty("C_STANDARD", "90");
}
return true;
}

View File

@ -1098,6 +1098,12 @@ private:
std::vector<cmSourceFile*> QtUiFilesWithOptions; std::vector<cmSourceFile*> QtUiFilesWithOptions;
unsigned int NumLastMatches; unsigned int NumLastMatches;
bool AddRequiredTargetCFeature(cmTarget *target,
const std::string& feature) const;
bool AddRequiredTargetCxxFeature(cmTarget *target,
const std::string& feature) const;
}; };
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------

View File

@ -314,6 +314,9 @@ void cmTarget::SetMakefile(cmMakefile* mf)
this->SetPropertyDefault("MACOSX_BUNDLE", 0); this->SetPropertyDefault("MACOSX_BUNDLE", 0);
this->SetPropertyDefault("MACOSX_RPATH", 0); this->SetPropertyDefault("MACOSX_RPATH", 0);
this->SetPropertyDefault("NO_SYSTEM_FROM_IMPORTED", 0); this->SetPropertyDefault("NO_SYSTEM_FROM_IMPORTED", 0);
this->SetPropertyDefault("C_STANDARD", 0);
this->SetPropertyDefault("C_STANDARD_REQUIRED", 0);
this->SetPropertyDefault("C_EXTENSIONS", 0);
this->SetPropertyDefault("CXX_STANDARD", 0); this->SetPropertyDefault("CXX_STANDARD", 0);
this->SetPropertyDefault("CXX_STANDARD_REQUIRED", 0); this->SetPropertyDefault("CXX_STANDARD_REQUIRED", 0);
this->SetPropertyDefault("CXX_EXTENSIONS", 0); this->SetPropertyDefault("CXX_EXTENSIONS", 0);

View File

@ -2273,12 +2273,16 @@ const char *cmake::GetProperty(const std::string& prop,
} }
this->SetProperty("ENABLED_LANGUAGES", lang.c_str()); this->SetProperty("ENABLED_LANGUAGES", lang.c_str());
} }
#define STRING_LIST_ELEMENT(F) ";" #F
if (prop == "CMAKE_C_KNOWN_FEATURES")
{
return FOR_EACH_C_FEATURE(STRING_LIST_ELEMENT) + 1;
}
if (prop == "CMAKE_CXX_KNOWN_FEATURES") if (prop == "CMAKE_CXX_KNOWN_FEATURES")
{ {
#define STRING_LIST_ELEMENT(F) ";" #F
return FOR_EACH_CXX_FEATURE(STRING_LIST_ELEMENT) + 1; return FOR_EACH_CXX_FEATURE(STRING_LIST_ELEMENT) + 1;
#undef STRING_LIST_ELEMENT
} }
#undef STRING_LIST_ELEMENT
return this->Properties.GetPropertyValue(prop, scope, chain); return this->Properties.GetPropertyValue(prop, scope, chain);
} }

View File

@ -458,6 +458,12 @@ private:
{"-Wno-dev", "Suppress developer warnings."},\ {"-Wno-dev", "Suppress developer warnings."},\
{"-Wdev", "Enable developer warnings."} {"-Wdev", "Enable developer warnings."}
#define FOR_EACH_C_FEATURE(F) \
F(c_function_prototypes) \
F(c_restrict) \
F(c_static_assert) \
F(c_variadic_macros)
#define FOR_EACH_CXX_FEATURE(F) \ #define FOR_EACH_CXX_FEATURE(F) \
F(cxx_alias_templates) \ F(cxx_alias_templates) \
F(cxx_alignas) \ F(cxx_alignas) \

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.0) cmake_minimum_required(VERSION 3.0)
project(target_compile_features) project(target_compile_features)
if (NOT CMAKE_CXX_COMPILE_FEATURES) if (NOT CMAKE_CXX_COMPILE_FEATURES AND NOT CMAKE_C_COMPILE_FEATURES)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test_dummy.cpp" file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test_dummy.cpp"
"int main(int,char**) { return 0; }\n" "int main(int,char**) { return 0; }\n"
) )
@ -11,8 +11,27 @@ endif()
set(CMAKE_VERBOSE_MAKEFILE ON) set(CMAKE_VERBOSE_MAKEFILE ON)
add_executable(target_compile_features main.cpp) if (CMAKE_C_COMPILE_FEATURES)
add_executable(target_compile_features main.c)
target_compile_features(target_compile_features target_compile_features(target_compile_features
PRIVATE c_restrict
)
add_library(lib_restrict lib_restrict.c)
target_compile_features(lib_restrict
PUBLIC c_restrict
)
add_executable(restrict_user restrict_user.c)
target_link_libraries(restrict_user lib_restrict)
endif()
if (CMAKE_CXX_COMPILE_FEATURES)
if (CMAKE_C_COMPILE_FEATURES)
set(target_suffix _cxx)
endif()
add_executable(target_compile_features${target_suffix} main.cpp)
target_compile_features(target_compile_features${target_suffix}
PRIVATE cxx_auto_type PRIVATE cxx_auto_type
) )
@ -23,3 +42,4 @@ target_compile_features(lib_auto_type
add_executable(lib_user lib_user.cpp) add_executable(lib_user lib_user.cpp)
target_link_libraries(lib_user lib_auto_type) target_link_libraries(lib_user lib_auto_type)
endif()

View File

@ -0,0 +1,9 @@
#include "lib_restrict.h"
int foo(int * restrict a, int * restrict b)
{
(void)a;
(void)b;
return 0;
}

View File

@ -0,0 +1,7 @@
#ifndef LIB_RESTRICT_H
#define LIB_RESTRICT_H
int foo(int * restrict a, int * restrict b);
#endif

View File

@ -0,0 +1,12 @@
int foo(int * restrict a, int * restrict b)
{
(void)a;
(void)b;
return 0;
}
int main()
{
return 0;
}

View File

@ -0,0 +1,14 @@
#include "lib_restrict.h"
int bar(int * restrict a, int * restrict b)
{
(void)a;
(void)b;
return foo(a, b);
}
int main()
{
return 0;
}

View File

@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.0)
project(CompileFeatures) project(CompileFeatures)
if (NOT CMAKE_CXX_COMPILE_FEATURES) if (NOT CMAKE_C_COMPILE_FEATURES AND NOT CMAKE_CXX_COMPILE_FEATURES)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/dummy.cpp" file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/dummy.cpp"
"int main(int,char**) { return 0; }\n" "int main(int,char**) { return 0; }\n"
) )
@ -22,6 +22,10 @@ macro(run_test feature lang)
endif() endif()
endmacro() endmacro()
get_property(c_features GLOBAL PROPERTY CMAKE_C_KNOWN_FEATURES)
foreach(feature ${c_features})
run_test(${feature} C)
endforeach()
get_property(cxx_features GLOBAL PROPERTY CMAKE_CXX_KNOWN_FEATURES) get_property(cxx_features GLOBAL PROPERTY CMAKE_CXX_KNOWN_FEATURES)
foreach(feature ${cxx_features}) foreach(feature ${cxx_features})
run_test(${feature} CXX) run_test(${feature} CXX)
@ -34,9 +38,11 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL GNU
) )
endif() endif()
set(C_ext c)
set(C_standard_flag 11)
set(CXX_ext cpp) set(CXX_ext cpp)
set(CXX_standard_flag 11) set(CXX_standard_flag 11)
foreach(lang CXX) foreach(lang CXX C)
if (CMAKE_${lang}_COMPILE_FEATURES) if (CMAKE_${lang}_COMPILE_FEATURES)
foreach(feature ${${lang}_non_features}) foreach(feature ${${lang}_non_features})
message("Testing feature : ${feature}") message("Testing feature : ${feature}")

View File

@ -0,0 +1,9 @@
int someFunc(int a, int b);
int someFunc(int a, int b)
{
(void)a;
(void)b;
return 0;
}

View File

@ -0,0 +1,7 @@
int f (int * restrict a, int * restrict b)
{
(void)a;
(void)b;
return 0;
}

View File

@ -0,0 +1,2 @@
_Static_assert(1, "Static assert test");

View File

@ -0,0 +1,15 @@
int someFunc(int i1, char c, int i2)
{
(void)i1;
(void)c;
(void)i2;
return 0;
}
#define FUNC_WRAPPER(...) someFunc(__VA_ARGS__)
void otherFunc()
{
FUNC_WRAPPER(42, 'a', 7);
}

View File

@ -0,0 +1,12 @@
int foo(int * restrict a, int * restrict b)
{
(void)a;
(void)b;
return 0;
}
int main()
{
return 0;
}

View File

@ -26,7 +26,10 @@ target_link_libraries(sharediface INTERFACE sharedlib)
add_library(use_auto_type INTERFACE) add_library(use_auto_type INTERFACE)
target_compile_features(use_auto_type INTERFACE cxx_auto_type) target_compile_features(use_auto_type INTERFACE cxx_auto_type)
install(TARGETS headeronly sharediface use_auto_type add_library(use_c_restrict INTERFACE)
target_compile_features(use_c_restrict INTERFACE c_restrict)
install(TARGETS headeronly sharediface use_auto_type use_c_restrict
EXPORT expInterface EXPORT expInterface
) )
install(TARGETS sharedlib install(TARGETS sharedlib

View File

@ -17,6 +17,7 @@ set_property(TARGET bld::sharediface APPEND PROPERTY INTERFACE_LINK_LIBRARIES de
add_executable(interfacetest_bld interfacetest.cpp) add_executable(interfacetest_bld interfacetest.cpp)
target_link_libraries(interfacetest_bld bld::sharediface) target_link_libraries(interfacetest_bld bld::sharediface)
include(CheckCSourceCompiles)
include(CheckCXXSourceCompiles) include(CheckCXXSourceCompiles)
macro(do_try_compile prefix) macro(do_try_compile prefix)
@ -41,6 +42,26 @@ macro(do_try_compile prefix)
message(SEND_ERROR "${prefix} try_compile with IMPORTED INTERFACE target failed!\n\n${OUTPUT}") message(SEND_ERROR "${prefix} try_compile with IMPORTED INTERFACE target failed!\n\n${OUTPUT}")
endif() endif()
if (";${CMAKE_C_COMPILE_FEATURES};" MATCHES ";c_restrict;")
set(CMAKE_REQUIRED_LIBRARIES ${prefix}::use_c_restrict)
check_c_source_compiles(
"
int foo(int * restrict a, int * restrict b)
{
(void)a;
(void)b;
return 0;
}
int main()
{
return 0;
}
" ${prefix}IMPORTED_IFACE_C_RESTRICT)
if(NOT ${prefix}IMPORTED_IFACE_C_RESTRICT)
message(SEND_ERROR "${prefix} try_compile with IMPORTED INTERFACE target failed!\n\n${OUTPUT}")
endif()
endif()
if (";${CMAKE_CXX_COMPILE_FEATURES};" MATCHES ";cxx_auto_type;") if (";${CMAKE_CXX_COMPILE_FEATURES};" MATCHES ";cxx_auto_type;")
set(CMAKE_REQUIRED_LIBRARIES ${prefix}::use_auto_type) set(CMAKE_REQUIRED_LIBRARIES ${prefix}::use_auto_type)
check_cxx_source_compiles( check_cxx_source_compiles(

View File

@ -6,6 +6,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
include(WriteCompilerDetectionHeader) include(WriteCompilerDetectionHeader)
get_property(cxx_known_features GLOBAL PROPERTY CMAKE_CXX_KNOWN_FEATURES) get_property(cxx_known_features GLOBAL PROPERTY CMAKE_CXX_KNOWN_FEATURES)
get_property(c_known_features GLOBAL PROPERTY CMAKE_C_KNOWN_FEATURES)
write_compiler_detection_header( write_compiler_detection_header(
FILE "${CMAKE_CURRENT_BINARY_DIR}/test_compiler_detection.h" FILE "${CMAKE_CURRENT_BINARY_DIR}/test_compiler_detection.h"
@ -15,10 +16,10 @@ write_compiler_detection_header(
PROLOG "// something" PROLOG "// something"
EPILOG "// more" EPILOG "// more"
FEATURES FEATURES
${cxx_known_features} ${cxx_known_features} ${c_known_features}
) )
if (NOT CMAKE_CXX_COMPILE_FEATURES) if (NOT CMAKE_CXX_COMPILE_FEATURES AND NOT CMAKE_C_COMPILE_FEATURES)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/dummy.cpp" file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/dummy.cpp"
"int main(int,char**) { return 0; }\n" "int main(int,char**) { return 0; }\n"
) )

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,8 @@
CMake Error at NoSupportedCFeatures.cmake:[0-9]+ \(target_compile_features\):
target_compile_features no known features for C compiler
"[^"]*"
version *[.0-9]+\.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,5 @@
enable_language(C)
add_library(no_features empty.c)
target_compile_features(no_features PRIVATE c_static_assert)

View File

@ -0,0 +1,6 @@
CMake Error in CMakeLists.txt:
No known features for C compiler
"[^"]*"
version *[.0-9]+\.

View File

@ -0,0 +1,5 @@
enable_language(C)
add_library(no_features empty.c)
target_compile_features(no_features PRIVATE $<1:c_static_assert>)

View File

@ -9,11 +9,20 @@ run_cmake(NotAFeature_OriginDebugTransitive)
run_cmake(NotAFeature_OriginDebug_target_compile_features) run_cmake(NotAFeature_OriginDebug_target_compile_features)
run_cmake(generate_feature_list) run_cmake(generate_feature_list)
file(READ
"${RunCMake_BINARY_DIR}/generate_feature_list-build/c_features.txt"
C_FEATURES
)
file(READ file(READ
"${RunCMake_BINARY_DIR}/generate_feature_list-build/cxx_features.txt" "${RunCMake_BINARY_DIR}/generate_feature_list-build/cxx_features.txt"
CXX_FEATURES CXX_FEATURES
) )
if (NOT C_FEATURES)
run_cmake(NoSupportedCFeatures)
run_cmake(NoSupportedCFeaturesGenex)
endif()
if (NOT CXX_FEATURES) if (NOT CXX_FEATURES)
run_cmake(NoSupportedCxxFeatures) run_cmake(NoSupportedCxxFeatures)
run_cmake(NoSupportedCxxFeaturesGenex) run_cmake(NoSupportedCxxFeaturesGenex)

View File

@ -0,0 +1,7 @@
#ifdef _WIN32
__declspec(dllexport)
#endif
int empty()
{
return 0;
}

View File

@ -1,4 +1,10 @@
enable_language(C)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/c_features.txt"
"${CMAKE_C_COMPILE_FEATURES}"
)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cxx_features.txt" file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cxx_features.txt"
"${CMAKE_CXX_COMPILE_FEATURES}" "${CMAKE_CXX_COMPILE_FEATURES}"
) )

View File

@ -1,4 +1,4 @@
-- Detecting C compiler ABI info -- Detecting C compiler ABI info
-- Detecting C compiler ABI info - failed -- Detecting C compiler ABI info - failed.*
-- Configuring done -- Configuring done
-- Generating done -- Generating done

View File

@ -9,3 +9,5 @@ run_cmake(imported_target)
run_cmake(no_target) run_cmake(no_target)
run_cmake(not_a_cxx_feature) run_cmake(not_a_cxx_feature)
run_cmake(no_matching_cxx_feature) run_cmake(no_matching_cxx_feature)
run_cmake(not_a_c_feature)
run_cmake(no_matching_c_feature)

View File

@ -0,0 +1,7 @@
#ifdef _WIN32
__declspec(dllexport)
#endif
int empty()
{
return 0;
}

View File

@ -0,0 +1,8 @@
CMake Error at no_matching_c_feature.cmake:[0-9][0-9]? \((target_compile_features|message)\):
The compiler feature "gnu_c_dummy" is not known to C compiler
"GNU"
version 4.8.1.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,15 @@
if (NOT ";${CMAKE_C_COMPILE_FEATURES};" MATCHES ";gnu_c_typeof;")
# Simulate passing the test.
message(SEND_ERROR
"The compiler feature \"gnu_c_dummy\" is not known to C compiler\n\"GNU\"\nversion 4.8.1."
)
return()
endif()
add_executable(main empty.c)
target_compile_features(main
PRIVATE
gnu_c_typeof
)

View File

@ -0,0 +1,5 @@
CMake Error at not_a_c_feature.cmake:3 \(target_compile_features\):
target_compile_features specified unknown feature "c_not_a_feature" for
target "main".
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,6 @@
add_executable(main empty.c)
target_compile_features(main
PRIVATE
c_not_a_feature
)

View File

@ -19,6 +19,16 @@ CMAKE_COMPILER_IS_GNUCC == "${CMAKE_COMPILER_IS_GNUCC}"
CMAKE_COMPILER_IS_GNUCXX == "${CMAKE_COMPILER_IS_GNUCXX}" CMAKE_COMPILER_IS_GNUCXX == "${CMAKE_COMPILER_IS_GNUCXX}"
CMAKE_C_COMPILER_ID == "${CMAKE_C_COMPILER_ID}" CMAKE_C_COMPILER_ID == "${CMAKE_C_COMPILER_ID}"
CMAKE_C_COMPILER_VERSION == "${CMAKE_C_COMPILER_VERSION}" CMAKE_C_COMPILER_VERSION == "${CMAKE_C_COMPILER_VERSION}"
CMAKE_C90_STANDARD_COMPILE_OPTION == "${CMAKE_C90_STANDARD_COMPILE_OPTION}"
CMAKE_C99_STANDARD_COMPILE_OPTION == "${CMAKE_C99_STANDARD_COMPILE_OPTION}"
CMAKE_C11_STANDARD_COMPILE_OPTION == "${CMAKE_C11_STANDARD_COMPILE_OPTION}"
CMAKE_C90_EXTENSION_COMPILE_OPTION == "${CMAKE_C90_EXTENSION_COMPILE_OPTION}"
CMAKE_C99_EXTENSION_COMPILE_OPTION == "${CMAKE_C99_EXTENSION_COMPILE_OPTION}"
CMAKE_C11_EXTENSION_COMPILE_OPTION == "${CMAKE_C11_EXTENSION_COMPILE_OPTION}"
CMAKE_C_COMPILE_FEATURES == "${CMAKE_C_COMPILE_FEATURES}"
CMAKE_C90_COMPILE_FEATURES == "${CMAKE_C90_COMPILE_FEATURES}"
CMAKE_C99_COMPILE_FEATURES == "${CMAKE_C99_COMPILE_FEATURES}"
CMAKE_C11_COMPILE_FEATURES == "${CMAKE_C11_COMPILE_FEATURES}"
CMAKE_CXX_COMPILER_ID == "${CMAKE_CXX_COMPILER_ID}" CMAKE_CXX_COMPILER_ID == "${CMAKE_CXX_COMPILER_ID}"
CMAKE_CXX_COMPILER_VERSION == "${CMAKE_CXX_COMPILER_VERSION}" CMAKE_CXX_COMPILER_VERSION == "${CMAKE_CXX_COMPILER_VERSION}"
CMAKE_CXX98_STANDARD_COMPILE_OPTION == "${CMAKE_CXX98_STANDARD_COMPILE_OPTION}" CMAKE_CXX98_STANDARD_COMPILE_OPTION == "${CMAKE_CXX98_STANDARD_COMPILE_OPTION}"