https://github.com/Thirumalai-Shaktivel created 
https://github.com/llvm/llvm-project/pull/209379

Implement the GCC/Clang dependency-file flags for flang, which it
previously rejected as unknown arguments: -M, -MM, -MD, -MMD, -MF,
-MT and -MQ.

Feature:
 -  -MD/-MMD : write a .d file alongside a normal compile
 -  -M/-MM   : prescan only; emit deps to stdout by default
 -  -MF      : dependency-file path
 -  -MT/-MQ  : target name (verbatim / Make-quoted)
  (-M==-MM and -MD==-MMD; Fortran has no system/user header split.)

Design:
- `-M`/`-MM`: driver passes `-Eonly -dependency-file - -MT <target>`
  to `-fc1`. `-Eonly` selects `RunPreprocessorOnly`, which runs only
  the prescanner and produces no compiled output. The dependency file
  is the sole result, written to stdout by default.

- `-MD`/`-MMD`: driver passes `-dependency-file foo.d -MT <target>`
  alongside the normal compile action. The object file is produced as
  usual, and the dependency file is written as a side effect.

- `-MF <path>` overrides where the dependency file is written.
  `-MT <target>` sets the target name (the part before the colon in
  the Make rule) verbatim. `-MQ` does the same but additionally
  quotes characters that are special to Make.

- In `-fc1`, `-dependency-file` and `-MT` are stored in the frontend
  options. After the prescan, `writeDependencyFile()` is called
  unconditionally regardless of the action. It collects every file
  opened during prescan — the source plus all `INCLUDE`/`#include`
  files — via `GetIncludedFilePaths()`, then writes the
  `target: source includes...` Make rule to the specified file, or
  stdout if the path is `-`.

>From 95ff6cb5b546f40bb0378d2b53a068cc0a6f58ea Mon Sep 17 00:00:00 2001
From: Thirumalai-Shaktivel <[email protected]>
Date: Wed, 24 Jun 2026 13:13:27 +0530
Subject: [PATCH 1/2] [flang][driver] Support Makefile dependency generation
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Implement the GCC/Clang dependency-file flags for flang, which it
previously rejected as unknown arguments: -M, -MM, -MD, -MMD, -MF,
-MT and -MQ.

Feature:
 -  -MD/-MMD : write a .d file alongside a normal compile
 -  -M/-MM   : prescan only; emit deps to stdout by default
 -  -MF      : dependency-file path
 -  -MT/-MQ  : target name (verbatim / Make-quoted)
  (-M==-MM and -MD==-MMD; Fortran has no system/user header split.)

Design:
- `-M`/`-MM`: driver passes `-Eonly -dependency-file - -MT <target>`
  to `-fc1`. `-Eonly` selects `RunPreprocessorOnly`, which runs only
  the prescanner and produces no compiled output. The dependency file
  is the sole result, written to stdout by default.

- `-MD`/`-MMD`: driver passes `-dependency-file foo.d -MT <target>`
  alongside the normal compile action. The object file is produced as
  usual, and the dependency file is written as a side effect.

- `-MF <path>` overrides where the dependency file is written.
  `-MT <target>` sets the target name (the part before the colon in
  the Make rule) verbatim. `-MQ` does the same but additionally
  quotes characters that are special to Make.

- In `-fc1`, `-dependency-file` and `-MT` are stored in the frontend
  options. After the prescan, `writeDependencyFile()` is called
  unconditionally regardless of the action. It collects every file
  opened during prescan — the source plus all `INCLUDE`/`#include`
  files — via `GetIncludedFilePaths()`, then writes the
  `target: source includes...` Make rule to the specified file, or
  stdout if the path is `-`.
---
 clang/include/clang/Basic/MakeSupport.h       |  20 +++
 .../clang/Frontend/DependencyOutputOptions.h  |   4 +-
 clang/include/clang/Options/Options.td        |  18 ++-
 clang/lib/Basic/MakeSupport.cpp               | 144 ++++++++++++++++++
 clang/lib/Driver/ToolChains/Flang.cpp         | 107 ++++++++++++-
 clang/lib/Frontend/DependencyFile.cpp         | 137 +----------------
 flang/docs/FlangDriver.md                     |  15 ++
 flang/include/flang/Frontend/FrontendAction.h |   2 +
 .../include/flang/Frontend/FrontendActions.h  |   6 +
 .../include/flang/Frontend/FrontendOptions.h  |   9 ++
 flang/include/flang/Parser/provenance.h       |   5 +
 flang/lib/Frontend/CompilerInvocation.cpp     |   6 +
 flang/lib/Frontend/FrontendAction.cpp         |  42 +++++
 .../ExecuteCompilerInvocation.cpp             |   2 +
 flang/lib/Parser/provenance.cpp               |  15 ++
 flang/test/Driver/dependency-file-gen-mod.f90 |  33 ++++
 flang/test/Driver/dependency-file-gen.f90     |  31 ++++
 flang/test/Driver/dependency-file.f90         | 106 +++++++++++++
 18 files changed, 557 insertions(+), 145 deletions(-)
 create mode 100644 flang/test/Driver/dependency-file-gen-mod.f90
 create mode 100644 flang/test/Driver/dependency-file-gen.f90
 create mode 100644 flang/test/Driver/dependency-file.f90

diff --git a/clang/include/clang/Basic/MakeSupport.h 
b/clang/include/clang/Basic/MakeSupport.h
index c663014ba7bcf..5cd5dd92c5476 100644
--- a/clang/include/clang/Basic/MakeSupport.h
+++ b/clang/include/clang/Basic/MakeSupport.h
@@ -10,6 +10,7 @@
 #define LLVM_CLANG_BASIC_MAKESUPPORT_H
 
 #include "clang/Basic/LLVM.h"
