Browse Source

Fortran: Add support for submodule dependencies

Since commit v3.7.0-rc1~73^2~1 (Fortran: Add support for submodule
syntax in dependency scanning, 2016-09-05) we support parsing Fortran
sources that use submodule syntax, but it left addition of `.smod`
dependencies to future work.  Add it now.

The syntax

    submodule (module_name) submodule_name

means the current source requires `module_name.mod` and provides
`module_name@submodule_name.smod`.  The syntax

    submodule (module_name:submodule_name) nested_submodule_name

means the current source requires `module_name@submodule_name.smod`
provides `module_name@nested_submodule_name.smod`.

Fixes: #17017
Brad King 7 years ago
parent
commit
402735314e

+ 7 - 0
Help/release/dev/fortran-submodule-depends.rst

@@ -0,0 +1,7 @@
+fortran-submodule-depends
+-------------------------
+
+* Fortran dependency scanning now supports dependencies implied by
+  `Fortran Submodules`_.
+
+.. _`Fortran Submodules`: http://fortranwiki.org/fortran/show/Submodules

+ 2 - 2
Source/LexerParser/cmFortranParser.cxx

@@ -1563,7 +1563,7 @@ yyreduce:
 #line 119 "cmFortranParser.y" /* yacc.c:1646  */
     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
-    cmFortranParser_RuleUse(parser, (yyvsp[-4].string));
+    cmFortranParser_RuleSubmodule(parser, (yyvsp[-4].string), (yyvsp[-2].string));
     free((yyvsp[-4].string));
     free((yyvsp[-2].string));
   }
@@ -1574,7 +1574,7 @@ yyreduce:
 #line 125 "cmFortranParser.y" /* yacc.c:1646  */
     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
-    cmFortranParser_RuleUse(parser, (yyvsp[-6].string));
+    cmFortranParser_RuleSubmoduleNested(parser, (yyvsp[-6].string), (yyvsp[-4].string), (yyvsp[-2].string));
     free((yyvsp[-6].string));
     free((yyvsp[-4].string));
     free((yyvsp[-2].string));

+ 2 - 2
Source/LexerParser/cmFortranParser.y

