1234287Sdim//===--- Tooling.cpp - Running clang standalone tools ---------------------===// 2234287Sdim// 3234287Sdim// The LLVM Compiler Infrastructure 4234287Sdim// 5234287Sdim// This file is distributed under the University of Illinois Open Source 6234287Sdim// License. See LICENSE.TXT for details. 7234287Sdim// 8234287Sdim//===----------------------------------------------------------------------===// 9234287Sdim// 10234287Sdim// This file implements functions to run clang tools standalone instead 11234287Sdim// of running them as a plugin. 12234287Sdim// 13234287Sdim//===----------------------------------------------------------------------===// 14234287Sdim 15234287Sdim#include "clang/Tooling/Tooling.h" 16234287Sdim#include "clang/Driver/Compilation.h" 17234287Sdim#include "clang/Driver/Driver.h" 18234287Sdim#include "clang/Driver/Tool.h" 19234287Sdim#include "clang/Frontend/CompilerInstance.h" 20234287Sdim#include "clang/Frontend/FrontendDiagnostic.h" 21234287Sdim#include "clang/Frontend/TextDiagnosticPrinter.h" 22249423Sdim#include "clang/Tooling/ArgumentsAdjusters.h" 23249423Sdim#include "clang/Tooling/CompilationDatabase.h" 24234287Sdim#include "llvm/ADT/STLExtras.h" 25249423Sdim#include "llvm/Support/Debug.h" 26234287Sdim#include "llvm/Support/FileSystem.h" 27234287Sdim#include "llvm/Support/Host.h" 28234287Sdim#include "llvm/Support/raw_ostream.h" 29234287Sdim 30239462Sdim// For chdir, see the comment in ClangTool::run for more information. 31239462Sdim#ifdef _WIN32 32239462Sdim# include <direct.h> 33239462Sdim#else 34239462Sdim# include <unistd.h> 35239462Sdim#endif 36239462Sdim 37234287Sdimnamespace clang { 38234287Sdimnamespace tooling { 39234287Sdim 40234287SdimFrontendActionFactory::~FrontendActionFactory() {} 41234287Sdim 42234287Sdim// FIXME: This file contains structural duplication with other parts of the 43234287Sdim// code that sets up a compiler to run tools on it, and we should refactor 44234287Sdim// it to be based on the same framework. 45234287Sdim 46234287Sdim/// \brief Builds a clang driver initialized for running clang tools. 47234287Sdimstatic clang::driver::Driver *newDriver(clang::DiagnosticsEngine *Diagnostics, 48234287Sdim const char *BinaryName) { 49234287Sdim const std::string DefaultOutputName = "a.out"; 50234287Sdim clang::driver::Driver *CompilerDriver = new clang::driver::Driver( 51239462Sdim BinaryName, llvm::sys::getDefaultTargetTriple(), 52249423Sdim DefaultOutputName, *Diagnostics); 53234287Sdim CompilerDriver->setTitle("clang_based_tool"); 54234287Sdim return CompilerDriver; 55234287Sdim} 56234287Sdim 57234287Sdim/// \brief Retrieves the clang CC1 specific flags out of the compilation's jobs. 58234287Sdim/// 59234287Sdim/// Returns NULL on error. 60234287Sdimstatic const clang::driver::ArgStringList *getCC1Arguments( 61234287Sdim clang::DiagnosticsEngine *Diagnostics, 62234287Sdim clang::driver::Compilation *Compilation) { 63234287Sdim // We expect to get back exactly one Command job, if we didn't something 64234287Sdim // failed. Extract that job from the Compilation. 65234287Sdim const clang::driver::JobList &Jobs = Compilation->getJobs(); 66234287Sdim if (Jobs.size() != 1 || !isa<clang::driver::Command>(*Jobs.begin())) { 67249423Sdim SmallString<256> error_msg; 68234287Sdim llvm::raw_svector_ostream error_stream(error_msg); 69234287Sdim Compilation->PrintJob(error_stream, Compilation->getJobs(), "; ", true); 70234287Sdim Diagnostics->Report(clang::diag::err_fe_expected_compiler_job) 71234287Sdim << error_stream.str(); 72234287Sdim return NULL; 73234287Sdim } 74234287Sdim 75234287Sdim // The one job we find should be to invoke clang again. 76234287Sdim const clang::driver::Command *Cmd = 77234287Sdim cast<clang::driver::Command>(*Jobs.begin()); 78234287Sdim if (StringRef(Cmd->getCreator().getName()) != "clang") { 79234287Sdim Diagnostics->Report(clang::diag::err_fe_expected_clang_command); 80234287Sdim return NULL; 81234287Sdim } 82234287Sdim 83234287Sdim return &Cmd->getArguments(); 84234287Sdim} 85234287Sdim 86234287Sdim/// \brief Returns a clang build invocation initialized from the CC1 flags. 87234287Sdimstatic clang::CompilerInvocation *newInvocation( 88234287Sdim clang::DiagnosticsEngine *Diagnostics, 89234287Sdim const clang::driver::ArgStringList &CC1Args) { 90234287Sdim assert(!CC1Args.empty() && "Must at least contain the program name!"); 91234287Sdim clang::CompilerInvocation *Invocation = new clang::CompilerInvocation; 92234287Sdim clang::CompilerInvocation::CreateFromArgs( 93234287Sdim *Invocation, CC1Args.data() + 1, CC1Args.data() + CC1Args.size(), 94234287Sdim *Diagnostics); 95234287Sdim Invocation->getFrontendOpts().DisableFree = false; 96234287Sdim return Invocation; 97234287Sdim} 98234287Sdim 99234287Sdimbool runToolOnCode(clang::FrontendAction *ToolAction, const Twine &Code, 100234287Sdim const Twine &FileName) { 101243830Sdim return runToolOnCodeWithArgs( 102243830Sdim ToolAction, Code, std::vector<std::string>(), FileName); 103243830Sdim} 104243830Sdim 105243830Sdimbool runToolOnCodeWithArgs(clang::FrontendAction *ToolAction, const Twine &Code, 106243830Sdim const std::vector<std::string> &Args, 107243830Sdim const Twine &FileName) { 108234287Sdim SmallString<16> FileNameStorage; 109234287Sdim StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage); 110243830Sdim std::vector<std::string> Commands; 111243830Sdim Commands.push_back("clang-tool"); 112243830Sdim Commands.push_back("-fsyntax-only"); 113243830Sdim Commands.insert(Commands.end(), Args.begin(), Args.end()); 114243830Sdim Commands.push_back(FileNameRef.data()); 115234287Sdim FileManager Files((FileSystemOptions())); 116243830Sdim ToolInvocation Invocation(Commands, ToolAction, &Files); 117234287Sdim 118234287Sdim SmallString<1024> CodeStorage; 119234287Sdim Invocation.mapVirtualFile(FileNameRef, 120234287Sdim Code.toNullTerminatedStringRef(CodeStorage)); 121234287Sdim return Invocation.run(); 122234287Sdim} 123234287Sdim 124239462Sdimstd::string getAbsolutePath(StringRef File) { 125249423Sdim SmallString<1024> BaseDirectory; 126239462Sdim if (const char *PWD = ::getenv("PWD")) 127239462Sdim BaseDirectory = PWD; 128239462Sdim else 129239462Sdim llvm::sys::fs::current_path(BaseDirectory); 130239462Sdim SmallString<1024> PathStorage; 131234287Sdim if (llvm::sys::path::is_absolute(File)) { 132239462Sdim llvm::sys::path::native(File, PathStorage); 133239462Sdim return PathStorage.str(); 134234287Sdim } 135234287Sdim StringRef RelativePath(File); 136239462Sdim // FIXME: Should '.\\' be accepted on Win32? 137234287Sdim if (RelativePath.startswith("./")) { 138234287Sdim RelativePath = RelativePath.substr(strlen("./")); 139234287Sdim } 140249423Sdim SmallString<1024> AbsolutePath(BaseDirectory); 141234287Sdim llvm::sys::path::append(AbsolutePath, RelativePath); 142239462Sdim llvm::sys::path::native(Twine(AbsolutePath), PathStorage); 143239462Sdim return PathStorage.str(); 144234287Sdim} 145234287Sdim 146234287SdimToolInvocation::ToolInvocation( 147234287Sdim ArrayRef<std::string> CommandLine, FrontendAction *ToolAction, 148234287Sdim FileManager *Files) 149234287Sdim : CommandLine(CommandLine.vec()), ToolAction(ToolAction), Files(Files) { 150234287Sdim} 151234287Sdim 152234287Sdimvoid ToolInvocation::mapVirtualFile(StringRef FilePath, StringRef Content) { 153239462Sdim SmallString<1024> PathStorage; 154239462Sdim llvm::sys::path::native(FilePath, PathStorage); 155239462Sdim MappedFileContents[PathStorage] = Content; 156234287Sdim} 157234287Sdim 158234287Sdimbool ToolInvocation::run() { 159234287Sdim std::vector<const char*> Argv; 160234287Sdim for (int I = 0, E = CommandLine.size(); I != E; ++I) 161234287Sdim Argv.push_back(CommandLine[I].c_str()); 162234287Sdim const char *const BinaryName = Argv[0]; 163243830Sdim IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 164234287Sdim TextDiagnosticPrinter DiagnosticPrinter( 165243830Sdim llvm::errs(), &*DiagOpts); 166243830Sdim DiagnosticsEngine Diagnostics( 167249423Sdim IntrusiveRefCntPtr<clang::DiagnosticIDs>(new DiagnosticIDs()), 168243830Sdim &*DiagOpts, &DiagnosticPrinter, false); 169234287Sdim 170249423Sdim const OwningPtr<clang::driver::Driver> Driver( 171234287Sdim newDriver(&Diagnostics, BinaryName)); 172234287Sdim // Since the input might only be virtual, don't check whether it exists. 173234287Sdim Driver->setCheckInputsExist(false); 174249423Sdim const OwningPtr<clang::driver::Compilation> Compilation( 175234287Sdim Driver->BuildCompilation(llvm::makeArrayRef(Argv))); 176234287Sdim const clang::driver::ArgStringList *const CC1Args = getCC1Arguments( 177234287Sdim &Diagnostics, Compilation.get()); 178234287Sdim if (CC1Args == NULL) { 179234287Sdim return false; 180234287Sdim } 181249423Sdim OwningPtr<clang::CompilerInvocation> Invocation( 182234287Sdim newInvocation(&Diagnostics, *CC1Args)); 183249423Sdim return runInvocation(BinaryName, Compilation.get(), Invocation.take()); 184234287Sdim} 185234287Sdim 186234287Sdimbool ToolInvocation::runInvocation( 187234287Sdim const char *BinaryName, 188234287Sdim clang::driver::Compilation *Compilation, 189249423Sdim clang::CompilerInvocation *Invocation) { 190234287Sdim // Show the invocation, with -v. 191234287Sdim if (Invocation->getHeaderSearchOpts().Verbose) { 192234287Sdim llvm::errs() << "clang Invocation:\n"; 193234287Sdim Compilation->PrintJob(llvm::errs(), Compilation->getJobs(), "\n", true); 194234287Sdim llvm::errs() << "\n"; 195234287Sdim } 196234287Sdim 197234287Sdim // Create a compiler instance to handle the actual work. 198234287Sdim clang::CompilerInstance Compiler; 199234287Sdim Compiler.setInvocation(Invocation); 200234287Sdim Compiler.setFileManager(Files); 201234287Sdim // FIXME: What about LangOpts? 202234287Sdim 203239462Sdim // ToolAction can have lifetime requirements for Compiler or its members, and 204239462Sdim // we need to ensure it's deleted earlier than Compiler. So we pass it to an 205239462Sdim // OwningPtr declared after the Compiler variable. 206249423Sdim OwningPtr<FrontendAction> ScopedToolAction(ToolAction.take()); 207239462Sdim 208234287Sdim // Create the compilers actual diagnostics engine. 209249423Sdim Compiler.createDiagnostics(); 210234287Sdim if (!Compiler.hasDiagnostics()) 211234287Sdim return false; 212234287Sdim 213234287Sdim Compiler.createSourceManager(*Files); 214234287Sdim addFileMappingsTo(Compiler.getSourceManager()); 215234287Sdim 216239462Sdim const bool Success = Compiler.ExecuteAction(*ScopedToolAction); 217234287Sdim 218234287Sdim Compiler.resetAndLeakFileManager(); 219239462Sdim Files->clearStatCaches(); 220234287Sdim return Success; 221234287Sdim} 222234287Sdim 223234287Sdimvoid ToolInvocation::addFileMappingsTo(SourceManager &Sources) { 224234287Sdim for (llvm::StringMap<StringRef>::const_iterator 225234287Sdim It = MappedFileContents.begin(), End = MappedFileContents.end(); 226234287Sdim It != End; ++It) { 227234287Sdim // Inject the code as the given file name into the preprocessor options. 228234287Sdim const llvm::MemoryBuffer *Input = 229234287Sdim llvm::MemoryBuffer::getMemBuffer(It->getValue()); 230234287Sdim // FIXME: figure out what '0' stands for. 231234287Sdim const FileEntry *FromFile = Files->getVirtualFile( 232234287Sdim It->getKey(), Input->getBufferSize(), 0); 233239462Sdim Sources.overrideFileContents(FromFile, Input); 234234287Sdim } 235234287Sdim} 236234287Sdim 237234287SdimClangTool::ClangTool(const CompilationDatabase &Compilations, 238234287Sdim ArrayRef<std::string> SourcePaths) 239239462Sdim : Files((FileSystemOptions())), 240239462Sdim ArgsAdjuster(new ClangSyntaxOnlyAdjuster()) { 241234287Sdim for (unsigned I = 0, E = SourcePaths.size(); I != E; ++I) { 242249423Sdim SmallString<1024> File(getAbsolutePath(SourcePaths[I])); 243234287Sdim 244239462Sdim std::vector<CompileCommand> CompileCommandsForFile = 245234287Sdim Compilations.getCompileCommands(File.str()); 246239462Sdim if (!CompileCommandsForFile.empty()) { 247239462Sdim for (int I = 0, E = CompileCommandsForFile.size(); I != E; ++I) { 248239462Sdim CompileCommands.push_back(std::make_pair(File.str(), 249239462Sdim CompileCommandsForFile[I])); 250234287Sdim } 251234287Sdim } else { 252234287Sdim // FIXME: There are two use cases here: doing a fuzzy 253234287Sdim // "find . -name '*.cc' |xargs tool" match, where as a user I don't care 254234287Sdim // about the .cc files that were not found, and the use case where I 255234287Sdim // specify all files I want to run over explicitly, where this should 256234287Sdim // be an error. We'll want to add an option for this. 257234287Sdim llvm::outs() << "Skipping " << File << ". Command line not found.\n"; 258234287Sdim } 259234287Sdim } 260234287Sdim} 261234287Sdim 262234287Sdimvoid ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) { 263234287Sdim MappedFileContents.push_back(std::make_pair(FilePath, Content)); 264234287Sdim} 265234287Sdim 266239462Sdimvoid ClangTool::setArgumentsAdjuster(ArgumentsAdjuster *Adjuster) { 267239462Sdim ArgsAdjuster.reset(Adjuster); 268239462Sdim} 269239462Sdim 270234287Sdimint ClangTool::run(FrontendActionFactory *ActionFactory) { 271239462Sdim // Exists solely for the purpose of lookup of the resource path. 272239462Sdim // This just needs to be some symbol in the binary. 273239462Sdim static int StaticSymbol; 274239462Sdim // The driver detects the builtin header path based on the path of the 275239462Sdim // executable. 276239462Sdim // FIXME: On linux, GetMainExecutable is independent of the value of the 277239462Sdim // first argument, thus allowing ClangTool and runToolOnCode to just 278239462Sdim // pass in made-up names here. Make sure this works on other platforms. 279239462Sdim std::string MainExecutable = 280239462Sdim llvm::sys::Path::GetMainExecutable("clang_tool", &StaticSymbol).str(); 281239462Sdim 282234287Sdim bool ProcessingFailed = false; 283239462Sdim for (unsigned I = 0; I < CompileCommands.size(); ++I) { 284239462Sdim std::string File = CompileCommands[I].first; 285239462Sdim // FIXME: chdir is thread hostile; on the other hand, creating the same 286239462Sdim // behavior as chdir is complex: chdir resolves the path once, thus 287239462Sdim // guaranteeing that all subsequent relative path operations work 288239462Sdim // on the same path the original chdir resulted in. This makes a difference 289239462Sdim // for example on network filesystems, where symlinks might be switched 290239462Sdim // during runtime of the tool. Fixing this depends on having a file system 291239462Sdim // abstraction that allows openat() style interactions. 292239462Sdim if (chdir(CompileCommands[I].second.Directory.c_str())) 293239462Sdim llvm::report_fatal_error("Cannot chdir into \"" + 294239462Sdim CompileCommands[I].second.Directory + "\n!"); 295239462Sdim std::vector<std::string> CommandLine = 296239462Sdim ArgsAdjuster->Adjust(CompileCommands[I].second.CommandLine); 297239462Sdim assert(!CommandLine.empty()); 298239462Sdim CommandLine[0] = MainExecutable; 299249423Sdim // FIXME: We need a callback mechanism for the tool writer to output a 300249423Sdim // customized message for each file. 301249423Sdim DEBUG({ 302249423Sdim llvm::dbgs() << "Processing: " << File << ".\n"; 303249423Sdim }); 304234287Sdim ToolInvocation Invocation(CommandLine, ActionFactory->create(), &Files); 305234287Sdim for (int I = 0, E = MappedFileContents.size(); I != E; ++I) { 306234287Sdim Invocation.mapVirtualFile(MappedFileContents[I].first, 307234287Sdim MappedFileContents[I].second); 308234287Sdim } 309234287Sdim if (!Invocation.run()) { 310249423Sdim // FIXME: Diagnostics should be used instead. 311249423Sdim llvm::errs() << "Error while processing " << File << ".\n"; 312234287Sdim ProcessingFailed = true; 313234287Sdim } 314234287Sdim } 315234287Sdim return ProcessingFailed ? 1 : 0; 316234287Sdim} 317234287Sdim 318234287Sdim} // end namespace tooling 319234287Sdim} // end namespace clang 320