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 const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue(); 182 std::vector<CompileCommand> Commands; 183 for (int I = 0, E = CommandsRef.size(); I != E; ++I) { 184 llvm::SmallString<8> DirectoryStorage; 185 llvm::SmallString<1024> CommandStorage; 186 Commands.push_back(CompileCommand( 187 // FIXME: Escape correctly: 188 CommandsRef[I].first->getValue(DirectoryStorage), 189 unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage)))); 190 } 191 return Commands; 192 } 193 194 std::vector<std::string> 195 JSONCompilationDatabase::getAllFiles() const { 196 std::vector<std::string> Result; 197 198 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 199 CommandsRefI = IndexByFile.begin(); 200 const llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 201 CommandsRefEnd = IndexByFile.end(); 202 for (; CommandsRefI != CommandsRefEnd; ++CommandsRefI) { 203 Result.push_back(CommandsRefI->first().str()); 204 } 205 206 return Result; 207 } 208 209 bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { 210 llvm::yaml::document_iterator I = YAMLStream.begin(); 211 if (I == YAMLStream.end()) { 212 ErrorMessage = "Error while parsing YAML."; 213 return false; 214 } 215 llvm::yaml::Node *Root = I->getRoot(); 216 if (Root == NULL) { 217 ErrorMessage = "Error while parsing YAML."; 218 return false; 219 } 220 llvm::yaml::SequenceNode *Array = 221 llvm::dyn_cast<llvm::yaml::SequenceNode>(Root); 222 if (Array == NULL) { 223 ErrorMessage = "Expected array."; 224 return false; 225 } 226 for (llvm::yaml::SequenceNode::iterator AI = Array->begin(), 227 AE = Array->end(); 228 AI != AE; ++AI) { 229 llvm::yaml::MappingNode *Object = 230 llvm::dyn_cast<llvm::yaml::MappingNode>(&*AI); 231 if (Object == NULL) { 232 ErrorMessage = "Expected object."; 233 return false; 234 } 235 llvm::yaml::ScalarNode *Directory = NULL; 236 llvm::yaml::ScalarNode *Command = NULL; 237 llvm::yaml::ScalarNode *File = NULL; 238 for (llvm::yaml::MappingNode::iterator KVI = Object->begin(), 239 KVE = Object->end(); 240 KVI != KVE; ++KVI) { 241 llvm::yaml::Node *Value = (*KVI).getValue(); 242 if (Value == NULL) { 243 ErrorMessage = "Expected value."; 244 return false; 245 } 246 llvm::yaml::ScalarNode *ValueString = 247 llvm::dyn_cast<llvm::yaml::ScalarNode>(Value); 248 if (ValueString == NULL) { 249 ErrorMessage = "Expected string as value."; 250 return false; 251 } 252 llvm::yaml::ScalarNode *KeyString = 253 llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey()); 254 if (KeyString == NULL) { 255 ErrorMessage = "Expected strings as key."; 256 return false; 257 } 258 llvm::SmallString<8> KeyStorage; 259 if (KeyString->getValue(KeyStorage) == "directory") { 260 Directory = ValueString; 261 } else if (KeyString->getValue(KeyStorage) == "command") { 262 Command = ValueString; 263 } else if (KeyString->getValue(KeyStorage) == "file") { 264 File = ValueString; 265 } else { 266 ErrorMessage = ("Unknown key: \"" + 267 KeyString->getRawValue() + "\"").str(); 268 return false; 269 } 270 } 271 if (!File) { 272 ErrorMessage = "Missing key: \"file\"."; 273 return false; 274 } 275 if (!Command) { 276 ErrorMessage = "Missing key: \"command\"."; 277 return false; 278 } 279 if (!Directory) { 280 ErrorMessage = "Missing key: \"directory\"."; 281 return false; 282 } 283 llvm::SmallString<8> FileStorage; 284 StringRef FileName = File->getValue(FileStorage); 285 llvm::SmallString<128> NativeFilePath; 286 if (llvm::sys::path::is_relative(FileName)) { 287 llvm::SmallString<8> DirectoryStorage; 288 llvm::SmallString<128> AbsolutePath( 289 Directory->getValue(DirectoryStorage)); 290 llvm::sys::path::append(AbsolutePath, FileName); 291 llvm::sys::path::native(AbsolutePath.str(), NativeFilePath); 292 } else { 293 llvm::sys::path::native(FileName, NativeFilePath); 294 } 295 IndexByFile[NativeFilePath].push_back( 296 CompileCommandRef(Directory, Command)); 297 MatchTrie.insert(NativeFilePath.str()); 298 } 299 return true; 300 } 301 302 } // end namespace tooling 303 } // end namespace clang 304