@@ -118,13 +118,13 @@ stmt:
   }
 | SUBMODULE LPAREN WORD RPAREN WORD other EOSTMT {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
-    cmFortranParser_RuleUse(parser, $3);
+    cmFortranParser_RuleSubmodule(parser, $3, $5);
     free($3);
     free($5);
   }
 | SUBMODULE LPAREN WORD COLON WORD RPAREN WORD other EOSTMT {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
-    cmFortranParser_RuleUse(parser, $3);
+    cmFortranParser_RuleSubmoduleNested(parser, $3, $5, $7);
     free($3);
     free($5);
     free($7);

+ 5 - 2
Source/cmDependsFortran.cxx

@@ -31,6 +31,8 @@ static void cmFortranModuleAppendUpperLower(std::string const& mod,
   std::string::size_type ext_len = 0;
   if (cmHasLiteralSuffix(mod, ".mod")) {
     ext_len = 4;
+  } else if (cmHasLiteralSuffix(mod, ".smod")) {
+    ext_len = 5;
   }
   std::string const& name = mod.substr(0, mod.size() - ext_len);
   std::string const& ext = mod.substr(mod.size() - ext_len);
@@ -283,7 +285,8 @@ void cmDependsFortran::MatchRemoteModules(std::istream& fin,
     if (line[0] == ' ') {
       if (doing_provides) {
         std::string mod = line;
-        if (!cmHasLiteralSuffix(mod, ".mod")) {
+        if (!cmHasLiteralSuffix(mod, ".mod") &&
+            !cmHasLiteralSuffix(mod, ".smod")) {
           // Support fortran.internal files left by older versions of CMake.
           // They do not include the ".mod" extension.
           mod += ".mod";
@@ -486,7 +489,7 @@ bool cmDependsFortran::CopyModule(const std::vector<std::string>& args)
   if (args.size() >= 5) {
     compilerId = args[4];
   }
-  if (!cmHasLiteralSuffix(mod, ".mod")) {
+  if (!cmHasLiteralSuffix(mod, ".mod") && !cmHasLiteralSuffix(mod, ".smod")) {
     // Support depend.make files left by older versions of CMake.
     // They do not include the ".mod" extension.
     mod += ".mod";

+ 7 - 0
Source/cmFortranParser.h

@@ -45,6 +45,13 @@ void cmFortranParser_RuleLineDirective(cmFortranParser* parser,
 void cmFortranParser_RuleInclude(cmFortranParser* parser, const char* name);
 void cmFortranParser_RuleModule(cmFortranParser* parser,
                                 const char* module_name);
+void cmFortranParser_RuleSubmodule(cmFortranParser* parser,
+                                   const char* module_name,
+                                   const char* submodule_name);
+void cmFortranParser_RuleSubmoduleNested(cmFortranParser* parser,
+                                         const char* module_name,
+                                         const char* submodule_name,
+                                         const char* nested_submodule_name);
 void cmFortranParser_RuleDefine(cmFortranParser* parser, const char* name);
 void cmFortranParser_RuleUndef(cmFortranParser* parser, const char* name);
 void cmFortranParser_RuleIfdef(cmFortranParser* parser, const char* name);

+ 44 - 0
Source/cmFortranParserImpl.cxx

@@ -245,6 +245,50 @@ void cmFortranParser_RuleModule(cmFortranParser* parser,
   }
 }
 
+void cmFortranParser_RuleSubmodule(cmFortranParser* parser,
+                                   const char* module_name,
+                                   const char* submodule_name)
+{
+  if (parser->InPPFalseBranch) {
+    return;
+  }
+
+  // syntax:   "submodule (module_name) submodule_name"
+  // requires: "module_name.mod"
+  // provides: "module_name@submodule_name.smod"
+  //
+  // FIXME: Some compilers split the submodule part of a module into a
+  // separate "module_name.smod" file.  Whether it is generated or
+  // not depends on conditions more subtle than we currently detect.
+  // For now we depend directly on "module_name.mod".
+
+  std::string const& mod_name = cmSystemTools::LowerCase(module_name);
+  std::string const& sub_name = cmSystemTools::LowerCase(submodule_name);
+  parser->Info.Requires.insert(mod_name + ".mod");
+  parser->Info.Provides.insert(mod_name + "@" + sub_name + ".smod");
+}
+
+void cmFortranParser_RuleSubmoduleNested(cmFortranParser* parser,
+                                         const char* module_name,
+                                         const char* submodule_name,
+                                         const char* nested_submodule_name)
+{
+  if (parser->InPPFalseBranch) {
+    return;
+  }
+
+  // syntax:   "submodule (module_name:submodule_name) nested_submodule_name"
+  // requires: "module_name@submodule_name.smod"
+  // provides: "module_name@nested_submodule_name.smod"
+
+  std::string const& mod_name = cmSystemTools::LowerCase(module_name);
+  std::string const& sub_name = cmSystemTools::LowerCase(submodule_name);
+  std::string const& nest_name =
+    cmSystemTools::LowerCase(nested_submodule_name);
+  parser->Info.Requires.insert(mod_name + "@" + sub_name + ".smod");
+  parser->Info.Provides.insert(mod_name + "@" + nest_name + ".smod");
+}
+
 void cmFortranParser_RuleDefine(cmFortranParser* parser, const char* macro)
 {
   if (!parser->InPPFalseBranch) {

+ 20 - 1
Tests/FortranModules/Submodules/CMakeLists.txt

@@ -1 +1,20 @@
-add_executable(submod main.f90 provide.f90)
+# The program units in this file consist of a module/submodule
+# tree represented by the following graph:
+#
+#        parent
+#          |
+#         / \
+#        /   \
+#    child   sibling
+#      |
+#  grandchild
+#
+# where the parent node is a module and all other nodes are submodules.
+
+add_executable(submod
+  main.f90
+  parent.f90
+  child.f90
+  grandchild.f90
+  sibling.f90
+  )

+ 10 - 0
Tests/FortranModules/Submodules/child.f90

@@ -0,0 +1,10 @@
+! Test the notation for a 1st-generation direct
+! descendant of a parent module
+submodule ( parent ) child
+  implicit none
+contains
+  module function child_function() result(child_stuff)
+    logical :: child_stuff
+    child_stuff=.true.
+  end function
+end submodule child

+ 8 - 0
Tests/FortranModules/Submodules/grandchild.f90

@@ -0,0 +1,8 @@
+! Test the notation for an Nth-generation descendant
+! for N>1, which necessitates the colon.
+submodule ( parent : child ) grandchild
+contains
+  module subroutine grandchild_subroutine()
+    print *,"Test passed."
+  end subroutine
+end submodule grandchild

+ 17 - 0
Tests/FortranModules/Submodules/parent.f90

@@ -0,0 +1,17 @@
+module parent
+  implicit none
+
+  interface
+
+    ! Test Fortran 2008 "module function" syntax
+    module function child_function() result(child_stuff)
+      logical :: child_stuff
+    end function
+
+    ! Test Fortran 2008 "module subroutine" syntax
+    module subroutine grandchild_subroutine()
+    end subroutine
+
+  end interface
+
+end module parent

+ 0 - 57
Tests/FortranModules/Submodules/provide.f90

@@ -1,57 +0,0 @@
-! The program units in this file consist of a
-! module/submodule tree represented by the following
-! graph:
-!
-!        parent
-!          |
-!         / \
-!        /   \
-!    child   sibling
-!      |
-!  grandchild
-!
-! where the parent node is a module and all other
-! nodes are submodules.
-
-module parent
-  implicit none
-
-  interface
-
-    ! Test Fortran 2008 "module function" syntax
-    module function child_function() result(child_stuff)
-      logical :: child_stuff
-    end function
-
-    ! Test Fortran 2008 "module subroutine" syntax
-    module subroutine grandchild_subroutine()
-    end subroutine
-
-  end interface
-
-end module parent
-
-! Test the notation for a 1st-generation direct
-! descendant of a parent module
-submodule ( parent ) child
-  implicit none
-contains
-  module function child_function() result(child_stuff)
-    logical :: child_stuff
-    child_stuff=.true.
-  end function
-end submodule child
-
-! Empty submodule for checking disambiguation of
-! nodes at the same vertical level in the tree
-submodule ( parent ) sibling
-end submodule sibling
-
-! Test the notation for an Nth-generation descendant
-! for N>1, which necessitates the colon.
-submodule ( parent : child ) grandchild
-contains
-  module subroutine grandchild_subroutine()
-    print *,"Test passed."
-  end subroutine
-end submodule grandchild

+ 4 - 0
Tests/FortranModules/Submodules/sibling.f90

@@ -0,0 +1,4 @@
+! Empty submodule for checking disambiguation of
+! nodes at the same vertical level in the tree
+submodule ( parent ) sibling
+end submodule sibling