try_compile: Add policy CMP0067 to honor language standards

Projects use `try_compile` to check if they will be able to compile some
particular source code.  When a language standard variable like
`CMAKE_CXX_STANDARD` is set, then the project intends to compile source
code using a compiler mode for that standard.  Therefore it makes sense
for `try_compile` to use that standard in the test project too.

Unfortunately this was not done when support for the
`CMAKE_CXX_STANDARD` variable was first implemented.  Add a policy to
introduce the improved behavior in a compatible way.

Closes: #16456
This commit is contained in:
Brad King
2016-12-01 15:46:34 -05:00
parent 45aa03b97a
commit f72ba42b7c
13 changed files with 239 additions and 1 deletions

View File

@@ -135,3 +135,17 @@ the type of target used for the source file signature.
Set the :variable:`CMAKE_TRY_COMPILE_PLATFORM_VARIABLES` variable to specify
variables that must be propagated into the test project. This variable is
meant for use only in toolchain files.
If :policy:`CMP0067` is set to ``NEW``, or any of the ``<LANG>_STANDARD``,
``<LANG>_STANDARD_REQUIRED``, or ``<LANG>_EXTENSIONS`` options are used,
then the language standard variables are honored:
* :variable:`CMAKE_C_STANDARD`
* :variable:`CMAKE_C_STANDARD_REQUIRED`
* :variable:`CMAKE_C_EXTENSIONS`
* :variable:`CMAKE_CXX_STANDARD`
* :variable:`CMAKE_CXX_STANDARD_REQUIRED`
* :variable:`CMAKE_CXX_EXTENSIONS`
Their values are used to set the corresponding target properties in
the generated project (unless overridden by an explicit option).

View File

@@ -51,6 +51,14 @@ The :variable:`CMAKE_MINIMUM_REQUIRED_VERSION` variable may also be used
to determine whether to report an error on use of deprecated macros or
functions.
Policies Introduced by CMake 3.8
================================
.. toctree::
:maxdepth: 1
CMP0067: Honor language standard in try_compile() source-file signature. </policy/CMP0067>
Policies Introduced by CMake 3.7
================================

34
Help/policy/CMP0067.rst Normal file
View File

