Home | History | Annotate | Download | only in Tooling
      1 //===--- Tooling.cpp - Running clang standalone tools ---------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 //  This file implements functions to run clang tools standalone instead
     11 //  of running them as a plugin.
     12 //
     13 //===----------------------------------------------------------------------===//
     14 
     15 #include "clang/Tooling/Tooling.h"
     16 #include "clang/AST/ASTConsumer.h"
     17 #include "clang/Driver/Compilation.h"
     18 #include "clang/Driver/Driver.h"
     19 #include "clang/Driver/Tool.h"
     20 #include "clang/Frontend/ASTUnit.h"
     21 #include "clang/Frontend/CompilerInstance.h"
     22 #include "clang/Frontend/FrontendDiagnostic.h"
     23 #include "clang/Frontend/TextDiagnosticPrinter.h"
     24 #include "clang/Tooling/ArgumentsAdjusters.h"
     25 #include "clang/Tooling/CompilationDatabase.h"
     26 #include "llvm/ADT/STLExtras.h"
     27 #include "llvm/Config/llvm-config.h"
     28 #include "llvm/Option/Option.h"
     29 #include "llvm/Support/Debug.h"
     30 #include "llvm/Support/FileSystem.h"
     31 #include "llvm/Support/Host.h"
     32 #include "llvm/Support/raw_ostream.h"
     33 
     34 // For chdir, see the comment in ClangTool::run for more information.
     35 #ifdef LLVM_ON_WIN32
     36 #  include <direct.h>
     37 #else
     38 #  include <unistd.h>
     39 #endif
     40 
     41 #define DEBUG_TYPE "clang-tooling"
     42 
     43 namespace clang {
     44 namespace tooling {
     45 
     46 ToolAction::~ToolAction() {}
     47 
     48 FrontendActionFactory::~FrontendActionFactory() {}
     49 
     50 // FIXME: This file contains structural duplication with other parts of the
     51 // code that sets up a compiler to run tools on it, and we should refactor
     52 // it to be based on the same framework.
     53 
     54 /// \brief Builds a clang driver initialized for running clang tools.
     55 static clang::driver::Driver *newDriver(clang::DiagnosticsEngine *Diagnostics,
     56                                         const char *BinaryName) {
     57   clang::driver::Driver *CompilerDriver = new clang::driver::Driver(
     58       BinaryName, llvm::sys::getDefaultTargetTriple(), *Diagnostics);
     59   CompilerDriver->setTitle("clang_based_tool");
     60   return CompilerDriver;
     61 }
     62 
     63 /// \brief Retrieves the clang CC1 specific flags out of the compilation's jobs.
     64 ///
     65 /// Returns NULL on error.
     66 static const llvm::opt::ArgStringList *getCC1Arguments(
     67     clang::DiagnosticsEngine *Diagnostics,
     68     clang::driver::Compilation *Compilation) {
     69   // We expect to get back exactly one Command job, if we didn't something
     70   // failed. Extract that job from the Compilation.
     71   const clang::driver::JobList &Jobs = Compilation->getJobs();
     72   if (Jobs.size() != 1 || !isa<clang::driver::Command>(*Jobs.begin())) {
     73     SmallString<256> error_msg;
     74     llvm::raw_svector_ostream error_stream(error_msg);
     75     Jobs.Print(error_stream, "; ", true);
     76     Diagnostics->Report(clang::diag::err_fe_expected_compiler_job)
     77         << error_stream.str();
     78     return nullptr;
     79   }
     80 
     81   // The one job we find should be to invoke clang again.
     82   const clang::driver::Command *Cmd =
     83       cast<clang::driver::Command>(*Jobs.begin());
     84   if (StringRef(Cmd->getCreator().getName()) != "clang") {
     85     Diagnostics->Report(clang::diag::err_fe_expected_clang_command);
     86     return nullptr;
     87   }
     88 
     89   return &Cmd->getArguments();
     90 }
     91 
     92 /// \brief Returns a clang build invocation initialized from the CC1 flags.
     93 static clang::CompilerInvocation *newInvocation(
     94     clang::DiagnosticsEngine *Diagnostics,
     95     const llvm::opt::ArgStringList &CC1Args) {
     96   assert(!CC1Args.empty() && "Must at least contain the program name!");
     97   clang::CompilerInvocation *Invocation = new clang::CompilerInvocation;
     98   clang::CompilerInvocation::CreateFromArgs(
     99       *Invocation, CC1Args.data() + 1, CC1Args.data() + CC1Args.size(),
    100       *Diagnostics);
    101   Invocation->getFrontendOpts().DisableFree = false;
    102   Invocation->getCodeGenOpts().DisableFree = false;
    103   Invocation->getDependencyOutputOpts() = DependencyOutputOptions();
    104   return Invocation;
    105 }
    106 
    107 bool runToolOnCode(clang::FrontendAction *ToolAction, const Twine &Code,
    108                    const Twine &FileName) {
    109   return runToolOnCodeWithArgs(
    110       ToolAction, Code, std::vector<std::string>(), FileName);
    111 }
    112 
    113 static std::vector<std::string>
    114 getSyntaxOnlyToolArgs(const std::vector<std::string> &ExtraArgs,
    115                       StringRef FileName) {
    116   std::vector<std::string> Args;
    117   Args.push_back("clang-tool");
    118   Args.push_back("-fsyntax-only");
    119   Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
    120   Args.push_back(FileName.str());
    121   return Args;
    122 }
    123 
    124 bool runToolOnCodeWithArgs(clang::FrontendAction *ToolAction, const Twine &Code,
    125                            const std::vector<std::string> &Args,
    126                            const Twine &FileName) {
    127   SmallString<16> FileNameStorage;
    128   StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
    129   llvm::IntrusiveRefCntPtr<FileManager> Files(
    130       new FileManager(FileSystemOptions()));
    131   ToolInvocation Invocation(getSyntaxOnlyToolArgs(Args, FileNameRef), ToolAction,
    132                             Files.get());
    133 
    134   SmallString<1024> CodeStorage;
    135   Invocation.mapVirtualFile(FileNameRef,
    136                             Code.toNullTerminatedStringRef(CodeStorage));
    137   return Invocation.run();
    138 }
    139 
    140 std::string getAbsolutePath(StringRef File) {
    141   StringRef RelativePath(File);
    142   // FIXME: Should '.\\' be accepted on Win32?
    143   if (RelativePath.startswith("./")) {
    144     RelativePath = RelativePath.substr(strlen("./"));
    145   }
    146 
    147   SmallString<1024> AbsolutePath = RelativePath;
    148   std::error_code EC = llvm::sys::fs::make_absolute(AbsolutePath);
    149   assert(!EC);
    150   (void)EC;
    151   llvm::sys::path::native(AbsolutePath);
    152   return AbsolutePath.str();
    153 }
    154 
    155 namespace {
    156 
    157 class SingleFrontendActionFactory : public FrontendActionFactory {
    158   FrontendAction *Action;
    159 
    160 public:
    161   SingleFrontendActionFactory(FrontendAction *Action) : Action(Action) {}
    162 
    163   FrontendAction *create() override { return Action; }
    164 };
    165 
    166 }
    167 
    168 ToolInvocation::ToolInvocation(std::vector<std::string> CommandLine,
    169                                ToolAction *Action, FileManager *Files)
    170     : CommandLine(std::move(CommandLine)),
    171       Action(Action),
    172       OwnsAction(false),
    173       Files(Files),
    174       DiagConsumer(nullptr) {}
    175 
    176 ToolInvocation::ToolInvocation(std::vector<std::string> CommandLine,
    177                                FrontendAction *FAction, FileManager *Files)
    178     : CommandLine(std::move(CommandLine)),
    179       Action(new SingleFrontendActionFactory(FAction)),
    180       OwnsAction(true),
    181       Files(Files),
    182       DiagConsumer(nullptr) {}
    183 
    184 ToolInvocation::~ToolInvocation() {
    185   if (OwnsAction)
    186     delete Action;
    187 }
    188 
    189 void ToolInvocation::setDiagnosticConsumer(DiagnosticConsumer *D) {
    190   DiagConsumer = D;
    191 }
    192 
    193 void ToolInvocation::mapVirtualFile(StringRef FilePath, StringRef Content) {
    194   SmallString<1024> PathStorage;
    195   llvm::sys::path::native(FilePath, PathStorage);
    196   MappedFileContents[PathStorage] = Content;
    197 }
    198 
    199 bool ToolInvocation::run() {
    200   std::vector<const char*> Argv;
    201   for (const std::string &Str : CommandLine)
    202     Argv.push_back(Str.c_str());
    203   const char *const BinaryName = Argv[0];
    204   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
    205   TextDiagnosticPrinter DiagnosticPrinter(
    206       llvm::errs(), &*DiagOpts);
    207   DiagnosticsEngine Diagnostics(
    208       IntrusiveRefCntPtr<clang::DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
    209       DiagConsumer ? DiagConsumer : &DiagnosticPrinter, false);
    210 
    211   const std::unique_ptr<clang::driver::Driver> Driver(
    212       newDriver(&Diagnostics, BinaryName));
    213   // Since the input might only be virtual, don't check whether it exists.
    214   Driver->setCheckInputsExist(false);
    215   const std::unique_ptr<clang::driver::Compilation> Compilation(
    216       Driver->BuildCompilation(llvm::makeArrayRef(Argv)));
    217   const llvm::opt::ArgStringList *const CC1Args = getCC1Arguments(
    218       &Diagnostics, Compilation.get());
    219   if (!CC1Args) {
    220     return false;
    221   }
    222   std::unique_ptr<clang::CompilerInvocation> Invocation(
    223       newInvocation(&Diagnostics, *CC1Args));
    224   for (const auto &It : MappedFileContents) {
    225     // Inject the code as the given file name into the preprocessor options.
    226     auto *Input = llvm::MemoryBuffer::getMemBuffer(It.getValue());
    227     Invocation->getPreprocessorOpts().addRemappedFile(It.getKey(), Input);
    228   }
    229   return runInvocation(BinaryName, Compilation.get(), Invocation.release());
    230 }
    231 
    232 bool ToolInvocation::runInvocation(
    233     const char *BinaryName,
    234     clang::driver::Compilation *Compilation,
    235     clang::CompilerInvocation *Invocation) {
    236   // Show the invocation, with -v.
    237   if (Invocation->getHeaderSearchOpts().Verbose) {
    238     llvm::errs() << "clang Invocation:\n";
    239     Compilation->getJobs().Print(llvm::errs(), "\n", true);
    240     llvm::errs() << "\n";
    241   }
    242 
    243   return Action->runInvocation(Invocation, Files, DiagConsumer);
    244 }
    245 
    246 bool FrontendActionFactory::runInvocation(CompilerInvocation *Invocation,
    247                                           FileManager *Files,
    248                                           DiagnosticConsumer *DiagConsumer) {
    249   // Create a compiler instance to handle the actual work.
    250   clang::CompilerInstance Compiler;
    251   Compiler.setInvocation(Invocation);
    252   Compiler.setFileManager(Files);
    253 
    254   // The FrontendAction can have lifetime requirements for Compiler or its
    255   // members, and we need to ensure it's deleted earlier than Compiler. So we
    256   // pass it to an std::unique_ptr declared after the Compiler variable.
    257   std::unique_ptr<FrontendAction> ScopedToolAction(create());
    258 
    259   // Create the compiler's actual diagnostics engine.
    260   Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
    261   if (!Compiler.hasDiagnostics())
    262     return false;
    263 
    264   Compiler.createSourceManager(*Files);
    265 
    266   const bool Success = Compiler.ExecuteAction(*ScopedToolAction);
    267 
    268   Files->clearStatCaches();
    269   return Success;
    270 }
    271 
    272 ClangTool::ClangTool(const CompilationDatabase &Compilations,
    273                      ArrayRef<std::string> SourcePaths)
    274     : Files(new FileManager(FileSystemOptions())), DiagConsumer(nullptr) {
    275   ArgsAdjusters.push_back(new ClangStripOutputAdjuster());
    276   ArgsAdjusters.push_back(new ClangSyntaxOnlyAdjuster());
    277   for (const auto &SourcePath : SourcePaths) {
    278     std::string File(getAbsolutePath(SourcePath));
    279 
    280     std::vector<CompileCommand> CompileCommandsForFile =
    281       Compilations.getCompileCommands(File);
    282     if (!CompileCommandsForFile.empty()) {
    283       for (CompileCommand &CompileCommand : CompileCommandsForFile) {
    284         CompileCommands.push_back(
    285             std::make_pair(File, std::move(CompileCommand)));
    286       }
    287     } else {
    288       // FIXME: There are two use cases here: doing a fuzzy
    289       // "find . -name '*.cc' |xargs tool" match, where as a user I don't care
    290       // about the .cc files that were not found, and the use case where I
    291       // specify all files I want to run over explicitly, where this should
    292       // be an error. We'll want to add an option for this.
    293       llvm::errs() << "Skipping " << File << ". Compile command not found.\n";
    294     }
    295   }
    296 }
    297 
    298 void ClangTool::setDiagnosticConsumer(DiagnosticConsumer *D) {
    299   DiagConsumer = D;
    300 }
    301 
    302 void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) {
    303   MappedFileContents.push_back(std::make_pair(FilePath, Content));
    304 }
    305 
    306 void ClangTool::setArgumentsAdjuster(ArgumentsAdjuster *Adjuster) {
    307   clearArgumentsAdjusters();
    308   appendArgumentsAdjuster(Adjuster);
    309 }
    310 
    311 void ClangTool::appendArgumentsAdjuster(ArgumentsAdjuster *Adjuster) {
    312   ArgsAdjusters.push_back(Adjuster);
    313 }
    314 
    315 void ClangTool::clearArgumentsAdjusters() {
    316   for (unsigned I = 0, E = ArgsAdjusters.size(); I != E; ++I)
    317     delete ArgsAdjusters[I];
    318   ArgsAdjusters.clear();
    319 }
    320 
    321 int ClangTool::run(ToolAction *Action) {
    322   // Exists solely for the purpose of lookup of the resource path.
    323   // This just needs to be some symbol in the binary.
    324   static int StaticSymbol;
    325   // The driver detects the builtin header path based on the path of the
    326   // executable.
    327   // FIXME: On linux, GetMainExecutable is independent of the value of the
    328   // first argument, thus allowing ClangTool and runToolOnCode to just
    329   // pass in made-up names here. Make sure this works on other platforms.
    330   std::string MainExecutable =
    331       llvm::sys::fs::getMainExecutable("clang_tool", &StaticSymbol);
    332 
    333   bool ProcessingFailed = false;
    334   for (const auto &Command : CompileCommands) {
    335     // FIXME: chdir is thread hostile; on the other hand, creating the same
    336     // behavior as chdir is complex: chdir resolves the path once, thus
    337     // guaranteeing that all subsequent relative path operations work
    338     // on the same path the original chdir resulted in. This makes a difference
    339     // for example on network filesystems, where symlinks might be switched
    340     // during runtime of the tool. Fixing this depends on having a file system
    341     // abstraction that allows openat() style interactions.
    342     if (chdir(Command.second.Directory.c_str()))
    343       llvm::report_fatal_error("Cannot chdir into \"" +
    344                                Twine(Command.second.Directory) + "\n!");
    345     std::vector<std::string> CommandLine = Command.second.CommandLine;
    346     for (ArgumentsAdjuster *Adjuster : ArgsAdjusters)
    347       CommandLine = Adjuster->Adjust(CommandLine);
    348     assert(!CommandLine.empty());
    349     CommandLine[0] = MainExecutable;
    350     // FIXME: We need a callback mechanism for the tool writer to output a
    351     // customized message for each file.
    352     DEBUG({
    353       llvm::dbgs() << "Processing: " << Command.first << ".\n";
    354     });
    355     ToolInvocation Invocation(std::move(CommandLine), Action, Files.get());
    356     Invocation.setDiagnosticConsumer(DiagConsumer);
    357     for (const auto &MappedFile : MappedFileContents) {
    358       Invocation.mapVirtualFile(MappedFile.first, MappedFile.second);
    359     }
    360     if (!Invocation.run()) {
    361       // FIXME: Diagnostics should be used instead.
    362       llvm::errs() << "Error while processing " << Command.first << ".\n";
    363       ProcessingFailed = true;
    364     }
    365   }
    366   return ProcessingFailed ? 1 : 0;
    367 }
    368 
    369 namespace {
    370 
    371 class ASTBuilderAction : public ToolAction {
    372   std::vector<std::unique_ptr<ASTUnit>> &ASTs;
    373 
    374 public:
    375   ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &ASTs) : ASTs(ASTs) {}
    376 
    377   bool runInvocation(CompilerInvocation *Invocation, FileManager *Files,
    378                      DiagnosticConsumer *DiagConsumer) override {
    379     // FIXME: This should use the provided FileManager.
    380     std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
    381         Invocation, CompilerInstance::createDiagnostics(
    382                         &Invocation->getDiagnosticOpts(), DiagConsumer,
    383                         /*ShouldOwnClient=*/false));
    384     if (!AST)
    385       return false;
    386 
    387     ASTs.push_back(std::move(AST));
    388     return true;
    389   }
    390 };
    391 
    392 }
    393 
    394 int ClangTool::buildASTs(std::vector<std::unique_ptr<ASTUnit>> &ASTs) {
    395   ASTBuilderAction Action(ASTs);
    396   return run(&Action);
    397 }
    398 
    399 std::unique_ptr<ASTUnit> buildASTFromCode(const Twine &Code,
    400                                           const Twine &FileName) {
    401   return buildASTFromCodeWithArgs(Code, std::vector<std::string>(), FileName);
    402 }
    403 
    404 std::unique_ptr<ASTUnit>
    405 buildASTFromCodeWithArgs(const Twine &Code,
    406                          const std::vector<std::string> &Args,
    407                          const Twine &FileName) {
    408   SmallString<16> FileNameStorage;
    409   StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
    410 
    411   std::vector<std::unique_ptr<ASTUnit>> ASTs;
    412   ASTBuilderAction Action(ASTs);
    413   ToolInvocation Invocation(getSyntaxOnlyToolArgs(Args, FileNameRef), &Action,
    414                             nullptr);
    415 
    416   SmallString<1024> CodeStorage;
    417   Invocation.mapVirtualFile(FileNameRef,
    418                             Code.toNullTerminatedStringRef(CodeStorage));
    419   if (!Invocation.run())
    420     return nullptr;
    421 
    422   assert(ASTs.size() == 1);
    423   return std::move(ASTs[0]);
    424 }
    425 
    426 } // end namespace tooling
    427 } // end namespace clang
    428