//===--- DependencyFile.cpp - Generate dependency file --------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This code generates dependency files. // //===----------------------------------------------------------------------===// #include "clang/Frontend/Utils.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" #include "clang/Frontend/DependencyOutputOptions.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Lex/DirectoryLookup.h" #include "clang/Lex/ModuleMap.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/Preprocessor.h" #include "clang/Serialization/ASTReader.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include <optional> usingnamespaceclang; namespace { struct DepCollectorPPCallbacks : public PPCallbacks { … }; struct DepCollectorMMCallbacks : public ModuleMapCallbacks { … }; struct DepCollectorASTListener : public ASTReaderListener { … }; } // end anonymous namespace void DependencyCollector::maybeAddDependency(StringRef Filename, bool FromModule, bool IsSystem, bool IsModuleFile, bool IsMissing) { … } bool DependencyCollector::addDependency(StringRef Filename) { … } static bool isSpecialFilename(StringRef Filename) { … } bool DependencyCollector::sawDependency(StringRef Filename, bool FromModule, bool IsSystem, bool IsModuleFile, bool IsMissing) { … } DependencyCollector::~DependencyCollector() { … } void DependencyCollector::attachToPreprocessor(Preprocessor &PP) { … } void DependencyCollector::attachToASTReader(ASTReader &R) { … } DependencyFileGenerator::DependencyFileGenerator( const DependencyOutputOptions &Opts) : … { … } void DependencyFileGenerator::attachToPreprocessor(Preprocessor &PP) { … } bool DependencyFileGenerator::sawDependency(StringRef Filename, bool FromModule, bool IsSystem, bool IsModuleFile, bool IsMissing) { … } void DependencyFileGenerator::finishedMainFile(DiagnosticsEngine &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) { … } void DependencyFileGenerator::outputDependencyFile(DiagnosticsEngine &Diags) { … } void DependencyFileGenerator::outputDependencyFile(llvm::raw_ostream &OS) { … }