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