CompilationDatabase.cpp revision 235633
1234287Sdim//===--- CompilationDatabase.cpp - ----------------------------------------===// 2234287Sdim// 3234287Sdim// The LLVM Compiler Infrastructure 4234287Sdim// 5234287Sdim// This file is distributed under the University of Illinois Open Source 6234287Sdim// License. See LICENSE.TXT for details. 7234287Sdim// 8234287Sdim//===----------------------------------------------------------------------===// 9234287Sdim// 10234287Sdim// This file contains multiple implementations for CompilationDatabases. 11234287Sdim// 12234287Sdim//===----------------------------------------------------------------------===// 13234287Sdim 14234287Sdim#include "clang/Tooling/CompilationDatabase.h" 15234287Sdim#include "llvm/ADT/SmallString.h" 16235633Sdim#include "llvm/Support/YAMLParser.h" 17234287Sdim#include "llvm/Support/Path.h" 18234287Sdim#include "llvm/Support/system_error.h" 19234287Sdim 20234287Sdimnamespace clang { 21234287Sdimnamespace tooling { 22234287Sdim 23234287Sdimnamespace { 24234287Sdim 25235633Sdim/// \brief A parser for escaped strings of command line arguments. 26234287Sdim/// 27234287Sdim/// Assumes \-escaping for quoted arguments (see the documentation of 28235633Sdim/// unescapeCommandLine(...)). 29234287Sdimclass CommandLineArgumentParser { 30234287Sdim public: 31234287Sdim CommandLineArgumentParser(StringRef CommandLine) 32234287Sdim : Input(CommandLine), Position(Input.begin()-1) {} 33234287Sdim 34234287Sdim std::vector<std::string> parse() { 35234287Sdim bool HasMoreInput = true; 36234287Sdim while (HasMoreInput && nextNonWhitespace()) { 37234287Sdim std::string Argument; 38234287Sdim HasMoreInput = parseStringInto(Argument); 39234287Sdim CommandLine.push_back(Argument); 40234287Sdim } 41234287Sdim return CommandLine; 42234287Sdim } 43234287Sdim 44234287Sdim private: 45234287Sdim // All private methods return true if there is more input available. 46234287Sdim 47234287Sdim bool parseStringInto(std::string &String) { 48234287Sdim do { 49234287Sdim if (*Position == '"') { 50234287Sdim if (!parseQuotedStringInto(String)) return false; 51234287Sdim } else { 52234287Sdim if (!parseFreeStringInto(String)) return false; 53234287Sdim } 54234287Sdim } while (*Position != ' '); 55234287Sdim return true; 56234287Sdim } 57234287Sdim 58234287Sdim bool parseQuotedStringInto(std::string &String) { 59234287Sdim if (!next()) return false; 60234287Sdim while (*Position != '"') { 61234287Sdim if (!skipEscapeCharacter()) return false; 62234287Sdim String.push_back(*Position); 63234287Sdim if (!next()) return false; 64234287Sdim } 65234287Sdim return next(); 66234287Sdim } 67234287Sdim 68234287Sdim bool parseFreeStringInto(std::string &String) { 69234287Sdim do { 70234287Sdim if (!skipEscapeCharacter()) return false; 71234287Sdim String.push_back(*Position); 72234287Sdim if (!next()) return false; 73234287Sdim } while (*Position != ' ' && *Position != '"'); 74234287Sdim return true; 75234287Sdim } 76234287Sdim 77234287Sdim bool skipEscapeCharacter() { 78234287Sdim if (*Position == '\\') { 79234287Sdim return next(); 80234287Sdim } 81234287Sdim return true; 82234287Sdim } 83234287Sdim 84234287Sdim bool nextNonWhitespace() { 85234287Sdim do { 86234287Sdim if (!next()) return false; 87234287Sdim } while (*Position == ' '); 88234287Sdim return true; 89234287Sdim } 90234287Sdim 91234287Sdim bool next() { 92234287Sdim ++Position; 93234287Sdim return Position != Input.end(); 94234287Sdim } 95234287Sdim 96234287Sdim const StringRef Input; 97234287Sdim StringRef::iterator Position; 98234287Sdim std::vector<std::string> CommandLine; 99234287Sdim}; 100234287Sdim 101235633Sdimstd::vector<std::string> unescapeCommandLine( 102235633Sdim StringRef EscapedCommandLine) { 103235633Sdim CommandLineArgumentParser parser(EscapedCommandLine); 104234287Sdim return parser.parse(); 105234287Sdim} 106234287Sdim 107234287Sdim} // end namespace 108234287Sdim 109234287SdimCompilationDatabase::~CompilationDatabase() {} 110234287Sdim 111234287SdimCompilationDatabase * 112234287SdimCompilationDatabase::loadFromDirectory(StringRef BuildDirectory, 113234287Sdim std::string &ErrorMessage) { 114234287Sdim llvm::SmallString<1024> JSONDatabasePath(BuildDirectory); 115234287Sdim llvm::sys::path::append(JSONDatabasePath, "compile_commands.json"); 116234287Sdim llvm::OwningPtr<CompilationDatabase> Database( 117234287Sdim JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage)); 118234287Sdim if (!Database) { 119234287Sdim return NULL; 120234287Sdim } 121234287Sdim return Database.take(); 122234287Sdim} 123234287Sdim 124235633SdimFixedCompilationDatabase * 125235633SdimFixedCompilationDatabase::loadFromCommandLine(int &Argc, 126235633Sdim const char **Argv, 127235633Sdim Twine Directory) { 128235633Sdim const char **DoubleDash = std::find(Argv, Argv + Argc, StringRef("--")); 129235633Sdim if (DoubleDash == Argv + Argc) 130235633Sdim return NULL; 131235633Sdim std::vector<std::string> CommandLine(DoubleDash + 1, Argv + Argc); 132235633Sdim Argc = DoubleDash - Argv; 133235633Sdim return new FixedCompilationDatabase(Directory, CommandLine); 134235633Sdim} 135235633Sdim 136235633SdimFixedCompilationDatabase:: 137235633SdimFixedCompilationDatabase(Twine Directory, ArrayRef<std::string> CommandLine) { 138235633Sdim std::vector<std::string> ToolCommandLine(1, "clang-tool"); 139235633Sdim ToolCommandLine.insert(ToolCommandLine.end(), 140235633Sdim CommandLine.begin(), CommandLine.end()); 141235633Sdim CompileCommands.push_back(CompileCommand(Directory, ToolCommandLine)); 142235633Sdim} 143235633Sdim 144235633Sdimstd::vector<CompileCommand> 145235633SdimFixedCompilationDatabase::getCompileCommands(StringRef FilePath) const { 146235633Sdim std::vector<CompileCommand> Result(CompileCommands); 147235633Sdim Result[0].CommandLine.push_back(FilePath); 148235633Sdim return Result; 149235633Sdim} 150235633Sdim 151234287SdimJSONCompilationDatabase * 152234287SdimJSONCompilationDatabase::loadFromFile(StringRef FilePath, 153234287Sdim std::string &ErrorMessage) { 154234287Sdim llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer; 155234287Sdim llvm::error_code Result = 156234287Sdim llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer); 157234287Sdim if (Result != 0) { 158234287Sdim ErrorMessage = "Error while opening JSON database: " + Result.message(); 159234287Sdim return NULL; 160234287Sdim } 161234287Sdim llvm::OwningPtr<JSONCompilationDatabase> Database( 162234287Sdim new JSONCompilationDatabase(DatabaseBuffer.take())); 163234287Sdim if (!Database->parse(ErrorMessage)) 164234287Sdim return NULL; 165234287Sdim return Database.take(); 166234287Sdim} 167234287Sdim 168234287SdimJSONCompilationDatabase * 169234287SdimJSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString, 170234287Sdim std::string &ErrorMessage) { 171234287Sdim llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer( 172234287Sdim llvm::MemoryBuffer::getMemBuffer(DatabaseString)); 173234287Sdim llvm::OwningPtr<JSONCompilationDatabase> Database( 174234287Sdim new JSONCompilationDatabase(DatabaseBuffer.take())); 175234287Sdim if (!Database->parse(ErrorMessage)) 176234287Sdim return NULL; 177234287Sdim return Database.take(); 178234287Sdim} 179234287Sdim 180234287Sdimstd::vector<CompileCommand> 181234287SdimJSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { 182234287Sdim llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 183234287Sdim CommandsRefI = IndexByFile.find(FilePath); 184234287Sdim if (CommandsRefI == IndexByFile.end()) 185234287Sdim return std::vector<CompileCommand>(); 186234287Sdim const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue(); 187234287Sdim std::vector<CompileCommand> Commands; 188234287Sdim for (int I = 0, E = CommandsRef.size(); I != E; ++I) { 189235633Sdim llvm::SmallString<8> DirectoryStorage; 190235633Sdim llvm::SmallString<1024> CommandStorage; 191234287Sdim Commands.push_back(CompileCommand( 192234287Sdim // FIXME: Escape correctly: 193235633Sdim CommandsRef[I].first->getValue(DirectoryStorage), 194235633Sdim unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage)))); 195234287Sdim } 196234287Sdim return Commands; 197234287Sdim} 198234287Sdim 199234287Sdimbool JSONCompilationDatabase::parse(std::string &ErrorMessage) { 200235633Sdim llvm::yaml::document_iterator I = YAMLStream.begin(); 201235633Sdim if (I == YAMLStream.end()) { 202235633Sdim ErrorMessage = "Error while parsing YAML."; 203235633Sdim return false; 204235633Sdim } 205235633Sdim llvm::yaml::Node *Root = I->getRoot(); 206234287Sdim if (Root == NULL) { 207235633Sdim ErrorMessage = "Error while parsing YAML."; 208234287Sdim return false; 209234287Sdim } 210235633Sdim llvm::yaml::SequenceNode *Array = 211235633Sdim llvm::dyn_cast<llvm::yaml::SequenceNode>(Root); 212234287Sdim if (Array == NULL) { 213234287Sdim ErrorMessage = "Expected array."; 214234287Sdim return false; 215234287Sdim } 216235633Sdim for (llvm::yaml::SequenceNode::iterator AI = Array->begin(), 217235633Sdim AE = Array->end(); 218234287Sdim AI != AE; ++AI) { 219235633Sdim llvm::yaml::MappingNode *Object = 220235633Sdim llvm::dyn_cast<llvm::yaml::MappingNode>(&*AI); 221234287Sdim if (Object == NULL) { 222234287Sdim ErrorMessage = "Expected object."; 223234287Sdim return false; 224234287Sdim } 225235633Sdim llvm::yaml::ScalarNode *Directory; 226235633Sdim llvm::yaml::ScalarNode *Command; 227235633Sdim llvm::SmallString<8> FileStorage; 228235633Sdim llvm::StringRef File; 229235633Sdim for (llvm::yaml::MappingNode::iterator KVI = Object->begin(), 230235633Sdim KVE = Object->end(); 231234287Sdim KVI != KVE; ++KVI) { 232235633Sdim llvm::yaml::Node *Value = (*KVI).getValue(); 233234287Sdim if (Value == NULL) { 234234287Sdim ErrorMessage = "Expected value."; 235234287Sdim return false; 236234287Sdim } 237235633Sdim llvm::yaml::ScalarNode *ValueString = 238235633Sdim llvm::dyn_cast<llvm::yaml::ScalarNode>(Value); 239234287Sdim if (ValueString == NULL) { 240234287Sdim ErrorMessage = "Expected string as value."; 241234287Sdim return false; 242234287Sdim } 243235633Sdim llvm::yaml::ScalarNode *KeyString = 244235633Sdim llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey()); 245235633Sdim llvm::SmallString<8> KeyStorage; 246235633Sdim if (KeyString->getValue(KeyStorage) == "directory") { 247235633Sdim Directory = ValueString; 248235633Sdim } else if (KeyString->getValue(KeyStorage) == "command") { 249235633Sdim Command = ValueString; 250235633Sdim } else if (KeyString->getValue(KeyStorage) == "file") { 251235633Sdim File = ValueString->getValue(FileStorage); 252234287Sdim } else { 253235633Sdim ErrorMessage = ("Unknown key: \"" + 254235633Sdim KeyString->getRawValue() + "\"").str(); 255234287Sdim return false; 256234287Sdim } 257234287Sdim } 258235633Sdim IndexByFile[File].push_back( 259235633Sdim CompileCommandRef(Directory, Command)); 260234287Sdim } 261234287Sdim return true; 262234287Sdim} 263234287Sdim 264234287Sdim} // end namespace tooling 265234287Sdim} // end namespace clang 266234287Sdim 267