1243791Sdim//===--- JSONCompilationDatabase.cpp - ------------------------------------===// 2243791Sdim// 3243791Sdim// The LLVM Compiler Infrastructure 4243791Sdim// 5243791Sdim// This file is distributed under the University of Illinois Open Source 6243791Sdim// License. See LICENSE.TXT for details. 7243791Sdim// 8243791Sdim//===----------------------------------------------------------------------===// 9243791Sdim// 10243791Sdim// This file contains the implementation of the JSONCompilationDatabase. 11243791Sdim// 12243791Sdim//===----------------------------------------------------------------------===// 13243791Sdim 14243791Sdim#include "clang/Tooling/JSONCompilationDatabase.h" 15243791Sdim#include "clang/Tooling/CompilationDatabase.h" 16243791Sdim#include "clang/Tooling/CompilationDatabasePluginRegistry.h" 17243791Sdim#include "clang/Tooling/Tooling.h" 18243791Sdim#include "llvm/ADT/SmallString.h" 19243791Sdim#include "llvm/Support/Path.h" 20243791Sdim#include "llvm/Support/system_error.h" 21243791Sdim 22243791Sdimnamespace clang { 23243791Sdimnamespace tooling { 24243791Sdim 25243791Sdimnamespace { 26243791Sdim 27243791Sdim/// \brief A parser for escaped strings of command line arguments. 28243791Sdim/// 29243791Sdim/// Assumes \-escaping for quoted arguments (see the documentation of 30243791Sdim/// unescapeCommandLine(...)). 31243791Sdimclass CommandLineArgumentParser { 32243791Sdim public: 33243791Sdim CommandLineArgumentParser(StringRef CommandLine) 34243791Sdim : Input(CommandLine), Position(Input.begin()-1) {} 35243791Sdim 36243791Sdim std::vector<std::string> parse() { 37243791Sdim bool HasMoreInput = true; 38243791Sdim while (HasMoreInput && nextNonWhitespace()) { 39243791Sdim std::string Argument; 40243791Sdim HasMoreInput = parseStringInto(Argument); 41243791Sdim CommandLine.push_back(Argument); 42243791Sdim } 43243791Sdim return CommandLine; 44243791Sdim } 45243791Sdim 46243791Sdim private: 47243791Sdim // All private methods return true if there is more input available. 48243791Sdim 49243791Sdim bool parseStringInto(std::string &String) { 50243791Sdim do { 51243791Sdim if (*Position == '"') { 52249423Sdim if (!parseDoubleQuotedStringInto(String)) return false; 53249423Sdim } else if (*Position == '\'') { 54249423Sdim if (!parseSingleQuotedStringInto(String)) return false; 55243791Sdim } else { 56243791Sdim if (!parseFreeStringInto(String)) return false; 57243791Sdim } 58243791Sdim } while (*Position != ' '); 59243791Sdim return true; 60243791Sdim } 61243791Sdim 62249423Sdim bool parseDoubleQuotedStringInto(std::string &String) { 63243791Sdim if (!next()) return false; 64243791Sdim while (*Position != '"') { 65243791Sdim if (!skipEscapeCharacter()) return false; 66243791Sdim String.push_back(*Position); 67243791Sdim if (!next()) return false; 68243791Sdim } 69243791Sdim return next(); 70243791Sdim } 71243791Sdim 72249423Sdim bool parseSingleQuotedStringInto(std::string &String) { 73249423Sdim if (!next()) return false; 74249423Sdim while (*Position != '\'') { 75249423Sdim String.push_back(*Position); 76249423Sdim if (!next()) return false; 77249423Sdim } 78249423Sdim return next(); 79249423Sdim } 80249423Sdim 81243791Sdim bool parseFreeStringInto(std::string &String) { 82243791Sdim do { 83243791Sdim if (!skipEscapeCharacter()) return false; 84243791Sdim String.push_back(*Position); 85243791Sdim if (!next()) return false; 86249423Sdim } while (*Position != ' ' && *Position != '"' && *Position != '\''); 87243791Sdim return true; 88243791Sdim } 89243791Sdim 90243791Sdim bool skipEscapeCharacter() { 91243791Sdim if (*Position == '\\') { 92243791Sdim return next(); 93243791Sdim } 94243791Sdim return true; 95243791Sdim } 96243791Sdim 97243791Sdim bool nextNonWhitespace() { 98243791Sdim do { 99243791Sdim if (!next()) return false; 100243791Sdim } while (*Position == ' '); 101243791Sdim return true; 102243791Sdim } 103243791Sdim 104243791Sdim bool next() { 105243791Sdim ++Position; 106243791Sdim return Position != Input.end(); 107243791Sdim } 108243791Sdim 109243791Sdim const StringRef Input; 110243791Sdim StringRef::iterator Position; 111243791Sdim std::vector<std::string> CommandLine; 112243791Sdim}; 113243791Sdim 114243791Sdimstd::vector<std::string> unescapeCommandLine( 115243791Sdim StringRef EscapedCommandLine) { 116243791Sdim CommandLineArgumentParser parser(EscapedCommandLine); 117243791Sdim return parser.parse(); 118243791Sdim} 119243791Sdim 120243791Sdimclass JSONCompilationDatabasePlugin : public CompilationDatabasePlugin { 121243791Sdim virtual CompilationDatabase *loadFromDirectory( 122243791Sdim StringRef Directory, std::string &ErrorMessage) { 123249423Sdim SmallString<1024> JSONDatabasePath(Directory); 124243791Sdim llvm::sys::path::append(JSONDatabasePath, "compile_commands.json"); 125249423Sdim OwningPtr<CompilationDatabase> Database( 126243791Sdim JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage)); 127243791Sdim if (!Database) 128243791Sdim return NULL; 129243791Sdim return Database.take(); 130243791Sdim } 131243791Sdim}; 132243791Sdim 133263508Sdim} // end namespace 134263508Sdim 135243791Sdim// Register the JSONCompilationDatabasePlugin with the 136243791Sdim// CompilationDatabasePluginRegistry using this statically initialized variable. 137243791Sdimstatic CompilationDatabasePluginRegistry::Add<JSONCompilationDatabasePlugin> 138243791SdimX("json-compilation-database", "Reads JSON formatted compilation databases"); 139243791Sdim 140243791Sdim// This anchor is used to force the linker to link in the generated object file 141243791Sdim// and thus register the JSONCompilationDatabasePlugin. 142243791Sdimvolatile int JSONAnchorSource = 0; 143243791Sdim 144243791SdimJSONCompilationDatabase * 145243791SdimJSONCompilationDatabase::loadFromFile(StringRef FilePath, 146243791Sdim std::string &ErrorMessage) { 147249423Sdim OwningPtr<llvm::MemoryBuffer> DatabaseBuffer; 148243791Sdim llvm::error_code Result = 149243791Sdim llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer); 150243791Sdim if (Result != 0) { 151243791Sdim ErrorMessage = "Error while opening JSON database: " + Result.message(); 152243791Sdim return NULL; 153243791Sdim } 154249423Sdim OwningPtr<JSONCompilationDatabase> Database( 155243791Sdim new JSONCompilationDatabase(DatabaseBuffer.take())); 156243791Sdim if (!Database->parse(ErrorMessage)) 157243791Sdim return NULL; 158243791Sdim return Database.take(); 159243791Sdim} 160243791Sdim 161243791SdimJSONCompilationDatabase * 162243791SdimJSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString, 163243791Sdim std::string &ErrorMessage) { 164249423Sdim OwningPtr<llvm::MemoryBuffer> DatabaseBuffer( 165243791Sdim llvm::MemoryBuffer::getMemBuffer(DatabaseString)); 166249423Sdim OwningPtr<JSONCompilationDatabase> Database( 167249423Sdim new JSONCompilationDatabase(DatabaseBuffer.take())); 168243791Sdim if (!Database->parse(ErrorMessage)) 169243791Sdim return NULL; 170243791Sdim return Database.take(); 171243791Sdim} 172243791Sdim 173243791Sdimstd::vector<CompileCommand> 174243791SdimJSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { 175249423Sdim SmallString<128> NativeFilePath; 176243791Sdim llvm::sys::path::native(FilePath, NativeFilePath); 177243791Sdim std::vector<StringRef> PossibleMatches; 178243791Sdim std::string Error; 179243791Sdim llvm::raw_string_ostream ES(Error); 180243791Sdim StringRef Match = MatchTrie.findEquivalent(NativeFilePath.str(), ES); 181249423Sdim if (Match.empty()) 182243791Sdim return std::vector<CompileCommand>(); 183243791Sdim llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 184243791Sdim CommandsRefI = IndexByFile.find(Match); 185243791Sdim if (CommandsRefI == IndexByFile.end()) 186243791Sdim return std::vector<CompileCommand>(); 187243791Sdim std::vector<CompileCommand> Commands; 188249423Sdim getCommands(CommandsRefI->getValue(), Commands); 189243791Sdim return Commands; 190243791Sdim} 191243791Sdim 192243791Sdimstd::vector<std::string> 193243791SdimJSONCompilationDatabase::getAllFiles() const { 194243791Sdim std::vector<std::string> Result; 195243791Sdim 196243791Sdim llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 197243791Sdim CommandsRefI = IndexByFile.begin(); 198243791Sdim const llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 199243791Sdim CommandsRefEnd = IndexByFile.end(); 200243791Sdim for (; CommandsRefI != CommandsRefEnd; ++CommandsRefI) { 201243791Sdim Result.push_back(CommandsRefI->first().str()); 202243791Sdim } 203243791Sdim 204243791Sdim return Result; 205243791Sdim} 206243791Sdim 207249423Sdimstd::vector<CompileCommand> 208249423SdimJSONCompilationDatabase::getAllCompileCommands() const { 209249423Sdim std::vector<CompileCommand> Commands; 210249423Sdim for (llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 211249423Sdim CommandsRefI = IndexByFile.begin(), CommandsRefEnd = IndexByFile.end(); 212249423Sdim CommandsRefI != CommandsRefEnd; ++CommandsRefI) { 213249423Sdim getCommands(CommandsRefI->getValue(), Commands); 214249423Sdim } 215249423Sdim return Commands; 216249423Sdim} 217249423Sdim 218249423Sdimvoid JSONCompilationDatabase::getCommands( 219249423Sdim ArrayRef<CompileCommandRef> CommandsRef, 220249423Sdim std::vector<CompileCommand> &Commands) const { 221249423Sdim for (int I = 0, E = CommandsRef.size(); I != E; ++I) { 222249423Sdim SmallString<8> DirectoryStorage; 223249423Sdim SmallString<1024> CommandStorage; 224249423Sdim Commands.push_back(CompileCommand( 225249423Sdim // FIXME: Escape correctly: 226249423Sdim CommandsRef[I].first->getValue(DirectoryStorage), 227249423Sdim unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage)))); 228249423Sdim } 229249423Sdim} 230249423Sdim 231243791Sdimbool JSONCompilationDatabase::parse(std::string &ErrorMessage) { 232243791Sdim llvm::yaml::document_iterator I = YAMLStream.begin(); 233243791Sdim if (I == YAMLStream.end()) { 234243791Sdim ErrorMessage = "Error while parsing YAML."; 235243791Sdim return false; 236243791Sdim } 237243791Sdim llvm::yaml::Node *Root = I->getRoot(); 238243791Sdim if (Root == NULL) { 239243791Sdim ErrorMessage = "Error while parsing YAML."; 240243791Sdim return false; 241243791Sdim } 242249423Sdim llvm::yaml::SequenceNode *Array = dyn_cast<llvm::yaml::SequenceNode>(Root); 243243791Sdim if (Array == NULL) { 244243791Sdim ErrorMessage = "Expected array."; 245243791Sdim return false; 246243791Sdim } 247243791Sdim for (llvm::yaml::SequenceNode::iterator AI = Array->begin(), 248243791Sdim AE = Array->end(); 249243791Sdim AI != AE; ++AI) { 250249423Sdim llvm::yaml::MappingNode *Object = dyn_cast<llvm::yaml::MappingNode>(&*AI); 251243791Sdim if (Object == NULL) { 252243791Sdim ErrorMessage = "Expected object."; 253243791Sdim return false; 254243791Sdim } 255243791Sdim llvm::yaml::ScalarNode *Directory = NULL; 256243791Sdim llvm::yaml::ScalarNode *Command = NULL; 257243791Sdim llvm::yaml::ScalarNode *File = NULL; 258243791Sdim for (llvm::yaml::MappingNode::iterator KVI = Object->begin(), 259243791Sdim KVE = Object->end(); 260243791Sdim KVI != KVE; ++KVI) { 261243791Sdim llvm::yaml::Node *Value = (*KVI).getValue(); 262243791Sdim if (Value == NULL) { 263243791Sdim ErrorMessage = "Expected value."; 264243791Sdim return false; 265243791Sdim } 266243791Sdim llvm::yaml::ScalarNode *ValueString = 267249423Sdim dyn_cast<llvm::yaml::ScalarNode>(Value); 268243791Sdim if (ValueString == NULL) { 269243791Sdim ErrorMessage = "Expected string as value."; 270243791Sdim return false; 271243791Sdim } 272243791Sdim llvm::yaml::ScalarNode *KeyString = 273249423Sdim dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey()); 274243791Sdim if (KeyString == NULL) { 275243791Sdim ErrorMessage = "Expected strings as key."; 276243791Sdim return false; 277243791Sdim } 278249423Sdim SmallString<8> KeyStorage; 279243791Sdim if (KeyString->getValue(KeyStorage) == "directory") { 280243791Sdim Directory = ValueString; 281243791Sdim } else if (KeyString->getValue(KeyStorage) == "command") { 282243791Sdim Command = ValueString; 283243791Sdim } else if (KeyString->getValue(KeyStorage) == "file") { 284243791Sdim File = ValueString; 285243791Sdim } else { 286243791Sdim ErrorMessage = ("Unknown key: \"" + 287243791Sdim KeyString->getRawValue() + "\"").str(); 288243791Sdim return false; 289243791Sdim } 290243791Sdim } 291243791Sdim if (!File) { 292243791Sdim ErrorMessage = "Missing key: \"file\"."; 293243791Sdim return false; 294243791Sdim } 295243791Sdim if (!Command) { 296243791Sdim ErrorMessage = "Missing key: \"command\"."; 297243791Sdim return false; 298243791Sdim } 299243791Sdim if (!Directory) { 300243791Sdim ErrorMessage = "Missing key: \"directory\"."; 301243791Sdim return false; 302243791Sdim } 303249423Sdim SmallString<8> FileStorage; 304243791Sdim StringRef FileName = File->getValue(FileStorage); 305249423Sdim SmallString<128> NativeFilePath; 306243791Sdim if (llvm::sys::path::is_relative(FileName)) { 307249423Sdim SmallString<8> DirectoryStorage; 308249423Sdim SmallString<128> AbsolutePath( 309243791Sdim Directory->getValue(DirectoryStorage)); 310243791Sdim llvm::sys::path::append(AbsolutePath, FileName); 311243791Sdim llvm::sys::path::native(AbsolutePath.str(), NativeFilePath); 312243791Sdim } else { 313243791Sdim llvm::sys::path::native(FileName, NativeFilePath); 314243791Sdim } 315243791Sdim IndexByFile[NativeFilePath].push_back( 316243791Sdim CompileCommandRef(Directory, Command)); 317243791Sdim MatchTrie.insert(NativeFilePath.str()); 318243791Sdim } 319243791Sdim return true; 320243791Sdim} 321243791Sdim 322243791Sdim} // end namespace tooling 323243791Sdim} // end namespace clang 324