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