From ad16ae5c70778e6bfd6d30b17a3ad9b248b2a569 Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 17 Nov 2022 10:24:29 -0500 Subject: VS: Recognize -std: flag in CMAKE_C_FLAGS in target with C++ sources Extend commit b325484928 (VS: Fix C language standard in target with C++ sources, 2020-09-28, v3.19.0-rc1~74^2) to account for users placing a `-std:` flag in `CMAKE_C_FLAGS`. --- Source/cmVisualStudio10TargetGenerator.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 7e43bee..cd73551 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -3293,6 +3293,8 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( this->GeneratorTarget->GetLanguages(languages, configName); if (languages.count("C")) { std::string flagsC; + this->LocalGenerator->AddLanguageFlags( + flagsC, this->GeneratorTarget, cmBuildStep::Compile, "C", configName); this->LocalGenerator->AddCompileOptions(flagsC, this->GeneratorTarget, "C", configName); Options optC(this->LocalGenerator, Options::Compiler, -- cgit v0.12 From 914571a04249ee561a30309197d8e1c1cacbe18b Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 17 Nov 2022 10:15:55 -0500 Subject: Place language standard flags just after CMAKE__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__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 --- Help/manual/cmake-compile-features.7.rst | 22 ++++++++++++++++++++++ Help/release/dev/lang-std-flag-order.rst | 7 +++++++ Source/cmLocalGenerator.cxx | 27 ++++++++++++--------------- Tests/CompileFeatures/CMakeLists.txt | 13 +++++++++++++ Tests/CompileFeatures/msvc_permissive.cxx | 9 +++++++++ 5 files changed, 63 insertions(+), 15 deletions(-) create mode 100644 Help/release/dev/lang-std-flag-order.rst create mode 100644 Tests/CompileFeatures/msvc_permissive.cxx diff --git a/Help/manual/cmake-compile-features.7.rst b/Help/manual/cmake-compile-features.7.rst index 8073511..1e87ec6 100644 --- a/Help/manual/cmake-compile-features.7.rst +++ b/Help/manual/cmake-compile-features.7.rst @@ -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__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__FLAGS` +and :variable:`CMAKE__FLAGS_`. + +.. 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. diff --git a/Help/release/dev/lang-std-flag-order.rst b/Help/release/dev/lang-std-flag-order.rst new file mode 100644 index 0000000..4ef4123 --- /dev/null +++ b/Help/release/dev/lang-std-flag-order.rst @@ -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__STANDARD`, + are now placed before flags added by :command:`target_compile_options`, + rather than after them. diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 9745142..550141f 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -1021,12 +1021,6 @@ void cmLocalGenerator::AddCompileOptions(std::vector>& 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)) { diff --git a/Tests/CompileFeatures/CMakeLists.txt b/Tests/CompileFeatures/CMakeLists.txt index f3d3a73..17f4408 100644 --- a/Tests/CompileFeatures/CMakeLists.txt +++ b/Tests/CompileFeatures/CMakeLists.txt @@ -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() diff --git a/Tests/CompileFeatures/msvc_permissive.cxx b/Tests/CompileFeatures/msvc_permissive.cxx new file mode 100644 index 0000000..a8f2ff3 --- /dev/null +++ b/Tests/CompileFeatures/msvc_permissive.cxx @@ -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]; -- cgit v0.12 From a1c20b08b4b1b913a205607dad7240d5c63f4a62 Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 17 Nov 2022 10:39:18 -0500 Subject: cmLocalGenerator: Inline AddCompilerRequirementFlag in only call site The call site in `AddLanguageFlags` is now the "one true place" for adding language standard flags. Inline the helper to reduce risk of adding other call sites later. --- Source/cmLocalGenerator.cxx | 33 +++++++++++++-------------------- Source/cmLocalGenerator.h | 4 ---- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 550141f..4d5371f 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -1935,7 +1935,19 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags, // 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); + cmStandardLevelResolver standardResolver(this->Makefile); + std::string const& optionFlagDef = + standardResolver.GetCompileOptionDef(target, lang, config); + if (!optionFlagDef.empty()) { + cmValue opt = + target->Target->GetMakefile()->GetDefinition(optionFlagDef); + if (opt) { + std::vector optVec = cmExpandedList(*opt); + for (std::string const& i : optVec) { + this->AppendFlagEscape(flags, i); + } + } + } } std::string compiler = this->Makefile->GetSafeDefinition( @@ -2221,25 +2233,6 @@ void cmLocalGenerator::AddSharedFlags(std::string& flags, } } -void cmLocalGenerator::AddCompilerRequirementFlag( - std::string& flags, cmGeneratorTarget const* target, const std::string& lang, - const std::string& config) -{ - cmStandardLevelResolver standardResolver(this->Makefile); - - std::string const& optionFlagDef = - standardResolver.GetCompileOptionDef(target, lang, config); - if (!optionFlagDef.empty()) { - cmValue opt = target->Target->GetMakefile()->GetDefinition(optionFlagDef); - if (opt) { - std::vector optVec = cmExpandedList(*opt); - for (std::string const& i : optVec) { - this->AppendFlagEscape(flags, i); - } - } - } -} - static void AddVisibilityCompileOption(std::string& flags, cmGeneratorTarget const* target, cmLocalGenerator* lg, diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index 765441c..20f23de 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -164,10 +164,6 @@ public: const std::string& lang); void AddConfigVariableFlags(std::string& flags, const std::string& var, const std::string& config); - void AddCompilerRequirementFlag(std::string& flags, - cmGeneratorTarget const* target, - const std::string& lang, - const std::string& config); void AddColorDiagnosticsFlags(std::string& flags, const std::string& lang); //! Append flags to a string. virtual void AppendFlags(std::string& flags, -- cgit v0.12