1 //===--- CompilationDatabase.cpp - ----------------------------------------===// 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 contains implementations of the CompilationDatabase base class 11 // and the FixedCompilationDatabase. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "clang/Tooling/CompilationDatabase.h" 16 #include "clang/Basic/Diagnostic.h" 17 #include "clang/Basic/DiagnosticOptions.h" 18 #include "clang/Driver/Action.h" 19 #include "clang/Driver/Compilation.h" 20 #include "clang/Driver/Driver.h" 21 #include "clang/Driver/DriverDiagnostic.h" 22 #include "clang/Driver/Job.h" 23 #include "clang/Frontend/TextDiagnosticPrinter.h" 24 #include "clang/Tooling/CompilationDatabasePluginRegistry.h" 25 #include "clang/Tooling/Tooling.h" 26 #include "llvm/ADT/SmallString.h" 27 #include "llvm/Option/Arg.h" 28 #include "llvm/Support/Host.h" 29 #include "llvm/Support/Path.h" 30 #include <sstream> 31 #include <system_error> 32 33 namespace clang { 34 namespace tooling { 35 36 CompilationDatabase::~CompilationDatabase() {} 37 38 std::unique_ptr<CompilationDatabase> 39 CompilationDatabase::loadFromDirectory(StringRef BuildDirectory, 40 std::string &ErrorMessage) { 41 std::stringstream ErrorStream; 42 for (CompilationDatabasePluginRegistry::iterator 43 It = CompilationDatabasePluginRegistry::begin(), 44 Ie = CompilationDatabasePluginRegistry::end(); 45 It != Ie; ++It) { 46 std::string DatabaseErrorMessage; 47 std::unique_ptr<CompilationDatabasePlugin> Plugin(It->instantiate()); 48 if (std::unique_ptr<CompilationDatabase> DB = 49 Plugin->loadFromDirectory(BuildDirectory, DatabaseErrorMessage)) 50 return DB; 51 ErrorStream << It->getName() << ": " << DatabaseErrorMessage << "\n"; 52 } 53 ErrorMessage = ErrorStream.str(); 54 return nullptr; 55 } 56 57 static std::unique_ptr<CompilationDatabase> 58 findCompilationDatabaseFromDirectory(StringRef Directory, 59 std::string &ErrorMessage) { 60 std::stringstream ErrorStream; 61 bool HasErrorMessage = false; 62 while (!Directory.empty()) { 63 std::string LoadErrorMessage; 64 65 if (std::unique_ptr<CompilationDatabase> DB = 66 CompilationDatabase::loadFromDirectory(Directory, LoadErrorMessage)) 67 return DB; 68 69 if (!HasErrorMessage) { 70 ErrorStream << "No compilation database found in " << Directory.str() 71 << " or any parent directory\n" << LoadErrorMessage; 72 HasErrorMessage = true; 73 } 74 75 Directory = llvm::sys::path::parent_path(Directory); 76 } 77 ErrorMessage = ErrorStream.str(); 78 return nullptr; 79 } 80 81 std::unique_ptr<CompilationDatabase> 82 CompilationDatabase::autoDetectFromSource(StringRef SourceFile, 83 std::string &ErrorMessage) { 84 SmallString<1024> AbsolutePath(getAbsolutePath(SourceFile)); 85 StringRef Directory = llvm::sys::path::parent_path(AbsolutePath); 86 87 std::unique_ptr<CompilationDatabase> DB = 88 findCompilationDatabaseFromDirectory(Directory, ErrorMessage); 89 90 if (!DB) 91 ErrorMessage = ("Could not auto-detect compilation database for file \"" + 92 SourceFile + "\"\n" + ErrorMessage).str(); 93 return DB; 94 } 95 96 std::unique_ptr<CompilationDatabase> 97 CompilationDatabase::autoDetectFromDirectory(StringRef SourceDir, 98 std::string &ErrorMessage) { 99 SmallString<1024> AbsolutePath(getAbsolutePath(SourceDir)); 100 101 std::unique_ptr<CompilationDatabase> DB = 102 findCompilationDatabaseFromDirectory(AbsolutePath, ErrorMessage); 103 104 if (!DB) 105 ErrorMessage = ("Could not auto-detect compilation database from directory \"" + 106 SourceDir + "\"\n" + ErrorMessage).str(); 107 return DB; 108 } 109 110 CompilationDatabasePlugin::~CompilationDatabasePlugin() {} 111 112 // Helper for recursively searching through a chain of actions and collecting 113 // all inputs, direct and indirect, of compile jobs. 114 struct CompileJobAnalyzer { 115 void run(const driver::Action *A) { 116 runImpl(A, false); 117 } 118 119 SmallVector<std::string, 2> Inputs; 120 121 private: 122 123 void runImpl(const driver::Action *A, bool Collect) { 124 bool CollectChildren = Collect; 125 switch (A->getKind()) { 126 case driver::Action::CompileJobClass: 127 CollectChildren = true; 128 break; 129 130 case driver::Action::InputClass: { 131 if (Collect) { 132 const driver::InputAction *IA = cast<driver::InputAction>(A); 133 Inputs.push_back(IA->getInputArg().getSpelling()); 134 } 135 } break; 136 137 default: 138 // Don't care about others 139 ; 140 } 141 142 for (driver::ActionList::const_iterator I = A->begin(), E = A->end(); 143 I != E; ++I) 144 runImpl(*I, CollectChildren); 145 } 146 }; 147 148 // Special DiagnosticConsumer that looks for warn_drv_input_file_unused 149 // diagnostics from the driver and collects the option strings for those unused 150 // options. 151 class UnusedInputDiagConsumer : public DiagnosticConsumer { 152 public: 153 UnusedInputDiagConsumer() : Other(nullptr) {} 154 155 // Useful for debugging, chain diagnostics to another consumer after 156 // recording for our own purposes. 157 UnusedInputDiagConsumer(DiagnosticConsumer *Other) : Other(Other) {} 158 159 virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 160 const Diagnostic &Info) override { 161 if (Info.getID() == clang::diag::warn_drv_input_file_unused) { 162 // Arg 1 for this diagnostic is the option that didn't get used. 163 UnusedInputs.push_back(Info.getArgStdStr(0)); 164 } 165 if (Other) 166 Other->HandleDiagnostic(DiagLevel, Info); 167 } 168 169 DiagnosticConsumer *Other; 170 SmallVector<std::string, 2> UnusedInputs; 171 }; 172 173 // Unary functor for asking "Given a StringRef S1, does there exist a string 174 // S2 in Arr where S1 == S2?" 175 struct MatchesAny { 176 MatchesAny(ArrayRef<std::string> Arr) : Arr(Arr) {} 177 bool operator() (StringRef S) { 178 for (const std::string *I = Arr.begin(), *E = Arr.end(); I != E; ++I) 179 if (*I == S) 180 return true; 181 return false; 182 } 183 private: 184 ArrayRef<std::string> Arr; 185 }; 186 187 /// \brief Strips any positional args and possible argv[0] from a command-line 188 /// provided by the user to construct a FixedCompilationDatabase. 189 /// 190 /// FixedCompilationDatabase requires a command line to be in this format as it 191 /// constructs the command line for each file by appending the name of the file 192 /// to be compiled. FixedCompilationDatabase also adds its own argv[0] to the 193 /// start of the command line although its value is not important as it's just 194 /// ignored by the Driver invoked by the ClangTool using the 195 /// FixedCompilationDatabase. 196 /// 197 /// FIXME: This functionality should probably be made available by 198 /// clang::driver::Driver although what the interface should look like is not 199 /// clear. 200 /// 201 /// \param[in] Args Args as provided by the user. 202 /// \return Resulting stripped command line. 203 /// \li true if successful. 204 /// \li false if \c Args cannot be used for compilation jobs (e.g. 205 /// contains an option like -E or -version). 206 static bool stripPositionalArgs(std::vector<const char *> Args, 207 std::vector<std::string> &Result) { 208 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 209 UnusedInputDiagConsumer DiagClient; 210 DiagnosticsEngine Diagnostics( 211 IntrusiveRefCntPtr<clang::DiagnosticIDs>(new DiagnosticIDs()), 212 &*DiagOpts, &DiagClient, false); 213 214 // The clang executable path isn't required since the jobs the driver builds 215 // will not be executed. 216 std::unique_ptr<driver::Driver> NewDriver(new driver::Driver( 217 /* ClangExecutable= */ "", llvm::sys::getDefaultTargetTriple(), 218 Diagnostics)); 219 NewDriver->setCheckInputsExist(false); 220 221 // This becomes the new argv[0]. The value is actually not important as it 222 // isn't used for invoking Tools. 223 Args.insert(Args.begin(), "clang-tool"); 224 225 // By adding -c, we force the driver to treat compilation as the last phase. 226 // It will then issue warnings via Diagnostics about un-used options that 227 // would have been used for linking. If the user provided a compiler name as 228 // the original argv[0], this will be treated as a linker input thanks to 229 // insertng a new argv[0] above. All un-used options get collected by 230 // UnusedInputdiagConsumer and get stripped out later. 231 Args.push_back("-c"); 232 233 // Put a dummy C++ file on to ensure there's at least one compile job for the 234 // driver to construct. If the user specified some other argument that 235 // prevents compilation, e.g. -E or something like -version, we may still end 236 // up with no jobs but then this is the user's fault. 237 Args.push_back("placeholder.cpp"); 238 239 // Remove -no-integrated-as; it's not used for syntax checking, 240 // and it confuses targets which don't support this option. 241 Args.erase(std::remove_if(Args.begin(), Args.end(), 242 MatchesAny(std::string("-no-integrated-as"))), 243 Args.end()); 244 245 const std::unique_ptr<driver::Compilation> Compilation( 246 NewDriver->BuildCompilation(Args)); 247 248 const driver::JobList &Jobs = Compilation->getJobs(); 249 250 CompileJobAnalyzer CompileAnalyzer; 251 252 for (const auto &Job : Jobs) { 253 if (Job.getKind() == driver::Job::CommandClass) { 254 const driver::Command &Cmd = cast<driver::Command>(Job); 255 // Collect only for Assemble jobs. If we do all jobs we get duplicates 256 // since Link jobs point to Assemble jobs as inputs. 257 if (Cmd.getSource().getKind() == driver::Action::AssembleJobClass) 258 CompileAnalyzer.run(&Cmd.getSource()); 259 } 260 } 261 262 if (CompileAnalyzer.Inputs.empty()) { 263 // No compile jobs found. 264 // FIXME: Emit a warning of some kind? 265 return false; 266 } 267 268 // Remove all compilation input files from the command line. This is 269 // necessary so that getCompileCommands() can construct a command line for 270 // each file. 271 std::vector<const char *>::iterator End = std::remove_if( 272 Args.begin(), Args.end(), MatchesAny(CompileAnalyzer.Inputs)); 273 274 // Remove all inputs deemed unused for compilation. 275 End = std::remove_if(Args.begin(), End, MatchesAny(DiagClient.UnusedInputs)); 276 277 // Remove the -c add above as well. It will be at the end right now. 278 assert(strcmp(*(End - 1), "-c") == 0); 279 --End; 280 281 Result = std::vector<std::string>(Args.begin() + 1, End); 282 return true; 283 } 284 285 FixedCompilationDatabase * 286 FixedCompilationDatabase::loadFromCommandLine(int &Argc, 287 const char **Argv, 288 Twine Directory) { 289 const char **DoubleDash = std::find(Argv, Argv + Argc, StringRef("--")); 290 if (DoubleDash == Argv + Argc) 291 return nullptr; 292 std::vector<const char *> CommandLine(DoubleDash + 1, Argv + Argc); 293 Argc = DoubleDash - Argv; 294 295 std::vector<std::string> StrippedArgs; 296 if (!stripPositionalArgs(CommandLine, StrippedArgs)) 297 return nullptr; 298 return new FixedCompilationDatabase(Directory, StrippedArgs); 299 } 300 301 FixedCompilationDatabase:: 302 FixedCompilationDatabase(Twine Directory, ArrayRef<std::string> CommandLine) { 303 std::vector<std::string> ToolCommandLine(1, "clang-tool"); 304 ToolCommandLine.insert(ToolCommandLine.end(), 305 CommandLine.begin(), CommandLine.end()); 306 CompileCommands.push_back( 307 CompileCommand(Directory, std::move(ToolCommandLine))); 308 } 309 310 std::vector<CompileCommand> 311 FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const { 312 std::vector<CompileCommand> Result(CompileCommands); 313 Result[0].CommandLine.push_back(FilePath); 314 return Result; 315 } 316 317 std::vector<std::string> 318 FixedCompilationDatabase::getAllFiles() const { 319 return std::vector<std::string>(); 320 } 321 322 std::vector<CompileCommand> 323 FixedCompilationDatabase::getAllCompileCommands() const { 324 return std::vector<CompileCommand>(); 325 } 326 327 // This anchor is used to force the linker to link in the generated object file 328 // and thus register the JSONCompilationDatabasePlugin. 329 extern volatile int JSONAnchorSource; 330 static int JSONAnchorDest = JSONAnchorSource; 331 332 } // end namespace tooling 333 } // end namespace clang 334