Browse Source

Ninja: Fix Fortran module deps in files INCLUDEd by preprocessed sources

If a preprocessed source also uses the Fortran `INCLUDE` directive,
search the original source file's directory.

Fixes: #25792
Brad King 1 year ago
parent
commit
36dc8d6d50

+ 18 - 3
Source/cmGlobalNinjaGenerator.cxx

@@ -2327,7 +2327,8 @@ struct cmSourceInfo
 };
 
 cm::optional<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran(
-  std::string const& arg_tdi, std::string const& arg_src);
+  std::string const& arg_tdi, std::string const& arg_src,
+  std::string const& arg_src_orig);
 }
 
 int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg,
@@ -2335,6 +2336,7 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg,
 {
   std::string arg_tdi;
   std::string arg_src;
+  std::string arg_src_orig;
   std::string arg_out;
   std::string arg_dep;
   std::string arg_obj;
@@ -2345,6 +2347,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, "--src-orig=")) {
+      arg_src_orig = arg.substr(11);
     } else if (cmHasLiteralPrefix(arg, "--out=")) {
       arg_out = arg.substr(6);
     } else if (cmHasLiteralPrefix(arg, "--dep=")) {
@@ -2396,7 +2400,7 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg,
 
   cm::optional<cmSourceInfo> info;
   if (arg_lang == "Fortran") {
-    info = cmcmd_cmake_ninja_depends_fortran(arg_tdi, arg_src);
+    info = cmcmd_cmake_ninja_depends_fortran(arg_tdi, arg_src, arg_src_orig);
   } else {
     cmSystemTools::Error(
       cmStrCat("-E cmake_ninja_depends does not understand the ", arg_lang,
@@ -2431,13 +2435,24 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg,
 namespace {
 
 cm::optional<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran(
-  std::string const& arg_tdi, std::string const& arg_src)
+  std::string const& arg_tdi, std::string const& arg_src,
+  std::string const& arg_src_orig)
 {
   cm::optional<cmSourceInfo> info;
   cmFortranCompiler fc;
   std::vector<std::string> includes;
   std::string dir_top_bld;
   std::string module_dir;
+
+  if (!arg_src_orig.empty()) {
+    // Prepend the original source file's directory as an include directory
+    // so Fortran INCLUDE statements can look for files in it.
+    std::string src_orig_dir = cmSystemTools::GetParentDirectory(arg_src_orig);
+    if (!src_orig_dir.empty()) {
+      includes.push_back(src_orig_dir);
+    }
+  }
+
   {
     Json::Value tdio;
     Json::Value const& tdi = tdio;

+ 9 - 6
Source/cmNinjaTargetGenerator.cxx

@@ -14,6 +14,7 @@
 #include <utility>
 
 #include <cm/memory>
+#include <cm/optional>
 #include <cm/string_view>
 #include <cmext/algorithm>
 #include <cmext/string_view>
@@ -589,13 +590,15 @@ void cmNinjaTargetGenerator::WriteLanguageRules(const std::string& language,
 
 namespace {
 // Create the command to run the dependency scanner
-std::string GetScanCommand(cm::string_view cmakeCmd, cm::string_view tdi,
-                           cm::string_view lang, cm::string_view srcFile,
-                           cm::string_view ddiFile)
+std::string GetScanCommand(
+  cm::string_view cmakeCmd, cm::string_view tdi, cm::string_view lang,
+  cm::string_view srcFile, cm::string_view ddiFile,
+  cm::optional<cm::string_view> srcOrigFile = cm::nullopt)
 {
   return cmStrCat(cmakeCmd, " -E cmake_ninja_depends --tdi=", tdi,
                   " --lang=", lang, " --src=", srcFile, " --out=$out",
-                  " --dep=$DEP_FILE --obj=$OBJ_FILE --ddi=", ddiFile);
+                  " --dep=$DEP_FILE --obj=$OBJ_FILE --ddi=", ddiFile,
+                  srcOrigFile ? cmStrCat(" --src-orig=", *srcOrigFile) : "");
 }
 
 // Helper function to create dependency scanning rule that may or may
@@ -759,8 +762,8 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang,
         for (auto& i : scanCommands) {
           i = cmStrCat(launcher, i);
         }
-        scanCommands.emplace_back(GetScanCommand(cmakeCmd, tdi, lang, "$out",
-                                                 "$DYNDEP_INTERMEDIATE_FILE"));
+        scanCommands.emplace_back(GetScanCommand(
+          cmakeCmd, tdi, lang, "$out", "$DYNDEP_INTERMEDIATE_FILE", "$in"));
       }
 
       auto scanRule = GetScanRule(

+ 1 - 1
Tests/FortranModules/CMakeLists.txt

@@ -67,7 +67,7 @@ add_definitions(-DFOO -DBAR=1)
 include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
 add_executable(test_preprocess test_preprocess.F90 test_preprocess_module.F90)
 
-add_executable(test_non_pp_include test_non_pp_include_main.f90)
+add_executable(test_non_pp_include test_non_pp_include_main.f90 test_non_pp_include_module.f90)
 
 # Build the external project separately using a custom target.
 # Make sure it uses the same build configuration as this test.

+ 4 - 3
Tests/FortranModules/non_pp_include.f90

@@ -1,3 +1,4 @@
-SUBROUTINE NON_PP_INCLUDE_SUBROUTINE
-   PRINT *, "Hello World!"
-END SUBROUTINE NON_PP_INCLUDE_SUBROUTINE
+subroutine non_pp_include_subroutine
+  use non_pp_include_module
+  call non_pp_include_module_subroutine
+end subroutine

+ 4 - 4
Tests/FortranModules/test_non_pp_include_main.f90

@@ -1,5 +1,5 @@
-INCLUDE "non_pp_include.f90"
+include "non_pp_include.f90"
 
-PROGRAM MAINF90
-   CALL NON_PP_INCLUDE_SUBROUTINE
-END PROGRAM MAINF90
+program test_non_pp_include_main
+  call non_pp_include_subroutine
+end program

+ 6 - 0
Tests/FortranModules/test_non_pp_include_module.f90

@@ -0,0 +1,6 @@
+module non_pp_include_module
+contains
+  subroutine non_pp_include_module_subroutine
+    print *, "Hello World!"
+  end subroutine
+end module