| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 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" | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 16 | #include "clang/Driver/Compilation.h" | 
 | 17 | #include "clang/Driver/Driver.h" | 
 | 18 | #include "clang/Driver/Tool.h" | 
 | 19 | #include "clang/Frontend/CompilerInstance.h" | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 20 | #include "clang/Frontend/FrontendDiagnostic.h" | 
 | 21 | #include "clang/Frontend/TextDiagnosticPrinter.h" | 
| Chandler Carruth | 55fc873 | 2012-12-04 09:13:33 +0000 | [diff] [blame] | 22 | #include "clang/Tooling/ArgumentsAdjusters.h" | 
 | 23 | #include "clang/Tooling/CompilationDatabase.h" | 
| NAKAMURA Takumi | 01b8ca5 | 2012-04-04 13:59:41 +0000 | [diff] [blame] | 24 | #include "llvm/ADT/STLExtras.h" | 
| Reid Kleckner | b1e25a1 | 2013-06-14 17:17:23 +0000 | [diff] [blame] | 25 | #include "llvm/Option/Option.h" | 
| Edwin Vane | ad7e160 | 2013-03-15 20:14:01 +0000 | [diff] [blame] | 26 | #include "llvm/Support/Debug.h" | 
| NAKAMURA Takumi | b175d0f | 2012-04-04 13:59:36 +0000 | [diff] [blame] | 27 | #include "llvm/Support/FileSystem.h" | 
| NAKAMURA Takumi | 01b8ca5 | 2012-04-04 13:59:41 +0000 | [diff] [blame] | 28 | #include "llvm/Support/Host.h" | 
 | 29 | #include "llvm/Support/raw_ostream.h" | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 30 |  | 
| Manuel Klimek | 3b6e319 | 2012-05-07 09:45:46 +0000 | [diff] [blame] | 31 | // For chdir, see the comment in ClangTool::run for more information. | 
| Manuel Klimek | ed5ee48 | 2012-05-07 10:02:55 +0000 | [diff] [blame] | 32 | #ifdef _WIN32 | 
| Manuel Klimek | 3b6e319 | 2012-05-07 09:45:46 +0000 | [diff] [blame] | 33 | #  include <direct.h> | 
| Manuel Klimek | ed5ee48 | 2012-05-07 10:02:55 +0000 | [diff] [blame] | 34 | #else | 
 | 35 | #  include <unistd.h> | 
