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

Ninja: Fix Fortran INCLUDE directive dependencies when not preprocessing

Since commit b0a6161190 (Fortran: Add Fortran_PREPROCESS property,
2020-04-24, v3.18.0-rc1~116^2~3), if `Fortran_PREPROCESS` is `OFF`, the
Ninja generator does not properly detect dependencies on sources loaded
via the Fortran INCLUDE directive.  Fix this and add a test.
Brad King 2 лет назад
Родитель
Сommit
1f1894af1f

+ 11 - 3
Source/cmGlobalNinjaGenerator.cxx

@@ -2227,7 +2227,7 @@ Compilation of source files within a target is split into the following steps:
       command = gfortran -cpp $DEFINES $INCLUDES $FLAGS -E $in -o $out &&
                 cmake -E cmake_ninja_depends \
                   --tdi=FortranDependInfo.json --lang=Fortran \
-                  --src=$out --dep=$DEP_FILE --obj=$OBJ_FILE \
+                  --src=$out --out=$out --dep=$DEP_FILE --obj=$OBJ_FILE \
                   --ddi=$DYNDEP_INTERMEDIATE_FILE
 
     build src.f90-pp.f90 | src.f90.o.ddi: Fortran_PREPROCESS src.f90
@@ -2304,6 +2304,7 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg,
 {
   std::string arg_tdi;
   std::string arg_src;
+  std::string arg_out;
   std::string arg_dep;
   std::string arg_obj;
   std::string arg_ddi;
@@ -2313,6 +2314,8 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg,
       arg_tdi = arg.substr(6);
     } else if (cmHasLiteralPrefix(arg, "--src=")) {
       arg_src = arg.substr(6);
+    } else if (cmHasLiteralPrefix(arg, "--out=")) {
+      arg_out = arg.substr(6);
     } else if (cmHasLiteralPrefix(arg, "--dep=")) {
       arg_dep = arg.substr(6);
     } else if (cmHasLiteralPrefix(arg, "--obj=")) {
@@ -2322,8 +2325,9 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg,
     } else if (cmHasLiteralPrefix(arg, "--lang=")) {
       arg_lang = arg.substr(7);
     } else if (cmHasLiteralPrefix(arg, "--pp=")) {
-      // CMake 3.26 and below used '--pp=' instead of '--src='.
+      // CMake 3.26 and below used '--pp=' instead of '--src=' and '--out='.
       arg_src = arg.substr(5);
+      arg_out = arg_src;
     } else {
       cmSystemTools::Error(
         cmStrCat("-E cmake_ninja_depends unknown argument: ", arg));
@@ -2338,6 +2342,10 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg,
     cmSystemTools::Error("-E cmake_ninja_depends requires value for --src=");
     return 1;
   }
