xref: /openbsd-src/gnu/llvm/clang/lib/Frontend/DependencyFile.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
1e5dd7070Spatrick //===--- DependencyFile.cpp - Generate dependency file --------------------===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick // This code generates dependency files.
10e5dd7070Spatrick //
11e5dd7070Spatrick //===----------------------------------------------------------------------===//
12e5dd7070Spatrick 
13e5dd7070Spatrick #include "clang/Frontend/Utils.h"
14e5dd7070Spatrick #include "clang/Basic/FileManager.h"
15e5dd7070Spatrick #include "clang/Basic/SourceManager.h"
16e5dd7070Spatrick #include "clang/Frontend/DependencyOutputOptions.h"
17e5dd7070Spatrick #include "clang/Frontend/FrontendDiagnostic.h"
18e5dd7070Spatrick #include "clang/Lex/DirectoryLookup.h"
19e5dd7070Spatrick #include "clang/Lex/ModuleMap.h"
20e5dd7070Spatrick #include "clang/Lex/PPCallbacks.h"
21e5dd7070Spatrick #include "clang/Lex/Preprocessor.h"
22e5dd7070Spatrick #include "clang/Serialization/ASTReader.h"
23e5dd7070Spatrick #include "llvm/ADT/StringSet.h"
24e5dd7070Spatrick #include "llvm/Support/FileSystem.h"
25e5dd7070Spatrick #include "llvm/Support/Path.h"
26e5dd7070Spatrick #include "llvm/Support/raw_ostream.h"
27*12c85518Srobert #include <optional>
28e5dd7070Spatrick 
29e5dd7070Spatrick using namespace clang;
30e5dd7070Spatrick 
31e5dd7070Spatrick namespace {
32e5dd7070Spatrick struct DepCollectorPPCallbacks : public PPCallbacks {
33e5dd7070Spatrick   DependencyCollector &DepCollector;
34*12c85518Srobert   Preprocessor &PP;
DepCollectorPPCallbacks__anone9e1041f0111::DepCollectorPPCallbacks35*12c85518Srobert   DepCollectorPPCallbacks(DependencyCollector &L, Preprocessor &PP)
36*12c85518Srobert       : DepCollector(L), PP(PP) {}
37e5dd7070Spatrick 
LexedFileChanged__anone9e1041f0111::DepCollectorPPCallbacks38*12c85518Srobert   void LexedFileChanged(FileID FID, LexedFileChangeReason Reason,
39*12c85518Srobert                         SrcMgr::CharacteristicKind FileType, FileID PrevFID,
40*12c85518Srobert                         SourceLocation Loc) override {
41*12c85518Srobert     if (Reason != PPCallbacks::LexedFileChangeReason::EnterFile)
42e5dd7070Spatrick       return;
43e5dd7070Spatrick 
44e5dd7070Spatrick     // Dependency generation really does want to go all the way to the
45e5dd7070Spatrick     // file entry for a source location to find out what is depended on.
46e5dd7070Spatrick     // We do not want #line markers to affect dependency generation!
47*12c85518Srobert     if (std::optional<StringRef> Filename =
48*12c85518Srobert             PP.getSourceManager().getNonBuiltinFilenameForID(FID))
49a9ac8606Spatrick       DepCollector.maybeAddDependency(
50a9ac8606Spatrick           llvm::sys::path::remove_leading_dotslash(*Filename),
51a9ac8606Spatrick           /*FromModule*/ false, isSystem(FileType), /*IsModuleFile*/ false,
52a9ac8606Spatrick           /*IsMissing*/ false);
53e5dd7070Spatrick   }
54e5dd7070Spatrick 
FileSkipped__anone9e1041f0111::DepCollectorPPCallbacks55e5dd7070Spatrick   void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok,
56e5dd7070Spatrick                    SrcMgr::CharacteristicKind FileType) override {
57e5dd7070Spatrick     StringRef Filename =
58e5dd7070Spatrick         llvm::sys::path::remove_leading_dotslash(SkippedFile.getName());
59e5dd7070Spatrick     DepCollector.maybeAddDependency(Filename, /*FromModule=*/false,
60e5dd7070Spatrick                                     /*IsSystem=*/isSystem(FileType),
61e5dd7070Spatrick                                     /*IsModuleFile=*/false,
62e5dd7070Spatrick                                     /*IsMissing=*/false);
63e5dd7070Spatrick   }
64e5dd7070Spatrick 
InclusionDirective__anone9e1041f0111::DepCollectorPPCallbacks65e5dd7070Spatrick   void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
66e5dd7070Spatrick                           StringRef FileName, bool IsAngled,
67*12c85518Srobert                           CharSourceRange FilenameRange,
68*12c85518Srobert                           OptionalFileEntryRef File, StringRef SearchPath,
69*12c85518Srobert                           StringRef RelativePath, const Module *Imported,
70e5dd7070Spatrick                           SrcMgr::CharacteristicKind FileType) override {
71e5dd7070Spatrick     if (!File)
72e5dd7070Spatrick       DepCollector.maybeAddDependency(FileName, /*FromModule*/false,
73e5dd7070Spatrick                                      /*IsSystem*/false, /*IsModuleFile*/false,
74e5dd7070Spatrick                                      /*IsMissing*/true);
75e5dd7070Spatrick     // Files that actually exist are handled by FileChanged.
76e5dd7070Spatrick   }
77e5dd7070Spatrick 
HasInclude__anone9e1041f0111::DepCollectorPPCallbacks78e5dd7070Spatrick   void HasInclude(SourceLocation Loc, StringRef SpelledFilename, bool IsAngled,
79*12c85518Srobert                   OptionalFileEntryRef File,
80e5dd7070Spatrick                   SrcMgr::CharacteristicKind FileType) override {
81e5dd7070Spatrick     if (!File)
82e5dd7070Spatrick       return;
83e5dd7070Spatrick     StringRef Filename =
84e5dd7070Spatrick         llvm::sys::path::remove_leading_dotslash(File->getName());
85e5dd7070Spatrick     DepCollector.maybeAddDependency(Filename, /*FromModule=*/false,
86e5dd7070Spatrick                                     /*IsSystem=*/isSystem(FileType),
87e5dd7070Spatrick                                     /*IsModuleFile=*/false,
88e5dd7070Spatrick                                     /*IsMissing=*/false);
89e5dd7070Spatrick   }
90e5dd7070Spatrick 
EndOfMainFile__anone9e1041f0111::DepCollectorPPCallbacks91*12c85518Srobert   void EndOfMainFile() override {
92*12c85518Srobert     DepCollector.finishedMainFile(PP.getDiagnostics());
93*12c85518Srobert   }
94e5dd7070Spatrick };
95e5dd7070Spatrick 
96e5dd7070Spatrick struct DepCollectorMMCallbacks : public ModuleMapCallbacks {
97e5dd7070Spatrick   DependencyCollector &DepCollector;
DepCollectorMMCallbacks__anone9e1041f0111::DepCollectorMMCallbacks98e5dd7070Spatrick   DepCollectorMMCallbacks(DependencyCollector &DC) : DepCollector(DC) {}
99e5dd7070Spatrick 
moduleMapFileRead__anone9e1041f0111::DepCollectorMMCallbacks100e5dd7070Spatrick   void moduleMapFileRead(SourceLocation Loc, const FileEntry &Entry,
101e5dd7070Spatrick                          bool IsSystem) override {
102e5dd7070Spatrick     StringRef Filename = Entry.getName();
103e5dd7070Spatrick     DepCollector.maybeAddDependency(Filename, /*FromModule*/false,
104e5dd7070Spatrick                                     /*IsSystem*/IsSystem,
105e5dd7070Spatrick                                     /*IsModuleFile*/false,
106e5dd7070Spatrick                                     /*IsMissing*/false);
107e5dd7070Spatrick   }
108e5dd7070Spatrick };
109e5dd7070Spatrick 
110e5dd7070Spatrick struct DepCollectorASTListener : public ASTReaderListener {
111e5dd7070Spatrick   DependencyCollector &DepCollector;
112*12c85518Srobert   FileManager &FileMgr;
DepCollectorASTListener__anone9e1041f0111::DepCollectorASTListener113*12c85518Srobert   DepCollectorASTListener(DependencyCollector &L, FileManager &FileMgr)
114*12c85518Srobert       : DepCollector(L), FileMgr(FileMgr) {}
needsInputFileVisitation__anone9e1041f0111::DepCollectorASTListener115e5dd7070Spatrick   bool needsInputFileVisitation() override { return true; }
needsSystemInputFileVisitation__anone9e1041f0111::DepCollectorASTListener116e5dd7070Spatrick   bool needsSystemInputFileVisitation() override {
117e5dd7070Spatrick     return DepCollector.needSystemDependencies();
118e5dd7070Spatrick   }
visitModuleFile__anone9e1041f0111::DepCollectorASTListener119e5dd7070Spatrick   void visitModuleFile(StringRef Filename,
120e5dd7070Spatrick                        serialization::ModuleKind Kind) override {
121e5dd7070Spatrick     DepCollector.maybeAddDependency(Filename, /*FromModule*/true,
122e5dd7070Spatrick                                    /*IsSystem*/false, /*IsModuleFile*/true,
123e5dd7070Spatrick                                    /*IsMissing*/false);
124e5dd7070Spatrick   }
visitInputFile__anone9e1041f0111::DepCollectorASTListener125e5dd7070Spatrick   bool visitInputFile(StringRef Filename, bool IsSystem,
126e5dd7070Spatrick                       bool IsOverridden, bool IsExplicitModule) override {
127e5dd7070Spatrick     if (IsOverridden || IsExplicitModule)
128e5dd7070Spatrick       return true;
129e5dd7070Spatrick 
130*12c85518Srobert     // Run this through the FileManager in order to respect 'use-external-name'
131*12c85518Srobert     // in case we have a VFS overlay.
132*12c85518Srobert     if (auto FE = FileMgr.getOptionalFileRef(Filename))
133*12c85518Srobert       Filename = FE->getName();
134*12c85518Srobert 
135e5dd7070Spatrick     DepCollector.maybeAddDependency(Filename, /*FromModule*/true, IsSystem,
136e5dd7070Spatrick                                    /*IsModuleFile*/false, /*IsMissing*/false);
137e5dd7070Spatrick     return true;
138e5dd7070Spatrick   }
139e5dd7070Spatrick };
140e5dd7070Spatrick } // end anonymous namespace
141e5dd7070Spatrick 
maybeAddDependency(StringRef Filename,bool FromModule,bool IsSystem,bool IsModuleFile,bool IsMissing)142ec727ea7Spatrick void DependencyCollector::maybeAddDependency(StringRef Filename,
143ec727ea7Spatrick                                              bool FromModule, bool IsSystem,
144ec727ea7Spatrick                                              bool IsModuleFile,
145e5dd7070Spatrick                                              bool IsMissing) {
146e5dd7070Spatrick   if (sawDependency(Filename, FromModule, IsSystem, IsModuleFile, IsMissing))
147e5dd7070Spatrick     addDependency(Filename);
148e5dd7070Spatrick }
149e5dd7070Spatrick 
addDependency(StringRef Filename)150e5dd7070Spatrick bool DependencyCollector::addDependency(StringRef Filename) {
151a9ac8606Spatrick   StringRef SearchPath;
152a9ac8606Spatrick #ifdef _WIN32
153a9ac8606Spatrick   // Make the search insensitive to case and separators.
154a9ac8606Spatrick   llvm::SmallString<256> TmpPath = Filename;
155a9ac8606Spatrick   llvm::sys::path::native(TmpPath);
156a9ac8606Spatrick   std::transform(TmpPath.begin(), TmpPath.end(), TmpPath.begin(), ::tolower);
157a9ac8606Spatrick   SearchPath = TmpPath.str();
158a9ac8606Spatrick #else
159a9ac8606Spatrick   SearchPath = Filename;
160a9ac8606Spatrick #endif
161a9ac8606Spatrick 
162a9ac8606Spatrick   if (Seen.insert(SearchPath).second) {
163ec727ea7Spatrick     Dependencies.push_back(std::string(Filename));
164e5dd7070Spatrick     return true;
165e5dd7070Spatrick   }
166e5dd7070Spatrick   return false;
167e5dd7070Spatrick }
168e5dd7070Spatrick 
isSpecialFilename(StringRef Filename)169e5dd7070Spatrick static bool isSpecialFilename(StringRef Filename) {
170*12c85518Srobert   return Filename == "<built-in>";
171e5dd7070Spatrick }
172e5dd7070Spatrick 
sawDependency(StringRef Filename,bool FromModule,bool IsSystem,bool IsModuleFile,bool IsMissing)173e5dd7070Spatrick bool DependencyCollector::sawDependency(StringRef Filename, bool FromModule,
174e5dd7070Spatrick                                         bool IsSystem, bool IsModuleFile,
175e5dd7070Spatrick                                         bool IsMissing) {
176e5dd7070Spatrick   return !isSpecialFilename(Filename) &&
177e5dd7070Spatrick          (needSystemDependencies() || !IsSystem);
178e5dd7070Spatrick }
179e5dd7070Spatrick 
~DependencyCollector()180e5dd7070Spatrick DependencyCollector::~DependencyCollector() { }
attachToPreprocessor(Preprocessor & PP)181e5dd7070Spatrick void DependencyCollector::attachToPreprocessor(Preprocessor &PP) {
182*12c85518Srobert   PP.addPPCallbacks(std::make_unique<DepCollectorPPCallbacks>(*this, PP));
183e5dd7070Spatrick   PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks(
184e5dd7070Spatrick       std::make_unique<DepCollectorMMCallbacks>(*this));
185e5dd7070Spatrick }
attachToASTReader(ASTReader & R)186e5dd7070Spatrick void DependencyCollector::attachToASTReader(ASTReader &R) {
187*12c85518Srobert   R.addListener(
188*12c85518Srobert       std::make_unique<DepCollectorASTListener>(*this, R.getFileManager()));
189e5dd7070Spatrick }
190e5dd7070Spatrick 
DependencyFileGenerator(const DependencyOutputOptions & Opts)191e5dd7070Spatrick DependencyFileGenerator::DependencyFileGenerator(
192e5dd7070Spatrick     const DependencyOutputOptions &Opts)
193e5dd7070Spatrick     : OutputFile(Opts.OutputFile), Targets(Opts.Targets),
194e5dd7070Spatrick       IncludeSystemHeaders(Opts.IncludeSystemHeaders),
195e5dd7070Spatrick       PhonyTarget(Opts.UsePhonyTargets),
196e5dd7070Spatrick       AddMissingHeaderDeps(Opts.AddMissingHeaderDeps), SeenMissingHeader(false),
197e5dd7070Spatrick       IncludeModuleFiles(Opts.IncludeModuleFiles),
198e5dd7070Spatrick       OutputFormat(Opts.OutputFormat), InputFileIndex(0) {
199e5dd7070Spatrick   for (const auto &ExtraDep : Opts.ExtraDeps) {
200a9ac8606Spatrick     if (addDependency(ExtraDep.first))
201e5dd7070Spatrick       ++InputFileIndex;
202e5dd7070Spatrick   }
203e5dd7070Spatrick }
204e5dd7070Spatrick 
attachToPreprocessor(Preprocessor & PP)205e5dd7070Spatrick void DependencyFileGenerator::attachToPreprocessor(Preprocessor &PP) {
206e5dd7070Spatrick   // Disable the "file not found" diagnostic if the -MG option was given.
207e5dd7070Spatrick   if (AddMissingHeaderDeps)
208e5dd7070Spatrick     PP.SetSuppressIncludeNotFoundError(true);
209e5dd7070Spatrick 
210e5dd7070Spatrick   DependencyCollector::attachToPreprocessor(PP);
211e5dd7070Spatrick }
212e5dd7070Spatrick 
sawDependency(StringRef Filename,bool FromModule,bool IsSystem,bool IsModuleFile,bool IsMissing)213e5dd7070Spatrick bool DependencyFileGenerator::sawDependency(StringRef Filename, bool FromModule,
214e5dd7070Spatrick                                             bool IsSystem, bool IsModuleFile,
215e5dd7070Spatrick                                             bool IsMissing) {
216e5dd7070Spatrick   if (IsMissing) {
217e5dd7070Spatrick     // Handle the case of missing file from an inclusion directive.
218e5dd7070Spatrick     if (AddMissingHeaderDeps)
219e5dd7070Spatrick       return true;
220e5dd7070Spatrick     SeenMissingHeader = true;
221e5dd7070Spatrick     return false;
222e5dd7070Spatrick   }
223e5dd7070Spatrick   if (IsModuleFile && !IncludeModuleFiles)
224e5dd7070Spatrick     return false;
225e5dd7070Spatrick 
226e5dd7070Spatrick   if (isSpecialFilename(Filename))
227e5dd7070Spatrick     return false;
228e5dd7070Spatrick 
229e5dd7070Spatrick   if (IncludeSystemHeaders)
230e5dd7070Spatrick     return true;
231e5dd7070Spatrick 
232e5dd7070Spatrick   return !IsSystem;
233e5dd7070Spatrick }
234e5dd7070Spatrick 
finishedMainFile(DiagnosticsEngine & Diags)235e5dd7070Spatrick void DependencyFileGenerator::finishedMainFile(DiagnosticsEngine &Diags) {
236e5dd7070Spatrick   outputDependencyFile(Diags);
237e5dd7070Spatrick }
238e5dd7070Spatrick 
239e5dd7070Spatrick /// Print the filename, with escaping or quoting that accommodates the three
240e5dd7070Spatrick /// most likely tools that use dependency files: GNU Make, BSD Make, and
241e5dd7070Spatrick /// NMake/Jom.
242e5dd7070Spatrick ///
243e5dd7070Spatrick /// BSD Make is the simplest case: It does no escaping at all.  This means
244e5dd7070Spatrick /// characters that are normally delimiters, i.e. space and # (the comment
245e5dd7070Spatrick /// character) simply aren't supported in filenames.
246e5dd7070Spatrick ///
247e5dd7070Spatrick /// GNU Make does allow space and # in filenames, but to avoid being treated
248e5dd7070Spatrick /// as a delimiter or comment, these must be escaped with a backslash. Because
249e5dd7070Spatrick /// backslash is itself the escape character, if a backslash appears in a
250e5dd7070Spatrick /// filename, it should be escaped as well.  (As a special case, $ is escaped
251e5dd7070Spatrick /// as $$, which is the normal Make way to handle the $ character.)
252e5dd7070Spatrick /// For compatibility with BSD Make and historical practice, if GNU Make
253e5dd7070Spatrick /// un-escapes characters in a filename but doesn't find a match, it will
254e5dd7070Spatrick /// retry with the unmodified original string.
255e5dd7070Spatrick ///
256e5dd7070Spatrick /// GCC tries to accommodate both Make formats by escaping any space or #
257e5dd7070Spatrick /// characters in the original filename, but not escaping backslashes.  The
258e5dd7070Spatrick /// apparent intent is so that filenames with backslashes will be handled
259e5dd7070Spatrick /// correctly by BSD Make, and by GNU Make in its fallback mode of using the
260e5dd7070Spatrick /// unmodified original string; filenames with # or space characters aren't
261e5dd7070Spatrick /// supported by BSD Make at all, but will be handled correctly by GNU Make
262e5dd7070Spatrick /// due to the escaping.
263e5dd7070Spatrick ///
264e5dd7070Spatrick /// A corner case that GCC gets only partly right is when the original filename
265e5dd7070Spatrick /// has a backslash immediately followed by space or #.  GNU Make would expect
266e5dd7070Spatrick /// this backslash to be escaped; however GCC escapes the original backslash
267e5dd7070Spatrick /// only when followed by space, not #.  It will therefore take a dependency
268e5dd7070Spatrick /// from a directive such as
269e5dd7070Spatrick ///     #include "a\ b\#c.h"
270e5dd7070Spatrick /// and emit it as
271e5dd7070Spatrick ///     a\\\ b\\#c.h
272e5dd7070Spatrick /// which GNU Make will interpret as
273e5dd7070Spatrick ///     a\ b\
274e5dd7070Spatrick /// followed by a comment. Failing to find this file, it will fall back to the
275e5dd7070Spatrick /// original string, which probably doesn't exist either; in any case it won't
276e5dd7070Spatrick /// find
277e5dd7070Spatrick ///     a\ b\#c.h
278e5dd7070Spatrick /// which is the actual filename specified by the include directive.
279e5dd7070Spatrick ///
280e5dd7070Spatrick /// Clang does what GCC does, rather than what GNU Make expects.
281e5dd7070Spatrick ///
282e5dd7070Spatrick /// NMake/Jom has a different set of scary characters, but wraps filespecs in
283e5dd7070Spatrick /// double-quotes to avoid misinterpreting them; see
284e5dd7070Spatrick /// https://msdn.microsoft.com/en-us/library/dd9y37ha.aspx for NMake info,
285e5dd7070Spatrick /// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
286e5dd7070Spatrick /// for Windows file-naming info.
PrintFilename(raw_ostream & OS,StringRef Filename,DependencyOutputFormat OutputFormat)287e5dd7070Spatrick static void PrintFilename(raw_ostream &OS, StringRef Filename,
288e5dd7070Spatrick                           DependencyOutputFormat OutputFormat) {
289e5dd7070Spatrick   // Convert filename to platform native path
290e5dd7070Spatrick   llvm::SmallString<256> NativePath;
291e5dd7070Spatrick   llvm::sys::path::native(Filename.str(), NativePath);
292e5dd7070Spatrick 
293e5dd7070Spatrick   if (OutputFormat == DependencyOutputFormat::NMake) {
294e5dd7070Spatrick     // Add quotes if needed. These are the characters listed as "special" to
295e5dd7070Spatrick     // NMake, that are legal in a Windows filespec, and that could cause
296e5dd7070Spatrick     // misinterpretation of the dependency string.
297e5dd7070Spatrick     if (NativePath.find_first_of(" #${}^!") != StringRef::npos)
298e5dd7070Spatrick       OS << '\"' << NativePath << '\"';
299e5dd7070Spatrick     else
300e5dd7070Spatrick       OS << NativePath;
301e5dd7070Spatrick     return;
302e5dd7070Spatrick   }
303e5dd7070Spatrick   assert(OutputFormat == DependencyOutputFormat::Make);
304e5dd7070Spatrick   for (unsigned i = 0, e = NativePath.size(); i != e; ++i) {
305e5dd7070Spatrick     if (NativePath[i] == '#') // Handle '#' the broken gcc way.
306e5dd7070Spatrick       OS << '\\';
307e5dd7070Spatrick     else if (NativePath[i] == ' ') { // Handle space correctly.
308e5dd7070Spatrick       OS << '\\';
309e5dd7070Spatrick       unsigned j = i;
310e5dd7070Spatrick       while (j > 0 && NativePath[--j] == '\\')
311e5dd7070Spatrick         OS << '\\';
312e5dd7070Spatrick     } else if (NativePath[i] == '$') // $ is escaped by $$.
313e5dd7070Spatrick       OS << '$';
314e5dd7070Spatrick     OS << NativePath[i];
315e5dd7070Spatrick   }
316e5dd7070Spatrick }
317e5dd7070Spatrick 
outputDependencyFile(DiagnosticsEngine & Diags)318e5dd7070Spatrick void DependencyFileGenerator::outputDependencyFile(DiagnosticsEngine &Diags) {
319e5dd7070Spatrick   if (SeenMissingHeader) {
320e5dd7070Spatrick     llvm::sys::fs::remove(OutputFile);
321e5dd7070Spatrick     return;
322e5dd7070Spatrick   }
323e5dd7070Spatrick 
324e5dd7070Spatrick   std::error_code EC;
325a9ac8606Spatrick   llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF);
326e5dd7070Spatrick   if (EC) {
327e5dd7070Spatrick     Diags.Report(diag::err_fe_error_opening) << OutputFile << EC.message();
328e5dd7070Spatrick     return;
329e5dd7070Spatrick   }
330e5dd7070Spatrick 
331e5dd7070Spatrick   outputDependencyFile(OS);
332e5dd7070Spatrick }
333e5dd7070Spatrick 
outputDependencyFile(llvm::raw_ostream & OS)334e5dd7070Spatrick void DependencyFileGenerator::outputDependencyFile(llvm::raw_ostream &OS) {
335e5dd7070Spatrick   // Write out the dependency targets, trying to avoid overly long
336e5dd7070Spatrick   // lines when possible. We try our best to emit exactly the same
337*12c85518Srobert   // dependency file as GCC>=10, assuming the included files are the
338e5dd7070Spatrick   // same.
339e5dd7070Spatrick   const unsigned MaxColumns = 75;
340e5dd7070Spatrick   unsigned Columns = 0;
341e5dd7070Spatrick 
342e5dd7070Spatrick   for (StringRef Target : Targets) {
343e5dd7070Spatrick     unsigned N = Target.size();
344e5dd7070Spatrick     if (Columns == 0) {
345e5dd7070Spatrick       Columns += N;
346e5dd7070Spatrick     } else if (Columns + N + 2 > MaxColumns) {
347e5dd7070Spatrick       Columns = N + 2;
348e5dd7070Spatrick       OS << " \\\n  ";
349e5dd7070Spatrick     } else {
350e5dd7070Spatrick       Columns += N + 1;
351e5dd7070Spatrick       OS << ' ';
352e5dd7070Spatrick     }
353e5dd7070Spatrick     // Targets already quoted as needed.
354e5dd7070Spatrick     OS << Target;
355e5dd7070Spatrick   }
356e5dd7070Spatrick 
357e5dd7070Spatrick   OS << ':';
358e5dd7070Spatrick   Columns += 1;
359e5dd7070Spatrick 
360e5dd7070Spatrick   // Now add each dependency in the order it was seen, but avoiding
361e5dd7070Spatrick   // duplicates.
362e5dd7070Spatrick   ArrayRef<std::string> Files = getDependencies();
363e5dd7070Spatrick   for (StringRef File : Files) {
364*12c85518Srobert     if (File == "<stdin>")
365*12c85518Srobert       continue;
366e5dd7070Spatrick     // Start a new line if this would exceed the column limit. Make
367e5dd7070Spatrick     // sure to leave space for a trailing " \" in case we need to
368e5dd7070Spatrick     // break the line on the next iteration.
369e5dd7070Spatrick     unsigned N = File.size();
370e5dd7070Spatrick     if (Columns + (N + 1) + 2 > MaxColumns) {
371e5dd7070Spatrick       OS << " \\\n ";
372e5dd7070Spatrick       Columns = 2;
373e5dd7070Spatrick     }
374e5dd7070Spatrick     OS << ' ';
375e5dd7070Spatrick     PrintFilename(OS, File, OutputFormat);
376e5dd7070Spatrick     Columns += N + 1;
377e5dd7070Spatrick   }
378e5dd7070Spatrick   OS << '\n';
379e5dd7070Spatrick 
380e5dd7070Spatrick   // Create phony targets if requested.
381e5dd7070Spatrick   if (PhonyTarget && !Files.empty()) {
382e5dd7070Spatrick     unsigned Index = 0;
383e5dd7070Spatrick     for (auto I = Files.begin(), E = Files.end(); I != E; ++I) {
384e5dd7070Spatrick       if (Index++ == InputFileIndex)
385e5dd7070Spatrick         continue;
386e5dd7070Spatrick       PrintFilename(OS, *I, OutputFormat);
387e5dd7070Spatrick       OS << ":\n";
388e5dd7070Spatrick     }
389e5dd7070Spatrick   }
390e5dd7070Spatrick }
391