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