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/Tooling/CompilationDatabase.h" 17 #include "clang/Driver/Compilation.h" 18 #include "clang/Driver/Driver.h" 19 #include "clang/Driver/Tool.h" 20 #include "clang/Frontend/CompilerInstance.h" 21 #include "clang/Frontend/FrontendAction.h" 22 #include "clang/Frontend/FrontendDiagnostic.h" 23 #include "clang/Frontend/TextDiagnosticPrinter.h" 24 #include "llvm/ADT/STLExtras.h" 25 #include "llvm/Support/FileSystem.h" 26 #include "llvm/Support/Host.h" 27 #include "llvm/Support/raw_ostream.h" 28 29 namespace clang { 30 namespace tooling { 31 32 FrontendActionFactory::~FrontendActionFactory() {} 33 34 // FIXME: This file contains structural duplication with other parts of the 35 // code that sets up a compiler to run tools on it, and we should refactor 36 // it to be based on the same framework. 37 38 /// \brief Builds a clang driver initialized for running clang tools. 39 static clang::driver::Driver *newDriver(clang::DiagnosticsEngine *Diagnostics, 40 const char *BinaryName) { 41 const std::string DefaultOutputName = "a.out"; 42 clang::driver::Driver *CompilerDriver = new clang::driver::Driver( 43 BinaryName, llvm::sys::getDefaultTargetTriple(), 44 DefaultOutputName, false, *Diagnostics); 45 CompilerDriver->setTitle("clang_based_tool"); 46 return CompilerDriver; 47 } 48 49 /// \brief Retrieves the clang CC1 specific flags out of the compilation's jobs. 50 /// 51 /// Returns NULL on error. 52 static const clang::driver::ArgStringList *getCC1Arguments( 53 clang::DiagnosticsEngine *Diagnostics, 54 clang::driver::Compilation *Compilation) { 55 // We expect to get back exactly one Command job, if we didn't something 56 // failed. Extract that job from the Compilation. 57 const clang::driver::JobList &Jobs = Compilation->getJobs(); 58 if (Jobs.size() != 1 || !isa<clang::driver::Command>(*Jobs.begin())) { 59 llvm::SmallString<256> error_msg; 60 llvm::raw_svector_ostream error_stream(error_msg); 61 Compilation->PrintJob(error_stream, Compilation->getJobs(), "; ", true); 62 Diagnostics->Report(clang::diag::err_fe_expected_compiler_job) 63 << error_stream.str(); 64 return NULL; 65 } 66 67 // The one job we find should be to invoke clang again. 68 const clang::driver::Command *Cmd = 69 cast<clang::driver::Command>(*Jobs.begin()); 70 if (StringRef(Cmd->getCreator().getName()) != "clang") { 71 Diagnostics->Report(clang::diag::err_fe_expected_clang_command); 72 return NULL; 73 } 74 75 return &Cmd->getArguments(); 76 } 77 78 /// \brief Returns a clang build invocation initialized from the CC1 flags. 79 static clang::CompilerInvocation *newInvocation( 80 clang::DiagnosticsEngine *Diagnostics, 81 const clang::driver::ArgStringList &CC1Args) { 82 assert(!CC1Args.empty() && "Must at least contain the program name!"); 83 clang::CompilerInvocation *Invocation = new clang::CompilerInvocation; 84 clang::CompilerInvocation::CreateFromArgs( 85 *Invocation, CC1Args.data() + 1, CC1Args.data() + CC1Args.size(), 86 *Diagnostics); 87 Invocation->getFrontendOpts().DisableFree = false; 88 return Invocation; 89 } 90 91 bool runToolOnCode(clang::FrontendAction *ToolAction, const Twine &Code, 92 const Twine &FileName) { 93 SmallString<16> FileNameStorage; 94 StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage); 95 const char *const CommandLine[] = { 96 "clang-tool", "-fsyntax-only", FileNameRef.data() 97 }; 98 FileManager Files((FileSystemOptions())); 99 ToolInvocation Invocation( 100 std::vector<std::string>( 101 CommandLine, 102 CommandLine + llvm::array_lengthof(CommandLine)), 103 ToolAction, &Files); 104 105 SmallString<1024> CodeStorage; 106 Invocation.mapVirtualFile(FileNameRef, 107 Code.toNullTerminatedStringRef(CodeStorage)); 108 return Invocation.run(); 109 } 110 111 /// \brief Returns the absolute path of 'File', by prepending it with 112 /// 'BaseDirectory' if 'File' is not absolute. 113 /// 114 /// Otherwise returns 'File'. 115 /// If 'File' starts with "./", the returned path will not contain the "./". 116 /// Otherwise, the returned path will contain the literal path-concatenation of 117 /// 'BaseDirectory' and 'File'. 118 /// 119 /// \param File Either an absolute or relative path. 120 /// \param BaseDirectory An absolute path. 121 static std::string getAbsolutePath( 122 StringRef File, StringRef BaseDirectory) { 123 assert(llvm::sys::path::is_absolute(BaseDirectory)); 124 if (llvm::sys::path::is_absolute(File)) { 125 return File; 126 } 127 StringRef RelativePath(File); 128 if (RelativePath.startswith("./")) { 129 RelativePath = RelativePath.substr(strlen("./")); 130 } 131 llvm::SmallString<1024> AbsolutePath(BaseDirectory); 132 llvm::sys::path::append(AbsolutePath, RelativePath); 133 return AbsolutePath.str(); 134 } 135 136 ToolInvocation::ToolInvocation( 137 ArrayRef<std::string> CommandLine, FrontendAction *ToolAction, 138 FileManager *Files) 139 : CommandLine(CommandLine.vec()), ToolAction(ToolAction), Files(Files) { 140 } 141 142 void ToolInvocation::mapVirtualFile(StringRef FilePath, StringRef Content) { 143 MappedFileContents[FilePath] = Content; 144 } 145 146 bool ToolInvocation::run() { 147 std::vector<const char*> Argv; 148 for (int I = 0, E = CommandLine.size(); I != E; ++I) 149 Argv.push_back(CommandLine[I].c_str()); 150 const char *const BinaryName = Argv[0]; 151 DiagnosticOptions DefaultDiagnosticOptions; 152 TextDiagnosticPrinter DiagnosticPrinter( 153 llvm::errs(), DefaultDiagnosticOptions); 154 DiagnosticsEngine Diagnostics(llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>( 155 new DiagnosticIDs()), &DiagnosticPrinter, false); 156 157 const llvm::OwningPtr<clang::driver::Driver> Driver( 158 newDriver(&Diagnostics, BinaryName)); 159 // Since the input might only be virtual, don't check whether it exists. 160 Driver->setCheckInputsExist(false); 161 const llvm::OwningPtr<clang::driver::Compilation> Compilation( 162 Driver->BuildCompilation(llvm::makeArrayRef(Argv))); 163 const clang::driver::ArgStringList *const CC1Args = getCC1Arguments( 164 &Diagnostics, Compilation.get()); 165 if (CC1Args == NULL) { 166 return false; 167 } 168 llvm::OwningPtr<clang::CompilerInvocation> Invocation( 169 newInvocation(&Diagnostics, *CC1Args)); 170 return runInvocation(BinaryName, Compilation.get(), 171 Invocation.take(), *CC1Args, ToolAction.take()); 172 } 173 174 // Exists solely for the purpose of lookup of the resource path. 175 static int StaticSymbol; 176 177 bool ToolInvocation::runInvocation( 178 const char *BinaryName, 179 clang::driver::Compilation *Compilation, 180 clang::CompilerInvocation *Invocation, 181 const clang::driver::ArgStringList &CC1Args, 182 clang::FrontendAction *ToolAction) { 183 llvm::OwningPtr<clang::FrontendAction> ScopedToolAction(ToolAction); 184 // Show the invocation, with -v. 185 if (Invocation->getHeaderSearchOpts().Verbose) { 186 llvm::errs() << "clang Invocation:\n"; 187 Compilation->PrintJob(llvm::errs(), Compilation->getJobs(), "\n", true); 188 llvm::errs() << "\n"; 189 } 190 191 // Create a compiler instance to handle the actual work. 192 clang::CompilerInstance Compiler; 193 Compiler.setInvocation(Invocation); 194 Compiler.setFileManager(Files); 195 // FIXME: What about LangOpts? 196 197 // Create the compilers actual diagnostics engine. 198 Compiler.createDiagnostics(CC1Args.size(), 199 const_cast<char**>(CC1Args.data())); 200 if (!Compiler.hasDiagnostics()) 201 return false; 202 203 Compiler.createSourceManager(*Files); 204 addFileMappingsTo(Compiler.getSourceManager()); 205 206 // Infer the builtin include path if unspecified. 207 if (Compiler.getHeaderSearchOpts().UseBuiltinIncludes && 208 Compiler.getHeaderSearchOpts().ResourceDir.empty()) { 209 // This just needs to be some symbol in the binary. 210 void *const SymbolAddr = &StaticSymbol; 211 Compiler.getHeaderSearchOpts().ResourceDir = 212 clang::CompilerInvocation::GetResourcesPath(BinaryName, SymbolAddr); 213 } 214 215 const bool Success = Compiler.ExecuteAction(*ToolAction); 216 217 Compiler.resetAndLeakFileManager(); 218 return Success; 219 } 220 221 void ToolInvocation::addFileMappingsTo(SourceManager &Sources) { 222 for (llvm::StringMap<StringRef>::const_iterator 223 It = MappedFileContents.begin(), End = MappedFileContents.end(); 224 It != End; ++It) { 225 // Inject the code as the given file name into the preprocessor options. 226 const llvm::MemoryBuffer *Input = 227 llvm::MemoryBuffer::getMemBuffer(It->getValue()); 228 // FIXME: figure out what '0' stands for. 229 const FileEntry *FromFile = Files->getVirtualFile( 230 It->getKey(), Input->getBufferSize(), 0); 231 // FIXME: figure out memory management ('true'). 232 Sources.overrideFileContents(FromFile, Input, true); 233 } 234 } 235 236 ClangTool::ClangTool(const CompilationDatabase &Compilations, 237 ArrayRef<std::string> SourcePaths) 238 : Files((FileSystemOptions())) { 239 llvm::SmallString<1024> BaseDirectory; 240 if (const char *PWD = ::getenv("PWD")) 241 BaseDirectory = PWD; 242 else 243 llvm::sys::fs::current_path(BaseDirectory); 244 for (unsigned I = 0, E = SourcePaths.size(); I != E; ++I) { 245 llvm::SmallString<1024> File(getAbsolutePath( 246 SourcePaths[I], BaseDirectory)); 247 248 std::vector<CompileCommand> CompileCommands = 249 Compilations.getCompileCommands(File.str()); 250 if (!CompileCommands.empty()) { 251 for (int I = 0, E = CompileCommands.size(); I != E; ++I) { 252 CompileCommand &Command = CompileCommands[I]; 253 if (!Command.Directory.empty()) { 254 // FIXME: What should happen if CommandLine includes -working-directory 255 // as well? 256 Command.CommandLine.push_back( 257 "-working-directory=" + Command.Directory); 258 } 259 CommandLines.push_back(std::make_pair(File.str(), Command.CommandLine)); 260 } 261 } else { 262 // FIXME: There are two use cases here: doing a fuzzy 263 // "find . -name '*.cc' |xargs tool" match, where as a user I don't care 264 // about the .cc files that were not found, and the use case where I 265 // specify all files I want to run over explicitly, where this should 266 // be an error. We'll want to add an option for this. 267 llvm::outs() << "Skipping " << File << ". Command line not found.\n"; 268 } 269 } 270 } 271 272 void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) { 273 MappedFileContents.push_back(std::make_pair(FilePath, Content)); 274 } 275 276 int ClangTool::run(FrontendActionFactory *ActionFactory) { 277 bool ProcessingFailed = false; 278 for (unsigned I = 0; I < CommandLines.size(); ++I) { 279 std::string File = CommandLines[I].first; 280 std::vector<std::string> &CommandLine = CommandLines[I].second; 281 llvm::outs() << "Processing: " << File << ".\n"; 282 ToolInvocation Invocation(CommandLine, ActionFactory->create(), &Files); 283 for (int I = 0, E = MappedFileContents.size(); I != E; ++I) { 284 Invocation.mapVirtualFile(MappedFileContents[I].first, 285 MappedFileContents[I].second); 286 } 287 if (!Invocation.run()) { 288 llvm::outs() << "Error while processing " << File << ".\n"; 289 ProcessingFailed = true; 290 } 291 } 292 return ProcessingFailed ? 1 : 0; 293 } 294 295 } // end namespace tooling 296 } // end namespace clang 297