Place language standard flags just after CMAKE_<LANG>_FLAGS

Previously we added the language standard flag near the end of all
options, even after those added by `add_compile_options` and friends.
However, on some compilers such as MSVC, the `-std` flag may reset
defaults for flags that precede it on the command line.  Move the
language standard flag to before all other flags that CMake adds for
other abstractions, and before those added by `add_compile_options`.

`CMAKE_<LANG>_FLAGS` should still precede the language flags though,
because they are meant to be treated as language-wide modifications to
the compiler defaults, similar to `$CC $CFLAGS`.

Fixes: #23860
Fixes: #24170
This commit is contained in:
Brad King
2022-11-17 10:15:55 -05:00
parent ad16ae5c70
commit 914571a042
5 changed files with 63 additions and 15 deletions

View File

@@ -282,3 +282,25 @@ versions specified for each:
* ``Clang``: Clang compiler 5.0+.
* ``NVIDIA``: NVIDIA nvcc compiler 7.5+.
.. _`Language Standard Flags`:
Language Standard Flags
=======================
In order to satisfy requirements specified by the
:command:`target_compile_features` command or the
:variable:`CMAKE_<LANG>_STANDARD` variable, CMake may pass a
language standard flag to the compiler, such as ``-std=c++11``.
For :ref:`Visual Studio Generators`, CMake cannot precisely control
the placement of the language standard flag on the compiler command line.
For :ref:`Ninja Generators`, :ref:`Makefile Generators`, and
:generator:`Xcode`, CMake places the language standard flag just after
the language-wide flags from :variable:`CMAKE_<LANG>_FLAGS`
and :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>`.
.. versionchanged:: 3.26
The language standard flag is placed before flags specified by other
abstractions such as the :command:`target_compile_options` command.
Prior to CMake 3.26, the language standard flag was placed after them.

View File

@@ -0,0 +1,7 @@
lang-std-flag-order
-------------------
* :ref:`Language Standard Flags`, such as ``-std=c++11``, when generated due
to :command:`target_compile_features` or :variable:`CMAKE_<LANG>_STANDARD`,
are now placed before flags added by :command:`target_compile_options`,
rather than after them.

View File

@@ -1021,12 +1021,6 @@ void cmLocalGenerator::AddCompileOptions(std::vector<BT<std::string>>& flags,
}
}
std::string compReqFlag;
this->AddCompilerRequirementFlag(compReqFlag, target, lang, config);
if (!compReqFlag.empty()) {
flags.emplace_back(std::move(compReqFlag));
}
// Add Warning as errors flags
if (!this->GetCMakeInstance()->GetIgnoreWarningAsError()) {
const cmValue wError = target->GetProperty("COMPILE_WARNING_AS_ERROR");
@@ -1932,6 +1926,18 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags,
this->AddConfigVariableFlags(flags, cmStrCat("CMAKE_", lang, "_FLAGS"),
config);
// Add the language standard flag for compiling, and sometimes linking.
if (compileOrLink == cmBuildStep::Compile ||
(compileOrLink == cmBuildStep::Link &&
// Some toolchains require use of the language standard flag
// when linking in order to use the matching standard library.
// FIXME: If CMake gains an abstraction for standard library
// selection, this will have to be reconciled with it.
this->Makefile->IsOn(
cmStrCat("CMAKE_", lang, "_LINK_WITH_STANDARD_COMPILE_OPTION")))) {
this->AddCompilerRequirementFlag(flags, target, lang, config);
}
std::string compiler = this->Makefile->GetSafeDefinition(
cmStrCat("CMAKE_", lang, "_COMPILER_ID"));
@@ -2076,15 +2082,6 @@ void cmLocalGenerator::AddLanguageFlagsForLinking(
std::string& flags, cmGeneratorTarget const* target, const std::string& lang,
const std::string& config)
{
if (this->Makefile->IsOn("CMAKE_" + lang +
"_LINK_WITH_STANDARD_COMPILE_OPTION")) {
// This toolchain requires use of the language standard flag
// when linking in order to use the matching standard library.
// FIXME: If CMake gains an abstraction for standard library
// selection, this will have to be reconciled with it.
this->AddCompilerRequirementFlag(flags, target, lang, config);
}
this->AddLanguageFlags(flags, target, cmBuildStep::Link, lang, config);
if (target->IsIPOEnabled(lang, config)) {

View File

@@ -374,3 +374,16 @@ else()
target_link_libraries(CompileFeaturesGenex3 PRIVATE std_11_iface)
target_compile_definitions(CompileFeaturesGenex3 PRIVATE ${genex_test_defs} ALLOW_LATER_STANDARDS=1)
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC"
AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.30
# The MSVC 14.29.30133 toolset supports C++20,
# but MSBuild puts the flags in the wrong order.
OR (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.29.30129 AND NOT CMAKE_GENERATOR MATCHES "Visual Studio")
)
)
add_library(msvc_permissive msvc_permissive.cxx)
target_compile_features(msvc_permissive PRIVATE cxx_std_20)
# The `-std:c++20` flag implies `-permissive-`. Test passing `-permissive` afterward.
target_compile_options(msvc_permissive PRIVATE -permissive)
endif()

View File

@@ -0,0 +1,9 @@
#if !defined(_MSVC_LANG) || _MSVC_LANG < 202002L
# error "This source must be compiled with MSVC as C++20 or later."
#endif
// Test a construct that is allowed by MSVC only with 'cl -permissive'.
enum class X
{
Y = 1
};
int array[X::Y];