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