DependencyFile.cpp 14 KB

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