1353358Sdim//===-- HeaderIncludeGen.cpp - Generate Header Includes -------------------===// 2218887Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6218887Sdim// 7218887Sdim//===----------------------------------------------------------------------===// 8218887Sdim 9309124Sdim#include "clang/Frontend/DependencyOutputOptions.h" 10218887Sdim#include "clang/Frontend/Utils.h" 11218887Sdim#include "clang/Basic/SourceManager.h" 12218887Sdim#include "clang/Frontend/FrontendDiagnostic.h" 13218887Sdim#include "clang/Lex/Preprocessor.h" 14234353Sdim#include "llvm/ADT/SmallString.h" 15218887Sdim#include "llvm/Support/raw_ostream.h" 16218887Sdimusing namespace clang; 17218887Sdim 18218887Sdimnamespace { 19218887Sdimclass HeaderIncludesCallback : public PPCallbacks { 20218887Sdim SourceManager &SM; 21226633Sdim raw_ostream *OutputFile; 22309124Sdim const DependencyOutputOptions &DepOpts; 23218887Sdim unsigned CurrentIncludeDepth; 24218887Sdim bool HasProcessedPredefines; 25218887Sdim bool OwnsOutputFile; 26218887Sdim bool ShowAllHeaders; 27221345Sdim bool ShowDepth; 28261991Sdim bool MSStyle; 29218887Sdim 30218887Sdimpublic: 31218887Sdim HeaderIncludesCallback(const Preprocessor *PP, bool ShowAllHeaders_, 32309124Sdim raw_ostream *OutputFile_, 33309124Sdim const DependencyOutputOptions &DepOpts, 34309124Sdim bool OwnsOutputFile_, bool ShowDepth_, bool MSStyle_) 35309124Sdim : SM(PP->getSourceManager()), OutputFile(OutputFile_), DepOpts(DepOpts), 36309124Sdim CurrentIncludeDepth(0), HasProcessedPredefines(false), 37309124Sdim OwnsOutputFile(OwnsOutputFile_), ShowAllHeaders(ShowAllHeaders_), 38309124Sdim ShowDepth(ShowDepth_), MSStyle(MSStyle_) {} 39218887Sdim 40288943Sdim ~HeaderIncludesCallback() override { 41218887Sdim if (OwnsOutputFile) 42218887Sdim delete OutputFile; 43218887Sdim } 44218887Sdim 45276479Sdim void FileChanged(SourceLocation Loc, FileChangeReason Reason, 46276479Sdim SrcMgr::CharacteristicKind FileType, 47276479Sdim FileID PrevFID) override; 48218887Sdim}; 49218887Sdim} 50218887Sdim 51309124Sdimstatic void PrintHeaderInfo(raw_ostream *OutputFile, StringRef Filename, 52296417Sdim bool ShowDepth, unsigned CurrentIncludeDepth, 53296417Sdim bool MSStyle) { 54309124Sdim // Write to a temporary string to avoid unnecessary flushing on errs(). 55309124Sdim SmallString<512> Pathname(Filename); 56309124Sdim if (!MSStyle) 57309124Sdim Lexer::Stringify(Pathname); 58296417Sdim 59309124Sdim SmallString<256> Msg; 60309124Sdim if (MSStyle) 61309124Sdim Msg += "Note: including file:"; 62296417Sdim 63309124Sdim if (ShowDepth) { 64309124Sdim // The main source file is at depth 1, so skip one dot. 65309124Sdim for (unsigned i = 1; i != CurrentIncludeDepth; ++i) 66309124Sdim Msg += MSStyle ? ' ' : '.'; 67296417Sdim 68309124Sdim if (!MSStyle) 69309124Sdim Msg += ' '; 70309124Sdim } 71309124Sdim Msg += Pathname; 72309124Sdim Msg += '\n'; 73296417Sdim 74309124Sdim *OutputFile << Msg; 75309124Sdim OutputFile->flush(); 76296417Sdim} 77296417Sdim 78296417Sdimvoid clang::AttachHeaderIncludeGen(Preprocessor &PP, 79309124Sdim const DependencyOutputOptions &DepOpts, 80309124Sdim bool ShowAllHeaders, StringRef OutputPath, 81309124Sdim bool ShowDepth, bool MSStyle) { 82341825Sdim raw_ostream *OutputFile = &llvm::errs(); 83218887Sdim bool OwnsOutputFile = false; 84218887Sdim 85341825Sdim // Choose output stream, when printing in cl.exe /showIncludes style. 86341825Sdim if (MSStyle) { 87341825Sdim switch (DepOpts.ShowIncludesDest) { 88341825Sdim default: 89341825Sdim llvm_unreachable("Invalid destination for /showIncludes output!"); 90341825Sdim case ShowIncludesDestination::Stderr: 91341825Sdim OutputFile = &llvm::errs(); 92341825Sdim break; 93341825Sdim case ShowIncludesDestination::Stdout: 94341825Sdim OutputFile = &llvm::outs(); 95341825Sdim break; 96341825Sdim } 97341825Sdim } 98341825Sdim 99218887Sdim // Open the output file, if used. 100218887Sdim if (!OutputPath.empty()) { 101280031Sdim std::error_code EC; 102218887Sdim llvm::raw_fd_ostream *OS = new llvm::raw_fd_ostream( 103360784Sdim OutputPath.str(), EC, 104360784Sdim llvm::sys::fs::OF_Append | llvm::sys::fs::OF_Text); 105280031Sdim if (EC) { 106280031Sdim PP.getDiagnostics().Report(clang::diag::warn_fe_cc_print_header_failure) 107280031Sdim << EC.message(); 108218887Sdim delete OS; 109218887Sdim } else { 110218887Sdim OS->SetUnbuffered(); 111218887Sdim OutputFile = OS; 112218887Sdim OwnsOutputFile = true; 113218887Sdim } 114218887Sdim } 115218887Sdim 116309124Sdim // Print header info for extra headers, pretending they were discovered by 117309124Sdim // the regular preprocessor. The primary use case is to support proper 118309124Sdim // generation of Make / Ninja file dependencies for implicit includes, such 119309124Sdim // as sanitizer blacklists. It's only important for cl.exe compatibility, 120309124Sdim // the GNU way to generate rules is -M / -MM / -MD / -MMD. 121309124Sdim for (const auto &Header : DepOpts.ExtraDeps) 122309124Sdim PrintHeaderInfo(OutputFile, Header, ShowDepth, 2, MSStyle); 123360784Sdim PP.addPPCallbacks(std::make_unique<HeaderIncludesCallback>( 124309124Sdim &PP, ShowAllHeaders, OutputFile, DepOpts, OwnsOutputFile, ShowDepth, 125309124Sdim MSStyle)); 126218887Sdim} 127218887Sdim 128218887Sdimvoid HeaderIncludesCallback::FileChanged(SourceLocation Loc, 129218887Sdim FileChangeReason Reason, 130226633Sdim SrcMgr::CharacteristicKind NewFileType, 131226633Sdim FileID PrevFID) { 132218887Sdim // Unless we are exiting a #include, make sure to skip ahead to the line the 133218887Sdim // #include directive was at. 134218887Sdim PresumedLoc UserLoc = SM.getPresumedLoc(Loc); 135218887Sdim if (UserLoc.isInvalid()) 136218887Sdim return; 137218887Sdim 138218887Sdim // Adjust the current include depth. 139218887Sdim if (Reason == PPCallbacks::EnterFile) { 140218887Sdim ++CurrentIncludeDepth; 141221345Sdim } else if (Reason == PPCallbacks::ExitFile) { 142218887Sdim if (CurrentIncludeDepth) 143218887Sdim --CurrentIncludeDepth; 144218887Sdim 145218887Sdim // We track when we are done with the predefines by watching for the first 146221345Sdim // place where we drop back to a nesting depth of 1. 147309124Sdim if (CurrentIncludeDepth == 1 && !HasProcessedPredefines) { 148309124Sdim if (!DepOpts.ShowIncludesPretendHeader.empty()) { 149309124Sdim PrintHeaderInfo(OutputFile, DepOpts.ShowIncludesPretendHeader, 150309124Sdim ShowDepth, 2, MSStyle); 151309124Sdim } 152218887Sdim HasProcessedPredefines = true; 153309124Sdim } 154218887Sdim 155221345Sdim return; 156221345Sdim } else 157221345Sdim return; 158221345Sdim 159218887Sdim // Show the header if we are (a) past the predefines, or (b) showing all 160218887Sdim // headers and in the predefines at a depth past the initial file and command 161218887Sdim // line buffers. 162218887Sdim bool ShowHeader = (HasProcessedPredefines || 163218887Sdim (ShowAllHeaders && CurrentIncludeDepth > 2)); 164309124Sdim unsigned IncludeDepth = CurrentIncludeDepth; 165309124Sdim if (!HasProcessedPredefines) 166309124Sdim --IncludeDepth; // Ignore indent from <built-in>. 167309124Sdim else if (!DepOpts.ShowIncludesPretendHeader.empty()) 168309124Sdim ++IncludeDepth; // Pretend inclusion by ShowIncludesPretendHeader. 169218887Sdim 170218887Sdim // Dump the header include information we are past the predefines buffer or 171309124Sdim // are showing all headers and this isn't the magic implicit <command line> 172309124Sdim // header. 173309124Sdim // FIXME: Identify headers in a more robust way than comparing their name to 174309124Sdim // "<command line>" and "<built-in>" in a bunch of places. 175309124Sdim if (ShowHeader && Reason == PPCallbacks::EnterFile && 176309124Sdim UserLoc.getFilename() != StringRef("<command line>")) { 177309124Sdim PrintHeaderInfo(OutputFile, UserLoc.getFilename(), ShowDepth, IncludeDepth, 178309124Sdim MSStyle); 179218887Sdim } 180218887Sdim} 181