From b0a6161190f8a85a5771fc96d5d745b0be2b07e5 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 24 Apr 2020 11:52:41 +0100 Subject: [PATCH 1/4] Fortran: Add Fortran_PREPROCESS property Issue: #18870 --- Auxiliary/vim/syntax/cmake.vim | 2 + Help/manual/cmake-properties.7.rst | 2 + Help/manual/cmake-variables.7.rst | 1 + Help/prop_sf/Fortran_FORMAT.rst | 10 +++-- Help/prop_sf/Fortran_PREPROCESS.rst | 12 +++++ Help/prop_tgt/Fortran_PREPROCESS.rst | 18 ++++++++ .../dev/fortran-preprocess-property.rst | 6 +++ Help/variable/CMAKE_Fortran_PREPROCESS.rst | 8 ++++ Modules/Compiler/Absoft-Fortran.cmake | 2 + Modules/Compiler/Cray-Fortran.cmake | 8 ++++ Modules/Compiler/Flang-Fortran.cmake | 3 ++ Modules/Compiler/G95-Fortran.cmake | 2 + Modules/Compiler/GNU-Fortran.cmake | 5 +++ Modules/Compiler/HP-Fortran.cmake | 3 ++ Modules/Compiler/Intel-Fortran.cmake | 2 + Modules/Compiler/NAG-Fortran.cmake | 1 + Modules/Compiler/PGI-Fortran.cmake | 1 + Modules/Compiler/PathScale-Fortran.cmake | 3 ++ Modules/Compiler/SunPro-Fortran.cmake | 2 + Modules/Compiler/XL-Fortran.cmake | 5 +++ Source/cmCommonTargetGenerator.cxx | 28 ++++++++++++ Source/cmCommonTargetGenerator.h | 3 ++ Source/cmLocalVisualStudio7Generator.cxx | 27 +++++++++++ Source/cmMakefileTargetGenerator.cxx | 1 + Source/cmNinjaTargetGenerator.cxx | 1 + Source/cmOutputConverter.cxx | 11 +++++ Source/cmOutputConverter.h | 8 ++++ Source/cmTarget.cxx | 1 + Tests/FortranOnly/CMakeLists.txt | 45 +++++++++++++++++++ .../FortranOnly/no_preprocess_source_fpp.fpp | 3 ++ .../FortranOnly/no_preprocess_source_lower.f | 3 ++ .../FortranOnly/no_preprocess_source_upper.F | 3 ++ .../FortranOnly/no_preprocess_target_fpp.fpp | 3 ++ .../FortranOnly/no_preprocess_target_lower.f | 3 ++ .../FortranOnly/no_preprocess_target_upper.F | 3 ++ Tests/FortranOnly/preprocess2.f | 4 ++ Tests/FortranOnly/preprocess3.f | 4 ++ 37 files changed, 243 insertions(+), 4 deletions(-) create mode 100644 Help/prop_sf/Fortran_PREPROCESS.rst create mode 100644 Help/prop_tgt/Fortran_PREPROCESS.rst create mode 100644 Help/release/dev/fortran-preprocess-property.rst create mode 100644 Help/variable/CMAKE_Fortran_PREPROCESS.rst create mode 100644 Tests/FortranOnly/no_preprocess_source_fpp.fpp create mode 100644 Tests/FortranOnly/no_preprocess_source_lower.f create mode 100644 Tests/FortranOnly/no_preprocess_source_upper.F create mode 100644 Tests/FortranOnly/no_preprocess_target_fpp.fpp create mode 100644 Tests/FortranOnly/no_preprocess_target_lower.f create mode 100644 Tests/FortranOnly/no_preprocess_target_upper.F create mode 100644 Tests/FortranOnly/preprocess2.f create mode 100644 Tests/FortranOnly/preprocess3.f diff --git a/Auxiliary/vim/syntax/cmake.vim b/Auxiliary/vim/syntax/cmake.vim index 0676f7ed29..2d63eb0d54 100644 --- a/Auxiliary/vim/syntax/cmake.vim +++ b/Auxiliary/vim/syntax/cmake.vim @@ -170,6 +170,7 @@ syn keyword cmakeProperty contained \ FRAMEWORK_VERSION \ Fortran_FORMAT \ Fortran_MODULE_DIRECTORY + \ Fortran_PREPROCESS \ GENERATED \ GENERATOR_FILE_NAME \ GENERATOR_IS_MULTI_CONFIG @@ -1019,6 +1020,7 @@ syn keyword cmakeVariable contained \ CMAKE_Fortran_MODULE_DIRECTORY \ CMAKE_Fortran_OUTPUT_EXTENSION \ CMAKE_Fortran_PLATFORM_ID + \ CMAKE_Fortran_PREPROCESS \ CMAKE_Fortran_SIMULATE_ID \ CMAKE_Fortran_SIMULATE_VERSION \ CMAKE_Fortran_SIZEOF_DATA_PTR diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 9031e9c69d..43bd08f85f 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -201,6 +201,7 @@ Properties on Targets /prop_tgt/FOLDER /prop_tgt/Fortran_FORMAT /prop_tgt/Fortran_MODULE_DIRECTORY + /prop_tgt/Fortran_PREPROCESS /prop_tgt/FRAMEWORK /prop_tgt/FRAMEWORK_MULTI_CONFIG_POSTFIX_CONFIG /prop_tgt/FRAMEWORK_VERSION @@ -465,6 +466,7 @@ Properties on Source Files /prop_sf/COMPILE_OPTIONS /prop_sf/EXTERNAL_OBJECT /prop_sf/Fortran_FORMAT + /prop_sf/Fortran_PREPROCESS /prop_sf/GENERATED /prop_sf/HEADER_FILE_ONLY /prop_sf/INCLUDE_DIRECTORIES diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 7d802e1b5c..4ce836510f 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -391,6 +391,7 @@ Variables that Control the Build /variable/CMAKE_FRAMEWORK_MULTI_CONFIG_POSTFIX_CONFIG /variable/CMAKE_Fortran_FORMAT /variable/CMAKE_Fortran_MODULE_DIRECTORY + /variable/CMAKE_Fortran_PREPROCESS /variable/CMAKE_GHS_NO_SOURCE_GROUP_FILE /variable/CMAKE_GLOBAL_AUTOGEN_TARGET /variable/CMAKE_GLOBAL_AUTOGEN_TARGET_NAME diff --git a/Help/prop_sf/Fortran_FORMAT.rst b/Help/prop_sf/Fortran_FORMAT.rst index 1cbbf4809e..ef33926190 100644 --- a/Help/prop_sf/Fortran_FORMAT.rst +++ b/Help/prop_sf/Fortran_FORMAT.rst @@ -4,7 +4,9 @@ Fortran_FORMAT Set to ``FIXED`` or ``FREE`` to indicate the Fortran source layout. This property tells CMake whether a given Fortran source file uses -fixed-format or free-format. CMake will pass the corresponding format -flag to the compiler. Consider using the target-wide -:prop_tgt:`Fortran_FORMAT` property if all source files in a target -share the same format. +fixed-format or free-format. CMake will pass the corresponding format flag +to the compiler. Consider using the target-wide :prop_tgt:`Fortran_FORMAT` +property if all source files in a target share the same format. + +.. note:: For some compilers, ``NAG``, ``PGI`` and ``Solaris Studio``, + setting this to ``OFF`` will have no effect. diff --git a/Help/prop_sf/Fortran_PREPROCESS.rst b/Help/prop_sf/Fortran_PREPROCESS.rst new file mode 100644 index 0000000000..92542d9a0c --- /dev/null +++ b/Help/prop_sf/Fortran_PREPROCESS.rst @@ -0,0 +1,12 @@ +Fortran_PREPROCESS +------------------ + +Control whether the Fortran source file should be unconditionally preprocessed. + +If unset or empty, rely on the compiler to determine whether the file +should be preprocessed. If explicitly set to ``OFF`` then the file +does not need to be preprocessed. If explicitly set to ``ON``, then +the file does need to be preprocessed as part of the compilation step. + +Consider using the target-wide :prop_tgt:`Fortran_PREPROCESS` property +if all source files in a target need to be preprocessed. diff --git a/Help/prop_tgt/Fortran_PREPROCESS.rst b/Help/prop_tgt/Fortran_PREPROCESS.rst new file mode 100644 index 0000000000..069d04ab51 --- /dev/null +++ b/Help/prop_tgt/Fortran_PREPROCESS.rst @@ -0,0 +1,18 @@ +Fortran_PREPROCESS +------------------ + +Control whether the Fortran source file should be unconditionally +preprocessed. + +If unset or empty, rely on the compiler to determine whether the file +should be preprocessed. If explicitly set to ``OFF`` then the file does not +need to be preprocessed. If explicitly set to ``ON``, then the file does +need to be preprocessed as part of the compilation step. + +Use the source-specific :prop_sf:`Fortran_PREPROCESS` property if a single +file needs to be preprocessed. If the variable +:variable:`CMAKE_Fortran_PREPROCESS` is set when a target is created its +value is used to initialize this property. + +.. note:: For some compilers, ``NAG``, ``PGI`` and ``Solaris Studio``, + setting this to ``OFF`` will have no effect. diff --git a/Help/release/dev/fortran-preprocess-property.rst b/Help/release/dev/fortran-preprocess-property.rst new file mode 100644 index 0000000000..d18e7b88c9 --- /dev/null +++ b/Help/release/dev/fortran-preprocess-property.rst @@ -0,0 +1,6 @@ +fortran-preprocess-property +--------------------------- + +* The :prop_tgt:`Fortran_PREPROCESS` target property and + :prop_sf:`Fortran_PREPROCESS` source-file property were added to + control preprocessing of Fortran source files. diff --git a/Help/variable/CMAKE_Fortran_PREPROCESS.rst b/Help/variable/CMAKE_Fortran_PREPROCESS.rst new file mode 100644 index 0000000000..74b2d8be0d --- /dev/null +++ b/Help/variable/CMAKE_Fortran_PREPROCESS.rst @@ -0,0 +1,8 @@ +CMAKE_Fortran_PREPROCESS +------------------------ + +Default value for :prop_tgt:`Fortran_PREPROCESS` of targets. + +This variable is used to initialize the :prop_tgt:`Fortran_PREPROCESS` +property on all the targets. See that target property for additional +information. diff --git a/Modules/Compiler/Absoft-Fortran.cmake b/Modules/Compiler/Absoft-Fortran.cmake index 76502dca75..8724f854a3 100644 --- a/Modules/Compiler/Absoft-Fortran.cmake +++ b/Modules/Compiler/Absoft-Fortran.cmake @@ -9,3 +9,5 @@ set(CMAKE_Fortran_VERBOSE_FLAG "-v") set(CMAKE_Fortran_FORMAT_FIXED_FLAG "-ffixed") set(CMAKE_Fortran_FORMAT_FREE_FLAG "-ffree") set(CMAKE_Fortran_LINKER_WRAPPER_FLAG "-X") +set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-cpp") +set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF "-no-cpp") diff --git a/Modules/Compiler/Cray-Fortran.cmake b/Modules/Compiler/Cray-Fortran.cmake index ccb7c2ee5a..696ae76074 100644 --- a/Modules/Compiler/Cray-Fortran.cmake +++ b/Modules/Compiler/Cray-Fortran.cmake @@ -11,3 +11,11 @@ set(CMAKE_Fortran_MODDIR_FLAG -J) set(CMAKE_Fortran_MODDIR_DEFAULT .) set(CMAKE_Fortran_FORMAT_FIXED_FLAG "-f fixed") set(CMAKE_Fortran_FORMAT_FREE_FLAG "-f free") + +if (NOT CMAKE_Fortran_COMPILER_VERSION VERSION_LESS 8.5) + set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-eT") + set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF "-dT") +else() + set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-eZ") + set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF "-dZ") +endif() diff --git a/Modules/Compiler/Flang-Fortran.cmake b/Modules/Compiler/Flang-Fortran.cmake index f0e61d8248..de0484e551 100644 --- a/Modules/Compiler/Flang-Fortran.cmake +++ b/Modules/Compiler/Flang-Fortran.cmake @@ -11,3 +11,6 @@ set(CMAKE_Fortran_FORMAT_FIXED_FLAG "-ffixed-form") set(CMAKE_Fortran_FORMAT_FREE_FLAG "-ffree-form") set(CMAKE_Fortran_MODDIR_FLAG "-J") + +set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-cpp") +set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF "-nocpp") diff --git a/Modules/Compiler/G95-Fortran.cmake b/Modules/Compiler/G95-Fortran.cmake index 03b7e08b94..5dba04efb6 100644 --- a/Modules/Compiler/G95-Fortran.cmake +++ b/Modules/Compiler/G95-Fortran.cmake @@ -9,3 +9,5 @@ set(CMAKE_Fortran_FORMAT_FIXED_FLAG "-ffixed-form") set(CMAKE_Fortran_FORMAT_FREE_FLAG "-ffree-form") set(CMAKE_Fortran_LINKER_WRAPPER_FLAG "-Wl,") set(CMAKE_Fortran_LINKER_WRAPPER_FLAG_SEP ",") +set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-cpp") +set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF "-no-cpp") diff --git a/Modules/Compiler/GNU-Fortran.cmake b/Modules/Compiler/GNU-Fortran.cmake index 6413769cfc..5dfb03e2eb 100644 --- a/Modules/Compiler/GNU-Fortran.cmake +++ b/Modules/Compiler/GNU-Fortran.cmake @@ -10,6 +10,11 @@ set(CMAKE_Fortran_PREPROCESS_SOURCE set(CMAKE_Fortran_FORMAT_FIXED_FLAG "-ffixed-form") set(CMAKE_Fortran_FORMAT_FREE_FLAG "-ffree-form") +if (NOT CMAKE_Fortran_COMPILER_VERSION VERSION_LESS 4.4) + set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-cpp") + set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF "-nocpp") +endif() + set(CMAKE_Fortran_POSTPROCESS_FLAG "-fpreprocessed") # No -DNDEBUG for Fortran. diff --git a/Modules/Compiler/HP-Fortran.cmake b/Modules/Compiler/HP-Fortran.cmake index 63a033135f..d3e2a303cf 100644 --- a/Modules/Compiler/HP-Fortran.cmake +++ b/Modules/Compiler/HP-Fortran.cmake @@ -7,3 +7,6 @@ set(CMAKE_Fortran_CREATE_PREPROCESSED_SOURCE " set(CMAKE_Fortran_LINKER_WRAPPER_FLAG "-Wl,") set(CMAKE_Fortran_LINKER_WRAPPER_FLAG ",") + +set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "+cpp=yes") +set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF "+cpp=no") diff --git a/Modules/Compiler/Intel-Fortran.cmake b/Modules/Compiler/Intel-Fortran.cmake index 156b5339a2..71f25f43f9 100644 --- a/Modules/Compiler/Intel-Fortran.cmake +++ b/Modules/Compiler/Intel-Fortran.cmake @@ -15,3 +15,5 @@ set(CMAKE_Fortran_CREATE_ASSEMBLY_SOURCE " -fpp -E > ") +set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-fpp") +set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF "-nofpp") diff --git a/Modules/Compiler/NAG-Fortran.cmake b/Modules/Compiler/NAG-Fortran.cmake index 2111c65cad..ffce97ef7b 100644 --- a/Modules/Compiler/NAG-Fortran.cmake +++ b/Modules/Compiler/NAG-Fortran.cmake @@ -37,3 +37,4 @@ set(CMAKE_Fortran_FORMAT_FREE_FLAG "-free") set(CMAKE_Fortran_COMPILE_OPTIONS_PIC "-PIC") set(CMAKE_Fortran_COMPILE_OPTIONS_PIE "-PIC") set(CMAKE_Fortran_RESPONSE_FILE_LINK_FLAG "-Wl,@") +set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-fpp") diff --git a/Modules/Compiler/PGI-Fortran.cmake b/Modules/Compiler/PGI-Fortran.cmake index 3daf7987e2..ff875772cd 100644 --- a/Modules/Compiler/PGI-Fortran.cmake +++ b/Modules/Compiler/PGI-Fortran.cmake @@ -6,6 +6,7 @@ set(CMAKE_Fortran_SUBMODULE_EXT ".mod") set(CMAKE_Fortran_PREPROCESS_SOURCE " -Mpreprocess -E > ") +set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-Mpreprocess") set(CMAKE_Fortran_FORMAT_FIXED_FLAG "-Mnofreeform") set(CMAKE_Fortran_FORMAT_FREE_FLAG "-Mfreeform") diff --git a/Modules/Compiler/PathScale-Fortran.cmake b/Modules/Compiler/PathScale-Fortran.cmake index d903621046..891d93e3b5 100644 --- a/Modules/Compiler/PathScale-Fortran.cmake +++ b/Modules/Compiler/PathScale-Fortran.cmake @@ -4,3 +4,6 @@ __compiler_pathscale(Fortran) set(CMAKE_Fortran_MODDIR_FLAG "-module ") set(CMAKE_Fortran_FORMAT_FIXED_FLAG "-fixedform") set(CMAKE_Fortran_FORMAT_FREE_FLAG "-freeform") + +set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-cpp") +set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF "-nocpp") diff --git a/Modules/Compiler/SunPro-Fortran.cmake b/Modules/Compiler/SunPro-Fortran.cmake index 0c93c94a8a..0ba501574a 100644 --- a/Modules/Compiler/SunPro-Fortran.cmake +++ b/Modules/Compiler/SunPro-Fortran.cmake @@ -30,3 +30,5 @@ set(CMAKE_Fortran_PREPROCESS_SOURCE set(CMAKE_Fortran_CREATE_PREPROCESSED_SOURCE " -F -fpp -o ") set(CMAKE_Fortran_CREATE_ASSEMBLY_SOURCE " -S -o ") + +set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-fpp") diff --git a/Modules/Compiler/XL-Fortran.cmake b/Modules/Compiler/XL-Fortran.cmake index e01ec8e9c7..cc15e65e6a 100644 --- a/Modules/Compiler/XL-Fortran.cmake +++ b/Modules/Compiler/XL-Fortran.cmake @@ -23,3 +23,8 @@ set(CMAKE_Fortran_CREATE_ASSEMBLY_SOURCE) set(CMAKE_Fortran_PREPROCESS_SOURCE " -qpreprocess -qnoobject -qsuppress=1517-020 -tF -B \"${CMAKE_CURRENT_LIST_DIR}/XL-Fortran/\" -WF,--cpp,\"${CMAKE_Fortran_XL_CPP}\",--out, " ) + +if (NOT CMAKE_Fortran_COMPILER_VERSION VERSION_LESS 15.1.6) + set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-qpreprocess") + set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF "-qnopreprocess") +endif() diff --git a/Source/cmCommonTargetGenerator.cxx b/Source/cmCommonTargetGenerator.cxx index 5414409c28..32a33ee985 100644 --- a/Source/cmCommonTargetGenerator.cxx +++ b/Source/cmCommonTargetGenerator.cxx @@ -98,6 +98,34 @@ void cmCommonTargetGenerator::AppendFortranFormatFlags( } } +void cmCommonTargetGenerator::AppendFortranPreprocessFlags( + std::string& flags, cmSourceFile const& source) +{ + const std::string srcpp = source.GetSafeProperty("Fortran_PREPROCESS"); + cmOutputConverter::FortranPreprocess preprocess = + cmOutputConverter::GetFortranPreprocess(srcpp); + if (preprocess == cmOutputConverter::FortranPreprocess::Unset) { + std::string const& tgtpp = + this->GeneratorTarget->GetSafeProperty("Fortran_PREPROCESS"); + preprocess = cmOutputConverter::GetFortranPreprocess(tgtpp); + } + const char* var = nullptr; + switch (preprocess) { + case cmOutputConverter::FortranPreprocess::Needed: + var = "CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON"; + break; + case cmOutputConverter::FortranPreprocess::NotNeeded: + var = "CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF"; + break; + default: + break; + } + if (var) { + this->LocalCommonGenerator->AppendCompileOptions( + flags, this->Makefile->GetSafeDefinition(var)); + } +} + std::string cmCommonTargetGenerator::GetFlags(const std::string& l, const std::string& config, const std::string& arch) diff --git a/Source/cmCommonTargetGenerator.h b/Source/cmCommonTargetGenerator.h index 78cedf55e9..c3c3a3a88e 100644 --- a/Source/cmCommonTargetGenerator.h +++ b/Source/cmCommonTargetGenerator.h @@ -45,6 +45,9 @@ protected: void AppendFortranFormatFlags(std::string& flags, cmSourceFile const& source); + void AppendFortranPreprocessFlags(std::string& flags, + cmSourceFile const& source); + virtual void AddIncludeFlags(std::string& flags, std::string const& lang, const std::string& config) = 0; diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index e18fac3b0d..f7a0c5a01d 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -284,6 +284,7 @@ void cmLocalVisualStudio7Generator::WriteConfigurations( } cmVS7FlagTable cmLocalVisualStudio7GeneratorFortranFlagTable[] = { { "Preprocess", "fpp", "Run Preprocessor on files", "preprocessYes", 0 }, + { "Preprocess", "nofpp", "Run Preprocessor on files", "preprocessNo", 0 }, { "SuppressStartupBanner", "nologo", "SuppressStartupBanner", "true", 0 }, { "SourceFileFormat", "fixed", "Use Fixed Format", "fileFormatFixed", 0 }, { "SourceFileFormat", "free", "Use Free Format", "fileFormatFree", 0 }, @@ -682,6 +683,18 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( default: break; } + + switch (cmOutputConverter::GetFortranPreprocess( + target->GetSafeProperty("Fortran_PREPROCESS"))) { + case cmOutputConverter::FortranPreprocess::Needed: + flags += " -fpp"; + break; + case cmOutputConverter::FortranPreprocess::NotNeeded: + flags += " -nofpp"; + break; + default: + break; + } } // Get preprocessor definitions for this directory. @@ -1474,6 +1487,20 @@ cmLocalVisualStudio7GeneratorFCInfo::cmLocalVisualStudio7GeneratorFCInfo( } if (lg->FortranProject) { + switch (cmOutputConverter::GetFortranPreprocess( + sf.GetSafeProperty("Fortran_PREPROCESS"))) { + case cmOutputConverter::FortranPreprocess::Needed: + fc.CompileFlags = cmStrCat("-fpp ", fc.CompileFlags); + needfc = true; + break; + case cmOutputConverter::FortranPreprocess::NotNeeded: + fc.CompileFlags = cmStrCat("-nofpp ", fc.CompileFlags); + needfc = true; + break; + default: + break; + } + switch (cmOutputConverter::GetFortranFormat( sf.GetSafeProperty("Fortran_FORMAT"))) { case cmOutputConverter::FortranFormatFixed: diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index 5f0cfcf6a5..dcd121cf3c 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -541,6 +541,7 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( // Add Fortran format flags. if (lang == "Fortran") { this->AppendFortranFormatFlags(flags, source); + this->AppendFortranPreprocessFlags(flags, source); } // Add flags from source file properties. diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 06064844cc..b58434f34d 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -183,6 +183,7 @@ std::string cmNinjaTargetGenerator::ComputeFlagsForObject( // Add Fortran format flags. if (language == "Fortran") { this->AppendFortranFormatFlags(flags, *source); + this->AppendFortranPreprocessFlags(flags, *source); } // Add source file specific flags. diff --git a/Source/cmOutputConverter.cxx b/Source/cmOutputConverter.cxx index dc324ccb58..359e9f5a99 100644 --- a/Source/cmOutputConverter.cxx +++ b/Source/cmOutputConverter.cxx @@ -170,6 +170,17 @@ cmOutputConverter::FortranFormat cmOutputConverter::GetFortranFormat( return format; } +cmOutputConverter::FortranPreprocess cmOutputConverter::GetFortranPreprocess( + cm::string_view value) +{ + if (value.empty()) { + return FortranPreprocess::Unset; + } + + return cmIsOn(value) ? FortranPreprocess::Needed + : FortranPreprocess::NotNeeded; +} + void cmOutputConverter::SetLinkScriptShell(bool linkScriptShell) { this->LinkScriptShell = linkScriptShell; diff --git a/Source/cmOutputConverter.h b/Source/cmOutputConverter.h index 28582df04c..a8b4528421 100644 --- a/Source/cmOutputConverter.h +++ b/Source/cmOutputConverter.h @@ -95,6 +95,14 @@ public: }; static FortranFormat GetFortranFormat(cm::string_view value); + enum class FortranPreprocess + { + Unset, + NotNeeded, + Needed + }; + static FortranPreprocess GetFortranPreprocess(cm::string_view value); + private: cmState* GetState() const; diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index a776398857..16d7b6f26a 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -307,6 +307,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, initProp("Fortran_FORMAT"); initProp("Fortran_MODULE_DIRECTORY"); initProp("Fortran_COMPILER_LAUNCHER"); + initProp("Fortran_PREPROCESS"); initProp("GNUtoMS"); initProp("OSX_ARCHITECTURES"); initProp("IOS_INSTALL_COMBINED"); diff --git a/Tests/FortranOnly/CMakeLists.txt b/Tests/FortranOnly/CMakeLists.txt index 4327c2fcbe..59f139bc8e 100644 --- a/Tests/FortranOnly/CMakeLists.txt +++ b/Tests/FortranOnly/CMakeLists.txt @@ -116,3 +116,48 @@ if(CMAKE_Fortran_COMPILER_ID STREQUAL "Intel") set_property(TARGET IntelIfDef PROPERTY Fortran_FORMAT FIXED) target_compile_definitions(IntelIfDef PRIVATE SOME_DEF) endif() + +# Skip these tests if compiler/version doesn't have preprocessing flags +if((CMAKE_Fortran_COMPILER_ID STREQUAL "GNU" AND CMAKE_Fortran_COMPILER_VERSION VERSION_LESS 4.4) + OR (CMAKE_Fortran_COMPILER_ID STREQUAL "XL" AND CMAKE_Fortran_COMPILER_VERSION VERSION_LESS 15.1.6)) + set(test_pp_flags 0) +else() + set(test_pp_flags 1) +endif() + +if(test_pp_flags) + # Test that we can always preprocess a target + add_executable(preprocess_target preprocess2.f) + set_property(TARGET preprocess_target PROPERTY Fortran_PREPROCESS ON) + + # Test that we can preprocess a single source file + add_executable(preprocess_source preprocess3.f) + set_property(SOURCE preprocess3.f PROPERTY Fortran_PREPROCESS ON) +endif() + +if(NOT CMAKE_GENERATOR MATCHES "Ninja") + # Test that neither the compiler nor CMake performs unnecessary preprocessing. + add_library(no_preprocess_target_lower STATIC no_preprocess_target_lower.f) + target_compile_options(no_preprocess_target_lower PRIVATE -DINTEGER=nonsense) + set_property(TARGET no_preprocess_target_lower PROPERTY Fortran_PREPROCESS OFF) + add_library(no_preprocess_source_lower STATIC no_preprocess_source_lower.f) + target_compile_options(no_preprocess_source_lower PRIVATE -DINTEGER=nonsense) + set_property(SOURCE no_preprocess_source_lower.f PROPERTY Fortran_PREPROCESS OFF) + + # Test that we can explicitly not preprocess a target or source. + # This will not work on certain compilers due to either missing a + # "don't preprocess" flag, or due to choice of file extension. + if(test_pp_flags AND NOT CMAKE_Fortran_COMPILER_ID MATCHES "(Flang|NAG|PGI|SunPro|XL)") + add_library(no_preprocess_target STATIC no_preprocess_target_upper.F) + target_compile_options(no_preprocess_target PRIVATE -DINTEGER=nonsense) + add_library(no_preprocess_source STATIC no_preprocess_source_upper.F) + target_compile_options(no_preprocess_source PRIVATE -DINTEGER=nonsense) + if(NOT CMAKE_Fortran_COMPILER_ID STREQUAL "Cray" + AND NOT "${CMAKE_Fortran_COMPILER_ID};${CMAKE_Fortran_SIMULATE_ID}" STREQUAL "Intel;MSVC") + target_sources(no_preprocess_target PRIVATE no_preprocess_target_fpp.fpp) + target_sources(no_preprocess_source PRIVATE no_preprocess_source_fpp.fpp) + endif() + set_property(TARGET no_preprocess_target PROPERTY Fortran_PREPROCESS OFF) + set_property(SOURCE no_preprocess_source_upper.F no_preprocess_source_fpp.fpp PROPERTY Fortran_PREPROCESS OFF) + endif() +endif() diff --git a/Tests/FortranOnly/no_preprocess_source_fpp.fpp b/Tests/FortranOnly/no_preprocess_source_fpp.fpp new file mode 100644 index 0000000000..8e48902627 --- /dev/null +++ b/Tests/FortranOnly/no_preprocess_source_fpp.fpp @@ -0,0 +1,3 @@ + SUBROUTINE NOPREPROCESS_SOURCE_FPP + INTEGER F + END diff --git a/Tests/FortranOnly/no_preprocess_source_lower.f b/Tests/FortranOnly/no_preprocess_source_lower.f new file mode 100644 index 0000000000..3b08782bd0 --- /dev/null +++ b/Tests/FortranOnly/no_preprocess_source_lower.f @@ -0,0 +1,3 @@ + SUBROUTINE NOPREPROCESS_SOURCE_LOWER + INTEGER F + END diff --git a/Tests/FortranOnly/no_preprocess_source_upper.F b/Tests/FortranOnly/no_preprocess_source_upper.F new file mode 100644 index 0000000000..02485c9059 --- /dev/null +++ b/Tests/FortranOnly/no_preprocess_source_upper.F @@ -0,0 +1,3 @@ + SUBROUTINE NOPREPROCESS_SOURCE_UPPER + INTEGER F + END diff --git a/Tests/FortranOnly/no_preprocess_target_fpp.fpp b/Tests/FortranOnly/no_preprocess_target_fpp.fpp new file mode 100644 index 0000000000..f9e6e3b0ad --- /dev/null +++ b/Tests/FortranOnly/no_preprocess_target_fpp.fpp @@ -0,0 +1,3 @@ + SUBROUTINE NOPREPROCESS_TARGET_FPP + INTEGER F + END diff --git a/Tests/FortranOnly/no_preprocess_target_lower.f b/Tests/FortranOnly/no_preprocess_target_lower.f new file mode 100644 index 0000000000..ea23a706c1 --- /dev/null +++ b/Tests/FortranOnly/no_preprocess_target_lower.f @@ -0,0 +1,3 @@ + SUBROUTINE NOPREPROCESS_TARGET_LOWER + INTEGER F + END diff --git a/Tests/FortranOnly/no_preprocess_target_upper.F b/Tests/FortranOnly/no_preprocess_target_upper.F new file mode 100644 index 0000000000..34ee04d9cc --- /dev/null +++ b/Tests/FortranOnly/no_preprocess_target_upper.F @@ -0,0 +1,3 @@ + SUBROUTINE NOPREPROCESS_TARGET_UPPER + INTEGER F + END diff --git a/Tests/FortranOnly/preprocess2.f b/Tests/FortranOnly/preprocess2.f new file mode 100644 index 0000000000..6595d62cba --- /dev/null +++ b/Tests/FortranOnly/preprocess2.f @@ -0,0 +1,4 @@ +#define int INTEGER + PROGRAM PREPRO + int f + END diff --git a/Tests/FortranOnly/preprocess3.f b/Tests/FortranOnly/preprocess3.f new file mode 100644 index 0000000000..6595d62cba --- /dev/null +++ b/Tests/FortranOnly/preprocess3.f @@ -0,0 +1,4 @@ +#define int INTEGER + PROGRAM PREPRO + int f + END -- GitLab From 5cca1ec8934d84d9b10a7d891d31012d7aef8feb Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 8 May 2020 10:53:47 +0100 Subject: [PATCH 2/4] Ninja: Add helper functions to generate Fortran preprocess rule --- Source/cmNinjaTargetGenerator.cxx | 167 ++++++++++++++++++------------ 1 file changed, 98 insertions(+), 69 deletions(-) diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index b58434f34d..36fad08985 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -509,6 +509,91 @@ void cmNinjaTargetGenerator::WriteLanguageRules(const std::string& language, this->WriteCompileRule(language, config); } +namespace { +// Create the command to run the dependency scanner +std::string GetScanCommand(const std::string& cmakeCmd, const std::string& tdi, + const std::string& lang, const std::string& ppFile, + bool needDyndep, const std::string& ddiFile) +{ + std::string ccmd = + cmStrCat(cmakeCmd, " -E cmake_ninja_depends --tdi=", tdi, " --lang=", lang, + " --pp=", ppFile, " --dep=$DEP_FILE"); + if (needDyndep) { + ccmd = cmStrCat(ccmd, " --obj=$OBJ_FILE --ddi=", ddiFile); + } + return ccmd; +} + +// Helper function to create dependency scanning rule, with optional +// explicit preprocessing step if preprocessCommand is non-empty +cmNinjaRule GetPreprocessScanRule( + const std::string& ruleName, cmRulePlaceholderExpander::RuleVariables& vars, + const std::string& responseFlag, const std::string& flags, + const std::string& launcher, + cmRulePlaceholderExpander* const rulePlaceholderExpander, + std::string scanCommand, cmLocalNinjaGenerator* generator, + const std::string& preprocessCommand = "") +{ + cmNinjaRule rule(ruleName); + // Explicit preprocessing always uses a depfile. + rule.DepType = ""; // no deps= for multiple outputs + rule.DepFile = "$DEP_FILE"; + + cmRulePlaceholderExpander::RuleVariables ppVars; + ppVars.CMTargetName = vars.CMTargetName; + ppVars.CMTargetType = vars.CMTargetType; + ppVars.Language = vars.Language; + ppVars.Object = "$out"; // for RULE_LAUNCH_COMPILE + ppVars.PreprocessedSource = "$out"; + ppVars.DependencyFile = rule.DepFile.c_str(); + + // Preprocessing uses the original source, compilation uses + // preprocessed output or original source + ppVars.Source = vars.Source; + vars.Source = "$in"; + + // Copy preprocessor definitions to the preprocessor rule. + ppVars.Defines = vars.Defines; + + // Copy include directories to the preprocessor rule. The Fortran + // compilation rule still needs them for the INCLUDE directive. + ppVars.Includes = vars.Includes; + + // Preprocessing and compilation use the same flags. + std::string ppFlags = flags; + + // If using a response file, move defines, includes, and flags into it. + if (!responseFlag.empty()) { + rule.RspFile = "$RSP_FILE"; + rule.RspContent = + cmStrCat(' ', ppVars.Defines, ' ', ppVars.Includes, ' ', ppFlags); + ppFlags = cmStrCat(responseFlag, rule.RspFile); + ppVars.Defines = ""; + ppVars.Includes = ""; + } + + ppVars.Flags = ppFlags.c_str(); + + // Rule for preprocessing source file. + std::vector ppCmds; + + if (!preprocessCommand.empty()) { + // Lookup the explicit preprocessing rule. + cmExpandList(preprocessCommand, ppCmds); + for (std::string& i : ppCmds) { + i = cmStrCat(launcher, i); + rulePlaceholderExpander->ExpandRuleVariables(generator, i, ppVars); + } + } + + // Run CMake dependency scanner on either preprocessed output or source file + ppCmds.emplace_back(std::move(scanCommand)); + rule.Command = generator->BuildCommandLine(ppCmds); + + return rule; +} +} + void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang, const std::string& config) { @@ -566,82 +651,26 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang, cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL); if (explicitPP) { - cmNinjaRule rule(this->LanguagePreprocessRule(lang, config)); - // Explicit preprocessing always uses a depfile. - rule.DepType = ""; // no deps= for multiple outputs - rule.DepFile = "$DEP_FILE"; + // Combined preprocessing and dependency scanning + const auto ppScanCommand = GetScanCommand( + cmakeCmd, tdi, lang, "$out", needDyndep, "$DYNDEP_INTERMEDIATE_FILE"); + const auto ppVar = cmStrCat("CMAKE_", lang, "_PREPROCESS_SOURCE"); - cmRulePlaceholderExpander::RuleVariables ppVars; - ppVars.CMTargetName = vars.CMTargetName; - ppVars.CMTargetType = vars.CMTargetType; - ppVars.Language = vars.Language; - ppVars.Object = "$out"; // for RULE_LAUNCH_COMPILE - ppVars.PreprocessedSource = "$out"; - ppVars.DependencyFile = rule.DepFile.c_str(); + auto ppRule = GetPreprocessScanRule( + this->LanguagePreprocessRule(lang, config), vars, responseFlag, flags, + launcher, rulePlaceholderExpander.get(), ppScanCommand, + this->GetLocalGenerator(), mf->GetRequiredDefinition(ppVar)); - // Preprocessing uses the original source, - // compilation uses preprocessed output. - ppVars.Source = vars.Source; - vars.Source = "$in"; + // Write the rule for preprocessing file of the given language. + ppRule.Comment = cmStrCat("Rule for preprocessing ", lang, " files."); + ppRule.Description = cmStrCat("Building ", lang, " preprocessed $out"); - // Preprocessing and compilation use the same flags. - std::string ppFlags = flags; + this->GetGlobalGenerator()->AddRule(ppRule); if (!compilePPWithDefines) { - // Move preprocessor definitions to the preprocessor rule. - ppVars.Defines = vars.Defines; + // Remove preprocessor definitions from compilation step vars.Defines = ""; - } else { - // Copy preprocessor definitions to the preprocessor rule. - ppVars.Defines = vars.Defines; - } - - // Copy include directories to the preprocessor rule. The Fortran - // compilation rule still needs them for the INCLUDE directive. - ppVars.Includes = vars.Includes; - - // If using a response file, move defines, includes, and flags into it. - if (!responseFlag.empty()) { - rule.RspFile = "$RSP_FILE"; - rule.RspContent = - cmStrCat(' ', ppVars.Defines, ' ', ppVars.Includes, ' ', ppFlags); - ppFlags = cmStrCat(responseFlag, rule.RspFile); - ppVars.Defines = ""; - ppVars.Includes = ""; - } - - ppVars.Flags = ppFlags.c_str(); - - // Rule for preprocessing source file. - std::vector ppCmds; - { - // Lookup the explicit preprocessing rule. - std::string ppVar = cmStrCat("CMAKE_", lang, "_PREPROCESS_SOURCE"); - cmExpandList(this->GetMakefile()->GetRequiredDefinition(ppVar), ppCmds); - } - - for (std::string& i : ppCmds) { - i = cmStrCat(launcher, i); - rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), - i, ppVars); - } - - // Run CMake dependency scanner on preprocessed output. - { - std::string ccmd = - cmStrCat(cmakeCmd, " -E cmake_ninja_depends --tdi=", tdi, - " --lang=", lang, " --pp=$out --dep=$DEP_FILE"); - if (needDyndep) { - ccmd += " --obj=$OBJ_FILE --ddi=$DYNDEP_INTERMEDIATE_FILE"; - } - ppCmds.emplace_back(std::move(ccmd)); } - rule.Command = this->GetLocalGenerator()->BuildCommandLine(ppCmds); - - // Write the rule for preprocessing file of the given language. - rule.Comment = cmStrCat("Rule for preprocessing ", lang, " files."); - rule.Description = cmStrCat("Building ", lang, " preprocessed $out"); - this->GetGlobalGenerator()->AddRule(rule); } if (needDyndep) { -- GitLab From 66c4e87282176328bc2764afd53015dffd42d7f5 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 8 May 2020 12:02:26 +0100 Subject: [PATCH 3/4] Ninja: Add helper functions to generate Fortran build --- Source/cmNinjaTargetGenerator.cxx | 148 ++++++++++++++++++------------ 1 file changed, 87 insertions(+), 61 deletions(-) diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 36fad08985..63e95ee663 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -1026,6 +1026,78 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements( } } +namespace { +cmNinjaBuild GetPreprocessOrScanBuild( + const std::string& ruleName, const std::string& ppFileName, bool compilePP, + bool compilePPWithDefines, cmNinjaBuild& objBuild, cmNinjaVars& vars, + const std::string& depFileName, bool needDyndep, + const std::string& objectFileName) +{ + // Explicit preprocessing and dependency + cmNinjaBuild ppBuild(ruleName); + + if (!ppFileName.empty()) { + ppBuild.Outputs.push_back(ppFileName); + ppBuild.RspFile = cmStrCat(ppFileName, ".rsp"); + } else { + ppBuild.RspFile = "$out.rsp"; + } + + if (compilePP) { + // Move compilation dependencies to the preprocessing build statement. + std::swap(ppBuild.ExplicitDeps, objBuild.ExplicitDeps); + std::swap(ppBuild.ImplicitDeps, objBuild.ImplicitDeps); + std::swap(ppBuild.OrderOnlyDeps, objBuild.OrderOnlyDeps); + std::swap(ppBuild.Variables["IN_ABS"], vars["IN_ABS"]); + + // The actual compilation will now use the preprocessed source. + objBuild.ExplicitDeps.push_back(ppFileName); + } else { + // Copy compilation dependencies to the preprocessing build statement. + ppBuild.ExplicitDeps = objBuild.ExplicitDeps; + ppBuild.ImplicitDeps = objBuild.ImplicitDeps; + ppBuild.OrderOnlyDeps = objBuild.OrderOnlyDeps; + ppBuild.Variables["IN_ABS"] = vars["IN_ABS"]; + } + + // Preprocessing and compilation generally use the same flags. + ppBuild.Variables["FLAGS"] = vars["FLAGS"]; + + if (compilePP && !compilePPWithDefines) { + // Move preprocessor definitions to the preprocessor build statement. + std::swap(ppBuild.Variables["DEFINES"], vars["DEFINES"]); + } else { + // Copy preprocessor definitions to the preprocessor build statement. + ppBuild.Variables["DEFINES"] = vars["DEFINES"]; + } + + // Copy include directories to the preprocessor build statement. The + // Fortran compilation build statement still needs them for the INCLUDE + // directive. + ppBuild.Variables["INCLUDES"] = vars["INCLUDES"]; + + // Explicit preprocessing always uses a depfile. + ppBuild.Variables["DEP_FILE"] = depFileName; + if (compilePP) { + // The actual compilation does not need a depfile because it + // depends on the already-preprocessed source. + vars.erase("DEP_FILE"); + } + + if (needDyndep) { + // Tell dependency scanner the object file that will result from + // compiling the source. + ppBuild.Variables["OBJ_FILE"] = objectFileName; + + // Tell dependency scanner where to store dyndep intermediate results. + std::string const ddiFile = cmStrCat(objectFileName, ".ddi"); + ppBuild.Variables["DYNDEP_INTERMEDIATE_FILE"] = ddiFile; + ppBuild.ImplicitOuts.push_back(ddiFile); + } + return ppBuild; +} +} + void cmNinjaTargetGenerator::WriteObjectBuildStatement( cmSourceFile const* source, const std::string& config, const std::string& fileConfig, bool firstForConfig) @@ -1164,36 +1236,24 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( // For some cases we do an explicit preprocessor invocation. bool const explicitPP = this->NeedExplicitPreprocessing(language); if (explicitPP) { - cmNinjaBuild ppBuild(this->LanguagePreprocessRule(language, config)); + + bool const compilePP = this->UsePreprocessedSource(language); + bool const compilePPWithDefines = + compilePP && this->CompilePreprocessedSourceWithDefines(language); std::string const ppFileName = this->ConvertToNinjaPath(this->GetPreprocessedFilePath(source, config)); - ppBuild.Outputs.push_back(ppFileName); - ppBuild.RspFile = cmStrCat(ppFileName, ".rsp"); + std::string const buildName = + this->LanguagePreprocessRule(language, config); - bool const compilePP = this->UsePreprocessedSource(language); - bool const compilePPWithDefines = - compilePP && this->CompilePreprocessedSourceWithDefines(language); - if (compilePP) { - // Move compilation dependencies to the preprocessing build statement. - std::swap(ppBuild.ExplicitDeps, objBuild.ExplicitDeps); - std::swap(ppBuild.ImplicitDeps, objBuild.ImplicitDeps); - std::swap(ppBuild.OrderOnlyDeps, objBuild.OrderOnlyDeps); - std::swap(ppBuild.Variables["IN_ABS"], vars["IN_ABS"]); - - // The actual compilation will now use the preprocessed source. - objBuild.ExplicitDeps.push_back(ppFileName); - } else { - // Copy compilation dependencies to the preprocessing build statement. - ppBuild.ExplicitDeps = objBuild.ExplicitDeps; - ppBuild.ImplicitDeps = objBuild.ImplicitDeps; - ppBuild.OrderOnlyDeps = objBuild.OrderOnlyDeps; - ppBuild.Variables["IN_ABS"] = vars["IN_ABS"]; - } + const std::string depFileName = + this->GetLocalGenerator()->ConvertToOutputFormat( + cmStrCat(objectFileName, ".pp.d"), cmOutputConverter::SHELL); - // Preprocessing and compilation generally use the same flags. - ppBuild.Variables["FLAGS"] = vars["FLAGS"]; + cmNinjaBuild ppBuild = GetPreprocessOrScanBuild( + buildName, ppFileName, compilePP, compilePPWithDefines, objBuild, vars, + depFileName, needDyndep, objectFileName); if (compilePP) { // In case compilation requires flags that are incompatible with @@ -1201,22 +1261,7 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( std::string const& postFlag = this->Makefile->GetSafeDefinition( cmStrCat("CMAKE_", language, "_POSTPROCESS_FLAG")); this->LocalGenerator->AppendFlags(vars["FLAGS"], postFlag); - } - if (compilePP && !compilePPWithDefines) { - // Move preprocessor definitions to the preprocessor build statement. - std::swap(ppBuild.Variables["DEFINES"], vars["DEFINES"]); - } else { - // Copy preprocessor definitions to the preprocessor build statement. - ppBuild.Variables["DEFINES"] = vars["DEFINES"]; - } - - // Copy include directories to the preprocessor build statement. The - // Fortran compilation build statement still needs them for the INCLUDE - // directive. - ppBuild.Variables["INCLUDES"] = vars["INCLUDES"]; - - if (compilePP) { // Prepend source file's original directory as an include directory // so e.g. Fortran INCLUDE statements can look for files in it. std::vector sourceDirectory; @@ -1230,28 +1275,9 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( vars["INCLUDES"] = cmStrCat(sourceDirectoryFlag, ' ', vars["INCLUDES"]); } - // Explicit preprocessing always uses a depfile. - ppBuild.Variables["DEP_FILE"] = - this->GetLocalGenerator()->ConvertToOutputFormat( - cmStrCat(objectFileName, ".pp.d"), cmOutputConverter::SHELL); - if (compilePP) { - // The actual compilation does not need a depfile because it - // depends on the already-preprocessed source. - vars.erase("DEP_FILE"); - } - - if (needDyndep) { - // Tell dependency scanner the object file that will result from - // compiling the source. - ppBuild.Variables["OBJ_FILE"] = objectFileName; - - // Tell dependency scanner where to store dyndep intermediate results. - std::string const ddiFile = cmStrCat(objectFileName, ".ddi"); - ppBuild.Variables["DYNDEP_INTERMEDIATE_FILE"] = ddiFile; - ppBuild.ImplicitOuts.push_back(ddiFile); - if (firstForConfig) { - this->Configs[config].DDIFiles[language].push_back(ddiFile); - } + if (firstForConfig && needDyndep) { + const std::string& ddiFile = ppBuild.ImplicitOuts.back(); + this->Configs[config].DDIFiles[language].push_back(ddiFile); } this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(), -- GitLab From 3888de23daca814d66a40642d3e369a5c4747131 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 8 May 2020 12:25:57 +0100 Subject: [PATCH 4/4] Ninja: Skip Fortran preprocessing if Fortran_PREPROCESS is OFF If `Fortran_PREPROCESS` is explicitly turned off for a source file then we know it does not need to be preprocessed. Teach the Ninja generator to skip preprocessing in this case. Otherwise we still must preprocess just in case. Fixes: #18870 --- Help/prop_sf/Fortran_PREPROCESS.rst | 5 +++ Help/prop_tgt/Fortran_PREPROCESS.rst | 5 +++ Source/cmNinjaTargetGenerator.cxx | 64 +++++++++++++++++++++++----- Source/cmNinjaTargetGenerator.h | 2 + Tests/FortranOnly/CMakeLists.txt | 46 ++++++++++---------- 5 files changed, 88 insertions(+), 34 deletions(-) diff --git a/Help/prop_sf/Fortran_PREPROCESS.rst b/Help/prop_sf/Fortran_PREPROCESS.rst index 92542d9a0c..25ea827c88 100644 --- a/Help/prop_sf/Fortran_PREPROCESS.rst +++ b/Help/prop_sf/Fortran_PREPROCESS.rst @@ -8,5 +8,10 @@ should be preprocessed. If explicitly set to ``OFF`` then the file does not need to be preprocessed. If explicitly set to ``ON``, then the file does need to be preprocessed as part of the compilation step. +When using the :generator:`Ninja` generator, all source files are +first preprocessed in order to generate module dependency +information. Setting this property to ``OFF`` will make ``Ninja`` +skip this step. + Consider using the target-wide :prop_tgt:`Fortran_PREPROCESS` property if all source files in a target need to be preprocessed. diff --git a/Help/prop_tgt/Fortran_PREPROCESS.rst b/Help/prop_tgt/Fortran_PREPROCESS.rst index 069d04ab51..47a15c0566 100644 --- a/Help/prop_tgt/Fortran_PREPROCESS.rst +++ b/Help/prop_tgt/Fortran_PREPROCESS.rst @@ -9,6 +9,11 @@ should be preprocessed. If explicitly set to ``OFF`` then the file does not need to be preprocessed. If explicitly set to ``ON``, then the file does need to be preprocessed as part of the compilation step. +When using the :generator:`Ninja` generator, all source files are +first preprocessed in order to generate module dependency +information. Setting this property to ``OFF`` will make ``Ninja`` +skip this step. + Use the source-specific :prop_sf:`Fortran_PREPROCESS` property if a single file needs to be preprocessed. If the variable :variable:`CMAKE_Fortran_PREPROCESS` is set when a target is created its diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 63e95ee663..a499bc8fa8 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -106,7 +106,16 @@ std::string cmNinjaTargetGenerator::LanguagePreprocessRule( std::string const& lang, const std::string& config) const { return cmStrCat( - lang, "_PREPROCESS__", + lang, "_PREPROCESS_SCAN__", + cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()), + '_', config); +} + +std::string cmNinjaTargetGenerator::LanguageDependencyRule( + std::string const& lang, const std::string& config) const +{ + return cmStrCat( + lang, "_SCAN__", cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()), '_', config); } @@ -671,6 +680,22 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang, // Remove preprocessor definitions from compilation step vars.Defines = ""; } + + // Just dependency scanning for files that have preprocessing turned off + const auto scanCommand = + GetScanCommand(cmakeCmd, tdi, lang, "$in", needDyndep, "$out"); + + auto scanRule = GetPreprocessScanRule( + this->LanguageDependencyRule(lang, config), vars, "", flags, launcher, + rulePlaceholderExpander.get(), scanCommand, this->GetLocalGenerator()); + + // Write the rule for generating dependencies for the given language. + scanRule.Comment = cmStrCat("Rule for generating ", lang, + " dependencies on non-preprocessed files."); + scanRule.Description = + cmStrCat("Generating ", lang, " dependencies for $in"); + + this->GetGlobalGenerator()->AddRule(scanRule); } if (needDyndep) { @@ -1091,8 +1116,12 @@ cmNinjaBuild GetPreprocessOrScanBuild( // Tell dependency scanner where to store dyndep intermediate results. std::string const ddiFile = cmStrCat(objectFileName, ".ddi"); - ppBuild.Variables["DYNDEP_INTERMEDIATE_FILE"] = ddiFile; - ppBuild.ImplicitOuts.push_back(ddiFile); + if (ppFileName.empty()) { + ppBuild.Outputs.push_back(ddiFile); + } else { + ppBuild.Variables["DYNDEP_INTERMEDIATE_FILE"] = ddiFile; + ppBuild.ImplicitOuts.push_back(ddiFile); + } } return ppBuild; } @@ -1237,19 +1266,34 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( bool const explicitPP = this->NeedExplicitPreprocessing(language); if (explicitPP) { - bool const compilePP = this->UsePreprocessedSource(language); + // If source/target has preprocessing turned off, we still need to + // generate an explicit dependency step + const auto srcpp = source->GetSafeProperty("Fortran_PREPROCESS"); + cmOutputConverter::FortranPreprocess preprocess = + cmOutputConverter::GetFortranPreprocess(srcpp); + if (preprocess == cmOutputConverter::FortranPreprocess::Unset) { + const auto& tgtpp = + this->GeneratorTarget->GetSafeProperty("Fortran_PREPROCESS"); + preprocess = cmOutputConverter::GetFortranPreprocess(tgtpp); + } + + bool const compilePP = this->UsePreprocessedSource(language) && + (preprocess != cmOutputConverter::FortranPreprocess::NotNeeded); bool const compilePPWithDefines = compilePP && this->CompilePreprocessedSourceWithDefines(language); - std::string const ppFileName = - this->ConvertToNinjaPath(this->GetPreprocessedFilePath(source, config)); + std::string const ppFileName = compilePP + ? this->ConvertToNinjaPath(this->GetPreprocessedFilePath(source, config)) + : ""; - std::string const buildName = - this->LanguagePreprocessRule(language, config); + std::string const buildName = compilePP + ? this->LanguagePreprocessRule(language, config) + : this->LanguageDependencyRule(language, config); + const auto depExtension = compilePP ? ".pp.d" : ".d"; const std::string depFileName = this->GetLocalGenerator()->ConvertToOutputFormat( - cmStrCat(objectFileName, ".pp.d"), cmOutputConverter::SHELL); + cmStrCat(objectFileName, depExtension), cmOutputConverter::SHELL); cmNinjaBuild ppBuild = GetPreprocessOrScanBuild( buildName, ppFileName, compilePP, compilePPWithDefines, objBuild, vars, @@ -1276,7 +1320,7 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( } if (firstForConfig && needDyndep) { - const std::string& ddiFile = ppBuild.ImplicitOuts.back(); + std::string const ddiFile = cmStrCat(objectFileName, ".ddi"); this->Configs[config].DDIFiles[language].push_back(ddiFile); } diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index 8678dc3f66..2e0a5116ea 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h @@ -70,6 +70,8 @@ protected: const std::string& config) const; std::string LanguagePreprocessRule(std::string const& lang, const std::string& config) const; + std::string LanguageDependencyRule(std::string const& lang, + const std::string& config) const; bool NeedExplicitPreprocessing(std::string const& lang) const; std::string LanguageDyndepRule(std::string const& lang, const std::string& config) const; diff --git a/Tests/FortranOnly/CMakeLists.txt b/Tests/FortranOnly/CMakeLists.txt index 59f139bc8e..d24df2df84 100644 --- a/Tests/FortranOnly/CMakeLists.txt +++ b/Tests/FortranOnly/CMakeLists.txt @@ -135,29 +135,27 @@ if(test_pp_flags) set_property(SOURCE preprocess3.f PROPERTY Fortran_PREPROCESS ON) endif() -if(NOT CMAKE_GENERATOR MATCHES "Ninja") - # Test that neither the compiler nor CMake performs unnecessary preprocessing. - add_library(no_preprocess_target_lower STATIC no_preprocess_target_lower.f) - target_compile_options(no_preprocess_target_lower PRIVATE -DINTEGER=nonsense) - set_property(TARGET no_preprocess_target_lower PROPERTY Fortran_PREPROCESS OFF) - add_library(no_preprocess_source_lower STATIC no_preprocess_source_lower.f) - target_compile_options(no_preprocess_source_lower PRIVATE -DINTEGER=nonsense) - set_property(SOURCE no_preprocess_source_lower.f PROPERTY Fortran_PREPROCESS OFF) - - # Test that we can explicitly not preprocess a target or source. - # This will not work on certain compilers due to either missing a - # "don't preprocess" flag, or due to choice of file extension. - if(test_pp_flags AND NOT CMAKE_Fortran_COMPILER_ID MATCHES "(Flang|NAG|PGI|SunPro|XL)") - add_library(no_preprocess_target STATIC no_preprocess_target_upper.F) - target_compile_options(no_preprocess_target PRIVATE -DINTEGER=nonsense) - add_library(no_preprocess_source STATIC no_preprocess_source_upper.F) - target_compile_options(no_preprocess_source PRIVATE -DINTEGER=nonsense) - if(NOT CMAKE_Fortran_COMPILER_ID STREQUAL "Cray" - AND NOT "${CMAKE_Fortran_COMPILER_ID};${CMAKE_Fortran_SIMULATE_ID}" STREQUAL "Intel;MSVC") - target_sources(no_preprocess_target PRIVATE no_preprocess_target_fpp.fpp) - target_sources(no_preprocess_source PRIVATE no_preprocess_source_fpp.fpp) - endif() - set_property(TARGET no_preprocess_target PROPERTY Fortran_PREPROCESS OFF) - set_property(SOURCE no_preprocess_source_upper.F no_preprocess_source_fpp.fpp PROPERTY Fortran_PREPROCESS OFF) +# Test that neither the compiler nor CMake performs unnecessary preprocessing. +add_library(no_preprocess_target_lower STATIC no_preprocess_target_lower.f) +target_compile_options(no_preprocess_target_lower PRIVATE -DINTEGER=nonsense) +set_property(TARGET no_preprocess_target_lower PROPERTY Fortran_PREPROCESS OFF) +add_library(no_preprocess_source_lower STATIC no_preprocess_source_lower.f) +target_compile_options(no_preprocess_source_lower PRIVATE -DINTEGER=nonsense) +set_property(SOURCE no_preprocess_source_lower.f PROPERTY Fortran_PREPROCESS OFF) + +# Test that we can explicitly not preprocess a target or source. +# This will not work on certain compilers due to either missing a +# "don't preprocess" flag, or due to choice of file extension. +if(test_pp_flags AND NOT CMAKE_Fortran_COMPILER_ID MATCHES "(Flang|NAG|PGI|SunPro|XL)") + add_library(no_preprocess_target STATIC no_preprocess_target_upper.F) + target_compile_options(no_preprocess_target PRIVATE -DINTEGER=nonsense) + add_library(no_preprocess_source STATIC no_preprocess_source_upper.F) + target_compile_options(no_preprocess_source PRIVATE -DINTEGER=nonsense) + if(NOT CMAKE_Fortran_COMPILER_ID STREQUAL "Cray" + AND NOT "${CMAKE_Fortran_COMPILER_ID};${CMAKE_Fortran_SIMULATE_ID}" STREQUAL "Intel;MSVC") + target_sources(no_preprocess_target PRIVATE no_preprocess_target_fpp.fpp) + target_sources(no_preprocess_source PRIVATE no_preprocess_source_fpp.fpp) endif() + set_property(TARGET no_preprocess_target PROPERTY Fortran_PREPROCESS OFF) + set_property(SOURCE no_preprocess_source_upper.F no_preprocess_source_fpp.fpp PROPERTY Fortran_PREPROCESS OFF) endif() -- GitLab