@@ -0,0 +1,34 @@
CMP0067
-------
Honor language standard in :command:`try_compile` source-file signature.
The :command:`try_compile` source file signature is intended to allow
callers to check whether they will be able to compile a given source file
with the current toolchain. In order to match compiler behavior, any
language standard mode should match. However, CMake 3.7 and below did not
do this. CMake 3.8 and above prefer to honor the language standard settings
for ``C`` and ``CXX`` (C++) using the values of the variables:
* :variable:`CMAKE_C_STANDARD`
* :variable:`CMAKE_C_STANDARD_REQUIRED`
* :variable:`CMAKE_C_EXTENSIONS`
* :variable:`CMAKE_CXX_STANDARD`
* :variable:`CMAKE_CXX_STANDARD_REQUIRED`
* :variable:`CMAKE_CXX_EXTENSIONS`
This policy provides compatibility for projects that do not expect
the language standard settings to be used automatically.
The ``OLD`` behavior of this policy is to ignore language standard
setting variables when generating the ``try_compile`` test project.
The ``NEW`` behavior of this policy is to honor language standard
setting variables.
This policy was introduced in CMake version 3.8. Unlike most policies,
CMake version |release| does *not* warn by default when this policy
is not set and simply uses OLD behavior. See documentation of the
:variable:`CMAKE_POLICY_WARNING_CMP0067 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
variable to control the warning.
.. include:: DEPRECATED.txt

View File

@@ -3,3 +3,7 @@ try_compile-lang-std
* The :command:`try_compile` command source file signature gained new options
to specify the language standard to use in the generated test project.
* The :command:`try_compile` command source file signature now honors
language standard variables like :variable:`CMAKE_CXX_STANDARD`.
See policy :policy:`CMP0067`.

View File

@@ -17,6 +17,8 @@ warn by default:
policy :policy:`CMP0065`.
* ``CMAKE_POLICY_WARNING_CMP0066`` controls the warning for
policy :policy:`CMP0066`.
* ``CMAKE_POLICY_WARNING_CMP0067`` controls the warning for
policy :policy:`CMP0067`.
This variable should not be set by a project in CMake code. Project
developers running CMake may set this variable in their cache to

View File

@@ -54,6 +54,17 @@ static void writeProperty(FILE* fout, std::string const& targetName,
cmOutputConverter::EscapeForCMake(value).c_str());
}
std::string cmCoreTryCompile::LookupStdVar(std::string const& var,
bool warnCMP0067)
{
std::string value = this->Makefile->GetSafeDefinition(var);
if (warnCMP0067 && !value.empty()) {
value.clear();
this->WarnCMP0067.push_back(var);
}
return value;
}
int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
bool isTryRun)
{
@@ -620,6 +631,74 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
bool const testC = testLangs.find("C") != testLangs.end();
bool const testCxx = testLangs.find("CXX") != testLangs.end();
bool warnCMP0067 = false;
bool honorStandard = true;
if (!didCStandard && !didCxxStandard && !didCStandardRequired &&
!didCxxStandardRequired && !didCExtensions && !didCxxExtensions) {
switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0067)) {
case cmPolicies::WARN:
warnCMP0067 = this->Makefile->PolicyOptionalWarningEnabled(
"CMAKE_POLICY_WARNING_CMP0067");
case cmPolicies::OLD:
// OLD behavior is to not honor the language standard variables.
honorStandard = false;
break;
case cmPolicies::REQUIRED_IF_USED:
case cmPolicies::REQUIRED_ALWAYS:
this->Makefile->IssueMessage(
cmake::FATAL_ERROR,
cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0067));
case cmPolicies::NEW:
// NEW behavior is to honor the language standard variables.
// We already initialized honorStandard to true.
break;
}
}
if (honorStandard || warnCMP0067) {
if (testC) {
if (!didCStandard) {
cStandard = this->LookupStdVar("CMAKE_C_STANDARD", warnCMP0067);
}
if (!didCStandardRequired) {
cStandardRequired =
this->LookupStdVar("CMAKE_C_STANDARD_REQUIRED", warnCMP0067);
}
if (!didCExtensions) {
cExtensions = this->LookupStdVar("CMAKE_C_EXTENSIONS", warnCMP0067);
}
}
if (testCxx) {
if (!didCxxStandard) {
cxxStandard = this->LookupStdVar("CMAKE_CXX_STANDARD", warnCMP0067);
}
if (!didCxxStandardRequired) {
cxxStandardRequired =
this->LookupStdVar("CMAKE_CXX_STANDARD_REQUIRED", warnCMP0067);
}
if (!didCxxExtensions) {
cxxExtensions =
this->LookupStdVar("CMAKE_CXX_EXTENSIONS", warnCMP0067);
}
}
}
if (!this->WarnCMP0067.empty()) {
std::ostringstream w;
/* clang-format off */
w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0067) << "\n"
"For compatibility with older versions of CMake, try_compile "
"is not honoring language standard variables in the test project:\n"
;
/* clang-format on */
for (std::vector<std::string>::iterator vi = this->WarnCMP0067.begin();
vi != this->WarnCMP0067.end(); ++vi) {
w << " " << *vi << "\n";
}
this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, w.str());
}
if (testC) {
if (!cStandard.empty()) {
writeProperty(fout, targetName, "C_STANDARD", cStandard);

View File

@@ -47,6 +47,10 @@ protected:
std::string OutputFile;
std::string FindErrorMessage;
bool SrcFileSignature;
private:
std::vector<std::string> WarnCMP0067;
std::string LookupStdVar(std::string const& var, bool warnCMP0067);
};
#endif

View File

@@ -197,7 +197,10 @@ class cmMakefile;
3, 4, 0, cmPolicies::WARN) \
SELECT(POLICY, CMP0066, \
"Honor per-config flags in try_compile() source-file signature.", 3, \
7, 0, cmPolicies::WARN)
7, 0, cmPolicies::WARN) \
SELECT(POLICY, CMP0067, \
"Honor language standard in try_compile() source-file signature.", \
3, 8, 0, cmPolicies::WARN)
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
#define CM_FOR_EACH_POLICY_ID(POLICY) \

View File

@@ -0,0 +1,25 @@
before try_compile with CMP0067 WARN-enabled but no variables
after try_compile with CMP0067 WARN-enabled but no variables
before try_compile with CMP0067 WARN-default
after try_compile with CMP0067 WARN-default
before try_compile with CMP0067 WARN-enabled
CMake Warning \(dev\) at CMP0067.cmake:[0-9]+ \(try_compile\):
Policy CMP0067 is not set: Honor language standard in try_compile\(\)
source-file signature. Run "cmake --help-policy CMP0067" for policy
details. Use the cmake_policy command to set the policy and suppress this
warning.
For compatibility with older versions of CMake, try_compile is not honoring
language standard variables in the test project:
CMAKE_C_STANDARD
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
This warning is for project developers. Use -Wno-dev to suppress it.
after try_compile with CMP0067 WARN-enabled
before try_compile with CMP0067 OLD
after try_compile with CMP0067 OLD
before try_compile with CMP0067 NEW
after try_compile with CMP0067 NEW

View File

@@ -0,0 +1,40 @@
enable_language(C)
set(CMAKE_POLICY_WARNING_CMP0067 ON)
message("before try_compile with CMP0067 WARN-enabled but no variables")
try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/src.c
)
message("after try_compile with CMP0067 WARN-enabled but no variables")
set(CMAKE_POLICY_WARNING_CMP0067 OFF)
#-----------------------------------------------------------------------------
set(CMAKE_C_STANDARD 90)
message("before try_compile with CMP0067 WARN-default")
try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/src.c
)
message("after try_compile with CMP0067 WARN-default")
set(CMAKE_POLICY_WARNING_CMP0067 ON)
message("before try_compile with CMP0067 WARN-enabled")
try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/src.c
)
message("after try_compile with CMP0067 WARN-enabled")
cmake_policy(SET CMP0067 OLD)
message("before try_compile with CMP0067 OLD")
try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/src.c
)
message("after try_compile with CMP0067 OLD")
cmake_policy(SET CMP0066 NEW)
message("before try_compile with CMP0067 NEW")
try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/src.c
)
message("after try_compile with CMP0067 NEW")

View File

@@ -9,3 +9,15 @@ try_compile(result ${CMAKE_CURRENT_BINARY_DIR}
if(NOT result)
message(FATAL_ERROR "try_compile failed:\n${out}")
endif()
cmake_policy(SET CMP0067 NEW)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED 1)
set(CMAKE_C_EXTENSIONS 0)
try_compile(result ${CMAKE_CURRENT_BINARY_DIR}
SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/CStandardGNU.c
OUTPUT_VARIABLE out
)
if(NOT result)
message(FATAL_ERROR "try_compile failed:\n${out}")
endif()

View File

@@ -9,3 +9,15 @@ try_compile(result ${CMAKE_CURRENT_BINARY_DIR}
if(NOT result)
message(FATAL_ERROR "try_compile failed:\n${out}")
endif()
cmake_policy(SET CMP0067 NEW)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED 1)
set(CMAKE_CXX_EXTENSIONS 0)
try_compile(result ${CMAKE_CURRENT_BINARY_DIR}
SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/CxxStandardGNU.cxx
OUTPUT_VARIABLE out
)
if(NOT result)
message(FATAL_ERROR "try_compile failed:\n${out}")
endif()

View File

@@ -44,6 +44,7 @@ endif()
run_cmake(CMP0056)
run_cmake(CMP0066)
run_cmake(CMP0067)
if(RunCMake_GENERATOR MATCHES "Make|Ninja")
# Use a single build tree for a few tests without cleaning.