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 // 1047c245a5SManuel Klimek // This file contains multiple implementations for CompilationDatabases. 1147c245a5SManuel Klimek // 1247c245a5SManuel Klimek //===----------------------------------------------------------------------===// 1347c245a5SManuel Klimek 1447c245a5SManuel Klimek #include "clang/Tooling/CompilationDatabase.h" 1547c245a5SManuel Klimek #include "llvm/ADT/SmallString.h" 1640636c99SManuel Klimek #include "llvm/Support/YAMLParser.h" 1747c245a5SManuel Klimek #include "llvm/Support/Path.h" 1847c245a5SManuel Klimek #include "llvm/Support/system_error.h" 1947c245a5SManuel Klimek 2047c245a5SManuel Klimek namespace clang { 2147c245a5SManuel Klimek namespace tooling { 2247c245a5SManuel Klimek 2347c245a5SManuel Klimek namespace { 2447c245a5SManuel Klimek 2540636c99SManuel Klimek /// \brief A parser for escaped strings of command line arguments. 2647c245a5SManuel Klimek /// 2747c245a5SManuel Klimek /// Assumes \-escaping for quoted arguments (see the documentation of 2840636c99SManuel Klimek /// unescapeCommandLine(...)). 2947c245a5SManuel Klimek class CommandLineArgumentParser { 3047c245a5SManuel Klimek public: 3147c245a5SManuel Klimek CommandLineArgumentParser(StringRef CommandLine) 3247c245a5SManuel Klimek : Input(CommandLine), Position(Input.begin()-1) {} 3347c245a5SManuel Klimek 3447c245a5SManuel Klimek std::vector<std::string> parse() { 3547c245a5SManuel Klimek bool HasMoreInput = true; 3647c245a5SManuel Klimek while (HasMoreInput && nextNonWhitespace()) { 3747c245a5SManuel Klimek std::string Argument; 3847c245a5SManuel Klimek HasMoreInput = parseStringInto(Argument); 3947c245a5SManuel Klimek CommandLine.push_back(Argument); 4047c245a5SManuel Klimek } 4147c245a5SManuel Klimek return CommandLine; 4247c245a5SManuel Klimek } 4347c245a5SManuel Klimek 4447c245a5SManuel Klimek private: 4547c245a5SManuel Klimek // All private methods return true if there is more input available. 4647c245a5SManuel Klimek 4747c245a5SManuel Klimek bool parseStringInto(std::string &String) { 4847c245a5SManuel Klimek do { 4947c245a5SManuel Klimek if (*Position == '"') { 5047c245a5SManuel Klimek if (!parseQuotedStringInto(String)) return false; 5147c245a5SManuel Klimek } else { 5247c245a5SManuel Klimek if (!parseFreeStringInto(String)) return false; 5347c245a5SManuel Klimek } 5447c245a5SManuel Klimek } while (*Position != ' '); 5547c245a5SManuel Klimek return true; 5647c245a5SManuel Klimek } 5747c245a5SManuel Klimek 5847c245a5SManuel Klimek bool parseQuotedStringInto(std::string &String) { 5947c245a5SManuel Klimek if (!next()) return false; 6047c245a5SManuel Klimek while (*Position != '"') { 6147c245a5SManuel Klimek if (!skipEscapeCharacter()) return false; 6247c245a5SManuel Klimek String.push_back(*Position); 6347c245a5SManuel Klimek if (!next()) return false; 6447c245a5SManuel Klimek } 6547c245a5SManuel Klimek return next(); 6647c245a5SManuel Klimek } 6747c245a5SManuel Klimek 6847c245a5SManuel Klimek bool parseFreeStringInto(std::string &String) { 6947c245a5SManuel Klimek do { 7047c245a5SManuel Klimek if (!skipEscapeCharacter()) return false; 7147c245a5SManuel Klimek String.push_back(*Position); 7247c245a5SManuel Klimek if (!next()) return false; 7347c245a5SManuel Klimek } while (*Position != ' ' && *Position != '"'); 7447c245a5SManuel Klimek return true; 7547c245a5SManuel Klimek } 7647c245a5SManuel Klimek 7747c245a5SManuel Klimek bool skipEscapeCharacter() { 7847c245a5SManuel Klimek if (*Position == '\\') { 7947c245a5SManuel Klimek return next(); 8047c245a5SManuel Klimek } 8147c245a5SManuel Klimek return true; 8247c245a5SManuel Klimek } 8347c245a5SManuel Klimek 8447c245a5SManuel Klimek bool nextNonWhitespace() { 8547c245a5SManuel Klimek do { 8647c245a5SManuel Klimek if (!next()) return false; 8747c245a5SManuel Klimek } while (*Position == ' '); 8847c245a5SManuel Klimek return true; 8947c245a5SManuel Klimek } 9047c245a5SManuel Klimek 9147c245a5SManuel Klimek bool next() { 9247c245a5SManuel Klimek ++Position; 9347c245a5SManuel Klimek return Position != Input.end(); 9447c245a5SManuel Klimek } 9547c245a5SManuel Klimek 9647c245a5SManuel Klimek const StringRef Input; 9747c245a5SManuel Klimek StringRef::iterator Position; 9847c245a5SManuel Klimek std::vector<std::string> CommandLine; 9947c245a5SManuel Klimek }; 10047c245a5SManuel Klimek 10140636c99SManuel Klimek std::vector<std::string> unescapeCommandLine( 10240636c99SManuel Klimek StringRef EscapedCommandLine) { 10340636c99SManuel Klimek CommandLineArgumentParser parser(EscapedCommandLine); 10447c245a5SManuel Klimek return parser.parse(); 10547c245a5SManuel Klimek } 10647c245a5SManuel Klimek 10747c245a5SManuel Klimek } // end namespace 10847c245a5SManuel Klimek 10947c245a5SManuel Klimek CompilationDatabase::~CompilationDatabase() {} 11047c245a5SManuel Klimek 11147c245a5SManuel Klimek CompilationDatabase * 11247c245a5SManuel Klimek CompilationDatabase::loadFromDirectory(StringRef BuildDirectory, 11347c245a5SManuel Klimek std::string &ErrorMessage) { 11447c245a5SManuel Klimek llvm::SmallString<1024> JSONDatabasePath(BuildDirectory); 11547c245a5SManuel Klimek llvm::sys::path::append(JSONDatabasePath, "compile_commands.json"); 11647c245a5SManuel Klimek llvm::OwningPtr<CompilationDatabase> Database( 11747c245a5SManuel Klimek JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage)); 11847c245a5SManuel Klimek if (!Database) { 11947c245a5SManuel Klimek return NULL; 12047c245a5SManuel Klimek } 12147c245a5SManuel Klimek return Database.take(); 12247c245a5SManuel Klimek } 12347c245a5SManuel Klimek 124*ff26efceSManuel Klimek FixedCompilationDatabase * 125*ff26efceSManuel Klimek FixedCompilationDatabase::loadFromCommandLine(int &Argc, 126*ff26efceSManuel Klimek const char **Argv, 127*ff26efceSManuel Klimek Twine Directory) { 128*ff26efceSManuel Klimek const char **DoubleDash = std::find(Argv, Argv + Argc, StringRef("--")); 129*ff26efceSManuel Klimek if (DoubleDash == Argv + Argc) 130*ff26efceSManuel Klimek return NULL; 131*ff26efceSManuel Klimek std::vector<std::string> CommandLine(DoubleDash + 1, Argv + Argc); 132*ff26efceSManuel Klimek Argc = DoubleDash - Argv; 133*ff26efceSManuel Klimek return new FixedCompilationDatabase(Directory, CommandLine); 134*ff26efceSManuel Klimek } 135*ff26efceSManuel Klimek 136*ff26efceSManuel Klimek FixedCompilationDatabase:: 137*ff26efceSManuel Klimek FixedCompilationDatabase(Twine Directory, ArrayRef<std::string> CommandLine) { 138*ff26efceSManuel Klimek std::vector<std::string> ToolCommandLine(1, "clang-tool"); 139*ff26efceSManuel Klimek ToolCommandLine.insert(ToolCommandLine.end(), 140*ff26efceSManuel Klimek CommandLine.begin(), CommandLine.end()); 141*ff26efceSManuel Klimek CompileCommands.push_back(CompileCommand(Directory, ToolCommandLine)); 142*ff26efceSManuel Klimek } 143*ff26efceSManuel Klimek 144*ff26efceSManuel Klimek std::vector<CompileCommand> 145*ff26efceSManuel Klimek FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const { 146*ff26efceSManuel Klimek std::vector<CompileCommand> Result(CompileCommands); 147*ff26efceSManuel Klimek Result[0].CommandLine.push_back(FilePath); 148*ff26efceSManuel Klimek return Result; 149*ff26efceSManuel Klimek } 150*ff26efceSManuel Klimek 15147c245a5SManuel Klimek JSONCompilationDatabase * 15247c245a5SManuel Klimek JSONCompilationDatabase::loadFromFile(StringRef FilePath, 15347c245a5SManuel Klimek std::string &ErrorMessage) { 15447c245a5SManuel Klimek llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer; 15547c245a5SManuel Klimek llvm::error_code Result = 15647c245a5SManuel Klimek llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer); 15747c245a5SManuel Klimek if (Result != 0) { 15847c245a5SManuel Klimek ErrorMessage = "Error while opening JSON database: " + Result.message(); 15947c245a5SManuel Klimek return NULL; 16047c245a5SManuel Klimek } 16147c245a5SManuel Klimek llvm::OwningPtr<JSONCompilationDatabase> Database( 16247c245a5SManuel Klimek new JSONCompilationDatabase(DatabaseBuffer.take())); 16347c245a5SManuel Klimek if (!Database->parse(ErrorMessage)) 16447c245a5SManuel Klimek return NULL; 16547c245a5SManuel Klimek return Database.take(); 16647c245a5SManuel Klimek } 16747c245a5SManuel Klimek 16847c245a5SManuel Klimek JSONCompilationDatabase * 16947c245a5SManuel Klimek JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString, 17047c245a5SManuel Klimek std::string &ErrorMessage) { 17147c245a5SManuel Klimek llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer( 17247c245a5SManuel Klimek llvm::MemoryBuffer::getMemBuffer(DatabaseString)); 17347c245a5SManuel Klimek llvm::OwningPtr<JSONCompilationDatabase> Database( 17447c245a5SManuel Klimek new JSONCompilationDatabase(DatabaseBuffer.take())); 17547c245a5SManuel Klimek if (!Database->parse(ErrorMessage)) 17647c245a5SManuel Klimek return NULL; 17747c245a5SManuel Klimek return Database.take(); 17847c245a5SManuel Klimek } 17947c245a5SManuel Klimek 18047c245a5SManuel Klimek std::vector<CompileCommand> 18147c245a5SManuel Klimek JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { 18247c245a5SManuel Klimek llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 18347c245a5SManuel Klimek CommandsRefI = IndexByFile.find(FilePath); 18447c245a5SManuel Klimek if (CommandsRefI == IndexByFile.end()) 18547c245a5SManuel Klimek return std::vector<CompileCommand>(); 18647c245a5SManuel Klimek const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue(); 18747c245a5SManuel Klimek std::vector<CompileCommand> Commands; 18847c245a5SManuel Klimek for (int I = 0, E = CommandsRef.size(); I != E; ++I) { 18940636c99SManuel Klimek llvm::SmallString<8> DirectoryStorage; 19040636c99SManuel Klimek llvm::SmallString<1024> CommandStorage; 19147c245a5SManuel Klimek Commands.push_back(CompileCommand( 19247c245a5SManuel Klimek // FIXME: Escape correctly: 19340636c99SManuel Klimek CommandsRef[I].first->getValue(DirectoryStorage), 19440636c99SManuel Klimek unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage)))); 19547c245a5SManuel Klimek } 19647c245a5SManuel Klimek return Commands; 19747c245a5SManuel Klimek } 19847c245a5SManuel Klimek 19947c245a5SManuel Klimek bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { 20040636c99SManuel Klimek llvm::yaml::document_iterator I = YAMLStream.begin(); 20140636c99SManuel Klimek if (I == YAMLStream.end()) { 20240636c99SManuel Klimek ErrorMessage = "Error while parsing YAML."; 20347c245a5SManuel Klimek return false; 20447c245a5SManuel Klimek } 20540636c99SManuel Klimek llvm::yaml::Node *Root = I->getRoot(); 20640636c99SManuel Klimek if (Root == NULL) { 20740636c99SManuel Klimek ErrorMessage = "Error while parsing YAML."; 20840636c99SManuel Klimek return false; 20940636c99SManuel Klimek } 21040636c99SManuel Klimek llvm::yaml::SequenceNode *Array = 21140636c99SManuel Klimek llvm::dyn_cast<llvm::yaml::SequenceNode>(Root); 21247c245a5SManuel Klimek if (Array == NULL) { 21347c245a5SManuel Klimek ErrorMessage = "Expected array."; 21447c245a5SManuel Klimek return false; 21547c245a5SManuel Klimek } 21640636c99SManuel Klimek for (llvm::yaml::SequenceNode::iterator AI = Array->begin(), 21740636c99SManuel Klimek AE = Array->end(); 21847c245a5SManuel Klimek AI != AE; ++AI) { 21940636c99SManuel Klimek llvm::yaml::MappingNode *Object = 22040636c99SManuel Klimek llvm::dyn_cast<llvm::yaml::MappingNode>(&*AI); 22147c245a5SManuel Klimek if (Object == NULL) { 22247c245a5SManuel Klimek ErrorMessage = "Expected object."; 22347c245a5SManuel Klimek return false; 22447c245a5SManuel Klimek } 22540636c99SManuel Klimek llvm::yaml::ScalarNode *Directory; 22640636c99SManuel Klimek llvm::yaml::ScalarNode *Command; 22740636c99SManuel Klimek llvm::SmallString<8> FileStorage; 22840636c99SManuel Klimek llvm::StringRef File; 22940636c99SManuel Klimek for (llvm::yaml::MappingNode::iterator KVI = Object->begin(), 23047c245a5SManuel Klimek KVE = Object->end(); 23147c245a5SManuel Klimek KVI != KVE; ++KVI) { 23240636c99SManuel Klimek llvm::yaml::Node *Value = (*KVI).getValue(); 23347c245a5SManuel Klimek if (Value == NULL) { 23447c245a5SManuel Klimek ErrorMessage = "Expected value."; 23547c245a5SManuel Klimek return false; 23647c245a5SManuel Klimek } 23740636c99SManuel Klimek llvm::yaml::ScalarNode *ValueString = 23840636c99SManuel Klimek llvm::dyn_cast<llvm::yaml::ScalarNode>(Value); 23947c245a5SManuel Klimek if (ValueString == NULL) { 24047c245a5SManuel Klimek ErrorMessage = "Expected string as value."; 24147c245a5SManuel Klimek return false; 24247c245a5SManuel Klimek } 24340636c99SManuel Klimek llvm::yaml::ScalarNode *KeyString = 24440636c99SManuel Klimek llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey()); 24540636c99SManuel Klimek llvm::SmallString<8> KeyStorage; 24640636c99SManuel Klimek if (KeyString->getValue(KeyStorage) == "directory") { 24740636c99SManuel Klimek Directory = ValueString; 24840636c99SManuel Klimek } else if (KeyString->getValue(KeyStorage) == "command") { 24940636c99SManuel Klimek Command = ValueString; 25040636c99SManuel Klimek } else if (KeyString->getValue(KeyStorage) == "file") { 25140636c99SManuel Klimek File = ValueString->getValue(FileStorage); 25247c245a5SManuel Klimek } else { 25340636c99SManuel Klimek ErrorMessage = ("Unknown key: \"" + 25440636c99SManuel Klimek KeyString->getRawValue() + "\"").str(); 25547c245a5SManuel Klimek return false; 25647c245a5SManuel Klimek } 25747c245a5SManuel Klimek } 25840636c99SManuel Klimek IndexByFile[File].push_back( 25940636c99SManuel Klimek CompileCommandRef(Directory, Command)); 26047c245a5SManuel Klimek } 26147c245a5SManuel Klimek return true; 26247c245a5SManuel Klimek } 26347c245a5SManuel Klimek 26447c245a5SManuel Klimek } // end namespace tooling 26547c245a5SManuel Klimek } // end namespace clang 26647c245a5SManuel Klimek 267