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