Merge topic 'fortran-preprocess-property'

3888de23da Ninja: Skip Fortran preprocessing if Fortran_PREPROCESS is OFF
66c4e87282 Ninja: Add helper functions to generate Fortran build
5cca1ec893 Ninja: Add helper functions to generate Fortran preprocess rule
b0a6161190 Fortran: Add Fortran_PREPROCESS property

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !4659
This commit is contained in:
Brad King
2020-05-22 14:34:53 +00:00
committed by Kitware Robot
38 changed files with 489 additions and 141 deletions
+2
View File
@@ -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
+2
View File
@@ -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
@@ -466,6 +467,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
+1
View File
@@ -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
+6 -4
View File
@@ -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.
+17
View File
@@ -0,0 +1,17 @@
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.
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.
+23
View File
@@ -0,0 +1,23 @@
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.
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
value is used to initialize this property.
.. note:: For some compilers, ``NAG``, ``PGI`` and ``Solaris Studio``,
setting this to ``OFF`` will have no effect.
@@ -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.
@@ -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.
+2
View File
@@ -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")
+8
View File
@@ -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()
+3
View File
@@ -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")
+2
View File
@@ -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")
+5
View File
@@ -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.
+3
View File
@@ -7,3 +7,6 @@ set(CMAKE_Fortran_CREATE_PREPROCESSED_SOURCE "<CMAKE_Fortran_COMPILER> <DEFINES>
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")
+2
View File
@@ -15,3 +15,5 @@ set(CMAKE_Fortran_CREATE_ASSEMBLY_SOURCE "<CMAKE_Fortran_COMPILER> <DEFINES> <IN
set(CMAKE_Fortran_PREPROCESS_SOURCE
"<CMAKE_Fortran_COMPILER> -fpp <DEFINES> <INCLUDES> <FLAGS> -E <SOURCE> > <PREPROCESSED_SOURCE>")
set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-fpp")
set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF "-nofpp")
+1
View File
@@ -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")
+1
View File
@@ -6,6 +6,7 @@ set(CMAKE_Fortran_SUBMODULE_EXT ".mod")
set(CMAKE_Fortran_PREPROCESS_SOURCE
"<CMAKE_Fortran_COMPILER> -Mpreprocess <DEFINES> <INCLUDES> <FLAGS> -E <SOURCE> > <PREPROCESSED_SOURCE>")
set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-Mpreprocess")
set(CMAKE_Fortran_FORMAT_FIXED_FLAG "-Mnofreeform")
set(CMAKE_Fortran_FORMAT_FREE_FLAG "-Mfreeform")
+3
View File
@@ -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")
+2
View File
@@ -30,3 +30,5 @@ set(CMAKE_Fortran_PREPROCESS_SOURCE
set(CMAKE_Fortran_CREATE_PREPROCESSED_SOURCE "<CMAKE_Fortran_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -F -fpp <SOURCE> -o <PREPROCESSED_SOURCE>")
set(CMAKE_Fortran_CREATE_ASSEMBLY_SOURCE "<CMAKE_Fortran_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -S <SOURCE> -o <ASSEMBLY_SOURCE>")
set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-fpp")
+5
View File
@@ -23,3 +23,8 @@ set(CMAKE_Fortran_CREATE_ASSEMBLY_SOURCE)
set(CMAKE_Fortran_PREPROCESS_SOURCE
"<CMAKE_Fortran_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -qpreprocess -qnoobject -qsuppress=1517-020 -tF -B \"${CMAKE_CURRENT_LIST_DIR}/XL-Fortran/\" -WF,--cpp,\"${CMAKE_Fortran_XL_CPP}\",--out,<PREPROCESSED_SOURCE> <SOURCE>"
)
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()
+28
View File
@@ -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)
+3
View File
@@ -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;
+27
View File
@@ -283,6 +283,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 },
@@ -681,6 +682,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.
@@ -1473,6 +1486,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:
+1
View File
@@ -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.
+237 -137
View File
@@ -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);
}
@@ -183,6 +192,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.
@@ -508,6 +518,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<std::string> 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)
{
@@ -565,82 +660,42 @@ 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();
// Preprocessing uses the original source,
// compilation uses preprocessed output.
ppVars.Source = vars.Source;
vars.Source = "$in";
// Preprocessing and compilation use the same flags.
std::string ppFlags = flags;
if (!compilePPWithDefines) {
// Move preprocessor definitions to the preprocessor rule.
ppVars.Defines = vars.Defines;
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<std::string> 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);
auto ppRule = GetPreprocessScanRule(
this->LanguagePreprocessRule(lang, config), vars, responseFlag, flags,
launcher, rulePlaceholderExpander.get(), ppScanCommand,
this->GetLocalGenerator(), mf->GetRequiredDefinition(ppVar));
// 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);
ppRule.Comment = cmStrCat("Rule for preprocessing ", lang, " files.");
ppRule.Description = cmStrCat("Building ", lang, " preprocessed $out");
this->GetGlobalGenerator()->AddRule(ppRule);
if (!compilePPWithDefines) {
// 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) {
@@ -996,6 +1051,82 @@ 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");
if (ppFileName.empty()) {
ppBuild.Outputs.push_back(ddiFile);
} else {
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)
@@ -1134,36 +1265,39 @@ 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));
std::string const ppFileName =
this->ConvertToNinjaPath(this->GetPreprocessedFilePath(source, config));
ppBuild.Outputs.push_back(ppFileName);
ppBuild.RspFile = cmStrCat(ppFileName, ".rsp");
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"];
// 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);
}
// Preprocessing and compilation generally use the same flags.
ppBuild.Variables["FLAGS"] = vars["FLAGS"];
bool const compilePP = this->UsePreprocessedSource(language) &&
(preprocess != cmOutputConverter::FortranPreprocess::NotNeeded);
bool const compilePPWithDefines =
compilePP && this->CompilePreprocessedSourceWithDefines(language);
std::string const ppFileName = compilePP
? this->ConvertToNinjaPath(this->GetPreprocessedFilePath(source, 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, depExtension), cmOutputConverter::SHELL);
cmNinjaBuild ppBuild = GetPreprocessOrScanBuild(
buildName, ppFileName, compilePP, compilePPWithDefines, objBuild, vars,
depFileName, needDyndep, objectFileName);
if (compilePP) {
// In case compilation requires flags that are incompatible with
@@ -1171,22 +1305,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<std::string> sourceDirectory;
@@ -1200,28 +1319,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.
if (firstForConfig && needDyndep) {
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);
}
this->Configs[config].DDIFiles[language].push_back(ddiFile);
}
this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(),
+2
View File
@@ -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;
+11
View File
@@ -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;
+8
View File
@@ -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;
+1
View File
@@ -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");
+43
View File
@@ -116,3 +116,46 @@ 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()
# 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()
@@ -0,0 +1,3 @@
SUBROUTINE NOPREPROCESS_SOURCE_FPP
INTEGER F
END
@@ -0,0 +1,3 @@
SUBROUTINE NOPREPROCESS_SOURCE_LOWER
INTEGER F
END
@@ -0,0 +1,3 @@
SUBROUTINE NOPREPROCESS_SOURCE_UPPER
INTEGER F
END
@@ -0,0 +1,3 @@
SUBROUTINE NOPREPROCESS_TARGET_FPP
INTEGER F
END
@@ -0,0 +1,3 @@
SUBROUTINE NOPREPROCESS_TARGET_LOWER
INTEGER F
END
@@ -0,0 +1,3 @@
SUBROUTINE NOPREPROCESS_TARGET_UPPER
INTEGER F
END
+4
View File
@@ -0,0 +1,4 @@
#define int INTEGER
PROGRAM PREPRO
int f
END
+4
View File
@@ -0,0 +1,4 @@
#define int INTEGER
PROGRAM PREPRO
int f
END