From b761da39c14c9f003d9113418aca9370f30a5e6e Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 9 Jan 2008 10:30:11 -0500 Subject: [PATCH] ENH: Patch from Maik to add preprocessor directive handling to Fortran dependency scanning. Also added -fpp flag to Intel Fortran compiler on Windows by default. --- Modules/Platform/Windows-ifort.cmake | 4 +- Source/cmDependsFortran.cxx | 199 ++++++++++++++++++++--- Source/cmDependsFortran.h | 4 +- Source/cmLocalUnixMakefileGenerator3.cxx | 16 +- Tests/Fortran/CMakeLists.txt | 3 + Tests/Fortran/test_preprocess.F90 | 55 +++++++ 6 files changed, 259 insertions(+), 22 deletions(-) create mode 100644 Tests/Fortran/test_preprocess.F90 diff --git a/Modules/Platform/Windows-ifort.cmake b/Modules/Platform/Windows-ifort.cmake index 1b3459dea..4c88910dd 100644 --- a/Modules/Platform/Windows-ifort.cmake +++ b/Modules/Platform/Windows-ifort.cmake @@ -7,6 +7,8 @@ ELSE(CMAKE_VERBOSE_MAKEFILE) SET(CMAKE_CL_NOLOGO "/nologo") ENDIF(CMAKE_VERBOSE_MAKEFILE) +SET(CMAKE_Fortran_MODDIR_FLAG "-module:") + SET(CMAKE_Fortran_CREATE_SHARED_LIBRARY "link ${CMAKE_CL_NOLOGO} ${CMAKE_START_TEMP_FILE} /out: /dll ${CMAKE_END_TEMP_FILE}") @@ -17,7 +19,7 @@ SET(CMAKE_Fortran_CREATE_STATIC_LIBRARY "lib ${CMAKE_CL_NOLOGO} /o # compile a C++ file into an object file SET(CMAKE_Fortran_COMPILE_OBJECT - " ${CMAKE_START_TEMP_FILE} ${CMAKE_CL_NOLOGO} /Fo -c ${CMAKE_END_TEMP_FILE}") + " ${CMAKE_START_TEMP_FILE} ${CMAKE_CL_NOLOGO} /fpp /Fo -c ${CMAKE_END_TEMP_FILE}") SET(CMAKE_COMPILE_RESOURCE "rc /fo ") diff --git a/Source/cmDependsFortran.cxx b/Source/cmDependsFortran.cxx index 1c24b0b12..23a199e37 100644 --- a/Source/cmDependsFortran.cxx +++ b/Source/cmDependsFortran.cxx @@ -70,6 +70,7 @@ struct cmDependsFortranFile struct cmDependsFortranParser_s { cmDependsFortranParser_s(cmDependsFortran* self, + std::set& ppDefines, cmDependsFortranSourceInfo& info); ~cmDependsFortranParser_s(); @@ -89,9 +90,9 @@ struct cmDependsFortranParser_s bool InInterface; int OldStartcond; - bool InPPFalseBranch; - std::vector SkipToEnd; - int StepI; + std::set PPDefinitions; + std::size_t InPPFalseBranch; + std::stack SkipToEnd; // Information about the parsed source. cmDependsFortranSourceInfo& Info; @@ -130,14 +131,39 @@ public: //---------------------------------------------------------------------------- cmDependsFortran::cmDependsFortran(): - IncludePath(0), Internal(0) + IncludePath(0), PPDefinitions(0), Internal(0) { } //---------------------------------------------------------------------------- -cmDependsFortran::cmDependsFortran(std::vector const& includes): - IncludePath(&includes), Internal(new cmDependsFortranInternals) +cmDependsFortran +::cmDependsFortran(std::vector const& includes, + std::vector const& definitions): + IncludePath(&includes), + Internal(new cmDependsFortranInternals) { + // translate i.e. -DFOO=BAR to FOO and add it to the list of defined + // preprocessor symbols + std::string def; + for(std::vector::const_iterator + it = definitions.begin(); it != definitions.end(); ++it) + { + std::size_t match = it->find("-D"); + if(match != std::string::npos) + { + std::size_t assignment = it->find("="); + if(assignment != std::string::npos) + { + std::size_t length = assignment - (match+2); + def = it->substr(match+2, length); + } + else + { + def = it->substr(match+2); + } + this->PPDefinitions.push_back(def); + } + } } //---------------------------------------------------------------------------- @@ -171,8 +197,13 @@ bool cmDependsFortran::WriteDependencies(const char *src, const char *obj, cmDependsFortranSourceInfo& info = this->Internal->CreateObjectInfo(obj, src); - // Create the parser object. - cmDependsFortranParser parser(this, info); + // Make a copy of the macros defined via ADD_DEFINITIONS + std::set ppDefines(this->PPDefinitions.begin(), + this->PPDefinitions.end()); + + // Create the parser object. The constructor takes ppMacro and info per + // reference, so we may look into the resulting objects later. + cmDependsFortranParser parser(this, ppDefines, info); // Push on the starting file. cmDependsFortranParser_FilePush(&parser, src); @@ -882,10 +913,12 @@ bool cmDependsFortran::FindIncludeFile(const char* dir, //---------------------------------------------------------------------------- cmDependsFortranParser_s ::cmDependsFortranParser_s(cmDependsFortran* self, + std::set& ppDefines, cmDependsFortranSourceInfo& info): - Self(self), Info(info) + Self(self), PPDefinitions(ppDefines), Info(info) { this->InInterface = 0; + this->InPPFalseBranch = 0; // Initialize the lexical scanner. cmDependsFortran_yylex_init(&this->Scanner); @@ -986,6 +1019,11 @@ void cmDependsFortranParser_StringAppend(cmDependsFortranParser* parser, void cmDependsFortranParser_SetInInterface(cmDependsFortranParser* parser, bool in) { + if(parser->InPPFalseBranch) + { + return; + } + parser->InInterface = in; } @@ -1020,13 +1058,21 @@ void cmDependsFortranParser_Error(cmDependsFortranParser*, const char*) void cmDependsFortranParser_RuleUse(cmDependsFortranParser* parser, const char* name) { - parser->Info.Requires.insert(cmSystemTools::LowerCase(name) ); + if(!parser->InPPFalseBranch) + { + parser->Info.Requires.insert(cmSystemTools::LowerCase(name) ); + } } //---------------------------------------------------------------------------- void cmDependsFortranParser_RuleInclude(cmDependsFortranParser* parser, const char* name) { + if(parser->InPPFalseBranch) + { + return; + } + // If processing an include statement there must be an open file. assert(!parser->FileStack.empty()); @@ -1053,48 +1099,163 @@ void cmDependsFortranParser_RuleInclude(cmDependsFortranParser* parser, void cmDependsFortranParser_RuleModule(cmDependsFortranParser* parser, const char* name) { - if(!parser->InInterface ) + if(!parser->InPPFalseBranch && !parser->InInterface) { parser->Info.Provides.insert(cmSystemTools::LowerCase(name)); } } //---------------------------------------------------------------------------- -void cmDependsFortranParser_RuleDefine(cmDependsFortranParser*, const char*) +void cmDependsFortranParser_RuleDefine(cmDependsFortranParser* parser, + const char* macro) { + if(!parser->InPPFalseBranch) + { + parser->PPDefinitions.insert(macro); + } } //---------------------------------------------------------------------------- -void cmDependsFortranParser_RuleUndef(cmDependsFortranParser*, const char*) +void cmDependsFortranParser_RuleUndef(cmDependsFortranParser* parser, + const char* macro) { + if(!parser->InPPFalseBranch) + { + std::set::iterator match; + match = parser->PPDefinitions.find(macro); + if(match != parser->PPDefinitions.end()) + { + parser->PPDefinitions.erase(match); + } + } } //---------------------------------------------------------------------------- -void cmDependsFortranParser_RuleIfdef(cmDependsFortranParser*, const char*) +void cmDependsFortranParser_RuleIfdef(cmDependsFortranParser* parser, + const char* macro) { + // A new PP branch has been opened + parser->SkipToEnd.push(false); + + if (parser->InPPFalseBranch) + { + parser->InPPFalseBranch++; + } + else if(parser->PPDefinitions.find(macro) == parser->PPDefinitions.end()) + { + parser->InPPFalseBranch=1; + } + else + { + parser->SkipToEnd.top() = true; + } } //---------------------------------------------------------------------------- -void cmDependsFortranParser_RuleIfndef(cmDependsFortranParser*, const char*) +void cmDependsFortranParser_RuleIfndef(cmDependsFortranParser* parser, + const char* macro) { + // A new PP branch has been opened + parser->SkipToEnd.push(false); + + if (parser->InPPFalseBranch) + { + parser->InPPFalseBranch++; + } + else if(parser->PPDefinitions.find(macro) != parser->PPDefinitions.end()) + { + parser->InPPFalseBranch = 1; + } + else + { + // ignore other branches + parser->SkipToEnd.top() = true; + } } //---------------------------------------------------------------------------- -void cmDependsFortranParser_RuleIf(cmDependsFortranParser*) +void cmDependsFortranParser_RuleIf(cmDependsFortranParser* parser) { + /* Note: The current parser is _not_ able to get statements like + * #if 0 + * #if 1 + * #if MYSMBOL + * #if defined(MYSYMBOL) + * #if defined(MYSYMBOL) && ... + * right. The same for #elif. Thus in + * #if SYMBOL_1 + * .. + * #elif SYMBOL_2 + * ... + * ... + * #elif SYMBOL_N + * .. + * #else + * .. + * #endif + * _all_ N+1 branches are considered. If you got something like this + * #if defined(MYSYMBOL) + * #if !defined(MYSYMBOL) + * use + * #ifdef MYSYMBOL + * #ifndef MYSYMBOL + * instead. + */ + + // A new PP branch has been opened + // Never skip! See note above. + parser->SkipToEnd.push(false); } //---------------------------------------------------------------------------- -void cmDependsFortranParser_RuleElif(cmDependsFortranParser*) +void cmDependsFortranParser_RuleElif(cmDependsFortranParser* parser) { + /* Note: There are parser limitations. See the note at + * cmDependsFortranParser_RuleIf(..) + */ + + // Allways taken unless an #ifdef or #ifndef-branch has been taken + // already. If the second condition isn't meet already + // (parser->InPPFalseBranch == 0) correct it. + if(parser->SkipToEnd.top() && !parser->InPPFalseBranch) + { + parser->InPPFalseBranch = 1; + } } //---------------------------------------------------------------------------- -void cmDependsFortranParser_RuleElse(cmDependsFortranParser*) +void cmDependsFortranParser_RuleElse(cmDependsFortranParser* parser) { + // if the parent branch is false do nothing! + if(parser->InPPFalseBranch > 1) + { + return; + } + + // parser->InPPFalseBranch is either 0 or 1. We change it denpending on + // parser->SkipToEnd.top() + if(parser->SkipToEnd.top()) + { + parser->InPPFalseBranch = 1; + } + else + { + parser->InPPFalseBranch = 0; + } } //---------------------------------------------------------------------------- -void cmDependsFortranParser_RuleEndif(cmDependsFortranParser*) +void cmDependsFortranParser_RuleEndif(cmDependsFortranParser* parser) { + if(!parser->SkipToEnd.empty()) + { + parser->SkipToEnd.pop(); + } + + // #endif doesn't know if there was a "#else" in before, so it + // always decreases InPPFalseBranch + if(parser->InPPFalseBranch) + { + parser->InPPFalseBranch--; + } } diff --git a/Source/cmDependsFortran.h b/Source/cmDependsFortran.h index f6aaa7c2a..59e44da10 100644 --- a/Source/cmDependsFortran.h +++ b/Source/cmDependsFortran.h @@ -36,7 +36,8 @@ public: path from the build directory to the target file, the source file from which to start scanning, the include file search path, and the target directory. */ - cmDependsFortran(std::vector const& includes); + cmDependsFortran(std::vector const& includes, + std::vector const& defines); /** Virtual destructor to cleanup subclasses properly. */ virtual ~cmDependsFortran(); @@ -86,6 +87,7 @@ protected: // The include file search path. std::vector const* IncludePath; + std::vector PPDefinitions; // Internal implementation details. cmDependsFortranInternals* Internal; diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index 0976e1788..1613b82c7 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -1485,7 +1485,13 @@ cmLocalUnixMakefileGenerator3 #ifdef CMAKE_BUILD_WITH_CMAKE else if(lang == "Fortran") { - scanner = new cmDependsFortran(includes); + std::vector defines; + if(const char* c_defines = mf->GetDefinition("CMAKE_DEFINITIONS")) + { + cmSystemTools::ExpandListArgument(c_defines, defines); + } + + scanner = new cmDependsFortran(includes, defines); } else if(lang == "Java") { @@ -1845,6 +1851,14 @@ void cmLocalUnixMakefileGenerator3 << cid << "\")\n"; } } + + cmakefileStream + << "\n" + << "# Preprocessor definitions for this directory.\n" + << "SET(CMAKE_DEFINITIONS\n" + << " " << this->Makefile->GetDefineFlags() << "\n" + << " )\n"; + } //---------------------------------------------------------------------------- diff --git a/Tests/Fortran/CMakeLists.txt b/Tests/Fortran/CMakeLists.txt index 3a299b2a9..54f18d06d 100644 --- a/Tests/Fortran/CMakeLists.txt +++ b/Tests/Fortran/CMakeLists.txt @@ -23,6 +23,9 @@ IF(CMAKE_Fortran_COMPILER_SUPPORTS_F90) in_interface/main.f90 in_interface/module.f90) + ADD_DEFINITIONS(-DFOO -DBAR=1) + ADD_EXECUTABLE(test_preprocess test_preprocess.F90) + SET(TEST_MODULE_DEPENDS 1) ENDIF(CMAKE_Fortran_COMPILER_SUPPORTS_F90) diff --git a/Tests/Fortran/test_preprocess.F90 b/Tests/Fortran/test_preprocess.F90 new file mode 100644 index 000000000..374a6ff77 --- /dev/null +++ b/Tests/Fortran/test_preprocess.F90 @@ -0,0 +1,55 @@ +MODULE Available +! no conent +END MODULE + +PROGRAM PPTEST +! value of InPPFalseBranch ; values of SkipToEnd +! 0 +#ifndef FOO + ! 1 ; <0> + USE NotAvailable +# ifndef FOO + ! 2 ; <0,0> + USE NotAvailable +# else + ! 2 ; <0,0> + USE NotAvailable +# endif + ! 1 ; <0> +# ifdef FOO + ! 2 ; <0,1> + USE NotAvailable +# else + ! 2 ; <0,1> + USE NotAvailable +# endif + ! 1 ; <0> +#else + ! 0 ; <0> + USE Available +# ifndef FOO + ! 1 ; <0,0> + USE NotAvailable +# else + ! 0 ; <0,0> + USE Available +# endif + ! 0 ; <0> +# ifdef FOO + ! 0 ; <0,1> + USE Available +# else + ! 1 ; <0,1> + USE NotAvailable +# endif + ! 0 ; <0> +#endif +! 0 ; + +#ifdef BAR + PRINT * , 'BAR was defined via ADD_DEFINITIONS' +#else + PRINT *, 'If you can read this something went wrong' +#endif + +END PROGRAM