CompilationDatabase.cpp revision 234287
1//===--- CompilationDatabase.cpp - ----------------------------------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file contains multiple implementations for CompilationDatabases. 11// 12//===----------------------------------------------------------------------===// 13 14#include "clang/Tooling/CompilationDatabase.h" 15#include "llvm/ADT/SmallString.h" 16#include "llvm/Support/JSONParser.h" 17#include "llvm/Support/Path.h" 18#include "llvm/Support/system_error.h" 19 20namespace clang { 21namespace tooling { 22 23namespace { 24 25/// \brief A parser for JSON escaped strings of command line arguments. 26/// 27/// Assumes \-escaping for quoted arguments (see the documentation of 28/// unescapeJSONCommandLine(...)). 29class CommandLineArgumentParser { 30 public: 31 CommandLineArgumentParser(StringRef CommandLine) 32 : Input(CommandLine), Position(Input.begin()-1) {} 33 34 std::vector<std::string> parse() { 35 bool HasMoreInput = true; 36 while (HasMoreInput && nextNonWhitespace()) { 37 std::string Argument; 38 HasMoreInput = parseStringInto(Argument); 39 CommandLine.push_back(Argument); 40 } 41 return CommandLine; 42 } 43 44 private: 45 // All private methods return true if there is more input available. 46 47 bool parseStringInto(std::string &String) { 48 do { 49 if (*Position == '"') { 50 if (!parseQuotedStringInto(String)) return false; 51 } else { 52 if (!parseFreeStringInto(String)) return false; 53 } 54 } while (*Position != ' '); 55 return true; 56 } 57 58 bool parseQuotedStringInto(std::string &String) { 59 if (!next()) return false; 60 while (*Position != '"') { 61 if (!skipEscapeCharacter()) return false; 62 String.push_back(*Position); 63 if (!next()) return false; 64 } 65 return next(); 66 } 67 68 bool parseFreeStringInto(std::string &String) { 69 do { 70 if (!skipEscapeCharacter()) return false; 71 String.push_back(*Position); 72 if (!next()) return false; 73 } while (*Position != ' ' && *Position != '"'); 74 return true; 75 } 76 77 bool skipEscapeCharacter() { 78 if (*Position == '\\') { 79 return next(); 80 } 81 return true; 82 } 83 84 bool nextNonWhitespace() { 85 do { 86 if (!next()) return false; 87 } while (*Position == ' '); 88 return true; 89 } 90 91 bool next() { 92 ++Position; 93 if (Position == Input.end()) return false; 94 // Remove the JSON escaping first. This is done unconditionally. 95 if (*Position == '\\') ++Position; 96 return Position != Input.end(); 97 } 98 99 const StringRef Input; 100 StringRef::iterator Position; 101 std::vector<std::string> CommandLine; 102}; 103 104std::vector<std::string> unescapeJSONCommandLine( 105 StringRef JSONEscapedCommandLine) { 106 CommandLineArgumentParser parser(JSONEscapedCommandLine); 107 return parser.parse(); 108} 109 110} // end namespace 111 112CompilationDatabase::~CompilationDatabase() {} 113 114CompilationDatabase * 115CompilationDatabase::loadFromDirectory(StringRef BuildDirectory, 116 std::string &ErrorMessage) { 117 llvm::SmallString<1024> JSONDatabasePath(BuildDirectory); 118 llvm::sys::path::append(JSONDatabasePath, "compile_commands.json"); 119 llvm::OwningPtr<CompilationDatabase> Database( 120 JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage)); 121 if (!Database) { 122 return NULL; 123 } 124 return Database.take(); 125} 126 127JSONCompilationDatabase * 128JSONCompilationDatabase::loadFromFile(StringRef FilePath, 129 std::string &ErrorMessage) { 130 llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer; 131 llvm::error_code Result = 132 llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer); 133 if (Result != 0) { 134 ErrorMessage = "Error while opening JSON database: " + Result.message(); 135 return NULL; 136 } 137 llvm::OwningPtr<JSONCompilationDatabase> Database( 138 new JSONCompilationDatabase(DatabaseBuffer.take())); 139 if (!Database->parse(ErrorMessage)) 140 return NULL; 141 return Database.take(); 142} 143 144JSONCompilationDatabase * 145JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString, 146 std::string &ErrorMessage) { 147 llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer( 148 llvm::MemoryBuffer::getMemBuffer(DatabaseString)); 149 llvm::OwningPtr<JSONCompilationDatabase> Database( 150 new JSONCompilationDatabase(DatabaseBuffer.take())); 151 if (!Database->parse(ErrorMessage)) 152 return NULL; 153 return Database.take(); 154} 155 156std::vector<CompileCommand> 157JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { 158 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 159 CommandsRefI = IndexByFile.find(FilePath); 160 if (CommandsRefI == IndexByFile.end()) 161 return std::vector<CompileCommand>(); 162 const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue(); 163 std::vector<CompileCommand> Commands; 164 for (int I = 0, E = CommandsRef.size(); I != E; ++I) { 165 Commands.push_back(CompileCommand( 166 // FIXME: Escape correctly: 167 CommandsRef[I].first, 168 unescapeJSONCommandLine(CommandsRef[I].second))); 169 } 170 return Commands; 171} 172 173bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { 174 llvm::SourceMgr SM; 175 llvm::JSONParser Parser(Database->getBuffer(), &SM); 176 llvm::JSONValue *Root = Parser.parseRoot(); 177 if (Root == NULL) { 178 ErrorMessage = "Error while parsing JSON."; 179 return false; 180 } 181 llvm::JSONArray *Array = dyn_cast<llvm::JSONArray>(Root); 182 if (Array == NULL) { 183 ErrorMessage = "Expected array."; 184 return false; 185 } 186 for (llvm::JSONArray::const_iterator AI = Array->begin(), AE = Array->end(); 187 AI != AE; ++AI) { 188 const llvm::JSONObject *Object = dyn_cast<llvm::JSONObject>(*AI); 189 if (Object == NULL) { 190 ErrorMessage = "Expected object."; 191 return false; 192 } 193 StringRef EntryDirectory; 194 StringRef EntryFile; 195 StringRef EntryCommand; 196 for (llvm::JSONObject::const_iterator KVI = Object->begin(), 197 KVE = Object->end(); 198 KVI != KVE; ++KVI) { 199 const llvm::JSONValue *Value = (*KVI)->Value; 200 if (Value == NULL) { 201 ErrorMessage = "Expected value."; 202 return false; 203 } 204 const llvm::JSONString *ValueString = 205 dyn_cast<llvm::JSONString>(Value); 206 if (ValueString == NULL) { 207 ErrorMessage = "Expected string as value."; 208 return false; 209 } 210 if ((*KVI)->Key->getRawText() == "directory") { 211 EntryDirectory = ValueString->getRawText(); 212 } else if ((*KVI)->Key->getRawText() == "file") { 213 EntryFile = ValueString->getRawText(); 214 } else if ((*KVI)->Key->getRawText() == "command") { 215 EntryCommand = ValueString->getRawText(); 216 } else { 217 ErrorMessage = (Twine("Unknown key: \"") + 218 (*KVI)->Key->getRawText() + "\"").str(); 219 return false; 220 } 221 } 222 IndexByFile[EntryFile].push_back( 223 CompileCommandRef(EntryDirectory, EntryCommand)); 224 } 225 return true; 226} 227 228} // end namespace tooling 229} // end namespace clang 230 231