147c245a5SManuel Klimek //===--- CompilationDatabase.cpp - ----------------------------------------===// 247c245a5SManuel Klimek // 347c245a5SManuel Klimek // The LLVM Compiler Infrastructure 447c245a5SManuel Klimek // 547c245a5SManuel Klimek // This file is distributed under the University of Illinois Open Source 647c245a5SManuel Klimek // License. See LICENSE.TXT for details. 747c245a5SManuel Klimek // 847c245a5SManuel Klimek //===----------------------------------------------------------------------===// 947c245a5SManuel Klimek // 106ed1f85cSDaniel Jasper // This file contains implementations of the CompilationDatabase base class 116ed1f85cSDaniel Jasper // and the FixedCompilationDatabase. 1247c245a5SManuel Klimek // 1347c245a5SManuel Klimek //===----------------------------------------------------------------------===// 1447c245a5SManuel Klimek 1547c245a5SManuel Klimek #include "clang/Tooling/CompilationDatabase.h" 16eb56f4feSEdwin Vane #include "clang/Basic/Diagnostic.h" 17f3ca2698SBenjamin Kramer #include "clang/Basic/DiagnosticOptions.h" 18eb56f4feSEdwin Vane #include "clang/Driver/Action.h" 195553d0d4SChandler Carruth #include "clang/Driver/Compilation.h" 20eb56f4feSEdwin Vane #include "clang/Driver/Driver.h" 21eb56f4feSEdwin Vane #include "clang/Driver/DriverDiagnostic.h" 22eb56f4feSEdwin Vane #include "clang/Driver/Job.h" 23eb56f4feSEdwin Vane #include "clang/Frontend/TextDiagnosticPrinter.h" 245553d0d4SChandler Carruth #include "clang/Tooling/CompilationDatabasePluginRegistry.h" 255553d0d4SChandler Carruth #include "clang/Tooling/Tooling.h" 265553d0d4SChandler Carruth #include "llvm/ADT/SmallString.h" 27eb56f4feSEdwin Vane #include "llvm/Option/Arg.h" 285553d0d4SChandler Carruth #include "llvm/Support/Host.h" 295553d0d4SChandler Carruth #include "llvm/Support/Path.h" 30c46064c2SSerge Pavlov #include "llvm/Support/raw_ostream.h" 315553d0d4SChandler Carruth #include <sstream> 328a8e554aSRafael Espindola #include <system_error> 336a1457e6SBenjamin Kramer using namespace clang; 346a1457e6SBenjamin Kramer using namespace tooling; 3547c245a5SManuel Klimek 364d79ec7fSJohn Brawn LLVM_INSTANTIATE_REGISTRY(CompilationDatabasePluginRegistry) 374d79ec7fSJohn Brawn 38637d1e66SAngel Garcia Gomez CompilationDatabase::~CompilationDatabase() {} 3947c245a5SManuel Klimek 40cdba84c0SDavid Blaikie std::unique_ptr<CompilationDatabase> 4147c245a5SManuel Klimek CompilationDatabase::loadFromDirectory(StringRef BuildDirectory, 4247c245a5SManuel Klimek std::string &ErrorMessage) { 432d487db0SMehdi Amini llvm::raw_string_ostream ErrorStream(ErrorMessage); 446ed1f85cSDaniel Jasper for (CompilationDatabasePluginRegistry::iterator 456ed1f85cSDaniel Jasper It = CompilationDatabasePluginRegistry::begin(), 466ed1f85cSDaniel Jasper Ie = CompilationDatabasePluginRegistry::end(); 476ed1f85cSDaniel Jasper It != Ie; ++It) { 486ed1f85cSDaniel Jasper std::string DatabaseErrorMessage; 49b8984329SAhmed Charles std::unique_ptr<CompilationDatabasePlugin> Plugin(It->instantiate()); 50cdba84c0SDavid Blaikie if (std::unique_ptr<CompilationDatabase> DB = 516ed1f85cSDaniel Jasper Plugin->loadFromDirectory(BuildDirectory, DatabaseErrorMessage)) 526ed1f85cSDaniel Jasper return DB; 532d487db0SMehdi Amini ErrorStream << It->getName() << ": " << DatabaseErrorMessage << "\n"; 5447c245a5SManuel Klimek } 55ccbc35edSCraig Topper return nullptr; 5647c245a5SManuel Klimek } 5747c245a5SManuel Klimek 58cdba84c0SDavid Blaikie static std::unique_ptr<CompilationDatabase> 596ed1f85cSDaniel Jasper findCompilationDatabaseFromDirectory(StringRef Directory, 606ed1f85cSDaniel Jasper std::string &ErrorMessage) { 616ed1f85cSDaniel Jasper std::stringstream ErrorStream; 6274351ff4SDaniel Jasper bool HasErrorMessage = false; 63617f5269SArnaud A. de Grandmaison while (!Directory.empty()) { 64617f5269SArnaud A. de Grandmaison std::string LoadErrorMessage; 65617f5269SArnaud A. de Grandmaison 66cdba84c0SDavid Blaikie if (std::unique_ptr<CompilationDatabase> DB = 67617f5269SArnaud A. de Grandmaison CompilationDatabase::loadFromDirectory(Directory, LoadErrorMessage)) 68617f5269SArnaud A. de Grandmaison return DB; 6974351ff4SDaniel Jasper 7074351ff4SDaniel Jasper if (!HasErrorMessage) { 716ed1f85cSDaniel Jasper ErrorStream << "No compilation database found in " << Directory.str() 7274351ff4SDaniel Jasper << " or any parent directory\n" << LoadErrorMessage; 7374351ff4SDaniel Jasper HasErrorMessage = true; 7474351ff4SDaniel Jasper } 75617f5269SArnaud A. de Grandmaison 76617f5269SArnaud A. de Grandmaison Directory = llvm::sys::path::parent_path(Directory); 77617f5269SArnaud A. de Grandmaison } 786ed1f85cSDaniel Jasper ErrorMessage = ErrorStream.str(); 79ccbc35edSCraig Topper return nullptr; 80617f5269SArnaud A. de Grandmaison } 81617f5269SArnaud A. de Grandmaison 82cdba84c0SDavid Blaikie std::unique_ptr<CompilationDatabase> 8365fd0e1fSManuel Klimek CompilationDatabase::autoDetectFromSource(StringRef SourceFile, 8465fd0e1fSManuel Klimek std::string &ErrorMessage) { 85f857950dSDmitri Gribenko SmallString<1024> AbsolutePath(getAbsolutePath(SourceFile)); 8665fd0e1fSManuel Klimek StringRef Directory = llvm::sys::path::parent_path(AbsolutePath); 87617f5269SArnaud A. de Grandmaison 88cdba84c0SDavid Blaikie std::unique_ptr<CompilationDatabase> DB = 89cdba84c0SDavid Blaikie findCompilationDatabaseFromDirectory(Directory, ErrorMessage); 90617f5269SArnaud A. de Grandmaison 91617f5269SArnaud A. de Grandmaison if (!DB) 9265fd0e1fSManuel Klimek ErrorMessage = ("Could not auto-detect compilation database for file \"" + 936ed1f85cSDaniel Jasper SourceFile + "\"\n" + ErrorMessage).str(); 94617f5269SArnaud A. de Grandmaison return DB; 95617f5269SArnaud A. de Grandmaison } 96617f5269SArnaud A. de Grandmaison 97cdba84c0SDavid Blaikie std::unique_ptr<CompilationDatabase> 98617f5269SArnaud A. de Grandmaison CompilationDatabase::autoDetectFromDirectory(StringRef SourceDir, 99617f5269SArnaud A. de Grandmaison std::string &ErrorMessage) { 100f857950dSDmitri Gribenko SmallString<1024> AbsolutePath(getAbsolutePath(SourceDir)); 101617f5269SArnaud A. de Grandmaison 102cdba84c0SDavid Blaikie std::unique_ptr<CompilationDatabase> DB = 103cdba84c0SDavid Blaikie findCompilationDatabaseFromDirectory(AbsolutePath, ErrorMessage); 104617f5269SArnaud A. de Grandmaison 105617f5269SArnaud A. de Grandmaison if (!DB) 106617f5269SArnaud A. de Grandmaison ErrorMessage = ("Could not auto-detect compilation database from directory \"" + 1076ed1f85cSDaniel Jasper SourceDir + "\"\n" + ErrorMessage).str(); 108617f5269SArnaud A. de Grandmaison return DB; 10965fd0e1fSManuel Klimek } 11065fd0e1fSManuel Klimek 111637d1e66SAngel Garcia Gomez CompilationDatabasePlugin::~CompilationDatabasePlugin() {} 1126ed1f85cSDaniel Jasper 1136a1457e6SBenjamin Kramer namespace { 114eb56f4feSEdwin Vane // Helper for recursively searching through a chain of actions and collecting 115eb56f4feSEdwin Vane // all inputs, direct and indirect, of compile jobs. 116eb56f4feSEdwin Vane struct CompileJobAnalyzer { 117eb56f4feSEdwin Vane void run(const driver::Action *A) { 118eb56f4feSEdwin Vane runImpl(A, false); 119eb56f4feSEdwin Vane } 120eb56f4feSEdwin Vane 121eb56f4feSEdwin Vane SmallVector<std::string, 2> Inputs; 122eb56f4feSEdwin Vane 123eb56f4feSEdwin Vane private: 124eb56f4feSEdwin Vane 125eb56f4feSEdwin Vane void runImpl(const driver::Action *A, bool Collect) { 126eb56f4feSEdwin Vane bool CollectChildren = Collect; 127eb56f4feSEdwin Vane switch (A->getKind()) { 128eb56f4feSEdwin Vane case driver::Action::CompileJobClass: 129eb56f4feSEdwin Vane CollectChildren = true; 130eb56f4feSEdwin Vane break; 131eb56f4feSEdwin Vane 132eb56f4feSEdwin Vane case driver::Action::InputClass: { 133eb56f4feSEdwin Vane if (Collect) { 134eb56f4feSEdwin Vane const driver::InputAction *IA = cast<driver::InputAction>(A); 135eb56f4feSEdwin Vane Inputs.push_back(IA->getInputArg().getSpelling()); 136eb56f4feSEdwin Vane } 137eb56f4feSEdwin Vane } break; 138eb56f4feSEdwin Vane 139eb56f4feSEdwin Vane default: 140eb56f4feSEdwin Vane // Don't care about others 141eb56f4feSEdwin Vane ; 142eb56f4feSEdwin Vane } 143eb56f4feSEdwin Vane 1445a459f82SNico Weber for (const driver::Action *AI : A->inputs()) 1455a459f82SNico Weber runImpl(AI, CollectChildren); 146eb56f4feSEdwin Vane } 147eb56f4feSEdwin Vane }; 148eb56f4feSEdwin Vane 149eb56f4feSEdwin Vane // Special DiagnosticConsumer that looks for warn_drv_input_file_unused 150eb56f4feSEdwin Vane // diagnostics from the driver and collects the option strings for those unused 151eb56f4feSEdwin Vane // options. 152eb56f4feSEdwin Vane class UnusedInputDiagConsumer : public DiagnosticConsumer { 153eb56f4feSEdwin Vane public: 154c46064c2SSerge Pavlov UnusedInputDiagConsumer(DiagnosticConsumer &Other) : Other(Other) {} 155eb56f4feSEdwin Vane 15634eb2072SAlexander Kornienko void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 157a798a9dbSCraig Topper const Diagnostic &Info) override { 158eb56f4feSEdwin Vane if (Info.getID() == clang::diag::warn_drv_input_file_unused) { 159eb56f4feSEdwin Vane // Arg 1 for this diagnostic is the option that didn't get used. 160eb56f4feSEdwin Vane UnusedInputs.push_back(Info.getArgStdStr(0)); 161c46064c2SSerge Pavlov } else if (DiagLevel >= DiagnosticsEngine::Error) { 162c46064c2SSerge Pavlov // If driver failed to create compilation object, show the diagnostics 163c46064c2SSerge Pavlov // to user. 164c46064c2SSerge Pavlov Other.HandleDiagnostic(DiagLevel, Info); 165eb56f4feSEdwin Vane } 166eb56f4feSEdwin Vane } 167eb56f4feSEdwin Vane 168c46064c2SSerge Pavlov DiagnosticConsumer &Other; 169eb56f4feSEdwin Vane SmallVector<std::string, 2> UnusedInputs; 170eb56f4feSEdwin Vane }; 171eb56f4feSEdwin Vane 172eb56f4feSEdwin Vane // Unary functor for asking "Given a StringRef S1, does there exist a string 173eb56f4feSEdwin Vane // S2 in Arr where S1 == S2?" 174eb56f4feSEdwin Vane struct MatchesAny { 175eb56f4feSEdwin Vane MatchesAny(ArrayRef<std::string> Arr) : Arr(Arr) {} 176eb56f4feSEdwin Vane bool operator() (StringRef S) { 177eb56f4feSEdwin Vane for (const std::string *I = Arr.begin(), *E = Arr.end(); I != E; ++I) 178eb56f4feSEdwin Vane if (*I == S) 179eb56f4feSEdwin Vane return true; 180eb56f4feSEdwin Vane return false; 181eb56f4feSEdwin Vane } 182eb56f4feSEdwin Vane private: 183eb56f4feSEdwin Vane ArrayRef<std::string> Arr; 184eb56f4feSEdwin Vane }; 1856a1457e6SBenjamin Kramer } // namespace 186eb56f4feSEdwin Vane 187eb56f4feSEdwin Vane /// \brief Strips any positional args and possible argv[0] from a command-line 188eb56f4feSEdwin Vane /// provided by the user to construct a FixedCompilationDatabase. 189eb56f4feSEdwin Vane /// 190eb56f4feSEdwin Vane /// FixedCompilationDatabase requires a command line to be in this format as it 191eb56f4feSEdwin Vane /// constructs the command line for each file by appending the name of the file 192eb56f4feSEdwin Vane /// to be compiled. FixedCompilationDatabase also adds its own argv[0] to the 193eb56f4feSEdwin Vane /// start of the command line although its value is not important as it's just 194eb56f4feSEdwin Vane /// ignored by the Driver invoked by the ClangTool using the 195eb56f4feSEdwin Vane /// FixedCompilationDatabase. 196eb56f4feSEdwin Vane /// 197eb56f4feSEdwin Vane /// FIXME: This functionality should probably be made available by 198eb56f4feSEdwin Vane /// clang::driver::Driver although what the interface should look like is not 199eb56f4feSEdwin Vane /// clear. 200eb56f4feSEdwin Vane /// 201eb56f4feSEdwin Vane /// \param[in] Args Args as provided by the user. 2025335cf33SNAKAMURA Takumi /// \return Resulting stripped command line. 2035335cf33SNAKAMURA Takumi /// \li true if successful. 204eb56f4feSEdwin Vane /// \li false if \c Args cannot be used for compilation jobs (e.g. 205eb56f4feSEdwin Vane /// contains an option like -E or -version). 2063a4fe369SArtyom Skrobov static bool stripPositionalArgs(std::vector<const char *> Args, 207c46064c2SSerge Pavlov std::vector<std::string> &Result, 208c46064c2SSerge Pavlov std::string &ErrorMsg) { 209eb56f4feSEdwin Vane IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 210c46064c2SSerge Pavlov llvm::raw_string_ostream Output(ErrorMsg); 211c46064c2SSerge Pavlov TextDiagnosticPrinter DiagnosticPrinter(Output, &*DiagOpts); 212c46064c2SSerge Pavlov UnusedInputDiagConsumer DiagClient(DiagnosticPrinter); 213eb56f4feSEdwin Vane DiagnosticsEngine Diagnostics( 214eb56f4feSEdwin Vane IntrusiveRefCntPtr<clang::DiagnosticIDs>(new DiagnosticIDs()), 215eb56f4feSEdwin Vane &*DiagOpts, &DiagClient, false); 216eb56f4feSEdwin Vane 2171761f118SAlp Toker // The clang executable path isn't required since the jobs the driver builds 2181761f118SAlp Toker // will not be executed. 219b8984329SAhmed Charles std::unique_ptr<driver::Driver> NewDriver(new driver::Driver( 220eb56f4feSEdwin Vane /* ClangExecutable= */ "", llvm::sys::getDefaultTargetTriple(), 2211761f118SAlp Toker Diagnostics)); 222eb56f4feSEdwin Vane NewDriver->setCheckInputsExist(false); 223eb56f4feSEdwin Vane 224eb56f4feSEdwin Vane // This becomes the new argv[0]. The value is actually not important as it 225eb56f4feSEdwin Vane // isn't used for invoking Tools. 226eb56f4feSEdwin Vane Args.insert(Args.begin(), "clang-tool"); 227eb56f4feSEdwin Vane 228eb56f4feSEdwin Vane // By adding -c, we force the driver to treat compilation as the last phase. 229eb56f4feSEdwin Vane // It will then issue warnings via Diagnostics about un-used options that 230eb56f4feSEdwin Vane // would have been used for linking. If the user provided a compiler name as 231eb56f4feSEdwin Vane // the original argv[0], this will be treated as a linker input thanks to 232eb56f4feSEdwin Vane // insertng a new argv[0] above. All un-used options get collected by 233eb56f4feSEdwin Vane // UnusedInputdiagConsumer and get stripped out later. 234eb56f4feSEdwin Vane Args.push_back("-c"); 235eb56f4feSEdwin Vane 236eb56f4feSEdwin Vane // Put a dummy C++ file on to ensure there's at least one compile job for the 237eb56f4feSEdwin Vane // driver to construct. If the user specified some other argument that 238eb56f4feSEdwin Vane // prevents compilation, e.g. -E or something like -version, we may still end 239eb56f4feSEdwin Vane // up with no jobs but then this is the user's fault. 240eb56f4feSEdwin Vane Args.push_back("placeholder.cpp"); 241eb56f4feSEdwin Vane 2423a4fe369SArtyom Skrobov // Remove -no-integrated-as; it's not used for syntax checking, 2433a4fe369SArtyom Skrobov // and it confuses targets which don't support this option. 244e1265143SArnaud A. de Grandmaison Args.erase(std::remove_if(Args.begin(), Args.end(), 245e1265143SArnaud A. de Grandmaison MatchesAny(std::string("-no-integrated-as"))), 246e1265143SArnaud A. de Grandmaison Args.end()); 2473a4fe369SArtyom Skrobov 248b8984329SAhmed Charles const std::unique_ptr<driver::Compilation> Compilation( 249eb56f4feSEdwin Vane NewDriver->BuildCompilation(Args)); 250c46064c2SSerge Pavlov if (!Compilation) 251c46064c2SSerge Pavlov return false; 252eb56f4feSEdwin Vane 253eb56f4feSEdwin Vane const driver::JobList &Jobs = Compilation->getJobs(); 254eb56f4feSEdwin Vane 255eb56f4feSEdwin Vane CompileJobAnalyzer CompileAnalyzer; 256eb56f4feSEdwin Vane 2570cd9248dSJustin Bogner for (const auto &Cmd : Jobs) { 258e2863ecaSAlex Lorenz // Collect only for Assemble and Compile jobs. If we do all jobs we get 259e2863ecaSAlex Lorenz // duplicates since Link jobs point to Assemble jobs as inputs. 260e2863ecaSAlex Lorenz if (Cmd.getSource().getKind() == driver::Action::AssembleJobClass || 261e2863ecaSAlex Lorenz Cmd.getSource().getKind() == driver::Action::CompileJobClass) { 262c11bf802SDavid Blaikie CompileAnalyzer.run(&Cmd.getSource()); 263eb56f4feSEdwin Vane } 264e2863ecaSAlex Lorenz } 265eb56f4feSEdwin Vane 266eb56f4feSEdwin Vane if (CompileAnalyzer.Inputs.empty()) { 267c46064c2SSerge Pavlov ErrorMsg = "warning: no compile jobs found\n"; 268eb56f4feSEdwin Vane return false; 269eb56f4feSEdwin Vane } 270eb56f4feSEdwin Vane 271eb56f4feSEdwin Vane // Remove all compilation input files from the command line. This is 272eb56f4feSEdwin Vane // necessary so that getCompileCommands() can construct a command line for 273eb56f4feSEdwin Vane // each file. 274eb56f4feSEdwin Vane std::vector<const char *>::iterator End = std::remove_if( 275eb56f4feSEdwin Vane Args.begin(), Args.end(), MatchesAny(CompileAnalyzer.Inputs)); 276eb56f4feSEdwin Vane 277eb56f4feSEdwin Vane // Remove all inputs deemed unused for compilation. 278eb56f4feSEdwin Vane End = std::remove_if(Args.begin(), End, MatchesAny(DiagClient.UnusedInputs)); 279eb56f4feSEdwin Vane 280eb56f4feSEdwin Vane // Remove the -c add above as well. It will be at the end right now. 281e99bb4b2SRichard Trieu assert(strcmp(*(End - 1), "-c") == 0); 282eb56f4feSEdwin Vane --End; 283eb56f4feSEdwin Vane 284eb56f4feSEdwin Vane Result = std::vector<std::string>(Args.begin() + 1, End); 285eb56f4feSEdwin Vane return true; 286eb56f4feSEdwin Vane } 287eb56f4feSEdwin Vane 288c46064c2SSerge Pavlov std::unique_ptr<FixedCompilationDatabase> 289c46064c2SSerge Pavlov FixedCompilationDatabase::loadFromCommandLine(int &Argc, 290c46064c2SSerge Pavlov const char *const *Argv, 291c46064c2SSerge Pavlov std::string &ErrorMsg, 292*41a9ee98SZachary Turner Twine Directory) { 293c46064c2SSerge Pavlov ErrorMsg.clear(); 294c46064c2SSerge Pavlov if (Argc == 0) 295c46064c2SSerge Pavlov return nullptr; 296ab8f7d58SDavid Blaikie const char *const *DoubleDash = std::find(Argv, Argv + Argc, StringRef("--")); 297ff26efceSManuel Klimek if (DoubleDash == Argv + Argc) 298ccbc35edSCraig Topper return nullptr; 299eb56f4feSEdwin Vane std::vector<const char *> CommandLine(DoubleDash + 1, Argv + Argc); 300ff26efceSManuel Klimek Argc = DoubleDash - Argv; 301eb56f4feSEdwin Vane 302eb56f4feSEdwin Vane std::vector<std::string> StrippedArgs; 303c46064c2SSerge Pavlov if (!stripPositionalArgs(CommandLine, StrippedArgs, ErrorMsg)) 304ccbc35edSCraig Topper return nullptr; 305c46064c2SSerge Pavlov return std::unique_ptr<FixedCompilationDatabase>( 306c46064c2SSerge Pavlov new FixedCompilationDatabase(Directory, StrippedArgs)); 307ff26efceSManuel Klimek } 308ff26efceSManuel Klimek 309*41a9ee98SZachary Turner FixedCompilationDatabase:: 310*41a9ee98SZachary Turner FixedCompilationDatabase(Twine Directory, ArrayRef<std::string> CommandLine) { 311ff26efceSManuel Klimek std::vector<std::string> ToolCommandLine(1, "clang-tool"); 312ff26efceSManuel Klimek ToolCommandLine.insert(ToolCommandLine.end(), 313ff26efceSManuel Klimek CommandLine.begin(), CommandLine.end()); 31474bcd21eSArgyrios Kyrtzidis CompileCommands.emplace_back(Directory, StringRef(), 315399aea30SJoerg Sonnenberger std::move(ToolCommandLine), 316399aea30SJoerg Sonnenberger StringRef()); 317ff26efceSManuel Klimek } 318ff26efceSManuel Klimek 319ff26efceSManuel Klimek std::vector<CompileCommand> 320ff26efceSManuel Klimek FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const { 321ff26efceSManuel Klimek std::vector<CompileCommand> Result(CompileCommands); 322ff26efceSManuel Klimek Result[0].CommandLine.push_back(FilePath); 32374bcd21eSArgyrios Kyrtzidis Result[0].Filename = FilePath; 324ff26efceSManuel Klimek return Result; 325ff26efceSManuel Klimek } 326ff26efceSManuel Klimek 32760b80161SManuel Klimek std::vector<std::string> 32860b80161SManuel Klimek FixedCompilationDatabase::getAllFiles() const { 32960b80161SManuel Klimek return std::vector<std::string>(); 33060b80161SManuel Klimek } 33160b80161SManuel Klimek 332251ad5e0SArgyrios Kyrtzidis std::vector<CompileCommand> 333251ad5e0SArgyrios Kyrtzidis FixedCompilationDatabase::getAllCompileCommands() const { 334251ad5e0SArgyrios Kyrtzidis return std::vector<CompileCommand>(); 335251ad5e0SArgyrios Kyrtzidis } 336251ad5e0SArgyrios Kyrtzidis 3376a1457e6SBenjamin Kramer namespace clang { 3386a1457e6SBenjamin Kramer namespace tooling { 3396a1457e6SBenjamin Kramer 3406ed1f85cSDaniel Jasper // This anchor is used to force the linker to link in the generated object file 3416ed1f85cSDaniel Jasper // and thus register the JSONCompilationDatabasePlugin. 3426ed1f85cSDaniel Jasper extern volatile int JSONAnchorSource; 343b7e8c7c7SYaron Keren static int LLVM_ATTRIBUTE_UNUSED JSONAnchorDest = JSONAnchorSource; 34447c245a5SManuel Klimek 34547c245a5SManuel Klimek } // end namespace tooling 34647c245a5SManuel Klimek } // end namespace clang 347