JSONCompilationDatabase.cpp revision cfa88f893915ceb8ae4ce2f17c46c24a4d67502f
1//===--- JSONCompilationDatabase.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 the implementation of the JSONCompilationDatabase. 11// 12//===----------------------------------------------------------------------===// 13 14#include "clang/Tooling/JSONCompilationDatabase.h" 15#include "clang/Tooling/CompilationDatabase.h" 16#include "clang/Tooling/CompilationDatabasePluginRegistry.h" 17#include "clang/Tooling/Tooling.h" 18#include "llvm/ADT/SmallString.h" 19#include "llvm/Support/Path.h" 20#include "llvm/Support/system_error.h" 21 22namespace clang { 23namespace tooling { 24 25namespace { 26 27/// \brief A parser for escaped strings of command line arguments. 28/// 29/// Assumes \-escaping for quoted arguments (see the documentation of 30/// unescapeCommandLine(...)). 31class CommandLineArgumentParser { 32 public: 33 CommandLineArgumentParser(StringRef CommandLine) 34 : Input(CommandLine), Position(Input.begin()-1) {} 35 36 std::vector<std::string> parse() { 37 bool HasMoreInput = true; 38 while (HasMoreInput && nextNonWhitespace()) { 39 std::string Argument; 40 HasMoreInput = parseStringInto(Argument); 41 CommandLine.push_back(Argument); 42 } 43 return CommandLine; 44 } 45 46 private: 47 // All private methods return true if there is more input available. 48 49 bool parseStringInto(std::string &String) { 50 do { 51 if (*Position == '"') { 52 if (!parseQuotedStringInto(String)) return false; 53 } else { 54 if (!parseFreeStringInto(String)) return false; 55 } 56 } while (*Position != ' '); 57 return true; 58 } 59 60 bool parseQuotedStringInto(std::string &String) { 61 if (!next()) return false; 62 while (*Position != '"') { 63 if (!skipEscapeCharacter()) return false; 64 String.push_back(*Position); 65 if (!next()) return false; 66 } 67 return next(); 68 } 69 70 bool parseFreeStringInto(std::string &String) { 71 do { 72 if (!skipEscapeCharacter()) return false; 73 String.push_back(*Position); 74 if (!next()) return false; 75 } while (*Position != ' ' && *Position != '"'); 76 return true; 77 } 78 79 bool skipEscapeCharacter() { 80 if (*Position == '\\') { 81 return next(); 82 } 83 return true; 84 } 85 86 bool nextNonWhitespace() { 87 do { 88 if (!next()) return false; 89 } while (*Position == ' '); 90 return true; 91 } 92 93 bool next() { 94 ++Position; 95 return Position != Input.end(); 96 } 97 98 const StringRef Input; 99 StringRef::iterator Position; 100 std::vector<std::string> CommandLine; 101}; 102 103std::vector<std::string> unescapeCommandLine( 104 StringRef EscapedCommandLine) { 105 CommandLineArgumentParser parser(EscapedCommandLine); 106 return parser.parse(); 107} 108 109} // end namespace 110 111class JSONCompilationDatabasePlugin : public CompilationDatabasePlugin { 112 virtual CompilationDatabase *loadFromDirectory( 113 StringRef Directory, std::string &ErrorMessage) { 114 SmallString<1024> JSONDatabasePath(Directory); 115 llvm::sys::path::append(JSONDatabasePath, "compile_commands.json"); 116 OwningPtr<CompilationDatabase> Database( 117 JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage)); 118 if (!Database) 119 return NULL; 120 return Database.take(); 121 } 122}; 123 124// Register the JSONCompilationDatabasePlugin with the 125// CompilationDatabasePluginRegistry using this statically initialized variable. 126static CompilationDatabasePluginRegistry::Add<JSONCompilationDatabasePlugin> 127X("json-compilation-database", "Reads JSON formatted compilation databases"); 128 129// This anchor is used to force the linker to link in the generated object file 130// and thus register the JSONCompilationDatabasePlugin. 131volatile int JSONAnchorSource = 0; 132 133JSONCompilationDatabase * 134JSONCompilationDatabase::loadFromFile(StringRef FilePath, 135 std::string &ErrorMessage) { 136 OwningPtr<llvm::MemoryBuffer> DatabaseBuffer; 137 llvm::error_code Result = 138 llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer); 139 if (Result != 0) { 140 ErrorMessage = "Error while opening JSON database: " + Result.message(); 141 return NULL; 142 } 143 OwningPtr<JSONCompilationDatabase> Database( 144 new JSONCompilationDatabase(DatabaseBuffer.take())); 145 if (!Database->parse(ErrorMessage)) 146 return NULL; 147 return Database.take(); 148} 149 150JSONCompilationDatabase * 151JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString, 152 std::string &ErrorMessage) { 153 OwningPtr<llvm::MemoryBuffer> DatabaseBuffer( 154 llvm::MemoryBuffer::getMemBuffer(DatabaseString)); 155 OwningPtr<JSONCompilationDatabase> Database( 156 new JSONCompilationDatabase(DatabaseBuffer.take())); 157 if (!Database->parse(ErrorMessage)) 158 return NULL; 159 return Database.take(); 160} 161 162std::vector<CompileCommand> 163JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { 164 SmallString<128> NativeFilePath; 165 llvm::sys::path::native(FilePath, NativeFilePath); 166 std::vector<StringRef> PossibleMatches; 167 std::string Error; 168 llvm::raw_string_ostream ES(Error); 169 StringRef Match = MatchTrie.findEquivalent(NativeFilePath.str(), ES); 170 if (Match.empty()) 171 return std::vector<CompileCommand>(); 172 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 173 CommandsRefI = IndexByFile.find(Match); 174 if (CommandsRefI == IndexByFile.end()) 175 return std::vector<CompileCommand>(); 176 std::vector<CompileCommand> Commands; 177 getCommands(CommandsRefI->getValue(), Commands); 178 return Commands; 179} 180 181std::vector<std::string> 182JSONCompilationDatabase::getAllFiles() const { 183 std::vector<std::string> Result; 184 185 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 186 CommandsRefI = IndexByFile.begin(); 187 const llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 188 CommandsRefEnd = IndexByFile.end(); 189 for (; CommandsRefI != CommandsRefEnd; ++CommandsRefI) { 190 Result.push_back(CommandsRefI->first().str()); 191 } 192 193 return Result; 194} 195 196std::vector<CompileCommand> 197JSONCompilationDatabase::getAllCompileCommands() const { 198 std::vector<CompileCommand> Commands; 199 for (llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 200 CommandsRefI = IndexByFile.begin(), CommandsRefEnd = IndexByFile.end(); 201 CommandsRefI != CommandsRefEnd; ++CommandsRefI) { 202 getCommands(CommandsRefI->getValue(), Commands); 203 } 204 return Commands; 205} 206 207void JSONCompilationDatabase::getCommands( 208 ArrayRef<CompileCommandRef> CommandsRef, 209 std::vector<CompileCommand> &Commands) const { 210 for (int I = 0, E = CommandsRef.size(); I != E; ++I) { 211 SmallString<8> DirectoryStorage; 212 SmallString<1024> CommandStorage; 213 Commands.push_back(CompileCommand( 214 // FIXME: Escape correctly: 215 CommandsRef[I].first->getValue(DirectoryStorage), 216 unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage)))); 217 } 218} 219 220bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { 221 llvm::yaml::document_iterator I = YAMLStream.begin(); 222 if (I == YAMLStream.end()) { 223 ErrorMessage = "Error while parsing YAML."; 224 return false; 225 } 226 llvm::yaml::Node *Root = I->getRoot(); 227 if (Root == NULL) { 228 ErrorMessage = "Error while parsing YAML."; 229 return false; 230 } 231 llvm::yaml::SequenceNode *Array = dyn_cast<llvm::yaml::SequenceNode>(Root); 232 if (Array == NULL) { 233 ErrorMessage = "Expected array."; 234 return false; 235 } 236 for (llvm::yaml::SequenceNode::iterator AI = Array->begin(), 237 AE = Array->end(); 238 AI != AE; ++AI) { 239 llvm::yaml::MappingNode *Object = dyn_cast<llvm::yaml::MappingNode>(&*AI); 240 if (Object == NULL) { 241 ErrorMessage = "Expected object."; 242 return false; 243 } 244 llvm::yaml::ScalarNode *Directory = NULL; 245 llvm::yaml::ScalarNode *Command = NULL; 246 llvm::yaml::ScalarNode *File = NULL; 247 for (llvm::yaml::MappingNode::iterator KVI = Object->begin(), 248 KVE = Object->end(); 249 KVI != KVE; ++KVI) { 250 llvm::yaml::Node *Value = (*KVI).getValue(); 251 if (Value == NULL) { 252 ErrorMessage = "Expected value."; 253 return false; 254 } 255 llvm::yaml::ScalarNode *ValueString = 256 dyn_cast<llvm::yaml::ScalarNode>(Value); 257 if (ValueString == NULL) { 258 ErrorMessage = "Expected string as value."; 259 return false; 260 } 261 llvm::yaml::ScalarNode *KeyString = 262 dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey()); 263 if (KeyString == NULL) { 264 ErrorMessage = "Expected strings as key."; 265 return false; 266 } 267 SmallString<8> KeyStorage; 268 if (KeyString->getValue(KeyStorage) == "directory") { 269 Directory = ValueString; 270 } else if (KeyString->getValue(KeyStorage) == "command") { 271 Command = ValueString; 272 } else if (KeyString->getValue(KeyStorage) == "file") { 273 File = ValueString; 274 } else { 275 ErrorMessage = ("Unknown key: \"" + 276 KeyString->getRawValue() + "\"").str(); 277 return false; 278 } 279 } 280 if (!File) { 281 ErrorMessage = "Missing key: \"file\"."; 282 return false; 283 } 284 if (!Command) { 285 ErrorMessage = "Missing key: \"command\"."; 286 return false; 287 } 288 if (!Directory) { 289 ErrorMessage = "Missing key: \"directory\"."; 290 return false; 291 } 292 SmallString<8> FileStorage; 293 StringRef FileName = File->getValue(FileStorage); 294 SmallString<128> NativeFilePath; 295 if (llvm::sys::path::is_relative(FileName)) { 296 SmallString<8> DirectoryStorage; 297 SmallString<128> AbsolutePath( 298 Directory->getValue(DirectoryStorage)); 299 llvm::sys::path::append(AbsolutePath, FileName); 300 llvm::sys::path::native(AbsolutePath.str(), NativeFilePath); 301 } else { 302 llvm::sys::path::native(FileName, NativeFilePath); 303 } 304 IndexByFile[NativeFilePath].push_back( 305 CompileCommandRef(Directory, Command)); 306 MatchTrie.insert(NativeFilePath.str()); 307 } 308 return true; 309} 310 311} // end namespace tooling 312} // end namespace clang 313