JSONCompilationDatabase.cpp revision 296417
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" 20276479Sdim#include <system_error> 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 { 121280031Sdim std::unique_ptr<CompilationDatabase> 122280031Sdim loadFromDirectory(StringRef Directory, std::string &ErrorMessage) override { 123249423Sdim SmallString<1024> JSONDatabasePath(Directory); 124243791Sdim llvm::sys::path::append(JSONDatabasePath, "compile_commands.json"); 125276479Sdim std::unique_ptr<CompilationDatabase> Database( 126243791Sdim JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage)); 127243791Sdim if (!Database) 128276479Sdim return nullptr; 129280031Sdim return Database; 130243791Sdim } 131243791Sdim}; 132243791Sdim 133261991Sdim} // end namespace 134261991Sdim 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 144280031Sdimstd::unique_ptr<JSONCompilationDatabase> 145243791SdimJSONCompilationDatabase::loadFromFile(StringRef FilePath, 146243791Sdim std::string &ErrorMessage) { 147276479Sdim llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> DatabaseBuffer = 148276479Sdim llvm::MemoryBuffer::getFile(FilePath); 149276479Sdim if (std::error_code Result = DatabaseBuffer.getError()) { 150243791Sdim ErrorMessage = "Error while opening JSON database: " + Result.message(); 151276479Sdim return nullptr; 152243791Sdim } 153276479Sdim std::unique_ptr<JSONCompilationDatabase> Database( 154280031Sdim new JSONCompilationDatabase(std::move(*DatabaseBuffer))); 155243791Sdim if (!Database->parse(ErrorMessage)) 156276479Sdim return nullptr; 157280031Sdim return Database; 158243791Sdim} 159243791Sdim 160280031Sdimstd::unique_ptr<JSONCompilationDatabase> 161243791SdimJSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString, 162243791Sdim std::string &ErrorMessage) { 163276479Sdim std::unique_ptr<llvm::MemoryBuffer> DatabaseBuffer( 164243791Sdim llvm::MemoryBuffer::getMemBuffer(DatabaseString)); 165276479Sdim std::unique_ptr<JSONCompilationDatabase> Database( 166280031Sdim new JSONCompilationDatabase(std::move(DatabaseBuffer))); 167243791Sdim if (!Database->parse(ErrorMessage)) 168276479Sdim return nullptr; 169280031Sdim return Database; 170243791Sdim} 171243791Sdim 172243791Sdimstd::vector<CompileCommand> 173243791SdimJSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { 174249423Sdim SmallString<128> NativeFilePath; 175243791Sdim llvm::sys::path::native(FilePath, NativeFilePath); 176276479Sdim 177243791Sdim std::string Error; 178243791Sdim llvm::raw_string_ostream ES(Error); 179288943Sdim StringRef Match = MatchTrie.findEquivalent(NativeFilePath, ES); 180249423Sdim if (Match.empty()) 181243791Sdim return std::vector<CompileCommand>(); 182243791Sdim llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 183243791Sdim CommandsRefI = IndexByFile.find(Match); 184243791Sdim if (CommandsRefI == IndexByFile.end()) 185243791Sdim return std::vector<CompileCommand>(); 186243791Sdim std::vector<CompileCommand> Commands; 187249423Sdim getCommands(CommandsRefI->getValue(), Commands); 188243791Sdim return Commands; 189243791Sdim} 190243791Sdim 191243791Sdimstd::vector<std::string> 192243791SdimJSONCompilationDatabase::getAllFiles() const { 193243791Sdim std::vector<std::string> Result; 194243791Sdim 195243791Sdim llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 196243791Sdim CommandsRefI = IndexByFile.begin(); 197243791Sdim const llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 198243791Sdim CommandsRefEnd = IndexByFile.end(); 199243791Sdim for (; CommandsRefI != CommandsRefEnd; ++CommandsRefI) { 200243791Sdim Result.push_back(CommandsRefI->first().str()); 201243791Sdim } 202243791Sdim 203243791Sdim return Result; 204243791Sdim} 205243791Sdim 206249423Sdimstd::vector<CompileCommand> 207249423SdimJSONCompilationDatabase::getAllCompileCommands() const { 208249423Sdim std::vector<CompileCommand> Commands; 209296417Sdim getCommands(AllCommands, Commands); 210249423Sdim return Commands; 211249423Sdim} 212249423Sdim 213296417Sdimstatic std::vector<std::string> 214296417SdimnodeToCommandLine(const std::vector<llvm::yaml::ScalarNode *> &Nodes) { 215296417Sdim SmallString<1024> Storage; 216296417Sdim if (Nodes.size() == 1) { 217296417Sdim return unescapeCommandLine(Nodes[0]->getValue(Storage)); 218296417Sdim } 219296417Sdim std::vector<std::string> Arguments; 220296417Sdim for (auto *Node : Nodes) { 221296417Sdim Arguments.push_back(Node->getValue(Storage)); 222296417Sdim } 223296417Sdim return Arguments; 224296417Sdim} 225296417Sdim 226249423Sdimvoid JSONCompilationDatabase::getCommands( 227296417Sdim ArrayRef<CompileCommandRef> CommandsRef, 228296417Sdim std::vector<CompileCommand> &Commands) const { 229249423Sdim for (int I = 0, E = CommandsRef.size(); I != E; ++I) { 230249423Sdim SmallString<8> DirectoryStorage; 231296417Sdim SmallString<32> FilenameStorage; 232288943Sdim Commands.emplace_back( 233296417Sdim std::get<0>(CommandsRef[I])->getValue(DirectoryStorage), 234296417Sdim std::get<1>(CommandsRef[I])->getValue(FilenameStorage), 235296417Sdim nodeToCommandLine(std::get<2>(CommandsRef[I]))); 236249423Sdim } 237249423Sdim} 238249423Sdim 239243791Sdimbool JSONCompilationDatabase::parse(std::string &ErrorMessage) { 240243791Sdim llvm::yaml::document_iterator I = YAMLStream.begin(); 241243791Sdim if (I == YAMLStream.end()) { 242243791Sdim ErrorMessage = "Error while parsing YAML."; 243243791Sdim return false; 244243791Sdim } 245243791Sdim llvm::yaml::Node *Root = I->getRoot(); 246276479Sdim if (!Root) { 247243791Sdim ErrorMessage = "Error while parsing YAML."; 248243791Sdim return false; 249243791Sdim } 250249423Sdim llvm::yaml::SequenceNode *Array = dyn_cast<llvm::yaml::SequenceNode>(Root); 251276479Sdim if (!Array) { 252243791Sdim ErrorMessage = "Expected array."; 253243791Sdim return false; 254243791Sdim } 255296417Sdim for (auto& NextObject : *Array) { 256296417Sdim llvm::yaml::MappingNode *Object = dyn_cast<llvm::yaml::MappingNode>(&NextObject); 257276479Sdim if (!Object) { 258243791Sdim ErrorMessage = "Expected object."; 259243791Sdim return false; 260243791Sdim } 261276479Sdim llvm::yaml::ScalarNode *Directory = nullptr; 262296417Sdim llvm::Optional<std::vector<llvm::yaml::ScalarNode *>> Command; 263276479Sdim llvm::yaml::ScalarNode *File = nullptr; 264296417Sdim for (auto& NextKeyValue : *Object) { 265296417Sdim llvm::yaml::ScalarNode *KeyString = 266296417Sdim dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey()); 267296417Sdim if (!KeyString) { 268296417Sdim ErrorMessage = "Expected strings as key."; 269296417Sdim return false; 270296417Sdim } 271296417Sdim SmallString<10> KeyStorage; 272296417Sdim StringRef KeyValue = KeyString->getValue(KeyStorage); 273296417Sdim llvm::yaml::Node *Value = NextKeyValue.getValue(); 274276479Sdim if (!Value) { 275243791Sdim ErrorMessage = "Expected value."; 276243791Sdim return false; 277243791Sdim } 278243791Sdim llvm::yaml::ScalarNode *ValueString = 279249423Sdim dyn_cast<llvm::yaml::ScalarNode>(Value); 280296417Sdim llvm::yaml::SequenceNode *SequenceString = 281296417Sdim dyn_cast<llvm::yaml::SequenceNode>(Value); 282296417Sdim if (KeyValue == "arguments" && !SequenceString) { 283296417Sdim ErrorMessage = "Expected sequence as value."; 284296417Sdim return false; 285296417Sdim } else if (KeyValue != "arguments" && !ValueString) { 286243791Sdim ErrorMessage = "Expected string as value."; 287243791Sdim return false; 288243791Sdim } 289296417Sdim if (KeyValue == "directory") { 290243791Sdim Directory = ValueString; 291296417Sdim } else if (KeyValue == "arguments") { 292296417Sdim Command = std::vector<llvm::yaml::ScalarNode *>(); 293296417Sdim for (auto &Argument : *SequenceString) { 294296417Sdim auto Scalar = dyn_cast<llvm::yaml::ScalarNode>(&Argument); 295296417Sdim if (!Scalar) { 296296417Sdim ErrorMessage = "Only strings are allowed in 'arguments'."; 297296417Sdim return false; 298296417Sdim } 299296417Sdim Command->push_back(Scalar); 300296417Sdim } 301296417Sdim } else if (KeyValue == "command") { 302296417Sdim if (!Command) 303296417Sdim Command = std::vector<llvm::yaml::ScalarNode *>(1, ValueString); 304296417Sdim } else if (KeyValue == "file") { 305243791Sdim File = ValueString; 306243791Sdim } else { 307243791Sdim ErrorMessage = ("Unknown key: \"" + 308243791Sdim KeyString->getRawValue() + "\"").str(); 309243791Sdim return false; 310243791Sdim } 311243791Sdim } 312243791Sdim if (!File) { 313243791Sdim ErrorMessage = "Missing key: \"file\"."; 314243791Sdim return false; 315243791Sdim } 316243791Sdim if (!Command) { 317296417Sdim ErrorMessage = "Missing key: \"command\" or \"arguments\"."; 318243791Sdim return false; 319243791Sdim } 320243791Sdim if (!Directory) { 321243791Sdim ErrorMessage = "Missing key: \"directory\"."; 322243791Sdim return false; 323243791Sdim } 324249423Sdim SmallString<8> FileStorage; 325243791Sdim StringRef FileName = File->getValue(FileStorage); 326249423Sdim SmallString<128> NativeFilePath; 327243791Sdim if (llvm::sys::path::is_relative(FileName)) { 328249423Sdim SmallString<8> DirectoryStorage; 329249423Sdim SmallString<128> AbsolutePath( 330243791Sdim Directory->getValue(DirectoryStorage)); 331243791Sdim llvm::sys::path::append(AbsolutePath, FileName); 332288943Sdim llvm::sys::path::native(AbsolutePath, NativeFilePath); 333243791Sdim } else { 334243791Sdim llvm::sys::path::native(FileName, NativeFilePath); 335243791Sdim } 336296417Sdim auto Cmd = CompileCommandRef(Directory, File, *Command); 337296417Sdim IndexByFile[NativeFilePath].push_back(Cmd); 338296417Sdim AllCommands.push_back(Cmd); 339288943Sdim MatchTrie.insert(NativeFilePath); 340243791Sdim } 341243791Sdim return true; 342243791Sdim} 343243791Sdim 344243791Sdim} // end namespace tooling 345243791Sdim} // end namespace clang 346