Просмотр исходного кода

Makefiles: Avoid nested make calls for Fortran module dependencies

Makefiles generated by cmake use a series of nested calls to build
`*.provides.build` targets that are used when the 'requires' step is
needed.  That leads to significant degradation of the build time for
incremental builds.  Re-arrange dependencies to eliminate the nested
calls.

Explicit `.mod.stamp` targets introduced by this commit could lead to
situation when a stamp file always older than its dependency.  This
happens during the incremental build when building of an updated Fortran
source produces a module file that has no differences from the stored
stamp file.  In such case `cmake_copy_f90_mod` will be triggered on each
new build to compare a module file with the corresponding stamp file.
This behavior is expected and can not be changed without nested calls
that slow down the build.  The copy-if-different check is much cheaper
than an entire nested make call.
Yurii Batrak 8 лет назад
Родитель
Сommit
5f2e2c387d

+ 19 - 5
Source/cmDependsFortran.cxx

@@ -388,7 +388,6 @@ bool cmDependsFortran::WriteDependenciesReal(const char* obj,
   if (!info.Provides.empty()) {
     // Create a target to copy the module after the object file
     // changes.
-    makeDepends << obj_m << ".provides.build:\n";
     for (std::string const& i : info.Provides) {
       // Include this module in the set provided by this target.
       this->Internal->TargetProvides.insert(i);
@@ -407,11 +406,25 @@ bool cmDependsFortran::WriteDependenciesReal(const char* obj,
       stampFile += "/";
       stampFile += m;
       stampFile += ".mod.stamp";
-      stampFile = this->LocalGenerator->ConvertToOutputFormat(
-        this->MaybeConvertToRelativePath(binDir, stampFile),
-        cmOutputConverter::SHELL);
+      stampFile = this->MaybeConvertToRelativePath(binDir, stampFile);
+      std::string const stampFileForShell =
+        this->LocalGenerator->ConvertToOutputFormat(stampFile,
+                                                    cmOutputConverter::SHELL);
+      std::string const stampFileForMake =
+        cmSystemTools::ConvertToOutputPath(stampFile.c_str());
+
+      makeDepends << obj_m << ".provides.build"
+                  << ": " << stampFileForMake << "\n";
+      // Note that when cmake_copy_f90_mod finds that a module file
+      // and the corresponding stamp file have no differences, the stamp
+      // file is not updated. In such case the stamp file will be always
+      // older than its prerequisite and trigger cmake_copy_f90_mod
+      // on each new build. This is expected behavior for incremental
+      // builds and can not be changed without preforming recursive make
+      // calls that would considerably slow down the building process.
+      makeDepends << stampFileForMake << ": " << obj_m << "\n";
       makeDepends << "\t$(CMAKE_COMMAND) -E cmake_copy_f90_mod " << modFile
-                  << " " << stampFile;
+                  << " " << stampFileForShell;
       cmMakefile* mf = this->LocalGenerator->GetMakefile();
       const char* cid = mf->GetDefinition("CMAKE_Fortran_COMPILER_ID");
       if (cid && *cid) {
@@ -419,6 +432,7 @@ bool cmDependsFortran::WriteDependenciesReal(const char* obj,
       }
       makeDepends << "\n";
     }
+    makeDepends << obj_m << ".provides.build:\n";
     // After copying the modules update the timestamp file so that
     // copying will not be done again until the source rebuilds.
     makeDepends << "\t$(CMAKE_COMMAND) -E touch " << obj_m

+ 2 - 0
Source/cmLocalUnixMakefileGenerator3.cxx

@@ -1444,6 +1444,8 @@ bool cmLocalUnixMakefileGenerator3::ScanDependencies(
     }
 #ifdef CMAKE_BUILD_WITH_CMAKE
     else if (lang == "Fortran") {
+      ruleFileStream << "# Note that incremental build could trigger "
+                     << "a call to cmake_copy_f90_mod on each re-build\n";
       scanner = new cmDependsFortran(this);
     } else if (lang == "Java") {
       scanner = new cmDependsJava();

+ 1 - 5
Source/cmMakefileTargetGenerator.cxx

@@ -835,14 +835,10 @@ void cmMakefileTargetGenerator::WriteObjectBuildFile(
   std::string temp = relativeObj;
   temp += ".provides.build";
   std::vector<std::string> r_commands;
-  std::string tgtMakefileName =
-    this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget);
-  tgtMakefileName += "/build.make";
-  r_commands.push_back(
-    this->LocalGenerator->GetRecursiveMakeCall(tgtMakefileName.c_str(), temp));
 
   p_depends.clear();
   p_depends.push_back(objectRequires);
+  p_depends.push_back(temp);
   this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr,
                                       objectProvides, p_depends, r_commands,
                                       true);