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