| Manuel Klimek | 3b6e319 | 2012-05-07 09:45:46 +0000 | [diff] [blame] | 36 | #endif | 
 | 37 |  | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 38 | namespace clang { | 
 | 39 | namespace tooling { | 
 | 40 |  | 
 | 41 | FrontendActionFactory::~FrontendActionFactory() {} | 
 | 42 |  | 
 | 43 | // FIXME: This file contains structural duplication with other parts of the | 
 | 44 | // code that sets up a compiler to run tools on it, and we should refactor | 
 | 45 | // it to be based on the same framework. | 
 | 46 |  | 
 | 47 | /// \brief Builds a clang driver initialized for running clang tools. | 
 | 48 | static clang::driver::Driver *newDriver(clang::DiagnosticsEngine *Diagnostics, | 
 | 49 |                                         const char *BinaryName) { | 
 | 50 |   const std::string DefaultOutputName = "a.out"; | 
 | 51 |   clang::driver::Driver *CompilerDriver = new clang::driver::Driver( | 
| Alexander Kornienko | 30c009b | 2012-06-04 19:02:59 +0000 | [diff] [blame] | 52 |     BinaryName, llvm::sys::getDefaultTargetTriple(), | 
| Rafael Espindola | 17c874a | 2012-11-27 16:10:37 +0000 | [diff] [blame] | 53 |     DefaultOutputName, *Diagnostics); | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 54 |   CompilerDriver->setTitle("clang_based_tool"); | 
 | 55 |   return CompilerDriver; | 
 | 56 | } | 
 | 57 |  | 
 | 58 | /// \brief Retrieves the clang CC1 specific flags out of the compilation's jobs. | 
 | 59 | /// | 
 | 60 | /// Returns NULL on error. | 
| Reid Kleckner | b1e25a1 | 2013-06-14 17:17:23 +0000 | [diff] [blame] | 61 | static const llvm::opt::ArgStringList *getCC1Arguments( | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 62 |     clang::DiagnosticsEngine *Diagnostics, | 
 | 63 |     clang::driver::Compilation *Compilation) { | 
 | 64 |   // We expect to get back exactly one Command job, if we didn't something | 
 | 65 |   // failed. Extract that job from the Compilation. | 
 | 66 |   const clang::driver::JobList &Jobs = Compilation->getJobs(); | 
 | 67 |   if (Jobs.size() != 1 || !isa<clang::driver::Command>(*Jobs.begin())) { | 
| Dmitri Gribenko | cfa88f8 | 2013-01-12 19:30:44 +0000 | [diff] [blame] | 68 |     SmallString<256> error_msg; | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 69 |     llvm::raw_svector_ostream error_stream(error_msg); | 
 | 70 |     Compilation->PrintJob(error_stream, Compilation->getJobs(), "; ", true); | 
 | 71 |     Diagnostics->Report(clang::diag::err_fe_expected_compiler_job) | 
 | 72 |         << error_stream.str(); | 
 | 73 |     return NULL; | 
 | 74 |   } | 
 | 75 |  | 
 | 76 |   // The one job we find should be to invoke clang again. | 
 | 77 |   const clang::driver::Command *Cmd = | 
 | 78 |       cast<clang::driver::Command>(*Jobs.begin()); | 
 | 79 |   if (StringRef(Cmd->getCreator().getName()) != "clang") { | 
 | 80 |     Diagnostics->Report(clang::diag::err_fe_expected_clang_command); | 
 | 81 |     return NULL; | 
 | 82 |   } | 
 | 83 |  | 
 | 84 |   return &Cmd->getArguments(); | 
 | 85 | } | 
 | 86 |  | 
 | 87 | /// \brief Returns a clang build invocation initialized from the CC1 flags. | 
 | 88 | static clang::CompilerInvocation *newInvocation( | 
 | 89 |     clang::DiagnosticsEngine *Diagnostics, | 
| Reid Kleckner | b1e25a1 | 2013-06-14 17:17:23 +0000 | [diff] [blame] | 90 |     const llvm::opt::ArgStringList &CC1Args) { | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 91 |   assert(!CC1Args.empty() && "Must at least contain the program name!"); | 
 | 92 |   clang::CompilerInvocation *Invocation = new clang::CompilerInvocation; | 
 | 93 |   clang::CompilerInvocation::CreateFromArgs( | 
 | 94 |       *Invocation, CC1Args.data() + 1, CC1Args.data() + CC1Args.size(), | 
 | 95 |       *Diagnostics); | 
 | 96 |   Invocation->getFrontendOpts().DisableFree = false; | 
| Nick Lewycky | fe7ed9e | 2013-06-25 17:01:21 +0000 | [diff] [blame] | 97 |   Invocation->getCodeGenOpts().DisableFree = false; | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 98 |   return Invocation; | 
 | 99 | } | 
 | 100 |  | 
 | 101 | bool runToolOnCode(clang::FrontendAction *ToolAction, const Twine &Code, | 
 | 102 |                    const Twine &FileName) { | 
| Nico Weber | 5666988 | 2012-08-30 02:02:19 +0000 | [diff] [blame] | 103 |   return runToolOnCodeWithArgs( | 
 | 104 |       ToolAction, Code, std::vector<std::string>(), FileName); | 
 | 105 | } | 
 | 106 |  | 
 | 107 | bool runToolOnCodeWithArgs(clang::FrontendAction *ToolAction, const Twine &Code, | 
 | 108 |                            const std::vector<std::string> &Args, | 
 | 109 |                            const Twine &FileName) { | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 110 |   SmallString<16> FileNameStorage; | 
 | 111 |   StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage); | 
| Nico Weber | 5666988 | 2012-08-30 02:02:19 +0000 | [diff] [blame] | 112 |   std::vector<std::string> Commands; | 
 | 113 |   Commands.push_back("clang-tool"); | 
 | 114 |   Commands.push_back("-fsyntax-only"); | 
 | 115 |   Commands.insert(Commands.end(), Args.begin(), Args.end()); | 
 | 116 |   Commands.push_back(FileNameRef.data()); | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 117 |   FileManager Files((FileSystemOptions())); | 
| Nico Weber | 5666988 | 2012-08-30 02:02:19 +0000 | [diff] [blame] | 118 |   ToolInvocation Invocation(Commands, ToolAction, &Files); | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 119 |  | 
 | 120 |   SmallString<1024> CodeStorage; | 
 | 121 |   Invocation.mapVirtualFile(FileNameRef, | 
 | 122 |                             Code.toNullTerminatedStringRef(CodeStorage)); | 
 | 123 |   return Invocation.run(); | 
 | 124 | } | 
 | 125 |  | 
| Manuel Klimek | 8fa2fb8 | 2012-07-10 13:10:51 +0000 | [diff] [blame] | 126 | std::string getAbsolutePath(StringRef File) { | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 127 |   StringRef RelativePath(File); | 
| NAKAMURA Takumi | 62d198c | 2012-05-23 22:24:20 +0000 | [diff] [blame] | 128 |   // FIXME: Should '.\\' be accepted on Win32? | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 129 |   if (RelativePath.startswith("./")) { | 
 | 130 |     RelativePath = RelativePath.substr(strlen("./")); | 
 | 131 |   } | 
| Rafael Espindola | a214824 | 2013-08-10 01:40:10 +0000 | [diff] [blame] | 132 |  | 
 | 133 |   SmallString<1024> AbsolutePath = RelativePath; | 
 | 134 |   llvm::error_code EC = llvm::sys::fs::make_absolute(AbsolutePath); | 
 | 135 |   assert(!EC); | 
| Rafael Espindola | bcb2500 | 2013-08-10 04:25:53 +0000 | [diff] [blame] | 136 |   (void)EC; | 
| Rafael Espindola | a214824 | 2013-08-10 01:40:10 +0000 | [diff] [blame] | 137 |   SmallString<1024> PathStorage; | 
| NAKAMURA Takumi | 62d198c | 2012-05-23 22:24:20 +0000 | [diff] [blame] | 138 |   llvm::sys::path::native(Twine(AbsolutePath), PathStorage); | 
 | 139 |   return PathStorage.str(); | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 140 | } | 
 | 141 |  | 
 | 142 | ToolInvocation::ToolInvocation( | 
 | 143 |     ArrayRef<std::string> CommandLine, FrontendAction *ToolAction, | 
 | 144 |     FileManager *Files) | 
 | 145 |     : CommandLine(CommandLine.vec()), ToolAction(ToolAction), Files(Files) { | 
 | 146 | } | 
 | 147 |  | 
 | 148 | void ToolInvocation::mapVirtualFile(StringRef FilePath, StringRef Content) { | 
| NAKAMURA Takumi | 0ef8db2 | 2012-06-02 15:34:21 +0000 | [diff] [blame] | 149 |   SmallString<1024> PathStorage; | 
 | 150 |   llvm::sys::path::native(FilePath, PathStorage); | 
 | 151 |   MappedFileContents[PathStorage] = Content; | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 152 | } | 
 | 153 |  | 
 | 154 | bool ToolInvocation::run() { | 
 | 155 |   std::vector<const char*> Argv; | 
 | 156 |   for (int I = 0, E = CommandLine.size(); I != E; ++I) | 
 | 157 |     Argv.push_back(CommandLine[I].c_str()); | 
 | 158 |   const char *const BinaryName = Argv[0]; | 
| Douglas Gregor | 02c23eb | 2012-10-23 22:26:28 +0000 | [diff] [blame] | 159 |   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 160 |   TextDiagnosticPrinter DiagnosticPrinter( | 
| Douglas Gregor | 02c23eb | 2012-10-23 22:26:28 +0000 | [diff] [blame] | 161 |       llvm::errs(), &*DiagOpts); | 
 | 162 |   DiagnosticsEngine Diagnostics( | 
| Dmitri Gribenko | cfa88f8 | 2013-01-12 19:30:44 +0000 | [diff] [blame] | 163 |     IntrusiveRefCntPtr<clang::DiagnosticIDs>(new DiagnosticIDs()), | 
| Douglas Gregor | 02c23eb | 2012-10-23 22:26:28 +0000 | [diff] [blame] | 164 |     &*DiagOpts, &DiagnosticPrinter, false); | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 165 |  | 
| Dmitri Gribenko | cfa88f8 | 2013-01-12 19:30:44 +0000 | [diff] [blame] | 166 |   const OwningPtr<clang::driver::Driver> Driver( | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 167 |       newDriver(&Diagnostics, BinaryName)); | 
 | 168 |   // Since the input might only be virtual, don't check whether it exists. | 
 | 169 |   Driver->setCheckInputsExist(false); | 
| Dmitri Gribenko | cfa88f8 | 2013-01-12 19:30:44 +0000 | [diff] [blame] | 170 |   const OwningPtr<clang::driver::Compilation> Compilation( | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 171 |       Driver->BuildCompilation(llvm::makeArrayRef(Argv))); | 
| Reid Kleckner | b1e25a1 | 2013-06-14 17:17:23 +0000 | [diff] [blame] | 172 |   const llvm::opt::ArgStringList *const CC1Args = getCC1Arguments( | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 173 |       &Diagnostics, Compilation.get()); | 
 | 174 |   if (CC1Args == NULL) { | 
 | 175 |     return false; | 
 | 176 |   } | 
| Dmitri Gribenko | cfa88f8 | 2013-01-12 19:30:44 +0000 | [diff] [blame] | 177 |   OwningPtr<clang::CompilerInvocation> Invocation( | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 178 |       newInvocation(&Diagnostics, *CC1Args)); | 
| Sean Silva | d47afb9 | 2013-01-20 01:58:28 +0000 | [diff] [blame] | 179 |   return runInvocation(BinaryName, Compilation.get(), Invocation.take()); | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 180 | } | 
 | 181 |  | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 182 | bool ToolInvocation::runInvocation( | 
 | 183 |     const char *BinaryName, | 
 | 184 |     clang::driver::Compilation *Compilation, | 
| Sean Silva | d47afb9 | 2013-01-20 01:58:28 +0000 | [diff] [blame] | 185 |     clang::CompilerInvocation *Invocation) { | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 186 |   // Show the invocation, with -v. | 
 | 187 |   if (Invocation->getHeaderSearchOpts().Verbose) { | 
 | 188 |     llvm::errs() << "clang Invocation:\n"; | 
 | 189 |     Compilation->PrintJob(llvm::errs(), Compilation->getJobs(), "\n", true); | 
 | 190 |     llvm::errs() << "\n"; | 
 | 191 |   } | 
 | 192 |  | 
 | 193 |   // Create a compiler instance to handle the actual work. | 
 | 194 |   clang::CompilerInstance Compiler; | 
 | 195 |   Compiler.setInvocation(Invocation); | 
 | 196 |   Compiler.setFileManager(Files); | 
 | 197 |   // FIXME: What about LangOpts? | 
 | 198 |  | 
| Alexander Kornienko | 14a1924 | 2012-05-31 17:58:43 +0000 | [diff] [blame] | 199 |   // ToolAction can have lifetime requirements for Compiler or its members, and | 
 | 200 |   // we need to ensure it's deleted earlier than Compiler. So we pass it to an | 
 | 201 |   // OwningPtr declared after the Compiler variable. | 
| Dmitri Gribenko | cfa88f8 | 2013-01-12 19:30:44 +0000 | [diff] [blame] | 202 |   OwningPtr<FrontendAction> ScopedToolAction(ToolAction.take()); | 
| Alexander Kornienko | 14a1924 | 2012-05-31 17:58:43 +0000 | [diff] [blame] | 203 |  | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 204 |   // Create the compilers actual diagnostics engine. | 
| Sean Silva | d47afb9 | 2013-01-20 01:58:28 +0000 | [diff] [blame] | 205 |   Compiler.createDiagnostics(); | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 206 |   if (!Compiler.hasDiagnostics()) | 
 | 207 |     return false; | 
 | 208 |  | 
 | 209 |   Compiler.createSourceManager(*Files); | 
 | 210 |   addFileMappingsTo(Compiler.getSourceManager()); | 
 | 211 |  | 
| Alexander Kornienko | 14a1924 | 2012-05-31 17:58:43 +0000 | [diff] [blame] | 212 |   const bool Success = Compiler.ExecuteAction(*ScopedToolAction); | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 213 |  | 
 | 214 |   Compiler.resetAndLeakFileManager(); | 
| Manuel Klimek | 98be860 | 2012-07-31 13:56:54 +0000 | [diff] [blame] | 215 |   Files->clearStatCaches(); | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 216 |   return Success; | 
 | 217 | } | 
 | 218 |  | 
 | 219 | void ToolInvocation::addFileMappingsTo(SourceManager &Sources) { | 
 | 220 |   for (llvm::StringMap<StringRef>::const_iterator | 
 | 221 |            It = MappedFileContents.begin(), End = MappedFileContents.end(); | 
 | 222 |        It != End; ++It) { | 
 | 223 |     // Inject the code as the given file name into the preprocessor options. | 
 | 224 |     const llvm::MemoryBuffer *Input = | 
 | 225 |         llvm::MemoryBuffer::getMemBuffer(It->getValue()); | 
 | 226 |     // FIXME: figure out what '0' stands for. | 
 | 227 |     const FileEntry *FromFile = Files->getVirtualFile( | 
 | 228 |         It->getKey(), Input->getBufferSize(), 0); | 
| Alexander Kornienko | 240193b | 2012-05-30 12:10:28 +0000 | [diff] [blame] | 229 |     Sources.overrideFileContents(FromFile, Input); | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 230 |   } | 
 | 231 | } | 
 | 232 |  | 
 | 233 | ClangTool::ClangTool(const CompilationDatabase &Compilations, | 
 | 234 |                      ArrayRef<std::string> SourcePaths) | 
