Index: include/clang/Frontend/DependencyOutputOptions.h =================================================================== --- include/clang/Frontend/DependencyOutputOptions.h +++ include/clang/Frontend/DependencyOutputOptions.h @@ -47,6 +47,9 @@ /// must contain at least one entry. std::vector Targets; + /// A list of filenames to be used as extra dependencies for every target. + std::vector ExtraDeps; + /// \brief The file to write GraphViz-formatted header dependencies to. std::string DOTOutputFile; Index: include/clang/Frontend/Utils.h =================================================================== --- include/clang/Frontend/Utils.h +++ include/clang/Frontend/Utils.h @@ -148,6 +148,9 @@ /// AttachHeaderIncludeGen - Create a header include list generator, and attach /// it to the given preprocessor. /// +/// \param ExtraHeaders - If not empty, will write the header filenames, just +/// like they were included during a regular preprocessing. Useful for +/// implicit include dependencies, like sanitizer blacklists. /// \param ShowAllHeaders - If true, show all header information instead of just /// headers following the predefines buffer. This is useful for making sure /// includes mentioned on the command line are also reported, but differs from @@ -156,7 +159,9 @@ /// information to, instead of writing to stderr. /// \param ShowDepth - Whether to indent to show the nesting of the includes. /// \param MSStyle - Whether to print in cl.exe /showIncludes style. -void AttachHeaderIncludeGen(Preprocessor &PP, bool ShowAllHeaders = false, +void AttachHeaderIncludeGen(Preprocessor &PP, + const std::vector &ExtraHeaders, + bool ShowAllHeaders = false, StringRef OutputPath = "", bool ShowDepth = true, bool MSStyle = false); Index: lib/Frontend/CompilerInstance.cpp =================================================================== --- lib/Frontend/CompilerInstance.cpp +++ lib/Frontend/CompilerInstance.cpp @@ -354,17 +354,19 @@ // Handle generating header include information, if requested. if (DepOpts.ShowHeaderIncludes) - AttachHeaderIncludeGen(*PP); + AttachHeaderIncludeGen(*PP, DepOpts.ExtraDeps); if (!DepOpts.HeaderIncludeOutputFile.empty()) { StringRef OutputPath = DepOpts.HeaderIncludeOutputFile; if (OutputPath == "-") OutputPath = ""; - AttachHeaderIncludeGen(*PP, /*ShowAllHeaders=*/true, OutputPath, + AttachHeaderIncludeGen(*PP, DepOpts.ExtraDeps, + /*ShowAllHeaders=*/true, OutputPath, /*ShowDepth=*/false); } if (DepOpts.PrintShowIncludes) { - AttachHeaderIncludeGen(*PP, /*ShowAllHeaders=*/false, /*OutputPath=*/"", + AttachHeaderIncludeGen(*PP, DepOpts.ExtraDeps, + /*ShowAllHeaders=*/false, /*OutputPath=*/"", /*ShowDepth=*/true, /*MSStyle=*/true); } } Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -705,6 +705,10 @@ Args.getLastArgValue(OPT_module_dependency_dir); if (Args.hasArg(OPT_MV)) Opts.OutputFormat = DependencyOutputFormat::NMake; + // Add sanitizer blacklists as extra dependencies. + // They won't be discovered by the regular preprocessor, so + // we let make / ninja to know about this implicit dependency. + Opts.ExtraDeps = Args.getAllArgValues(OPT_fsanitize_blacklist); } bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, Index: lib/Frontend/DependencyFile.cpp =================================================================== --- lib/Frontend/DependencyFile.cpp +++ lib/Frontend/DependencyFile.cpp @@ -182,7 +182,11 @@ AddMissingHeaderDeps(Opts.AddMissingHeaderDeps), SeenMissingHeader(false), IncludeModuleFiles(Opts.IncludeModuleFiles), - OutputFormat(Opts.OutputFormat) {} + OutputFormat(Opts.OutputFormat) { + for (auto ExtraDep : Opts.ExtraDeps) { + AddFilename(ExtraDep); + } + } void FileChanged(SourceLocation Loc, FileChangeReason Reason, SrcMgr::CharacteristicKind FileType, Index: lib/Frontend/HeaderIncludeGen.cpp =================================================================== --- lib/Frontend/HeaderIncludeGen.cpp +++ lib/Frontend/HeaderIncludeGen.cpp @@ -46,7 +46,36 @@ }; } -void clang::AttachHeaderIncludeGen(Preprocessor &PP, bool ShowAllHeaders, +static void PrintHeaderInfo(raw_ostream *OutputFile, const char* Filename, + bool ShowDepth, unsigned CurrentIncludeDepth, + bool MSStyle) { + // Write to a temporary string to avoid unnecessary flushing on errs(). + SmallString<512> Pathname(Filename); + if (!MSStyle) + Lexer::Stringify(Pathname); + + SmallString<256> Msg; + if (MSStyle) + Msg += "Note: including file:"; + + if (ShowDepth) { + // The main source file is at depth 1, so skip one dot. + for (unsigned i = 1; i != CurrentIncludeDepth; ++i) + Msg += MSStyle ? ' ' : '.'; + + if (!MSStyle) + Msg += ' '; + } + Msg += Pathname; + Msg += '\n'; + + OutputFile->write(Msg.data(), Msg.size()); + OutputFile->flush(); +} + +void clang::AttachHeaderIncludeGen(Preprocessor &PP, + const std::vector &ExtraHeaders, + bool ShowAllHeaders, StringRef OutputPath, bool ShowDepth, bool MSStyle) { raw_ostream *OutputFile = MSStyle ? &llvm::outs() : &llvm::errs(); @@ -69,6 +98,14 @@ } } + // Print header info for extra headers, pretending they were discovered + // by the regular preprocessor. The primary use case is to support + // proper generation of Make / Ninja file dependencies for implicit includes, + // such as sanitizer blacklists. It's only important for cl.exe + // compatibility, the GNU way to generate rules is -M / -MM / -MD / -MMD. + for (auto Header : ExtraHeaders) { + PrintHeaderInfo(OutputFile, Header.c_str(), ShowDepth, 2, MSStyle); + } PP.addPPCallbacks(llvm::make_unique(&PP, ShowAllHeaders, OutputFile, @@ -112,27 +149,7 @@ // Dump the header include information we are past the predefines buffer or // are showing all headers. if (ShowHeader && Reason == PPCallbacks::EnterFile) { - // Write to a temporary string to avoid unnecessary flushing on errs(). - SmallString<512> Filename(UserLoc.getFilename()); - if (!MSStyle) - Lexer::Stringify(Filename); - - SmallString<256> Msg; - if (MSStyle) - Msg += "Note: including file:"; - - if (ShowDepth) { - // The main source file is at depth 1, so skip one dot. - for (unsigned i = 1; i != CurrentIncludeDepth; ++i) - Msg += MSStyle ? ' ' : '.'; - - if (!MSStyle) - Msg += ' '; - } - Msg += Filename; - Msg += '\n'; - - OutputFile->write(Msg.data(), Msg.size()); - OutputFile->flush(); + PrintHeaderInfo(OutputFile, UserLoc.getFilename(), + ShowDepth, CurrentIncludeDepth, MSStyle); } } Index: test/Frontend/dependency-gen.c =================================================================== --- test/Frontend/dependency-gen.c +++ test/Frontend/dependency-gen.c @@ -20,7 +20,16 @@ // RUN: cd a/b // RUN: %clang -MD -MF - %s -fsyntax-only -I ./ | FileCheck -check-prefix=CHECK-SIX %s // CHECK-SIX: {{ }}x.h - +// RUN: echo "fun:foo" > %t.blacklist +// RUN: %clang -MD -MF - %s -fsyntax-only -fsanitize=cfi-vcall -flto -fsanitize-blacklist=%t.blacklist -I ./ | FileCheck -check-prefix=CHECK-SEVEN %s +// CHECK-SEVEN: {{ }}x.h +// CHECK-SEVEN: .blacklist +// RUN: %clang -MD -MF - %s -fsyntax-only -fsanitize=address -flto -I . | FileCheck -check-prefix=CHECK-EIGHT %s +// CHECK-EIGHT: {{ }}x.h +// CHECK-EIGHT: asan_blacklist.txt +// RUN: %clang -MD -MF - %s -fsyntax-only -fsanitize=address -flto -I . -fno-sanitize-blacklist | FileCheck -check-prefix=CHECK-NINE %s +// CHECK-NINE: {{ }}x.h +// CHECK-NINE-NOT: asan_blacklist.txt #ifndef INCLUDE_FLAG_TEST #include #endif