Tooling.cpp revision cf3ce5d89628b3955c638fb102c8dc4c24e1986d
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 "llvm/ADT/ArrayRef.h" 17#include "llvm/ADT/SmallString.h" 18#include "llvm/Support/CommandLine.h" 19#include "llvm/Support/Host.h" 20#include "llvm/Support/MemoryBuffer.h" 21#include "llvm/Support/ManagedStatic.h" 22#include "llvm/Support/Path.h" 23#include "llvm/Support/raw_ostream.h" 24#include "clang/Basic/DiagnosticIDs.h" 25#include "clang/Driver/Compilation.h" 26#include "clang/Driver/Driver.h" 27#include "clang/Driver/Tool.h" 28#include "clang/Frontend/CompilerInstance.h" 29#include "clang/Frontend/FrontendAction.h" 30#include "clang/Frontend/FrontendDiagnostic.h" 31#include "clang/Frontend/TextDiagnosticPrinter.h" 32 33#include <string> 34#include <map> 35#include <vector> 36 37namespace clang { 38namespace tooling { 39 40// FIXME: This file contains structural duplication with other parts of the 41// code that sets up a compiler to run tools on it, and we should refactor 42// it to be based on the same framework. 43 44static clang::Diagnostic* NewTextDiagnostics() { 45 llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs( 46 new clang::DiagnosticIDs()); 47 clang::TextDiagnosticPrinter *DiagClient = new clang::TextDiagnosticPrinter( 48 llvm::errs(), clang::DiagnosticOptions()); 49 return new clang::Diagnostic(DiagIDs, DiagClient); 50} 51 52// Exists solely for the purpose of lookup of the main executable. 53static int StaticSymbol; 54 55/// \brief Builds a clang driver initialized for running clang tools. 56static clang::driver::Driver* NewDriver(clang::Diagnostic* Diagnostics, 57 const char* BinaryName) { 58 // This just needs to be some symbol in the binary. 59 void* const SymbolAddr = &StaticSymbol; 60 const llvm::sys::Path ExePath = 61 llvm::sys::Path::GetMainExecutable(BinaryName, SymbolAddr); 62 63 const std::string DefaultOutputName = "a.out"; 64 clang::driver::Driver* CompilerDriver = new clang::driver::Driver( 65 ExePath.str(), llvm::sys::getHostTriple(), 66 DefaultOutputName, false, false, *Diagnostics); 67 CompilerDriver->setTitle("clang_based_tool"); 68 return CompilerDriver; 69} 70 71/// \brief Retrieves the clang CC1 specific flags out of the compilation's jobs. 72/// Returns NULL on error. 73static const clang::driver::ArgStringList* GetCC1Arguments( 74 clang::Diagnostic* Diagnostics, clang::driver::Compilation* Compilation) { 75 // We expect to get back exactly one Command job, if we didn't something 76 // failed. Extract that job from the Compilation. 77 const clang::driver::JobList &Jobs = Compilation->getJobs(); 78 if (Jobs.size() != 1 || !isa<clang::driver::Command>(*Jobs.begin())) { 79 llvm::SmallString<256> error_msg; 80 llvm::raw_svector_ostream error_stream(error_msg); 81 Compilation->PrintJob(error_stream, Compilation->getJobs(), "; ", true); 82 Diagnostics->Report(clang::diag::err_fe_expected_compiler_job) 83 << error_stream.str(); 84 return NULL; 85 } 86 87 // The one job we find should be to invoke clang again. 88 const clang::driver::Command *Cmd = 89 cast<clang::driver::Command>(*Jobs.begin()); 90 if (llvm::StringRef(Cmd->getCreator().getName()) != "clang") { 91 Diagnostics->Report(clang::diag::err_fe_expected_clang_command); 92 return NULL; 93 } 94 95 return &Cmd->getArguments(); 96} 97 98/// \brief Returns a clang build invocation initialized from the CC1 flags. 99static clang::CompilerInvocation* NewInvocation( 100 clang::Diagnostic* Diagnostics, 101 const clang::driver::ArgStringList& CC1Args) { 102 clang::CompilerInvocation* Invocation = new clang::CompilerInvocation; 103 clang::CompilerInvocation::CreateFromArgs( 104 *Invocation, CC1Args.data(), CC1Args.data() + CC1Args.size(), 105 *Diagnostics); 106 Invocation->getFrontendOpts().DisableFree = false; 107 return Invocation; 108} 109 110/// \brief Runs the specified clang tool action and returns whether it executed 111/// successfully. 112static bool RunInvocation(const char* BinaryName, 113 clang::driver::Compilation* Compilation, 114 clang::CompilerInvocation* Invocation, 115 const clang::driver::ArgStringList& CC1Args, 116 clang::FrontendAction* ToolAction) { 117 llvm::OwningPtr<clang::FrontendAction> ScopedToolAction(ToolAction); 118 // Show the invocation, with -v. 119 if (Invocation->getHeaderSearchOpts().Verbose) { 120 llvm::errs() << "clang Invocation:\n"; 121 Compilation->PrintJob(llvm::errs(), Compilation->getJobs(), "\n", true); 122 llvm::errs() << "\n"; 123 } 124 125 // Create a compiler instance to handle the actual work. 126 clang::CompilerInstance Compiler; 127 Compiler.setInvocation(Invocation); 128 129 // Create the compilers actual diagnostics engine. 130 Compiler.createDiagnostics(CC1Args.size(), 131 const_cast<char**>(CC1Args.data())); 132 if (!Compiler.hasDiagnostics()) 133 return false; 134 135 // Infer the builtin include path if unspecified. 136 if (Compiler.getHeaderSearchOpts().UseBuiltinIncludes && 137 Compiler.getHeaderSearchOpts().ResourceDir.empty()) { 138 // This just needs to be some symbol in the binary. 139 void* const SymbolAddr = &StaticSymbol; 140 Compiler.getHeaderSearchOpts().ResourceDir = 141 clang::CompilerInvocation::GetResourcesPath(BinaryName, SymbolAddr); 142 } 143 144 const bool Success = Compiler.ExecuteAction(*ToolAction); 145 return Success; 146} 147 148/// \brief Converts a string vector representing a Command line into a C 149/// string vector representing the Argv (including the trailing NULL). 150std::vector<char*> CommandLineToArgv(const std::vector<std::string>* Command) { 151 std::vector<char*> Result(Command->size() + 1); 152 for (std::vector<char*>::size_type I = 0; I < Command->size(); ++I) { 153 Result[I] = const_cast<char*>((*Command)[I].c_str()); 154 } 155 Result[Command->size()] = NULL; 156 return Result; 157} 158 159/// \brief Runs 'ToolAction' on the code specified by 'FileContents'. 160/// 161/// \param FileContents A mapping from file name to source code. For each 162/// entry a virtual file mapping will be created when running the tool. 163bool RunToolWithFlagsOnCode( 164 const std::vector<std::string>& CommandLine, 165 const std::map<std::string, std::string>& FileContents, 166 clang::FrontendAction* ToolAction) { 167 const std::vector<char*> Argv = CommandLineToArgv(&CommandLine); 168 const char* const BinaryName = Argv[0]; 169 170 const llvm::OwningPtr<clang::Diagnostic> Diagnostics(NewTextDiagnostics()); 171 const llvm::OwningPtr<clang::driver::Driver> Driver( 172 NewDriver(Diagnostics.get(), BinaryName)); 173 174 // Since the Input is only virtual, don't check whether it exists. 175 Driver->setCheckInputsExist(false); 176 177 const llvm::OwningPtr<clang::driver::Compilation> Compilation( 178 Driver->BuildCompilation(llvm::ArrayRef<const char*>(&Argv[0], 179 Argv.size() - 1))); 180 const clang::driver::ArgStringList* const CC1Args = GetCC1Arguments( 181 Diagnostics.get(), Compilation.get()); 182 if (CC1Args == NULL) { 183 return false; 184 } 185 llvm::OwningPtr<clang::CompilerInvocation> Invocation( 186 NewInvocation(Diagnostics.get(), *CC1Args)); 187 188 for (std::map<std::string, std::string>::const_iterator 189 It = FileContents.begin(), End = FileContents.end(); 190 It != End; ++It) { 191 // Inject the code as the given file name into the preprocessor options. 192 const llvm::MemoryBuffer* Input = 193 llvm::MemoryBuffer::getMemBuffer(It->second.c_str()); 194 Invocation->getPreprocessorOpts().addRemappedFile(It->first.c_str(), Input); 195 } 196 197 return RunInvocation(BinaryName, Compilation.get(), 198 Invocation.take(), *CC1Args, ToolAction); 199} 200 201bool RunSyntaxOnlyToolOnCode( 202 clang::FrontendAction *ToolAction, llvm::StringRef Code) { 203 const char* const FileName = "input.cc"; 204 const char* const CommandLine[] = { 205 "clang-tool", "-fsyntax-only", FileName 206 }; 207 std::map<std::string, std::string> FileContents; 208 FileContents[FileName] = Code; 209 return RunToolWithFlagsOnCode( 210 std::vector<std::string>( 211 CommandLine, 212 CommandLine + sizeof(CommandLine)/sizeof(CommandLine[0])), 213 FileContents, ToolAction); 214} 215 216} // end namespace tooling 217} // end namespace clang 218 219