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