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