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 (!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 103 std::vector<std::string> unescapeCommandLine( 104 StringRef EscapedCommandLine) { 105 CommandLineArgumentParser parser(EscapedCommandLine); 106 return parser.parse(); 107 } 108 109 } // end namespace 110 111 class JSONCompilationDatabasePlugin : public CompilationDatabasePlugin { 112 virtual CompilationDatabase *loadFromDirectory( 113 StringRef Directory, std::string &ErrorMessage) { 114 llvm::SmallString<1024> JSONDatabasePath(Directory); 115 llvm::sys::path::append(JSONDatabasePath, "compile_commands.json"); 116 llvm::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. 126 static CompilationDatabasePluginRegistry::Add<JSONCompilationDatabasePlugin> 127 X("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. 131 volatile int JSONAnchorSource = 0; 132 133 JSONCompilationDatabase * 134 JSONCompilationDatabase::loadFromFile(StringRef FilePath, 135 std::string &ErrorMessage) { 136 llvm::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 llvm::OwningPtr<JSONCompilationDatabase> Database( 144 new JSONCompilationDatabase(DatabaseBuffer.take())); 145 if (!Database->parse(ErrorMessage)) 146 return NULL; 147 return Database.take(); 148 } 149 150 JSONCompilationDatabase * 151 JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString, 152 std::string &ErrorMessage) { 153 llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer( 154 llvm::MemoryBuffer::getMemBuffer(DatabaseString)); 155 llvm::OwningPtr<JSONCompilationDatabase> Database( 156 new JSONCompilationDatabase(DatabaseBuffer.take())); 157 if (!Database->parse(ErrorMessage)) 158 return NULL; 159 return Database.take(); 160 } 161 162 std::vector<CompileCommand> 163 JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { 164 llvm::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 if (Error.empty()) 172 Error = "No match found."; 173 llvm::outs() << Error << "\n"; 174 return std::vector<CompileCommand>(); 175 } 176 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 177 CommandsRefI = IndexByFile.find(Match); 178 if (CommandsRefI == IndexByFile.end()) 179 return std::vector<CompileCommand>(); 180 std::vector<CompileCommand> Commands; 181 getCommands(CommandsRefI->getValue(), Commands); 182 return Commands; 183 } 184 185 std::vector<std::string> 186 JSONCompilationDatabase::getAllFiles() const { 187 std::vector<std::string> Result; 188 189 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 190 CommandsRefI = IndexByFile.begin(); 191 const llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 192 CommandsRefEnd = IndexByFile.end(); 193 for (; CommandsRefI != CommandsRefEnd; ++CommandsRefI) { 194 Result.push_back(CommandsRefI->first().str()); 195 } 196 197 return Result; 198 } 199 200 std::vector<CompileCommand> 201 JSONCompilationDatabase::getAllCompileCommands() const { 202 std::vector<CompileCommand> Commands; 203 for (llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 204 CommandsRefI = IndexByFile.begin(), CommandsRefEnd = IndexByFile.end(); 205 CommandsRefI != CommandsRefEnd; ++CommandsRefI) { 206 getCommands(CommandsRefI->getValue(), Commands); 207 } 208 return Commands; 209 } 210 211 void JSONCompilationDatabase::getCommands( 212 ArrayRef<CompileCommandRef> CommandsRef, 213 std::vector<CompileCommand> &Commands) const { 214 for (int I = 0, E = CommandsRef.size(); I != E; ++I) { 215 llvm::SmallString<8> DirectoryStorage; 216 llvm::SmallString<1024> CommandStorage; 217 Commands.push_back(CompileCommand( 218 // FIXME: Escape correctly: 219 CommandsRef[I].first->getValue(DirectoryStorage), 220 unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage)))); 221 } 222 } 223 224 bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { 225 llvm::yaml::document_iterator I = YAMLStream.begin(); 226 if (I == YAMLStream.end()) { 227 ErrorMessage = "Error while parsing YAML."; 228 return false; 229 } 230 llvm::yaml::Node *Root = I->getRoot(); 231 if (Root == NULL) { 232 ErrorMessage = "Error while parsing YAML."; 233 return false; 234 } 235 llvm::yaml::SequenceNode *Array = 236 llvm::dyn_cast<llvm::yaml::SequenceNode>(Root); 237 if (Array == NULL) { 238 ErrorMessage = "Expected array."; 239 return false; 240 } 241 for (llvm::yaml::SequenceNode::iterator AI = Array->begin(), 242 AE = Array->end(); 243 AI != AE; ++AI) { 244 llvm::yaml::MappingNode *Object = 245 llvm::dyn_cast<llvm::yaml::MappingNode>(&*AI); 246 if (Object == NULL) { 247 ErrorMessage = "Expected object."; 248 return false; 249 } 250 llvm::yaml::ScalarNode *Directory = NULL; 251 llvm::yaml::ScalarNode *Command = NULL; 252 llvm::yaml::ScalarNode *File = NULL; 253 for (llvm::yaml::MappingNode::iterator KVI = Object->begin(), 254 KVE = Object->end(); 255 KVI != KVE; ++KVI) { 256 llvm::yaml::Node *Value = (*KVI).getValue(); 257 if (Value == NULL) { 258 ErrorMessage = "Expected value."; 259 return false; 260 } 261 llvm::yaml::ScalarNode *ValueString = 262 llvm::dyn_cast<llvm::yaml::ScalarNode>(Value); 263 if (ValueString == NULL) { 264 ErrorMessage = "Expected string as value."; 265 return false; 266 } 267 llvm::yaml::ScalarNode *KeyString = 268 llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey()); 269 if (KeyString == NULL) { 270 ErrorMessage = "Expected strings as key."; 271 return false; 272 } 273 llvm::SmallString<8> KeyStorage; 274 if (KeyString->getValue(KeyStorage) == "directory") { 275 Directory = ValueString; 276 } else if (KeyString->getValue(KeyStorage) == "command") { 277 Command = ValueString; 278 } else if (KeyString->getValue(KeyStorage) == "file") { 279 File = ValueString; 280 } else { 281 ErrorMessage = ("Unknown key: \"" + 282 KeyString->getRawValue() + "\"").str(); 283 return false; 284 } 285 } 286 if (!File) { 287 ErrorMessage = "Missing key: \"file\"."; 288 return false; 289 } 290 if (!Command) { 291 ErrorMessage = "Missing key: \"command\"."; 292 return false; 293 } 294 if (!Directory) { 295 ErrorMessage = "Missing key: \"directory\"."; 296 return false; 297 } 298 llvm::SmallString<8> FileStorage; 299 StringRef FileName = File->getValue(FileStorage); 300 llvm::SmallString<128> NativeFilePath; 301 if (llvm::sys::path::is_relative(FileName)) { 302 llvm::SmallString<8> DirectoryStorage; 303 llvm::SmallString<128> AbsolutePath( 304 Directory->getValue(DirectoryStorage)); 305 llvm::sys::path::append(AbsolutePath, FileName); 306 llvm::sys::path::native(AbsolutePath.str(), NativeFilePath); 307 } else { 308 llvm::sys::path::native(FileName, NativeFilePath); 309 } 310 IndexByFile[NativeFilePath].push_back( 311 CompileCommandRef(Directory, Command)); 312 MatchTrie.insert(NativeFilePath.str()); 313 } 314 return true; 315 } 316 317 } // end namespace tooling 318 } // end namespace clang 319