[flang-commits] [clang] [flang] [flang][driver] Support Makefile dependency generation (PR #209379)
via flang-commits
flang-commits at lists.llvm.org
Mon Jul 13 22:46:36 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-parser
Author: Thirumalai Shaktivel (Thirumalai-Shaktivel)
<details>
<summary>Changes</summary>
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 `-`.
---
Patch is 41.63 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/209379.diff
18 Files Affected:
- (modified) clang/include/clang/Basic/MakeSupport.h (+20)
- (modified) clang/include/clang/Frontend/DependencyOutputOptions.h (+1-3)
- (modified) clang/include/clang/Options/Options.td (+13-5)
- (modified) clang/lib/Basic/MakeSupport.cpp (+144)
- (modified) clang/lib/Driver/ToolChains/Flang.cpp (+104-3)
- (modified) clang/lib/Frontend/DependencyFile.cpp (+3-134)
- (modified) flang/docs/FlangDriver.md (+15)
- (modified) flang/include/flang/Frontend/FrontendAction.h (+2)
- (modified) flang/include/flang/Frontend/FrontendActions.h (+6)
- (modified) flang/include/flang/Frontend/FrontendOptions.h (+9)
- (modified) flang/include/flang/Parser/provenance.h (+5)
- (modified) flang/lib/Frontend/CompilerInvocation.cpp (+6)
- (modified) flang/lib/Frontend/FrontendAction.cpp (+42)
- (modified) flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp (+2)
- (modified) flang/lib/Parser/provenance.cpp (+15)
- (added) flang/test/Driver/dependency-file-gen-mod.f90 (+33)
- (added) flang/test/Driver/dependency-file-gen.f90 (+44)
- (added) flang/test/Driver/dependency-file.f90 (+106)
``````````diff
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 GC...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/209379
More information about the flang-commits
mailing list