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