Explorar o código

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.

Brad King %!s(int64=18) %!d(string=hai) anos
pai
achega
b761da39c1

+ 3 - 1
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:<TARGET> /dll  <LINK_FLAGS> <OBJECTS> <LINK_LIBRARIES> ${CMAKE_END_TEMP_FILE}")
 
@@ -17,7 +19,7 @@ SET(CMAKE_Fortran_CREATE_STATIC_LIBRARY  "lib ${CMAKE_CL_NOLOGO} <LINK_FLAGS> /o
 
 # compile a C++ file into an object file
 SET(CMAKE_Fortran_COMPILE_OBJECT
-    "<CMAKE_Fortran_COMPILER>  ${CMAKE_START_TEMP_FILE} ${CMAKE_CL_NOLOGO} /Fo<OBJECT> <FLAGS> -c <SOURCE>${CMAKE_END_TEMP_FILE}")
+    "<CMAKE_Fortran_COMPILER>  ${CMAKE_START_TEMP_FILE} ${CMAKE_CL_NOLOGO} /fpp /Fo<OBJECT> <FLAGS> -c <SOURCE>${CMAKE_END_TEMP_FILE}")
 
 SET(CMAKE_COMPILE_RESOURCE "rc <FLAGS> /fo<OBJECT> <SOURCE>")
 

+ 180 - 19
Source/cmDependsFortran.cxx

@@ -70,6 +70,7 @@ struct cmDependsFortranFile
 struct cmDependsFortranParser_s
 {
   cmDependsFortranParser_s(cmDependsFortran* self,
+                           std::set<std::string>& ppDefines,
                            cmDependsFortranSourceInfo& info);
   ~cmDependsFortranParser_s();
 
@@ -89,9 +90,9 @@ struct cmDependsFortranParser_s
   bool InInterface;
 
   int OldStartcond;
-  bool InPPFalseBranch;
-  std::vector<bool> SkipToEnd;
-  int StepI;
+  std::set<std::string> PPDefinitions;
+  std::size_t InPPFalseBranch;
+  std::stack<bool> 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<std::string> const& includes):
-  IncludePath(&includes), Internal(new cmDependsFortranInternals)
+cmDependsFortran
+::cmDependsFortran(std::vector<std::string> const& includes,
+                   std::vector<std::string> 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<std::string>::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<std::string> 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<std::string>& 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<std::string>::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--;
+    }
 }

+ 3 - 1
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<std::string> const& includes);
+  cmDependsFortran(std::vector<std::string> const& includes,
+    std::vector<std::string> const& defines);
 
   /** Virtual destructor to cleanup subclasses properly.  */
   virtual ~cmDependsFortran();
@@ -86,6 +87,7 @@ protected:
 
   // The include file search path.
   std::vector<std::string> const* IncludePath;
+  std::vector<std::string> PPDefinitions;
 
   // Internal implementation details.
   cmDependsFortranInternals* Internal;

+ 15 - 1
Source/cmLocalUnixMakefileGenerator3.cxx

@@ -1485,7 +1485,13 @@ cmLocalUnixMakefileGenerator3
 #ifdef CMAKE_BUILD_WITH_CMAKE
     else if(lang == "Fortran")
       {
-      scanner = new cmDependsFortran(includes);
+      std::vector<std::string> 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";
+
 }
 
 //----------------------------------------------------------------------------

+ 3 - 0
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)
 

+ 55 - 0
Tests/Fortran/test_preprocess.F90

@@ -0,0 +1,55 @@
+MODULE Available
+! no conent
+END MODULE
+
+PROGRAM PPTEST
+! value of InPPFalseBranch ; values of SkipToEnd
+! 0 <empty>
+#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 ; <empty>
+
+#ifdef BAR
+  PRINT * , 'BAR was defined via ADD_DEFINITIONS'
+#else
+  PRINT *, 'If you can read this something went wrong'
+#endif
+
+END PROGRAM