text: //===--- tools/pp-trace/PPTrace.cpp - Clang preprocessor tracer -----------===//
  //
  //                     The LLVM Compiler Infrastructure
  //
  // This file is distributed under the University of Illinois Open Source
  // License. See LICENSE.TXT for details.
  //
  //===----------------------------------------------------------------------===//
  //
  // This file implements pp-trace, a tool for displaying a textual trace
  // of the Clang preprocessor activity.  It's based on a derivation of the
  // PPCallbacks class, that once registerd with Clang, receives callback calls
  // to its virtual members, and outputs the information passed to the callbacks
  // in a high-level YAML format.
  //
  // The pp-trace tool also serves as the basis for a test of the PPCallbacks
  // mechanism.
  //
  // The pp-trace tool supports the following general command line format:
  //
  //    pp-trace [pp-trace options] (source file) [compiler options]
  //
  // Basically you put the pp-trace options first, then the source file or files,
  // and then any options you want to pass to the compiler.
  //
  // These are the pp-trace options:
  //
  //    -ignore (callback list)     Don't display output for a comma-separated
  //                                list of callbacks, i.e.:
  //                                  -ignore "FileChanged,InclusionDirective"
  //
  //    -output (file)              Output trace to the given file in a YAML
  //                                format, e.g.:
  //
  //                                  ---
  //                                  - Callback: Name
  //                                    Argument1: Value1
  //                                    Argument2: Value2
  //                                  (etc.)
  //                                  ...
  //
  // Future Directions:
  //
  // 1. Add option opposite to "-ignore" that specifys a comma-separated option
  // list of callbacs.  Perhaps "-only" or "-exclusive".
  //
  //===----------------------------------------------------------------------===//
  
  #include "PPCallbacksTracker.h"
  #include "clang/AST/ASTConsumer.h"
  #include "clang/AST/ASTContext.h"
  #include "clang/AST/RecursiveASTVisitor.h"
  #include "clang/Basic/SourceManager.h"
  #include "clang/Driver/Options.h"
  #include "clang/Frontend/CompilerInstance.h"
  #include "clang/Frontend/FrontendActions.h"
  #include "clang/Lex/Preprocessor.h"
  #include "clang/Tooling/CompilationDatabase.h"
  #include "clang/Tooling/Tooling.h"
  #include "llvm/Option/Arg.h"
  #include "llvm/Option/ArgList.h"
  #include "llvm/Option/OptTable.h"
  #include "llvm/Option/Option.h"
  #include "llvm/Support/CommandLine.h"
  #include "llvm/Support/FileSystem.h"
  #include "llvm/Support/MemoryBuffer.h"
  #include "llvm/Support/Path.h"
  #include "llvm/Support/ToolOutputFile.h"
  #include <algorithm>
  #include <fstream>
  #include <iterator>
  #include <string>
  #include <vector>
  
  using namespace clang;
  using namespace clang::driver;
  using namespace clang::driver::options;
  using namespace clang::tooling;
  using namespace llvm;
  using namespace llvm::opt;
  
  // Options:
  
  // Collect the source files.
  static cl::list<std::string> SourcePaths(cl::Positional,
                                           cl::desc("<source0> [... <sourceN>]"),
                                           cl::OneOrMore);
  
  // Option to specify a list or one or more callback names to ignore.
  static cl::opt<std::string> IgnoreCallbacks(
      "ignore", cl::init(""),
      cl::desc("Ignore callbacks, i.e. \"Callback1, Callback2...\"."));
  
  // Option to specify the trace output file name.
  static cl::opt<std::string> OutputFileName(
      "output", cl::init(""),
      cl::desc("Output trace to the given file name or '-' for stdout."));
  
  // Collect all other arguments, which will be passed to the front end.
  static cl::list<std::string>
      CC1Arguments(cl::ConsumeAfter,
                   cl::desc("<arguments to be passed to front end>..."));
  
  // Frontend action stuff:
  
  namespace {
  // Consumer is responsible for setting up the callbacks.
  class PPTraceConsumer : public ASTConsumer {
  public:
    PPTraceConsumer(SmallSet<std::string, 4> &Ignore,
                    std::vector<CallbackCall> &CallbackCalls, Preprocessor &PP) {
      // PP takes ownership.
      PP.addPPCallbacks(llvm::make_unique<PPCallbacksTracker>(Ignore,
                                                              CallbackCalls, PP));
    }
  };
  
  class PPTraceAction : public SyntaxOnlyAction {
  public:
    PPTraceAction(SmallSet<std::string, 4> &Ignore,
                  std::vector<CallbackCall> &CallbackCalls)
        : Ignore(Ignore), CallbackCalls(CallbackCalls) {}
  
  protected:
    std::unique_ptr<clang::ASTConsumer>
    CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override {
      return llvm::make_unique<PPTraceConsumer>(Ignore, CallbackCalls,
                                                CI.getPreprocessor());
    }
  
  private:
    SmallSet<std::string, 4> &Ignore;
    std::vector<CallbackCall> &CallbackCalls;
  };
  
  class PPTraceFrontendActionFactory : public FrontendActionFactory {
  public:
    PPTraceFrontendActionFactory(SmallSet<std::string, 4> &Ignore,
                                 std::vector<CallbackCall> &CallbackCalls)
        : Ignore(Ignore), CallbackCalls(CallbackCalls) {}
  
    PPTraceAction *create() override {
      return new PPTraceAction(Ignore, CallbackCalls);
    }
  
  private:
    SmallSet<std::string, 4> &Ignore;
    std::vector<CallbackCall> &CallbackCalls;
  };
  } // namespace
  
  // Output the trace given its data structure and a stream.
  static int outputPPTrace(std::vector<CallbackCall> &CallbackCalls,
                           llvm::raw_ostream &OS) {
    // Mark start of document.
    OS << "---\n";
  
    for (std::vector<CallbackCall>::const_iterator I = CallbackCalls.begin(),
                                                   E = CallbackCalls.end();
         I != E; ++I) {
      const CallbackCall &Callback = *I;
      OS << "- Callback: " << Callback.Name << "\n";
  
      for (auto AI = Callback.Arguments.begin(), AE = Callback.Arguments.end();
           AI != AE; ++AI) {
        const Argument &Arg = *AI;
        OS << "  " << Arg.Name << ": " << Arg.Value << "\n";
      }
    }
  
    // Mark end of document.
    OS << "...\n";
  
    return 0;
  }
  
  // Program entry point.
  int main(int Argc, const char **Argv) {
  
    // Parse command line.
    cl::ParseCommandLineOptions(Argc, Argv, "pp-trace.\n");
  
    // Parse the IgnoreCallbacks list into strings.
    SmallVector<StringRef, 32> IgnoreCallbacksStrings;
    StringRef(IgnoreCallbacks).split(IgnoreCallbacksStrings, ",",
                                     /*MaxSplit=*/ -1, /*KeepEmpty=*/false);
    SmallSet<std::string, 4> Ignore;
    for (SmallVector<StringRef, 32>::iterator I = IgnoreCallbacksStrings.begin(),
                                              E = IgnoreCallbacksStrings.end();
         I != E; ++I)
      Ignore.insert(*I);
  
    // Create the compilation database.
    SmallString<256> PathBuf;
    sys::fs::current_path(PathBuf);
    std::unique_ptr<CompilationDatabase> Compilations;
    Compilations.reset(
        new FixedCompilationDatabase(Twine(PathBuf), CC1Arguments));
  
    // Store the callback trace information here.
    std::vector<CallbackCall> CallbackCalls;
  
    // Create the tool and run the compilation.
    ClangTool Tool(*Compilations, SourcePaths);
    PPTraceFrontendActionFactory Factory(Ignore, CallbackCalls);
    int HadErrors = Tool.run(&Factory);
  
    // If we had errors, exit early.
    if (HadErrors)
      return HadErrors;
  
    // Do the output.
    if (!OutputFileName.size()) {
      HadErrors = outputPPTrace(CallbackCalls, llvm::outs());
    } else {
      // Set up output file.
      std::error_code EC;
      llvm::ToolOutputFile Out(OutputFileName, EC, llvm::sys::fs::F_Text);
      if (EC) {
        llvm::errs() << "pp-trace: error creating " << OutputFileName << ":"
                     << EC.message() << "\n";
        return 1;
      }
  
      HadErrors = outputPPTrace(CallbackCalls, Out.os());
  
      // Tell ToolOutputFile that we want to keep the file.
      if (HadErrors == 0)
        Out.keep();
    }
  
    return HadErrors;
  }
  