From b0a6161190f8a85a5771fc96d5d745b0be2b07e5 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 24 Apr 2020 11:52:41 +0100 Subject: 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 ++++++++++ Help/release/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 ++++++++++++++++++++++++ Tests/FortranOnly/no_preprocess_source_fpp.fpp | 3 ++ Tests/FortranOnly/no_preprocess_source_lower.f | 3 ++ Tests/FortranOnly/no_preprocess_source_upper.F | 3 ++ Tests/FortranOnly/no_preprocess_target_fpp.fpp | 3 ++ Tests/FortranOnly/no_preprocess_target_lower.f | 3 ++ Tests/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 0676f7e..2d63eb0 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 9031e9c..43bd08f 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 7d802e1..4ce8365 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 1cbbf48..ef33926 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 0000000..92542d9 --- /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 0000000..069d04a --- /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 0000000..d18e7b8 --- /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 0000000..74b2d8b --- /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 76502dc..8724f85 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 ccb7c2e..696ae76 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 f0e61d8..de0484e 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 03b7e08..5dba04e 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 6413769..5dfb03e 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 63a0331..d3e2a30 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 156b533..71f25f4 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 2111c65..ffce97e 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 3daf798..ff87577 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 d903621..891d93e 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 0c93c94..0ba5015 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 e01ec8e..cc15e65 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 5414409..32a33ee 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 78cedf5..c3c3a3a 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 e18fac3..f7a0c5a 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 5f0cfcf..dcd121c 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 0606484..b58434f 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 dc324cc..359e9f5 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 28582df..a8b4528 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 a776398..16d7b6f 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 4327c2f..59f139b 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 0000000..8e48902 --- /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 0000000..3b08782 --- /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 0000000..02485c9 --- /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 0000000..f9e6e3b --- /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 0000000..ea23a70 --- /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 0000000..34ee04d --- /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 0000000..6595d62 --- /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 0000000..6595d62 --- /dev/null +++ b/Tests/FortranOnly/preprocess3.f @@ -0,0 +1,4 @@ +#define int INTEGER + PROGRAM PREPRO + int f + END -- cgit v0.12 From 5cca1ec8934d84d9b10a7d891d31012d7aef8feb Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 8 May 2020 10:53:47 +0100 Subject: 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 b58434f..36fad08 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) { -- cgit v0.12 From 66c4e87282176328bc2764afd53015dffd42d7f5 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 8 May 2020 12:02:26 +0100 Subject: 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 36fad08..63e95ee 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(), -- cgit v0.12 From 3888de23daca814d66a40642d3e369a5c4747131 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 8 May 2020 12:25:57 +0100 Subject: 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 92542d9..25ea827 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 069d04a..47a15c0 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 63e95ee..a499bc8 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 8678dc3..2e0a511 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 59f139b..d24df2d 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() -- cgit v0.12