+  if (arg_out.empty()) {
+    cmSystemTools::Error("-E cmake_ninja_depends requires value for --out=");
+    return 1;
+  }
   if (arg_dep.empty()) {
     cmSystemTools::Error("-E cmake_ninja_depends requires value for --dep=");
     return 1;
@@ -2374,7 +2382,7 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg,
 
   {
     cmGeneratedFileStream depfile(arg_dep);
-    depfile << cmSystemTools::ConvertToUnixOutputPath(arg_src) << ":";
+    depfile << cmSystemTools::ConvertToUnixOutputPath(arg_out) << ":";
     for (std::string const& include : info->Includes) {
       depfile << " \\\n " << cmSystemTools::ConvertToUnixOutputPath(include);
     }

+ 12 - 3
Source/cmNinjaTargetGenerator.cxx

@@ -545,7 +545,7 @@ std::string GetScanCommand(cm::string_view cmakeCmd, cm::string_view tdi,
                            cm::string_view ddiFile)
 {
   return cmStrCat(cmakeCmd, " -E cmake_ninja_depends --tdi=", tdi,
-                  " --lang=", lang, " --src=", srcFile,
+                  " --lang=", lang, " --src=", srcFile, " --out=$out",
                   " --dep=$DEP_FILE --obj=$OBJ_FILE --ddi=", ddiFile);
 }
 
@@ -1258,6 +1258,7 @@ namespace {
 cmNinjaBuild GetScanBuildStatement(const std::string& ruleName,
                                    const std::string& ppFileName,
                                    bool compilePP, bool compilePPWithDefines,
+                                   bool compilationPreprocesses,
                                    cmNinjaBuild& objBuild, cmNinjaVars& vars,
                                    const std::string& objectFileName,
                                    cmLocalGenerator* lg)
@@ -1314,6 +1315,13 @@ cmNinjaBuild GetScanBuildStatement(const std::string& ruleName,
   } else {
     scanBuild.Outputs.push_back(ddiFile);
     scanBuild.Variables["PREPROCESSED_OUTPUT_FILE"] = ppFileName;
+    if (!compilationPreprocesses) {
+      // Compilation does not preprocess and we are not compiling an
+      // already-preprocessed source.  Make compilation depend on the scan
+      // results to honor implicit dependencies discovered during scanning
+      // (such as Fortran INCLUDE directives).
+      objBuild.ImplicitDeps.emplace_back(ddiFile);
+    }
   }
 
   // Scanning always provides a depfile for preprocessor dependencies. This
@@ -1520,8 +1528,9 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
     }
 
     cmNinjaBuild ppBuild = GetScanBuildStatement(
-      scanRuleName, ppFileName, compilePP, compilePPWithDefines, objBuild,
-      vars, objectFileName, this->LocalGenerator);
+      scanRuleName, ppFileName, compilePP, compilePPWithDefines,
+      compilationPreprocesses, objBuild, vars, objectFileName,
+      this->LocalGenerator);
 
     if (compilePP) {
       // In case compilation requires flags that are incompatible with

+ 1 - 0
Tests/RunCMake/BuildDepends/FortranInclude-build1-stderr.txt

@@ -0,0 +1 @@
+.*

+ 1 - 0
Tests/RunCMake/BuildDepends/FortranInclude-build2-stderr.txt

@@ -0,0 +1 @@
+.*

+ 20 - 0
Tests/RunCMake/BuildDepends/FortranInclude.cmake

@@ -0,0 +1,20 @@
+enable_language(Fortran)
+
+set(check_pairs "")
+
+add_executable(preprocess FortranIncludePreprocess.F)
+set_property(TARGET preprocess PROPERTY Fortran_PREPROCESS ON)
+target_include_directories(preprocess PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
+list(APPEND check_pairs "$<TARGET_FILE:preprocess>|${CMAKE_CURRENT_BINARY_DIR}/preprocess.inc")
+
+# LCC < 1.24 has no way to disable Fortran preprocessor
+if(NOT CMAKE_Fortran_COMPILER_ID STREQUAL "LCC" OR CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL "1.24.00")
+  add_executable(no_preprocess FortranIncludeNoPreprocess.f)
+  set_property(TARGET no_preprocess PROPERTY Fortran_PREPROCESS OFF)
+  target_include_directories(no_preprocess PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
+  list(APPEND check_pairs "$<TARGET_FILE:no_preprocess>|${CMAKE_CURRENT_BINARY_DIR}/no_preprocess.inc")
+endif()
+
+file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/check-$<LOWER_CASE:$<CONFIG>>.cmake CONTENT "
+set(check_pairs \"${check_pairs}\")
+")

+ 2 - 0
Tests/RunCMake/BuildDepends/FortranInclude.step1.cmake

@@ -0,0 +1,2 @@
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/preprocess.inc" "\tPRINT *, 'FortranIncludePreprocess 1'\n")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/no_preprocess.inc" "\tPRINT *, 'FortranIncludeNoPreprocess 1'\n")

+ 2 - 0
Tests/RunCMake/BuildDepends/FortranInclude.step2.cmake

@@ -0,0 +1,2 @@
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/preprocess.inc" "\tPRINT *, 'FortranIncludePreprocess 2'\n")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/no_preprocess.inc" "\tPRINT *, 'FortranIncludeNoPreprocess 2'\n")

+ 3 - 0
Tests/RunCMake/BuildDepends/FortranIncludeNoPreprocess.f

@@ -0,0 +1,3 @@
+        PROGRAM FortranIncludeNoPreprocess
+        INCLUDE 'no_preprocess.inc'
+        END

+ 3 - 0
Tests/RunCMake/BuildDepends/FortranIncludePreprocess.F

@@ -0,0 +1,3 @@
+        PROGRAM FortranIncludePreprocess
+#include "preprocess.inc"
+        END

+ 4 - 0
Tests/RunCMake/BuildDepends/RunCMakeTest.cmake

@@ -68,6 +68,10 @@ if(NOT RunCMake_GENERATOR STREQUAL "Xcode")
   unset(run_BuildDepends_skip_step_2)
 endif()
 
+if(CMake_TEST_Fortran)
+  run_BuildDepends(FortranInclude)
+endif()
+
 run_BuildDepends(Custom-Symbolic-and-Byproduct)
 run_BuildDepends(Custom-Always)
 

+ 4 - 0
Tests/RunCMake/CMakeLists.txt

@@ -292,6 +292,10 @@ if(NOT DEFINED CMake_TEST_BuildDepends_GNU_AS
   endif()
 endif()
 
+if(CMAKE_Fortran_COMPILER)
+  list(APPEND BuildDepends_ARGS -DCMake_TEST_Fortran=1)
+endif()
+
 add_RunCMake_test(BuildDepends
   -DMSVC_VERSION=${MSVC_VERSION}
   -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}