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 <system_error> 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 std::unique_ptr<CompilationDatabase> 122 loadFromDirectory(StringRef Directory, std::string &ErrorMessage) override { 123 SmallString<1024> JSONDatabasePath(Directory); 124 llvm::sys::path::append(JSONDatabasePath, "compile_commands.json"); 125 std::unique_ptr<CompilationDatabase> Database( 126 JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage)); 127 if (!Database) 128 return nullptr; 129 return Database; 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 std::unique_ptr<JSONCompilationDatabase> 145 JSONCompilationDatabase::loadFromFile(StringRef FilePath, 146 std::string &ErrorMessage) { 147 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> DatabaseBuffer = 148 llvm::MemoryBuffer::getFile(FilePath); 149 if (std::error_code Result = DatabaseBuffer.getError()) { 150 ErrorMessage = "Error while opening JSON database: " + Result.message(); 151 return nullptr; 152 } 153 std::unique_ptr<JSONCompilationDatabase> Database( 154 new JSONCompilationDatabase(std::move(*DatabaseBuffer))); 155 if (!Database->parse(ErrorMessage)) 156 return nullptr; 157 return Database; 158 } 159 160 std::unique_ptr<JSONCompilationDatabase> 161 JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString, 162 std::string &ErrorMessage) { 163 std::unique_ptr<llvm::MemoryBuffer> DatabaseBuffer( 164 llvm::MemoryBuffer::getMemBuffer(DatabaseString)); 165 std::unique_ptr<JSONCompilationDatabase> Database( 166 new JSONCompilationDatabase(std::move(DatabaseBuffer))); 167 if (!Database->parse(ErrorMessage)) 168 return nullptr; 169 return Database; 170 } 171 172 std::vector<CompileCommand> 173 JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { 174 SmallString<128> NativeFilePath; 175 llvm::sys::path::native(FilePath, NativeFilePath); 176 177 std::string Error; 178 llvm::raw_string_ostream ES(Error); 179 StringRef Match = MatchTrie.findEquivalent(NativeFilePath, ES); 180 if (Match.empty()) 181 return std::vector<CompileCommand>(); 182 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 183 CommandsRefI = IndexByFile.find(Match); 184 if (CommandsRefI == IndexByFile.end()) 185 return std::vector<CompileCommand>(); 186 std::vector<CompileCommand> Commands; 187 getCommands(CommandsRefI->getValue(), Commands); 188 return Commands; 189 } 190 191 std::vector<std::string> 192 JSONCompilationDatabase::getAllFiles() const { 193 std::vector<std::string> Result; 194 195 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 196 CommandsRefI = IndexByFile.begin(); 197 const llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 198 CommandsRefEnd = IndexByFile.end(); 199 for (; CommandsRefI != CommandsRefEnd; ++CommandsRefI) { 200 Result.push_back(CommandsRefI->first().str()); 201 } 202 203 return Result; 204 } 205 206 std::vector<CompileCommand> 207 JSONCompilationDatabase::getAllCompileCommands() const { 208 std::vector<CompileCommand> Commands; 209 for (llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 210 CommandsRefI = IndexByFile.begin(), CommandsRefEnd = IndexByFile.end(); 211 CommandsRefI != CommandsRefEnd; ++CommandsRefI) { 212 getCommands(CommandsRefI->getValue(), Commands); 213 } 214 return Commands; 215 } 216 217 static std::vector<std::string> 218 nodeToCommandLine(const std::vector<llvm::yaml::ScalarNode *> &Nodes) { 219 SmallString<1024> Storage; 220 if (Nodes.size() == 1) { 221 return unescapeCommandLine(Nodes[0]->getValue(Storage)); 222 } 223 std::vector<std::string> Arguments; 224 for (auto *Node : Nodes) { 225 Arguments.push_back(Node->getValue(Storage)); 226 } 227 return Arguments; 228 } 229 230 void JSONCompilationDatabase::getCommands( 231 ArrayRef<CompileCommandRef> CommandsRef, 232 std::vector<CompileCommand> &Commands) const { 233 for (int I = 0, E = CommandsRef.size(); I != E; ++I) { 234 SmallString<8> DirectoryStorage; 235 SmallString<32> FilenameStorage; 236 Commands.emplace_back( 237 std::get<0>(CommandsRef[I])->getValue(DirectoryStorage), 238 std::get<1>(CommandsRef[I])->getValue(FilenameStorage), 239 nodeToCommandLine(std::get<2>(CommandsRef[I]))); 240 } 241 } 242 243 bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { 244 llvm::yaml::document_iterator I = YAMLStream.begin(); 245 if (I == YAMLStream.end()) { 246 ErrorMessage = "Error while parsing YAML."; 247 return false; 248 } 249 llvm::yaml::Node *Root = I->getRoot(); 250 if (!Root) { 251 ErrorMessage = "Error while parsing YAML."; 252 return false; 253 } 254 llvm::yaml::SequenceNode *Array = dyn_cast<llvm::yaml::SequenceNode>(Root); 255 if (!Array) { 256 ErrorMessage = "Expected array."; 257 return false; 258 } 259 for (auto& NextObject : *Array) { 260 llvm::yaml::MappingNode *Object = dyn_cast<llvm::yaml::MappingNode>(&NextObject); 261 if (!Object) { 262 ErrorMessage = "Expected object."; 263 return false; 264 } 265 llvm::yaml::ScalarNode *Directory = nullptr; 266 llvm::Optional<std::vector<llvm::yaml::ScalarNode *>> Command; 267 llvm::yaml::ScalarNode *File = nullptr; 268 for (auto& NextKeyValue : *Object) { 269 llvm::yaml::ScalarNode *KeyString = 270 dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey()); 271 if (!KeyString) { 272 ErrorMessage = "Expected strings as key."; 273 return false; 274 } 275 SmallString<10> KeyStorage; 276 StringRef KeyValue = KeyString->getValue(KeyStorage); 277 llvm::yaml::Node *Value = NextKeyValue.getValue(); 278 if (!Value) { 279 ErrorMessage = "Expected value."; 280 return false; 281 } 282 llvm::yaml::ScalarNode *ValueString = 283 dyn_cast<llvm::yaml::ScalarNode>(Value); 284 llvm::yaml::SequenceNode *SequenceString = 285 dyn_cast<llvm::yaml::SequenceNode>(Value); 286 if (KeyValue == "arguments" && !SequenceString) { 287 ErrorMessage = "Expected sequence as value."; 288 return false; 289 } else if (KeyValue != "arguments" && !ValueString) { 290 ErrorMessage = "Expected string as value."; 291 return false; 292 } 293 if (KeyValue == "directory") { 294 Directory = ValueString; 295 } else if (KeyValue == "arguments") { 296 Command = std::vector<llvm::yaml::ScalarNode *>(); 297 for (auto &Argument : *SequenceString) { 298 auto Scalar = dyn_cast<llvm::yaml::ScalarNode>(&Argument); 299 if (!Scalar) { 300 ErrorMessage = "Only strings are allowed in 'arguments'."; 301 return false; 302 } 303 Command->push_back(Scalar); 304 } 305 } else if (KeyValue == "command") { 306 if (!Command) 307 Command = std::vector<llvm::yaml::ScalarNode *>(1, ValueString); 308 } else if (KeyValue == "file") { 309 File = ValueString; 310 } else { 311 ErrorMessage = ("Unknown key: \"" + 312 KeyString->getRawValue() + "\"").str(); 313 return false; 314 } 315 } 316 if (!File) { 317 ErrorMessage = "Missing key: \"file\"."; 318 return false; 319 } 320 if (!Command) { 321 ErrorMessage = "Missing key: \"command\" or \"arguments\"."; 322 return false; 323 } 324 if (!Directory) { 325 ErrorMessage = "Missing key: \"directory\"."; 326 return false; 327 } 328 SmallString<8> FileStorage; 329 StringRef FileName = File->getValue(FileStorage); 330 SmallString<128> NativeFilePath; 331 if (llvm::sys::path::is_relative(FileName)) { 332 SmallString<8> DirectoryStorage; 333 SmallString<128> AbsolutePath( 334 Directory->getValue(DirectoryStorage)); 335 llvm::sys::path::append(AbsolutePath, FileName); 336 llvm::sys::path::native(AbsolutePath, NativeFilePath); 337 } else { 338 llvm::sys::path::native(FileName, NativeFilePath); 339 } 340 IndexByFile[NativeFilePath].push_back( 341 CompileCommandRef(Directory, File, *Command)); 342 MatchTrie.insert(NativeFilePath); 343 } 344 return true; 345 } 346 347 } // end namespace tooling 348 } // end namespace clang 349