clang 24.0.0git
MakeSupport.cpp
Go to the documentation of this file.
1//===-- MakeSuport.cpp --------------------------------------------------*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "llvm/ADT/SmallString.h"
11#include "llvm/Support/Path.h"
12#include "llvm/Support/raw_ostream.h"
13
15 for (unsigned i = 0, e = Target.size(); i != e; ++i) {
16 switch (Target[i]) {
17 case ' ':
18 case '\t':
19 // Escape the preceding backslashes
20 for (int j = i - 1; j >= 0 && Target[j] == '\\'; --j)
21 Res.push_back('\\');
22
23 // Escape the space/tab
24 Res.push_back('\\');
25 break;
26 case '$':
27 Res.push_back('$');
28 break;
29 case '#':
30 Res.push_back('\\');
31 break;
32 default:
33 break;
34 }
35
36 Res.push_back(Target[i]);
37 }
38}
39
40/// Print the filename, with escaping or quoting that accommodates the three
41/// most likely tools that use dependency files: GNU Make, BSD Make, and
42/// NMake/Jom.
43///
44/// BSD Make is the simplest case: It does no escaping at all. This means
45/// characters that are normally delimiters, i.e. space and # (the comment
46/// character) simply aren't supported in filenames.
47///
48/// GNU Make does allow space and # in filenames, but to avoid being treated
49/// as a delimiter or comment, these must be escaped with a backslash. Because
50/// backslash is itself the escape character, if a backslash appears in a
51/// filename, it should be escaped as well. (As a special case, $ is escaped
52/// as $$, which is the normal Make way to handle the $ character.)
53/// For compatibility with BSD Make and historical practice, if GNU Make
54/// un-escapes characters in a filename but doesn't find a match, it will
55/// retry with the unmodified original string.
56///
57/// GCC tries to accommodate both Make formats by escaping any space or #
58/// characters in the original filename, but not escaping backslashes. The
59/// apparent intent is so that filenames with backslashes will be handled
60/// correctly by BSD Make, and by GNU Make in its fallback mode of using the
61/// unmodified original string; filenames with # or space characters aren't
62/// supported by BSD Make at all, but will be handled correctly by GNU Make
63/// due to the escaping.
64///
65/// A corner case that GCC gets only partly right is when the original filename
66/// has a backslash immediately followed by space or #. GNU Make would expect
67/// this backslash to be escaped; however GCC escapes the original backslash
68/// only when followed by space, not #. It will therefore take a dependency
69/// from a directive such as
70/// #include "a\ b\#c.h"
71/// and emit it as
72/// a\\\ b\\#c.h
73/// which GNU Make will interpret as
74/// a\ b\
75/// followed by a comment. Failing to find this file, it will fall back to the
76/// original string, which probably doesn't exist either; in any case it won't
77/// find
78/// a\ b\#c.h
79/// which is the actual filename specified by the include directive.
80///
81/// Clang does what GCC does, rather than what GNU Make expects.
82///
83/// NMake/Jom has a different set of scary characters, but wraps filespecs in
84/// double-quotes to avoid misinterpreting them; see
85/// https://msdn.microsoft.com/en-us/library/dd9y37ha.aspx for NMake info,
86/// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
87/// for Windows file-naming info.
88static void printFilename(llvm::raw_ostream &OS, llvm::StringRef Filename,
89 clang::DependencyOutputFormat OutputFormat) {
90 // Convert filename to platform native path
91 llvm::SmallString<256> NativePath;
92 llvm::sys::path::native(Filename, NativePath);
93
94 if (OutputFormat == clang::DependencyOutputFormat::NMake) {
95 // Add quotes if needed. These are the characters listed as "special" to
96 // NMake, that are legal in a Windows filespec, and that could cause
97 // misinterpretation of the dependency string.
98 if (NativePath.find_first_of(" #${}^!") != llvm::StringRef::npos)
99 OS << '\"' << NativePath << '\"';
100 else
101 OS << NativePath;
102 return;
103 }
104 assert(OutputFormat == clang::DependencyOutputFormat::Make);
105 for (unsigned i = 0, e = NativePath.size(); i != e; ++i) {
106 if (NativePath[i] == '#') // Handle '#' the broken gcc way.
107 OS << '\\';
108 else if (NativePath[i] == ' ') { // Handle space correctly.
109 OS << '\\';
110 unsigned j = i;
111 while (j > 0 && NativePath[--j] == '\\')
112 OS << '\\';
113 } else if (NativePath[i] == '$') // $ is escaped by $$.
114 OS << '$';
115 OS << NativePath[i];
116 }
117}
118
119void clang::printMakeDependencyFile(llvm::raw_ostream &OS,
123 bool PhonyTargets,
124 unsigned InputFileIndex) {
125 // Write out the dependency targets, trying to avoid overly long
126 // lines when possible. We try our best to emit exactly the same
127 // dependency file as GCC>=10, assuming the included files are the
128 // same.
129 const unsigned MaxColumns = 75;
130 unsigned Columns = 0;
131
132 for (StringRef Target : Targets) {
133 unsigned N = Target.size();
134 if (Columns == 0) {
135 Columns += N;
136 } else if (Columns + N + 2 > MaxColumns) {
137 Columns = N + 2;
138 OS << " \\\n ";
139 } else {
140 Columns += N + 1;
141 OS << ' ';
142 }
143 // Targets already quoted as needed.
144 OS << Target;
145 }
146
147 OS << ':';
148 Columns += 1;
149
150 // Now add each dependency in the order it was seen, but avoiding
151 // duplicates.
152 for (StringRef File : Files) {
153 if (File == "<stdin>")
154 continue;
155 // Start a new line if this would exceed the column limit. Make
156 // sure to leave space for a trailing " \" in case we need to
157 // break the line on the next iteration.
158 unsigned N = File.size();
159 if (Columns + (N + 1) + 2 > MaxColumns) {
160 OS << " \\\n ";
161 Columns = 2;
162 }
163 OS << ' ';
164 printFilename(OS, File, Format);
165 Columns += N + 1;
166 }
167 OS << '\n';
168
169 // Create phony targets if requested.
170 if (PhonyTargets && !Files.empty()) {
171 unsigned Index = 0;
172 for (auto I = Files.begin(), E = Files.end(); I != E; ++I) {
173 if (Index++ == InputFileIndex)
174 continue;
175 printFilename(OS, *I, Format);
176 OS << ":\n";
177 }
178 }
179}
static void printFilename(llvm::raw_ostream &OS, llvm::StringRef Filename, clang::DependencyOutputFormat OutputFormat)
Print the filename, with escaping or quoting that accommodates the three most likely tools that use d...
void quoteMakeTarget(StringRef Target, SmallVectorImpl< char > &Res)
Quote target names for inclusion in GNU Make dependency files.
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)
Write Make-style dependency output to the output stream, in the form: target1 target2 ....
DependencyOutputFormat
DependencyOutputFormat - Format for the compiler dependency file.
Definition MakeSupport.h:23