Tooling.cpp revision 234353
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
29namespace clang {
30namespace tooling {
31
32FrontendActionFactory::~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.
39static 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.
52static 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.
79static 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
91bool 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.
121static 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
136ToolInvocation::ToolInvocation(
137    ArrayRef<std::string> CommandLine, FrontendAction *ToolAction,
138    FileManager *Files)
139    : CommandLine(CommandLine.vec()), ToolAction(ToolAction), Files(Files) {
140}
141
142void ToolInvocation::mapVirtualFile(StringRef FilePath, StringRef Content) {
143  MappedFileContents[FilePath] = Content;
144}
145
146bool 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.
175static int StaticSymbol;
176
177bool 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
221void 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
236ClangTool::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
272void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) {
273  MappedFileContents.push_back(std::make_pair(FilePath, Content));
274}
275
276int 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