]> CyberLeo.Net >> Repos - FreeBSD/releng/9.1.git/blob - contrib/llvm/tools/clang/lib/Tooling/Tooling.cpp
Copy stable/9 to releng/9.1 as part of the 9.1-RELEASE release process.
[FreeBSD/releng/9.1.git] / contrib / llvm / tools / clang / lib / Tooling / Tooling.cpp
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
29 namespace clang {
30 namespace tooling {
31
32 FrontendActionFactory::~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.
39 static 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.
52 static 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.
79 static 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
91 bool 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.
121 static 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
136 ToolInvocation::ToolInvocation(
137     ArrayRef<std::string> CommandLine, FrontendAction *ToolAction,
138     FileManager *Files)
139     : CommandLine(CommandLine.vec()), ToolAction(ToolAction), Files(Files) {
140 }
141
142 void ToolInvocation::mapVirtualFile(StringRef FilePath, StringRef Content) {
143   MappedFileContents[FilePath] = Content;
144 }
145
146 bool 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.
175 static int StaticSymbol;
176
177 bool 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
221 void 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
236 ClangTool::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
272 void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) {
273   MappedFileContents.push_back(std::make_pair(FilePath, Content));
274 }
275
276 int 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