+#include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/StringRef.h"
 
 namespace clang {
@@ -18,6 +19,25 @@ namespace clang {
 /// Only the characters '$', '#', ' ', '\t' are quoted.
 void quoteMakeTarget(StringRef Target, SmallVectorImpl<char> &Res);
 
+/// DependencyOutputFormat - Format for the compiler dependency file.
+enum class DependencyOutputFormat { Make, NMake };
+
+/// Write Make-style dependency output to the output stream, in the form:
+///   target1 target2 ...: prereq1 prereq2 ...
+///
+/// \param Targets The targets, already quoted for Make via quoteMakeTarget().
+/// \param Files The prerequisites; each is escaped for \p Format when written.
+/// \param Format Escape prerequisites for GNU Make or NMake.
+/// \param PhonyTargets If true, also emit an empty "prereq:" line for each
+///   prerequisite (except \p InputFileIndex), so later deleting a prerequisite
+///   doesn't break the build.
+/// \param InputFileIndex Index in \p Files of the main input, skipped above.
+void printMakeDependencyFile(
+    llvm::raw_ostream &OS, llvm::ArrayRef<std::string> Targets,
+    llvm::ArrayRef<std::string> Files,
+    DependencyOutputFormat Format = DependencyOutputFormat::Make,
+    bool PhonyTargets = false, unsigned InputFileIndex = 0);
+
 } // namespace clang
 
 #endif // LLVM_CLANG_BASIC_MAKESUPPORT_H
diff --git a/clang/include/clang/Frontend/DependencyOutputOptions.h 
b/clang/include/clang/Frontend/DependencyOutputOptions.h
index 98728f4d53b43..c2607b833a9e4 100644
--- a/clang/include/clang/Frontend/DependencyOutputOptions.h
+++ b/clang/include/clang/Frontend/DependencyOutputOptions.h
@@ -10,6 +10,7 @@
 #define LLVM_CLANG_FRONTEND_DEPENDENCYOUTPUTOPTIONS_H
 
 #include "clang/Basic/HeaderInclude.h"
+#include "clang/Basic/MakeSupport.h"
 #include <string>
 #include <vector>
 
@@ -18,9 +19,6 @@ namespace clang {
 /// ShowIncludesDestination - Destination for /showIncludes output.
 enum class ShowIncludesDestination { None, Stdout, Stderr };
 
-/// DependencyOutputFormat - Format for the compiler dependency file.
-enum class DependencyOutputFormat { Make, NMake };
-
 /// ExtraDepKind - The kind of extra dependency file.
 enum ExtraDepKind {
   EDK_SanitizeIgnorelist,
diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index 4974209b8db30..767c9044e44e3 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -807,14 +807,19 @@ def embed_dir_EQ : Joined<["--"], "embed-dir=">, 
Group<Preprocessor_Group>,
     Visibility<[ClangOption, CC1Option]>, MetaVarName<"<dir>">,
     HelpText<"Add directory to embed search path">;
 def MD : Flag<["-"], "MD">, Group<M_Group>,
+    Visibility<[ClangOption, FlangOption]>,
     HelpText<"Write a depfile containing user and system headers">;
 def MMD : Flag<["-"], "MMD">, Group<M_Group>,
+    Visibility<[ClangOption, FlangOption]>,
     HelpText<"Write a depfile containing user headers">;
 def M : Flag<["-"], "M">, Group<M_Group>,
+    Visibility<[ClangOption, FlangOption]>,
     HelpText<"Like -MD, but also implies -E and writes to stdout by default">;
 def MM : Flag<["-"], "MM">, Group<M_Group>,
+    Visibility<[ClangOption, FlangOption]>,
     HelpText<"Like -MMD, but also implies -E and writes to stdout by default">;
 def MF : JoinedOrSeparate<["-"], "MF">, Group<M_Group>,
+    Visibility<[ClangOption, FlangOption]>,
     HelpText<"Write depfile output from -MMD, -MD, -MM, or -M to <file>">,
     MetaVarName<"<file>">;
 def MG : Flag<["-"], "MG">, Group<M_Group>, Visibility<[ClangOption, 
CC1Option]>,
@@ -826,10 +831,10 @@ def MP : Flag<["-"], "MP">, Group<M_Group>, 
Visibility<[ClangOption, CC1Option]>
     HelpText<"Create phony target for each dependency (other than main file)">,
     MarshallingInfoFlag<DependencyOutputOpts<"UsePhonyTargets">>;
 def MQ : JoinedOrSeparate<["-"], "MQ">, Group<M_Group>,
-  Visibility<[ClangOption, CC1Option]>,
+  Visibility<[ClangOption, CC1Option, FlangOption]>,
     HelpText<"Specify name of main file output to quote in depfile">;
 def MT : JoinedOrSeparate<["-"], "MT">, Group<M_Group>,
-  Visibility<[ClangOption, CC1Option]>,
+  Visibility<[ClangOption, CC1Option, FC1Option, FlangOption]>,
     HelpText<"Specify name of main file output in depfile">,
     MarshallingInfoStringVector<DependencyOutputOpts<"Targets">>;
 def MV : Flag<["-"], "MV">, Group<M_Group>, Visibility<[ClangOption, 
CC1Option]>,
@@ -1528,7 +1533,7 @@ def dM : Flag<["-"], "dM">, Group<d_Group>, 
Visibility<[ClangOption, CC1Option,
   HelpText<"Print macro definitions in -E mode instead of normal output">;
 def dead__strip : Flag<["-"], "dead_strip">;
 def dependency_file : Separate<["-"], "dependency-file">,
-  Visibility<[ClangOption, CC1Option]>,
+  Visibility<[ClangOption, CC1Option, FC1Option]>,
   HelpText<"Filename (or -) to write dependency output to">,
   MarshallingInfoString<DependencyOutputOpts<"OutputFile">>;
 def dependency_dot : Separate<["-"], "dependency-dot">,
@@ -8529,8 +8534,6 @@ defm recovery_ast_type : BoolOption<"f", 
"recovery-ast-type",
 
 let Group = Action_Group in {
 
-def Eonly : Flag<["-"], "Eonly">,
-  HelpText<"Just run preprocessor, no output (for timings)">;
 def dump_raw_tokens : Flag<["-"], "dump-raw-tokens">,
   HelpText<"Lex file in raw mode and dump raw tokens">;
 def analyze : Flag<["-"], "analyze">,
@@ -8673,6 +8676,11 @@ def finitial_counter_value_EQ : Joined<["-"], 
"finitial-counter-value=">,
 
 } // let Visibility = [CC1Option]
 
+def Eonly : Flag<["-"], "Eonly">, Group<Action_Group>,
+  Visibility<[CC1Option, FC1Option]>,
+  HelpText<"Just run preprocessor, no output (for timings or dependency "
+           "generation)">;
+
 
//===----------------------------------------------------------------------===//
 // Language Options
 
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/Basic/MakeSupport.cpp b/clang/lib/Basic/MakeSupport.cpp
index 4ddfcc350410c..afc22493aff0d 100644
--- a/clang/lib/Basic/MakeSupport.cpp
+++ b/clang/lib/Basic/MakeSupport.cpp
@@ -7,6 +7,9 @@
 
//===----------------------------------------------------------------------===//
 
 #include "clang/Basic/MakeSupport.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
 
 void clang::quoteMakeTarget(StringRef Target, SmallVectorImpl<char> &Res) {
   for (unsigned i = 0, e = Target.size(); i != e; ++i) {
@@ -33,3 +36,144 @@ void clang::quoteMakeTarget(StringRef Target, 
SmallVectorImpl<char> &Res) {
     Res.push_back(Target[i]);
   }
 }
+
+/// Print the filename, with escaping or quoting that accommodates the three
+/// most likely tools that use dependency files: GNU Make, BSD Make, and
+/// NMake/Jom.
+///
+/// BSD Make is the simplest case: It does no escaping at all.  This means
+/// characters that are normally delimiters, i.e. space and # (the comment
+/// character) simply aren't supported in filenames.
+///
+/// GNU Make does allow space and # in filenames, but to avoid being treated
+/// as a delimiter or comment, these must be escaped with a backslash. Because
+/// backslash is itself the escape character, if a backslash appears in a
+/// filename, it should be escaped as well.  (As a special case, $ is escaped
+/// as $$, which is the normal Make way to handle the $ character.)
+/// For compatibility with BSD Make and historical practice, if GNU Make
+/// un-escapes characters in a filename but doesn't find a match, it will
+/// retry with the unmodified original string.
+///
+/// GCC tries to accommodate both Make formats by escaping any space or #
+/// characters in the original filename, but not escaping backslashes.  The
+/// apparent intent is so that filenames with backslashes will be handled
+/// correctly by BSD Make, and by GNU Make in its fallback mode of using the
+/// unmodified original string; filenames with # or space characters aren't
+/// supported by BSD Make at all, but will be handled correctly by GNU Make
+/// due to the escaping.
+///
+/// A corner case that GCC gets only partly right is when the original filename
+/// has a backslash immediately followed by space or #.  GNU Make would expect
+/// this backslash to be escaped; however GCC escapes the original backslash
+/// only when followed by space, not #.  It will therefore take a dependency
+/// from a directive such as
+///     #include "a\ b\#c.h"
+/// and emit it as
+///     a\\\ b\\#c.h
+/// which GNU Make will interpret as
+///     a\ b\
+/// followed by a comment. Failing to find this file, it will fall back to the
+/// original string, which probably doesn't exist either; in any case it won't
+/// find
+///     a\ b\#c.h
+/// which is the actual filename specified by the include directive.
+///
+/// Clang does what GCC does, rather than what GNU Make expects.
+///
+/// NMake/Jom has a different set of scary characters, but wraps filespecs in
+/// double-quotes to avoid misinterpreting them; see
+/// https://msdn.microsoft.com/en-us/library/dd9y37ha.aspx for NMake info,
+/// 
https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
+/// for Windows file-naming info.
+static void printFilename(llvm::raw_ostream &OS, llvm::StringRef Filename,
+                          clang::DependencyOutputFormat OutputFormat) {
+  // Convert filename to platform native path
+  llvm::SmallString<256> NativePath;
+  llvm::sys::path::native(Filename, NativePath);
+
+  if (OutputFormat == clang::DependencyOutputFormat::NMake) {
+    // Add quotes if needed. These are the characters listed as "special" to
+    // NMake, that are legal in a Windows filespec, and that could cause
+    // misinterpretation of the dependency string.
+    if (NativePath.find_first_of(" #${}^!") != llvm::StringRef::npos)
+      OS << '\"' << NativePath << '\"';
+    else
+      OS << NativePath;
+    return;
+  }
+  assert(OutputFormat == clang::DependencyOutputFormat::Make);
+  for (unsigned i = 0, e = NativePath.size(); i != e; ++i) {
+    if (NativePath[i] == '#') // Handle '#' the broken gcc way.
+      OS << '\\';
+    else if (NativePath[i] == ' ') { // Handle space correctly.
+      OS << '\\';
+      unsigned j = i;
+      while (j > 0 && NativePath[--j] == '\\')
+        OS << '\\';
+    } else if (NativePath[i] == '$') // $ is escaped by $$.
+      OS << '$';
+    OS << NativePath[i];
+  }
+}
+
+void clang::printMakeDependencyFile(llvm::raw_ostream &OS,
+                                    llvm::ArrayRef<std::string> Targets,
+                                    llvm::ArrayRef<std::string> Files,
+                                    DependencyOutputFormat Format,
+                                    bool PhonyTargets,
+                                    unsigned InputFileIndex) {
+  // Write out the dependency targets, trying to avoid overly long
+  // lines when possible. We try our best to emit exactly the same
+  // dependency file as GCC>=10, assuming the included files are the
+  // same.
+  const unsigned MaxColumns = 75;
+  unsigned Columns = 0;
+
+  for (StringRef Target : Targets) {
+    unsigned N = Target.size();
+    if (Columns == 0) {
+      Columns += N;
+    } else if (Columns + N + 2 > MaxColumns) {
+      Columns = N + 2;
+      OS << " \\\n  ";
+    } else {
+      Columns += N + 1;
+      OS << ' ';
+    }
+    // Targets already quoted as needed.
+    OS << Target;
+  }
+
+  OS << ':';
+  Columns += 1;
+
+  // Now add each dependency in the order it was seen, but avoiding
+  // duplicates.
+  for (StringRef File : Files) {
+    if (File == "<stdin>")
+      continue;
+    // Start a new line if this would exceed the column limit. Make
+    // sure to leave space for a trailing " \" in case we need to
+    // break the line on the next iteration.
+    unsigned N = File.size();
+    if (Columns + (N + 1) + 2 > MaxColumns) {
+      OS << " \\\n ";
+      Columns = 2;
+    }
+    OS << ' ';
+    printFilename(OS, File, Format);
+    Columns += N + 1;
+  }
+  OS << '\n';
+
+  // Create phony targets if requested.
+  if (PhonyTargets && !Files.empty()) {
+    unsigned Index = 0;
+    for (auto I = Files.begin(), E = Files.end(); I != E; ++I) {
+      if (Index++ == InputFileIndex)
+        continue;
+      printFilename(OS, *I, Format);
+      OS << ":\n";
+    }
+  }
+}
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp 
b/clang/lib/Driver/ToolChains/Flang.cpp
index d900037230f20..a69913fc4872e 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -11,6 +11,7 @@
 #include "Cuda.h"
 
 #include "clang/Basic/CodeGenOptions.h"
+#include "clang/Basic/MakeSupport.h"
 #include "clang/Driver/CommonArgs.h"
 #include "clang/Options/OptionUtils.h"
 #include "clang/Options/Options.h"
@@ -35,6 +36,100 @@ static void addDashXForInput(const ArgList &Args, const 
InputInfo &Input,
   CmdArgs.push_back(types::getTypeName(Input.getType()));
 }
 
+// Translate the dependency-file options into the arguments understood by
+// `flang -fc1`. The options handled here:
+//   -M    Emit only the dependencies and skip code generation. They are
+//         written to stdout unless -MF redirects them.
+//   -MM   Treated identically to -M. The -MM/-M split exists to omit system
+//         headers, but Fortran has no notion of system vs user headers, so
+//         there is nothing for -MM to exclude.
+//   -MD   Compile normally and produce the object file, while also writing the
+//         dependency file. Its name defaults to the -o value, or the input
+//         file name when -o is absent, with the extension replaced by .d.
+//   -MMD  Treated identically to -MD, for the same reason -MM equals -M.
+//   -MF   Set the path of the dependency file to write.
+//   -MT   Set the dependency target name (the part before the colon).
+//   -MQ   Like -MT, but additionally quotes characters special to Make.
+static void renderDependencyGenerationOptions(Compilation &C,
+                                              const JobAction &JA,
+                                              const ArgList &Args,
+                                              const InputInfo &Output,
+                                              const InputInfoList &Inputs,
+                                              ArgStringList &CmdArgs) {
+  Arg *ArgM = Args.getLastArg(options::OPT_M, options::OPT_MM);
+  Arg *ArgMD = Args.getLastArg(options::OPT_MD, options::OPT_MMD);
+
+  // Drop warnings for -M/-MM so they don't mix into the dependency output.
+  if (ArgM) {
+    CmdArgs.push_back("-w");
+  } else {
+    ArgM = ArgMD;
+  }
+
+  if (!ArgM) {
+    return;
+  }
+
+  // Emit "-MT <target>", quoting Make metacharacters when requested.
+  auto addTarget = [&](StringRef Target, bool Quote) {
+    SmallString<128> Quoted;
+    if (Quote) {
+      clang::quoteMakeTarget(Target, Quoted);
+      Target = Quoted;
+    }
+    CmdArgs.push_back("-MT");
+    CmdArgs.push_back(Args.MakeArgString(Target));
+  };
+
+  // Decide where to write the dependency file.
+  const char *DepFile;
+  if (Arg *MF = Args.getLastArg(options::OPT_MF)) {
+    // -MF gives the path explicitly.
+    DepFile = MF->getValue();
+    C.addFailureResultFile(DepFile, &JA);
+  } else if (Output.getType() == types::TY_Dependencies) {
+    // Plain -M/-MM: the dependency file is the output, so use its name
+    DepFile = Output.getFilename();
+  } else if (!ArgMD) {
+    // -M/-MM with no -o: write the dependencies to stdout.
+    DepFile = "-";
+  } else {
+    // -MD/-MMD: name it after -o, else the input, with a .d extension.
+    SmallString<128> P;
+    if (Arg *OutputOpt = Args.getLastArg(options::OPT_o)) {
+      P = OutputOpt->getValue();
+    } else {
+      P = llvm::sys::path::filename(Inputs[0].getBaseInput());
+    }
+    llvm::sys::path::replace_extension(P, "d");
+    DepFile = Args.MakeArgString(P);
+    C.addFailureResultFile(DepFile, &JA);
+  }
+  CmdArgs.push_back("-dependency-file");
+  CmdArgs.push_back(DepFile);
+
+  // Render the explicit target(s). -MT is verbatim, -MQ is Make-quoted.
+  bool HasTarget = false;
+  for (const Arg *A : Args.filtered(options::OPT_MT, options::OPT_MQ)) {
+    HasTarget = true;
+    A->claim();
+    addTarget(A->getValue(), A->getOption().matches(options::OPT_MQ));
+  }
+
+  // With no explicit target, default to the object file. In -M/-MM mode -o
+  // names the dependency file, not the target, so derive <base>.o instead.
+  if (!HasTarget) {
+    if (Arg *OutputOpt = Args.getLastArg(options::OPT_o);
+        OutputOpt && Output.getType() != types::TY_Dependencies) {
+      addTarget(OutputOpt->getValue(), /*Quote=*/true);
+    } else {
+      SmallString<128> P(llvm::sys::path::filename(Inputs[0].getBaseInput()));
+      llvm::sys::path::replace_extension(P, "o");
+      addTarget(P, /*Quote=*/true);
+    }
+  }
+}
+
 void Flang::addFortranDialectOptions(const ArgList &Args,
                                      ArgStringList &CmdArgs) const {
   Args.addAllArgs(CmdArgs, {options::OPT_ffixed_form,
@@ -1052,9 +1147,13 @@ void Flang::ConstructJob(Compilation &C, const JobAction 
&JA,
   CmdArgs.push_back(Args.MakeArgString(TripleStr));
 
   if (isa<PreprocessJobAction>(JA)) {
-    CmdArgs.push_back("-E");
-    if (Args.getLastArg(options::OPT_dM)) {
-      CmdArgs.push_back("-dM");
+    if (Output.getType() == types::TY_Dependencies) {
+      CmdArgs.push_back("-Eonly");
+    } else {
+      CmdArgs.push_back("-E");
+      if (Args.getLastArg(options::OPT_dM)) {
+        CmdArgs.push_back("-dM");
+      }
     }
   } else if (isa<CompileJobAction>(JA) || isa<BackendJobAction>(JA)) {
     if (JA.getType() == types::TY_Nothing) {
@@ -1310,6 +1409,8 @@ void Flang::ConstructJob(Compilation &C, const JobAction 
&JA,
   if (Args.getLastArg(options::OPT_save_temps_EQ))
     Args.AddLastArg(CmdArgs, options::OPT_save_temps_EQ);
 
+  renderDependencyGenerationOptions(C, JA, Args, Output, Inputs, CmdArgs);
+
   addDashXForInput(Args, Input, CmdArgs);
 
   bool FRecordCmdLine = false;
diff --git a/clang/lib/Frontend/DependencyFile.cpp 
b/clang/lib/Frontend/DependencyFile.cpp
index 00f4a54269cfa..5702d1dabcc8b 100644
--- a/clang/lib/Frontend/DependencyFile.cpp
+++ b/clang/lib/Frontend/DependencyFile.cpp
@@ -12,6 +12,7 @@
 
 #include "clang/Basic/DiagnosticFrontend.h"
 #include "clang/Basic/FileManager.h"
+#include "clang/Basic/MakeSupport.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Frontend/DependencyOutputOptions.h"
 #include "clang/Frontend/Utils.h"
@@ -293,85 +294,6 @@ void 
DependencyFileGenerator::finishedMainFile(DiagnosticsEngine &Diags) {
   outputDependencyFile(Diags);
 }
 
-/// Print the filename, with escaping or quoting that accommodates the three
-/// most likely tools that use dependency files: GNU Make, BSD Make, and
-/// NMake/Jom.
-///
-/// BSD Make is the simplest case: It does no escaping at all.  This means
-/// characters that are normally delimiters, i.e. space and # (the comment
-/// character) simply aren't supported in filenames.
-///
-/// GNU Make does allow space and # in filenames, but to avoid being treated
-/// as a delimiter or comment, these must be escaped with a backslash. Because
-/// backslash is itself the escape character, if a backslash appears in a
-/// filename, it should be escaped as well.  (As a special case, $ is escaped
-/// as $$, which is the normal Make way to handle the $ character.)
-/// For compatibility with BSD Make and historical practice, if GNU Make
-/// un-escapes characters in a filename but doesn't find a match, it will
-/// retry with the unmodified original string.
-///
-/// GCC tries to accommodate both Make formats by escaping any space or #
-/// characters in the original filename, but not escaping backslashes.  The
-/// apparent intent is so that filenames with backslashes will be handled
-/// correctly by BSD Make, and by GNU Make in its fallback mode of using the
-/// unmodified original string; filenames with # or space characters aren't
-/// supported by BSD Make at all, but will be handled correctly by GNU Make
-/// due to the escaping.
-///
-/// A corner case that GCC gets only partly right is when the original filename
-/// has a backslash immediately followed by space or #.  GNU Make would expect
-/// this backslash to be escaped; however GCC escapes the original backslash
-/// only when followed by space, not #.  It will therefore take a dependency
-/// from a directive such as
-///     #include "a\ b\#c.h"
-/// and emit it as
-///     a\\\ b\\#c.h
-/// which GNU Make will interpret as
-///     a\ b\
-/// followed by a comment. Failing to find this file, it will fall back to the
-/// original string, which probably doesn't exist either; in any case it won't
-/// find
-///     a\ b\#c.h
-/// which is the actual filename specified by the include directive.
-///
-/// Clang does what GCC does, rather than what GNU Make expects.
-///
-/// NMake/Jom has a different set of scary characters, but wraps filespecs in
-/// double-quotes to avoid misinterpreting them; see
-/// https://msdn.microsoft.com/en-us/library/dd9y37ha.aspx for NMake info,
-/// 
https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
-/// for Windows file-naming info.
-static void PrintFilename(raw_ostream &OS, StringRef Filename,
-                          DependencyOutputFormat OutputFormat) {
-  // Convert filename to platform native path
-  llvm::SmallString<256> NativePath;
-  llvm::sys::path::native(Filename, NativePath);
-
-  if (OutputFormat == DependencyOutputFormat::NMake) {
-    // Add quotes if needed. These are the characters listed as "special" to
-    // NMake, that are legal in a Windows filespec, and that could cause
-    // misinterpretation of the dependency string.
-    if (NativePath.find_first_of(" #${}^!") != StringRef::npos)
-      OS << '\"' << NativePath << '\"';
-    else
-      OS << NativePath;
-    return;
-  }
-  assert(OutputFormat == DependencyOutputFormat::Make);
-  for (unsigned i = 0, e = NativePath.size(); i != e; ++i) {
-    if (NativePath[i] == '#') // Handle '#' the broken gcc way.
-      OS << '\\';
-    else if (NativePath[i] == ' ') { // Handle space correctly.
-      OS << '\\';
-      unsigned j = i;
-      while (j > 0 && NativePath[--j] == '\\')
-        OS << '\\';
-    } else if (NativePath[i] == '$') // $ is escaped by $$.
-      OS << '$';
-    OS << NativePath[i];
-  }
-}
-
 void DependencyFileGenerator::outputDependencyFile(DiagnosticsEngine &Diags) {
   if (SeenMissingHeader) {
     llvm::sys::fs::remove(OutputFile);
@@ -389,59 +311,6 @@ void 
DependencyFileGenerator::outputDependencyFile(DiagnosticsEngine &Diags) {
 }
 
 void DependencyFileGenerator::outputDependencyFile(llvm::raw_ostream &OS) {
-  // Write out the dependency targets, trying to avoid overly long
-  // lines when possible. We try our best to emit exactly the same
-  // dependency file as GCC>=10, assuming the included files are the
-  // same.
-  const unsigned MaxColumns = 75;
-  unsigned Columns = 0;
-
-  for (StringRef Target : Targets) {
-    unsigned N = Target.size();
-    if (Columns == 0) {
-      Columns += N;
-    } else if (Columns + N + 2 > MaxColumns) {
-      Columns = N + 2;
-      OS << " \\\n  ";
-    } else {
-      Columns += N + 1;
-      OS << ' ';
-    }
-    // Targets already quoted as needed.
-    OS << Target;
-  }
-
-  OS << ':';
-  Columns += 1;
-
-  // Now add each dependency in the order it was seen, but avoiding
-  // duplicates.
-  ArrayRef<std::string> Files = getDependencies();
-  for (StringRef File : Files) {
-    if (File == "<stdin>")
-      continue;
-    // Start a new line if this would exceed the column limit. Make
-    // sure to leave space for a trailing " \" in case we need to
-    // break the line on the next iteration.
-    unsigned N = File.size();
-    if (Columns + (N + 1) + 2 > MaxColumns) {
-      OS << " \\\n ";
-      Columns = 2;
-    }
-    OS << ' ';
-    PrintFilename(OS, File, OutputFormat);
-    Columns += N + 1;
-  }
-  OS << '\n';
-
-  // Create phony targets if requested.
-  if (PhonyTarget && !Files.empty()) {
-    unsigned Index = 0;
-    for (auto I = Files.begin(), E = Files.end(); I != E; ++I) {
-      if (Index++ == InputFileIndex)
-        continue;
-      PrintFilename(OS, *I, OutputFormat);
-      OS << ":\n";
-    }
-  }
+  clang::printMakeDependencyFile(OS, Targets, getDependencies(), OutputFormat,
+                                 PhonyTarget, InputFileIndex);
 }
diff --git a/flang/docs/FlangDriver.md b/flang/docs/FlangDriver.md
index 4edc99944ad44..d2b2e44c6dd58 100644
--- a/flang/docs/FlangDriver.md
+++ b/flang/docs/FlangDriver.md
@@ -236,6 +236,21 @@ is `ParseSyntaxOnlyAction`, which corresponds to 
`-fsyntax-only`. In other
 words, `flang -fc1 <input-file>` is equivalent to `flang -fc1 -fsyntax-only
 <input-file>`.
 
+## Dependency File Generation
+Flang can emit Makefile-style dependency rules with `-M`, `-MM`, `-MD` and
+`-MMD` (paired with `-MF`, `-MT` and `-MQ` to control the output file and the
+rule target).
+
+There is one behavioural difference to be aware of regarding module
+dependencies (the `.mod` files brought in by `use` statements):
+
+* `-MD` and `-MMD` run a full compilation, so the `.mod` files opened during
+  semantic analysis are recorded and appear in the dependency rule.
+* `-M` and `-MM` run the prescanner only. They report textual dependencies
+  (`INCLUDE` and `#include`) but do **not** resolve `use` statements, so module
+  `.mod` files are not listed. If you need module dependencies, use `-MD` or
+  `-MMD`.
+
 ## Adding new Compiler Options
 Adding a new compiler option in Flang consists of two steps:
 * define the new option in a dedicated TableGen file,
diff --git a/flang/include/flang/Frontend/FrontendAction.h 
b/flang/include/flang/Frontend/FrontendAction.h
index 1a800cc681cf9..4e2a9ead1a205 100644
--- a/flang/include/flang/Frontend/FrontendAction.h
+++ b/flang/include/flang/Frontend/FrontendAction.h
@@ -110,6 +110,8 @@ class FrontendAction {
   // Prescan the current input file. Return False if fatal errors are reported,
   // True otherwise.
   bool runPrescan();
+  // Write the dependency (.d) file if -dependency-file was given.
+  void writeDependencyFile();
   // Parse the current input file. Return False if fatal errors are reported,
   // True otherwise.
   bool runParse(bool emitMessages);
diff --git a/flang/include/flang/Frontend/FrontendActions.h 
b/flang/include/flang/Frontend/FrontendActions.h
index f9a45bd6c0a56..926fbd28c357b 100644
--- a/flang/include/flang/Frontend/FrontendActions.h
+++ b/flang/include/flang/Frontend/FrontendActions.h
@@ -48,6 +48,12 @@ class PrintPreprocessedAction : public PrescanAction {
   void executeAction() override;
 };
 
+// Runs the prescanner only (for -Eonly). The dependency file, if requested, is
+// written by PrescanAction::beginSourceFileAction; no other output is 
produced.
+class RunPreprocessorOnlyAction : public PrescanAction {
+  void executeAction() override {}
+};
+
 class DebugDumpProvenanceAction : public PrescanAction {
   void executeAction() override;
 };
diff --git a/flang/include/flang/Frontend/FrontendOptions.h 
b/flang/include/flang/Frontend/FrontendOptions.h
index 0bd2e621813ca..30968d7cd9618 100644
--- a/flang/include/flang/Frontend/FrontendOptions.h
+++ b/flang/include/flang/Frontend/FrontendOptions.h
@@ -31,6 +31,9 @@ enum ActionKind {
   /// -E mode
   PrintPreprocessedInput,
 
+  /// -Eonly mode: prescan only and write just the dependency file (-M, -MM)
+  RunPreprocessorOnly,
+
   /// -fsyntax-only
   ParseSyntaxOnly,
 
@@ -273,6 +276,12 @@ struct FrontendOptions {
   /// The output file, if any.
   std::string outputFile;
 
+  /// The dependency-file (.d) to write, if any (-dependency-file).
+  std::string dependencyOutputFile;
+
+  /// Target name(s) for the dependency rule (-MT), already quoted for Make.
+  std::vector<std::string> dependencyTargets;
+
   /// The frontend action to perform.
   frontend::ActionKind programAction = ParseSyntaxOnly;
 
diff --git a/flang/include/flang/Parser/provenance.h 
b/flang/include/flang/Parser/provenance.h
index a9224b727fd05..75dda030f1e17 100644
--- a/flang/include/flang/Parser/provenance.h
+++ b/flang/include/flang/Parser/provenance.h
@@ -156,6 +156,11 @@ class AllSources {
       std::optional<std::string> &&prependPath = std::nullopt);
   const SourceFile *ReadStandardInput(llvm::raw_ostream &error);
 
+  // Returns the paths of every source file opened so far: the main file and 
any
+  // files pulled in by INCLUDE lines or #include directives. Paths are 
returned
+  // without duplicates, in the order they were first opened.
+  std::vector<std::string> GetIncludedFilePaths() const;
+
   ProvenanceRange AddIncludedFile(
       const SourceFile &, ProvenanceRange, bool isModule = false);
   ProvenanceRange AddMacroCall(
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp 
b/flang/lib/Frontend/CompilerInvocation.cpp
index 79ad08353b64c..c9ab8a763d9a0 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -651,6 +651,9 @@ static bool parseFrontendArgs(FrontendOptions &opts, 
llvm::opt::ArgList &args,
     case clang::options::OPT_E:
       opts.programAction = PrintPreprocessedInput;
       break;
+    case clang::options::OPT_Eonly:
+      opts.programAction = RunPreprocessorOnly;
+      break;
     case clang::options::OPT_fsyntax_only:
       opts.programAction = ParseSyntaxOnly;
       break;
@@ -763,6 +766,9 @@ static bool parseFrontendArgs(FrontendOptions &opts, 
llvm::opt::ArgList &args,
   }
 
   opts.outputFile = args.getLastArgValue(clang::options::OPT_o);
+  opts.dependencyOutputFile =
+      args.getLastArgValue(clang::options::OPT_dependency_file);
+  opts.dependencyTargets = args.getAllArgValues(clang::options::OPT_MT);
   opts.showHelp = args.hasArg(clang::options::OPT_help);
   opts.showVersion = args.hasArg(clang::options::OPT_version);
   opts.printSupportedCPUs =
diff --git a/flang/lib/Frontend/FrontendAction.cpp 
b/flang/lib/Frontend/FrontendAction.cpp
index e17f7ac42b5e6..8bd98705d530d 100644
--- a/flang/lib/Frontend/FrontendAction.cpp
+++ b/flang/lib/Frontend/FrontendAction.cpp
@@ -17,7 +17,11 @@
 #include "flang/Frontend/FrontendPluginRegistry.h"
 #include "flang/Parser/parsing.h"
 #include "clang/Basic/DiagnosticFrontend.h"
+#include "clang/Basic/MakeSupport.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
 #include "llvm/Support/VirtualFileSystem.h"
+#include "llvm/Support/raw_ostream.h"
 
 using namespace Fortran::frontend;
 
@@ -111,6 +115,9 @@ bool FrontendAction::beginSourceFile(CompilerInstance &ci,
     return false;
   }
 
+  // Written after semantics so -MD/-MMD also capture .mod files from `use`.
+  writeDependencyFile();
+
   return true;
 }
 
@@ -158,6 +165,41 @@ bool FrontendAction::runPrescan() {
   return !reportFatalScanningErrors();
 }
 
+void FrontendAction::writeDependencyFile() {
+  CompilerInstance &ci = this->getInstance();
+  const FrontendOptions &opts = ci.getFrontendOpts();
+  if (opts.dependencyOutputFile.empty())
+    return;
+
+  // Use the -MT targets, or derive one from the output/input file name.
+  std::vector<std::string> targets = opts.dependencyTargets;
+  if (targets.empty()) {
+    llvm::SmallString<128> target;
+    if (opts.outputFile.empty()) {
+      target = llvm::sys::path::filename(getCurrentFileOrBufferName());
+      llvm::sys::path::replace_extension(target, "o");
+    } else {
+      target = opts.outputFile;
+    }
+    llvm::SmallString<128> quoted;
+    clang::quoteMakeTarget(target, quoted);
+    targets.push_back(std::string(quoted));
+  }
+
+  std::error_code ec;
+  llvm::raw_fd_ostream os(opts.dependencyOutputFile, ec,
+                          llvm::sys::fs::OF_TextWithCRLF);
+  if (ec) {
+    unsigned diagID = ci.getDiagnostics().getCustomDiagID(
+        clang::DiagnosticsEngine::Error, "unable to open dependency file %0");
+    ci.getDiagnostics().Report(diagID) << opts.dependencyOutputFile;
+    return;
+  }
+
+  clang::printMakeDependencyFile(os, targets,
+                                 ci.getAllSources().GetIncludedFilePaths());
+}
+
 bool FrontendAction::runParse(bool emitMessages) {
   CompilerInstance &ci = this->getInstance();
 
diff --git a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp 
b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
index 0396889fc57a4..7a883eba941b1 100644
--- a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -40,6 +40,8 @@ createFrontendAction(CompilerInstance &ci) {
     return std::make_unique<InputOutputTestAction>();
   case PrintPreprocessedInput:
     return std::make_unique<PrintPreprocessedAction>();
+  case RunPreprocessorOnly:
+    return std::make_unique<RunPreprocessorOnlyAction>();
   case ParseSyntaxOnly:
     return std::make_unique<ParseSyntaxOnlyAction>();
   case EmitFIR:
diff --git a/flang/lib/Parser/provenance.cpp b/flang/lib/Parser/provenance.cpp
index fe92aa7f64fb1..ab75fe145c707 100644
--- a/flang/lib/Parser/provenance.cpp
+++ b/flang/lib/Parser/provenance.cpp
@@ -206,6 +206,21 @@ const SourceFile 
*AllSources::ReadStandardInput(llvm::raw_ostream &error) {
   return nullptr;
 }
 
+std::vector<std::string> AllSources::GetIncludedFilePaths() const {
+  std::vector<std::string> paths;
+  std::set<std::string> seen;
+  for (const auto &source : ownedSourceFiles_) {
+    const std::string &path{source->path()};
+    if (path.empty() || path == "-") {
+      continue;
+    }
+    if (seen.insert(path).second) {
+      paths.push_back(path);
+    }
+  }
+  return paths;
+}
+
 ProvenanceRange AllSources::AddIncludedFile(
     const SourceFile &source, ProvenanceRange from, bool isModule) {
   ProvenanceRange covers{range_.NextAfter(), source.bytes()};
diff --git a/flang/test/Driver/dependency-file-gen-mod.f90 
b/flang/test/Driver/dependency-file-gen-mod.f90
new file mode 100644
index 0000000000000..b99d331833c35
--- /dev/null
+++ b/flang/test/Driver/dependency-file-gen-mod.f90
@@ -0,0 +1,33 @@
+! Module files used via `use` are real build dependencies. -MD/-MMD run a full
+! compile, so the .mod opened during semantics is recorded in the output. Plain
+! -M/-MM is prescan-only and does not resolve modules, so it lists includes 
only.
+
+! RUN: rm -rf %t && split-file %s %t
+! RUN: %flang -fsyntax-only %t/mymod.f90 -J %t
+
+! -MD: the output lists the user module plus the intrinsic modules opened 
during
+! semantics (compiler-provided __fortran_builtins.mod / 
__fortran_type_info.mod).
+! RUN: %flang -MD -c %t/main.f90 -J %t -o %t/main.o
+! RUN: FileCheck %s --input-file=%t/main.d --check-prefix=MD
+! MD: main.o:
+! MD-DAG: main.f90
+! MD-DAG: mymod.mod
+! MD-DAG: __fortran_builtins.mod
+! MD-DAG: __fortran_type_info.mod
+
+! -M stays prescan-only: no module dependency.
+! RUN: %flang -M %t/main.f90 -J %t 2>&1 | FileCheck %s --check-prefix=M
+! M: main.o:
+! M-NOT: mymod.mod
+
+!--- mymod.f90
+module mymod
+  integer :: v = 1
+end module mymod
+
+!--- main.f90
+program main
+  use mymod
+  implicit none
+  print *, v
+end program main
diff --git a/flang/test/Driver/dependency-file-gen.f90 
b/flang/test/Driver/dependency-file-gen.f90
new file mode 100644
index 0000000000000..256aa5feccfac
--- /dev/null
+++ b/flang/test/Driver/dependency-file-gen.f90
@@ -0,0 +1,31 @@
+! Ensure the frontend lists files brought in by an INCLUDE line or by a
+! preprocessor #include directive in the generated dependency file.
+
+! RUN: rm -rf %t && split-file %s %t
+
+! INCLUDE statement.
+! RUN: %flang_fc1 -fsyntax-only %t/use-include.f90 -dependency-file %t/inc.d 
-MT custom.o
+! RUN: FileCheck %s --input-file=%t/inc.d --check-prefix=INCLUDE
+! INCLUDE: custom.o:
+! INCLUDE: use-include.f90
+! INCLUDE: header.h
+
+! Preprocessor #include directive.
+! RUN: %flang_fc1 -fsyntax-only -cpp %t/use-cpp.F90 -dependency-file %t/cpp.d 
-MT custom.o
+! RUN: FileCheck %s --input-file=%t/cpp.d --check-prefix=CPP
+! CPP: custom.o:
+! CPP: use-cpp.F90
+! CPP: header.h
+
+!--- header.h
+      integer :: x
+
+!--- use-include.f90
+program test
+  include 'header.h'
+end program test
+
+!--- use-cpp.F90
+program test
+#include "header.h"
+end program test
diff --git a/flang/test/Driver/dependency-file.f90 
b/flang/test/Driver/dependency-file.f90
new file mode 100644
index 0000000000000..18c3b8ddb70ac
--- /dev/null
+++ b/flang/test/Driver/dependency-file.f90
@@ -0,0 +1,106 @@
+! Test how the driver lowers -M, -MM, -MD, -MMD, -MF, -MT and -MQ to `flang
+! -fc1` and the generated dependency file. Fortran has no system/user header
+! split, so -M behaves like -MM and -MD like -MMD.
+
+!--------------------------------------------------------------------------
+! -M / -MM: emit only the dependencies (prescan-only -Eonly), default
+! to stdout, imply -w, and use the object file as the default target.
+!--------------------------------------------------------------------------
+! RUN: %flang -### -M  %s 2>&1 | FileCheck %s --check-prefix=M
+! RUN: %flang -### -MM %s 2>&1 | FileCheck %s --check-prefix=M
+! M: "-fc1"
+! M-SAME: "-Eonly"
+! M-SAME: "-w"
+! M-SAME: "-dependency-file" "-"
+! M-SAME: "-MT" "dependency-file.o"
+
+!--------------------------------------------------------------------------
+! -M with -o: -o names the dependency file, NOT the dependency target. The 
target
+! still defaults to the object file derived from the input.
+!--------------------------------------------------------------------------
+! RUN: %flang -### -M  -o named.d %s 2>&1 | FileCheck %s --check-prefix=M-O
+! RUN: %flang -### -MM -o named.d %s 2>&1 | FileCheck %s --check-prefix=M-O
+! M-O: "-Eonly"
+! M-O-SAME: "-dependency-file" "named.d"
+! M-O-SAME: "-MT" "dependency-file.o"
+
+!--------------------------------------------------------------------------
+! -M with -o -: dependencies go to stdout, the target is still the object.
+!--------------------------------------------------------------------------
+! RUN: %flang -### -M -o - %s 2>&1 | FileCheck %s --check-prefix=M-O-STDOUT
+! M-O-STDOUT: "-dependency-file" "-"
+! M-O-STDOUT-SAME: "-MT" "dependency-file.o"
+
+!--------------------------------------------------------------------------
+! -MF redirects the dependency file; the target is unaffected.
+!--------------------------------------------------------------------------
+! RUN: %flang -### -M -MF custom.d %s 2>&1 | FileCheck %s --check-prefix=M-MF
+! M-MF: "-Eonly"
+! M-MF-SAME: "-dependency-file" "custom.d"
+! M-MF-SAME: "-MT" "dependency-file.o"
+
+!--------------------------------------------------------------------------
+! -MT sets the target verbatim and replaces the default object target.
+!--------------------------------------------------------------------------
+! RUN: %flang -### -M -MT my_target %s 2>&1 | FileCheck %s --check-prefix=M-MT
+! M-MT: "-dependency-file" "-"
+! M-MT-SAME: "-MT" "my_target"
+! M-MT-NOT: "-MT" "dependency-file.o"
+
+!--------------------------------------------------------------------------
+! -MQ sets the target but quotes characters special to Make ($ -> $$), whereas
+! -MT writes the target verbatim. Checked on the written output.
+!--------------------------------------------------------------------------
+! RUN: %flang -M -MQ 'a$b' %s 2>&1 | FileCheck %s --check-prefix=MQ
+! MQ: a$$b:
+! RUN: %flang -M -MT 'a$b' %s 2>&1 | FileCheck %s --check-prefix=MT
+! MT: a$b:
+
+!--------------------------------------------------------------------------
+! -MD / -MMD: compile normally (no -Eonly) and also write the dependency
+! file, defaulting its name and the target to those derived from the input.
+!--------------------------------------------------------------------------
+! RUN: %flang -### -MD  -c %s 2>&1 | FileCheck %s --check-prefix=MD
+! RUN: %flang -### -MMD -c %s 2>&1 | FileCheck %s --check-prefix=MD
+! MD: "-fc1"
+! MD-NOT: "-Eonly"
+! MD-SAME: "-dependency-file" "dependency-file.d"
+! MD-SAME: "-MT" "dependency-file.o"
+
+!--------------------------------------------------------------------------
+! -MD with -o: the dependency file name and target both follow -o.
+!--------------------------------------------------------------------------
+! RUN: %flang -### -MD -c -o out.o %s 2>&1 | FileCheck %s --check-prefix=MD-O
+! MD-O: "-dependency-file" "out.d"
+! MD-O-SAME: "-MT" "out.o"
+
+!--------------------------------------------------------------------------
+! -MD combined with -MF and -MQ.
+!--------------------------------------------------------------------------
+! RUN: %flang -### -MD -MF dep.d -MQ obj.o -c -o out.o %s 2>&1 | FileCheck %s 
--check-prefix=MD-MIX
+! MD-MIX: "-dependency-file" "dep.d"
+! MD-MIX-SAME: "-MT" "obj.o"
+
+!--------------------------------------------------------------------------
+! End-to-end: the dependencies are written and no preprocessed source leaks.
+!--------------------------------------------------------------------------
+! RUN: %flang -M %s -MT custom.o 2>&1 | FileCheck %s --check-prefix=DEPS
+! DEPS: custom.o:
+! DEPS: dependency-file.f90
+
+! End-to-end with -o: dependencies are written to the named file with the
+! object as the dependency target.
+! RUN: rm -rf %t && mkdir %t
+! RUN: %flang -M -o %t/deps.d %s
+! RUN: FileCheck %s --check-prefix=DEPS-O --input-file=%t/deps.d
+! DEPS-O: dependency-file.o:
+! DEPS-O: dependency-file.f90
+
+! End-to-end with -o -: dependencies go to stdout, object is the target.
+! RUN: %flang -M -o - %s 2>&1 | FileCheck %s --check-prefix=DEPS-STDOUT
+! DEPS-STDOUT: dependency-file.o:
+! DEPS-STDOUT: dependency-file.f90
+
+program test
+  x = 1
+end program test

>From c5c851cd74b7e4c5f63040a84faf2b06f7340e54 Mon Sep 17 00:00:00 2001
From: Thirumalai-Shaktivel <[email protected]>
Date: Wed, 24 Jun 2026 16:28:56 +0530
Subject: [PATCH 2/2] [flang][Test] Add more FC1 tests

---
 flang/test/Driver/dependency-file-gen.f90 | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/flang/test/Driver/dependency-file-gen.f90 
b/flang/test/Driver/dependency-file-gen.f90
index 256aa5feccfac..375c0b8cb28a1 100644
--- a/flang/test/Driver/dependency-file-gen.f90
+++ b/flang/test/Driver/dependency-file-gen.f90
@@ -1,5 +1,5 @@
-! Ensure the frontend lists files brought in by an INCLUDE line or by a
-! preprocessor #include directive in the generated dependency file.
+! Frontend (-fc1) dependency-file generation: list INCLUDE and #include files,
+! derive a default target, and support -Eonly (dependencies only, no source).
 
 ! RUN: rm -rf %t && split-file %s %t
 
@@ -17,6 +17,19 @@
 ! CPP: use-cpp.F90
 ! CPP: header.h
 
+! No -MT (and no -o): the target is derived from the input file name.
+! RUN: %flang_fc1 -fsyntax-only %t/use-include.f90 -dependency-file %t/def.d
+! RUN: FileCheck %s --input-file=%t/def.d --check-prefix=DEFAULT
+! DEFAULT: use-include.o:
+! DEFAULT: use-include.f90
+
+! -Eonly: run the prescanner only, writing the dependencies to stdout with no
+! preprocessed source.
+! RUN: %flang_fc1 -Eonly %t/use-include.f90 -dependency-file - -MT custom.o 
2>&1 \
+! RUN:   | FileCheck %s --check-prefix=EONLY
+! EONLY: custom.o:
+! EONLY: use-include.f90
+
 !--- header.h
       integer :: x
 

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to