diff options
-rw-r--r-- | Modules/Compiler/GNU-Fortran.cmake | 3 | ||||
-rw-r--r-- | Modules/Compiler/Intel-Fortran.cmake | 3 | ||||
-rw-r--r-- | Modules/Compiler/SunPro-Fortran.cmake | 3 | ||||
-rw-r--r-- | Source/cmNinjaTargetGenerator.cxx | 166 | ||||
-rw-r--r-- | Source/cmNinjaTargetGenerator.h | 5 |
5 files changed, 179 insertions, 1 deletions
diff --git a/Modules/Compiler/GNU-Fortran.cmake b/Modules/Compiler/GNU-Fortran.cmake index fc848ac..94dc275 100644 --- a/Modules/Compiler/GNU-Fortran.cmake +++ b/Modules/Compiler/GNU-Fortran.cmake @@ -1,6 +1,9 @@ include(Compiler/GNU) __compiler_gnu(Fortran) +set(CMAKE_Fortran_PREPROCESS_SOURCE + "<CMAKE_Fortran_COMPILER> -cpp <DEFINES> <INCLUDES> <FLAGS> -E <SOURCE> -o <PREPROCESSED_SOURCE>") + set(CMAKE_Fortran_FORMAT_FIXED_FLAG "-ffixed-form") set(CMAKE_Fortran_FORMAT_FREE_FLAG "-ffree-form") diff --git a/Modules/Compiler/Intel-Fortran.cmake b/Modules/Compiler/Intel-Fortran.cmake index ef7aa3a..a132055 100644 --- a/Modules/Compiler/Intel-Fortran.cmake +++ b/Modules/Compiler/Intel-Fortran.cmake @@ -7,3 +7,6 @@ set(CMAKE_Fortran_FORMAT_FREE_FLAG "-free") set(CMAKE_Fortran_CREATE_PREPROCESSED_SOURCE "<CMAKE_Fortran_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -E <SOURCE> > <PREPROCESSED_SOURCE>") set(CMAKE_Fortran_CREATE_ASSEMBLY_SOURCE "<CMAKE_Fortran_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -S <SOURCE> -o <ASSEMBLY_SOURCE>") + +set(CMAKE_Fortran_PREPROCESS_SOURCE + "<CMAKE_Fortran_COMPILER> -fpp <DEFINES> <INCLUDES> <FLAGS> -E <SOURCE> > <PREPROCESSED_SOURCE>") diff --git a/Modules/Compiler/SunPro-Fortran.cmake b/Modules/Compiler/SunPro-Fortran.cmake index a0e07d4..6607926 100644 --- a/Modules/Compiler/SunPro-Fortran.cmake +++ b/Modules/Compiler/SunPro-Fortran.cmake @@ -18,5 +18,8 @@ string(APPEND CMAKE_Fortran_FLAGS_RELWITHDEBINFO_INIT " -g -xO2 -DNDEBUG") set(CMAKE_Fortran_MODDIR_FLAG "-moddir=") set(CMAKE_Fortran_MODPATH_FLAG "-M") +set(CMAKE_Fortran_PREPROCESS_SOURCE + "<CMAKE_Fortran_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -F <SOURCE> -o <PREPROCESSED_SOURCE>") + set(CMAKE_Fortran_CREATE_PREPROCESSED_SOURCE "<CMAKE_Fortran_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -F <SOURCE> -o <PREPROCESSED_SOURCE>") set(CMAKE_Fortran_CREATE_ASSEMBLY_SOURCE "<CMAKE_Fortran_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -S <SOURCE> -o <ASSEMBLY_SOURCE>") diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index fb09bfe..76bae6b 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -93,6 +93,19 @@ std::string cmNinjaTargetGenerator::LanguageCompilerRule( cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()); } +std::string cmNinjaTargetGenerator::LanguagePreprocessRule( + std::string const& lang) const +{ + return lang + "_PREPROCESS__" + + cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()); +} + +bool cmNinjaTargetGenerator::NeedExplicitPreprocessing( + std::string const& lang) const +{ + return lang == "Fortran"; +} + std::string cmNinjaTargetGenerator::OrderDependsTargetForTarget() { return "cmake_order_depends_target_" + this->GetTargetName(); @@ -229,6 +242,41 @@ std::string cmNinjaTargetGenerator::GetObjectFilePath( return path; } +std::string cmNinjaTargetGenerator::GetPreprocessedFilePath( + cmSourceFile const* source) const +{ + // Choose an extension to compile already-preprocessed source. + std::string ppExt = source->GetExtension(); + if (cmHasLiteralPrefix(ppExt, "F")) { + // Some Fortran compilers automatically enable preprocessing for + // upper-case extensions. Since the source is already preprocessed, + // use a lower-case extension. + ppExt = cmSystemTools::LowerCase(ppExt); + } + if (ppExt == "fpp") { + // Some Fortran compilers automatically enable preprocessing for + // the ".fpp" extension. Since the source is already preprocessed, + // use the ".f" extension. + ppExt = "f"; + } + + // Take the object file name and replace the extension. + std::string const& objName = this->GeneratorTarget->GetObjectName(source); + std::string const& objExt = + this->GetGlobalGenerator()->GetLanguageOutputExtension(*source); + assert(objName.size() >= objExt.size()); + std::string const ppName = + objName.substr(0, objName.size() - objExt.size()) + "-pp." + ppExt; + + std::string path = this->LocalGenerator->GetHomeRelativeOutputPath(); + if (!path.empty()) + path += "/"; + path += this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget); + path += "/"; + path += ppName; + return path; +} + std::string cmNinjaTargetGenerator::GetTargetOutputDir() const { std::string dir = this->GeneratorTarget->GetDirectory(this->GetConfigName()); @@ -311,6 +359,9 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) vars.ObjectDir = "$OBJECT_DIR"; vars.ObjectFileDir = "$OBJECT_FILE_DIR"; + // For some cases we do an explicit preprocessor invocation. + bool const explicitPP = this->NeedExplicitPreprocessing(lang); + cmMakefile* mf = this->GetMakefile(); std::string flags = "$FLAGS"; @@ -331,7 +382,9 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) std::string deptype; std::string depfile; std::string cldeps; - if (this->NeedDepTypeMSVC(lang)) { + if (explicitPP) { + // The explicit preprocessing step will handle dependency scanning. + } else if (this->NeedDepTypeMSVC(lang)) { deptype = "msvc"; depfile = ""; flags += " /showIncludes"; @@ -371,6 +424,66 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) vars.Flags = flags.c_str(); vars.DependencyFile = depfile.c_str(); + if (explicitPP) { + // Lookup the explicit preprocessing rule. + std::string const ppVar = "CMAKE_" + lang + "_PREPROCESS_SOURCE"; + std::string const ppCmd = + this->GetMakefile()->GetRequiredDefinition(ppVar); + + // Explicit preprocessing always uses a depfile. + std::string const ppDeptype = "gcc"; + std::string const ppDepfile = "$DEP_FILE"; + + cmLocalGenerator::RuleVariables ppVars; + ppVars.RuleLauncher = vars.RuleLauncher; + ppVars.CMTarget = vars.CMTarget; + ppVars.Language = vars.Language; + ppVars.Object = "$out"; // for RULE_LAUNCH_COMPILE + ppVars.PreprocessedSource = "$out"; + ppVars.DependencyFile = ppDepfile.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. + ppVars.Flags = vars.Flags; + + // Move preprocessor definitions to the preprocessor rule. + ppVars.Defines = vars.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; + + // Rule for preprocessing source file. + std::vector<std::string> ppCmds; + cmSystemTools::ExpandListArgument(ppCmd, ppCmds); + + for (std::vector<std::string>::iterator i = ppCmds.begin(); + i != ppCmds.end(); ++i) { + this->GetLocalGenerator()->ExpandRuleVariables(*i, ppVars); + } + + std::string const ppCmdLine = + this->GetLocalGenerator()->BuildCommandLine(ppCmds); + + // Write the rule for preprocessing file of the given language. + std::ostringstream ppComment; + ppComment << "Rule for preprocessing " << lang << " files."; + std::ostringstream ppDesc; + ppDesc << "Building " << lang << " preprocessed $out"; + this->GetGlobalGenerator()->AddRule(this->LanguagePreprocessRule(lang), + ppCmdLine, ppDesc.str(), + ppComment.str(), ppDepfile, ppDeptype, + /*rspfile*/ "", + /*rspcontent*/ "", + /*restat*/ "", + /*generator*/ false); + } + // Rule for compiling object file. const std::string cmdVar = std::string("CMAKE_") + lang + "_COMPILE_OBJECT"; std::string compileCmd = mf->GetRequiredDefinition(cmdVar); @@ -589,6 +702,57 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( orderOnlyDeps); } + // For some cases we do an explicit preprocessor invocation. + bool const explicitPP = this->NeedExplicitPreprocessing(language); + if (explicitPP) { + std::string const ppComment; + std::string const ppRule = this->LanguagePreprocessRule(language); + cmNinjaDeps ppOutputs; + cmNinjaDeps ppImplicitOuts; + cmNinjaDeps ppExplicitDeps; + cmNinjaDeps ppImplicitDeps; + cmNinjaDeps ppOrderOnlyDeps; + cmNinjaVars ppVars; + + std::string const ppFileName = + this->ConvertToNinjaPath(this->GetPreprocessedFilePath(source)); + ppOutputs.push_back(ppFileName); + + // Move compilation dependencies to the preprocessing build statement. + std::swap(ppExplicitDeps, explicitDeps); + std::swap(ppImplicitDeps, implicitDeps); + std::swap(ppOrderOnlyDeps, orderOnlyDeps); + std::swap(ppVars["IN_ABS"], vars["IN_ABS"]); + + // The actual compilation will now use the preprocessed source. + explicitDeps.push_back(ppFileName); + + // Preprocessing and compilation use the same flags. + ppVars["FLAGS"] = vars["FLAGS"]; + + // Move preprocessor definitions to the preprocessor build statement. + std::swap(ppVars["DEFINES"], vars["DEFINES"]); + + // Copy include directories to the preprocessor build statement. The + // Fortran compilation build statement still needs them for the INCLUDE + // directive. + ppVars["INCLUDES"] = vars["INCLUDES"]; + + // Explicit preprocessing always uses a depfile. + ppVars["DEP_FILE"] = + cmGlobalNinjaGenerator::EncodeDepfileSpace(ppFileName + ".d"); + // The actual compilation does not need a depfile because it + // depends on the already-preprocessed source. + vars.erase("DEP_FILE"); + + this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(), + ppVars); + + this->GetGlobalGenerator()->WriteBuild( + this->GetBuildFileStream(), ppComment, ppRule, ppOutputs, ppImplicitOuts, + ppExplicitDeps, ppImplicitDeps, ppOrderOnlyDeps, ppVars); + } + EnsureParentDirectoryExists(objectFileName); vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat( diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index 2b26788..e6816db 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h @@ -70,6 +70,8 @@ protected: cmMakefile* GetMakefile() const { return this->Makefile; } std::string LanguageCompilerRule(const std::string& lang) const; + std::string LanguagePreprocessRule(std::string const& lang) const; + bool NeedExplicitPreprocessing(std::string const& lang) const; std::string OrderDependsTargetForTarget(); @@ -107,6 +109,9 @@ protected: /// @return the object file path for the given @a source. std::string GetObjectFilePath(cmSourceFile const* source) const; + /// @return the preprocessed source file path for the given @a source. + std::string GetPreprocessedFilePath(cmSourceFile const* source) const; + /// @return the file path where the target named @a name is generated. std::string GetTargetFilePath(const std::string& name) const; |