| Pavel Labath | 63d5335 | 2013-06-06 11:52:19 +0000 | [diff] [blame] | 235 |     : Files((FileSystemOptions())) { | 
 | 236 |   ArgsAdjusters.push_back(new ClangStripOutputAdjuster()); | 
 | 237 |   ArgsAdjusters.push_back(new ClangSyntaxOnlyAdjuster()); | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 238 |   for (unsigned I = 0, E = SourcePaths.size(); I != E; ++I) { | 
| Dmitri Gribenko | cfa88f8 | 2013-01-12 19:30:44 +0000 | [diff] [blame] | 239 |     SmallString<1024> File(getAbsolutePath(SourcePaths[I])); | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 240 |  | 
| Manuel Klimek | 00f3c4f | 2012-05-07 09:17:48 +0000 | [diff] [blame] | 241 |     std::vector<CompileCommand> CompileCommandsForFile = | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 242 |       Compilations.getCompileCommands(File.str()); | 
| Manuel Klimek | 00f3c4f | 2012-05-07 09:17:48 +0000 | [diff] [blame] | 243 |     if (!CompileCommandsForFile.empty()) { | 
 | 244 |       for (int I = 0, E = CompileCommandsForFile.size(); I != E; ++I) { | 
 | 245 |         CompileCommands.push_back(std::make_pair(File.str(), | 
 | 246 |                                   CompileCommandsForFile[I])); | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 247 |       } | 
 | 248 |     } else { | 
 | 249 |       // FIXME: There are two use cases here: doing a fuzzy | 
 | 250 |       // "find . -name '*.cc' |xargs tool" match, where as a user I don't care | 
 | 251 |       // about the .cc files that were not found, and the use case where I | 
 | 252 |       // specify all files I want to run over explicitly, where this should | 
 | 253 |       // be an error. We'll want to add an option for this. | 
 | 254 |       llvm::outs() << "Skipping " << File << ". Command line not found.\n"; | 
 | 255 |     } | 
 | 256 |   } | 
 | 257 | } | 
 | 258 |  | 
 | 259 | void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) { | 
 | 260 |   MappedFileContents.push_back(std::make_pair(FilePath, Content)); | 
 | 261 | } | 
 | 262 |  | 
| Simon Atanasyan | a01ddc7 | 2012-05-09 16:18:30 +0000 | [diff] [blame] | 263 | void ClangTool::setArgumentsAdjuster(ArgumentsAdjuster *Adjuster) { | 
| Manuel Klimek | 48b3f0f | 2013-06-04 14:44:44 +0000 | [diff] [blame] | 264 |   clearArgumentsAdjusters(); | 
 | 265 |   appendArgumentsAdjuster(Adjuster); | 
 | 266 | } | 
 | 267 |  | 
 | 268 | void ClangTool::appendArgumentsAdjuster(ArgumentsAdjuster *Adjuster) { | 
 | 269 |   ArgsAdjusters.push_back(Adjuster); | 
 | 270 | } | 
 | 271 |  | 
 | 272 | void ClangTool::clearArgumentsAdjusters() { | 
 | 273 |   for (unsigned I = 0, E = ArgsAdjusters.size(); I != E; ++I) | 
 | 274 |     delete ArgsAdjusters[I]; | 
 | 275 |   ArgsAdjusters.clear(); | 
| Simon Atanasyan | a01ddc7 | 2012-05-09 16:18:30 +0000 | [diff] [blame] | 276 | } | 
 | 277 |  | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 278 | int ClangTool::run(FrontendActionFactory *ActionFactory) { | 
| Alexander Kornienko | 30c009b | 2012-06-04 19:02:59 +0000 | [diff] [blame] | 279 |   // Exists solely for the purpose of lookup of the resource path. | 
 | 280 |   // This just needs to be some symbol in the binary. | 
 | 281 |   static int StaticSymbol; | 
 | 282 |   // The driver detects the builtin header path based on the path of the | 
 | 283 |   // executable. | 
 | 284 |   // FIXME: On linux, GetMainExecutable is independent of the value of the | 
 | 285 |   // first argument, thus allowing ClangTool and runToolOnCode to just | 
 | 286 |   // pass in made-up names here. Make sure this works on other platforms. | 
 | 287 |   std::string MainExecutable = | 
| Rafael Espindola | ac1db6b | 2013-06-26 05:03:40 +0000 | [diff] [blame] | 288 |       llvm::sys::fs::getMainExecutable("clang_tool", &StaticSymbol); | 
| Alexander Kornienko | 30c009b | 2012-06-04 19:02:59 +0000 | [diff] [blame] | 289 |  | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 290 |   bool ProcessingFailed = false; | 
| Manuel Klimek | 00f3c4f | 2012-05-07 09:17:48 +0000 | [diff] [blame] | 291 |   for (unsigned I = 0; I < CompileCommands.size(); ++I) { | 
 | 292 |     std::string File = CompileCommands[I].first; | 
 | 293 |     // FIXME: chdir is thread hostile; on the other hand, creating the same | 
 | 294 |     // behavior as chdir is complex: chdir resolves the path once, thus | 
 | 295 |     // guaranteeing that all subsequent relative path operations work | 
 | 296 |     // on the same path the original chdir resulted in. This makes a difference | 
| Alexander Kornienko | 30c009b | 2012-06-04 19:02:59 +0000 | [diff] [blame] | 297 |     // for example on network filesystems, where symlinks might be switched | 
| Manuel Klimek | 00f3c4f | 2012-05-07 09:17:48 +0000 | [diff] [blame] | 298 |     // during runtime of the tool. Fixing this depends on having a file system | 
 | 299 |     // abstraction that allows openat() style interactions. | 
 | 300 |     if (chdir(CompileCommands[I].second.Directory.c_str())) | 
 | 301 |       llvm::report_fatal_error("Cannot chdir into \"" + | 
 | 302 |                                CompileCommands[I].second.Directory + "\n!"); | 
| Manuel Klimek | 48b3f0f | 2013-06-04 14:44:44 +0000 | [diff] [blame] | 303 |     std::vector<std::string> CommandLine = CompileCommands[I].second.CommandLine; | 
 | 304 |     for (unsigned I = 0, E = ArgsAdjusters.size(); I != E; ++I) | 
 | 305 |       CommandLine = ArgsAdjusters[I]->Adjust(CommandLine); | 
| Alexander Kornienko | 30c009b | 2012-06-04 19:02:59 +0000 | [diff] [blame] | 306 |     assert(!CommandLine.empty()); | 
 | 307 |     CommandLine[0] = MainExecutable; | 
| Edwin Vane | ad7e160 | 2013-03-15 20:14:01 +0000 | [diff] [blame] | 308 |     // FIXME: We need a callback mechanism for the tool writer to output a | 
 | 309 |     // customized message for each file. | 
 | 310 |     DEBUG({ | 
 | 311 |       llvm::dbgs() << "Processing: " << File << ".\n"; | 
 | 312 |     }); | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 313 |     ToolInvocation Invocation(CommandLine, ActionFactory->create(), &Files); | 
 | 314 |     for (int I = 0, E = MappedFileContents.size(); I != E; ++I) { | 
 | 315 |       Invocation.mapVirtualFile(MappedFileContents[I].first, | 
 | 316 |                                 MappedFileContents[I].second); | 
 | 317 |     } | 
 | 318 |     if (!Invocation.run()) { | 
| Edwin Vane | ad7e160 | 2013-03-15 20:14:01 +0000 | [diff] [blame] | 319 |       // FIXME: Diagnostics should be used instead. | 
 | 320 |       llvm::errs() << "Error while processing " << File << ".\n"; | 
| Manuel Klimek | cb971c6 | 2012-04-04 12:07:46 +0000 | [diff] [blame] | 321 |       ProcessingFailed = true; | 
 | 322 |     } | 
 | 323 |   } | 
 | 324 |   return ProcessingFailed ? 1 : 0; | 
 | 325 | } | 
 | 326 |  | 
 | 327 | } // end namespace tooling | 
 | 328 | } // end namespace clang |