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 23namespace clang { 24namespace tooling { 25 26namespace { 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(...)). 32class 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 104std::vector<std::string> unescapeCommandLine( 105 StringRef EscapedCommandLine) { 106 CommandLineArgumentParser parser(EscapedCommandLine); 107 return parser.parse(); 108} 109 110} // end namespace 111 112class 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. 127static CompilationDatabasePluginRegistry::Add<JSONCompilationDatabasePlugin> 128X("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. 132volatile int JSONAnchorSource = 0; 133 134JSONCompilationDatabase * 135JSONCompilationDatabase::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 151JSONCompilationDatabase * 152JSONCompilationDatabase::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 163std::vector<CompileCommand> 164JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { 165 llvm::SmallString<128> NativeFilePath; 166 llvm::sys::path::native(FilePath, NativeFilePath); 167 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 168 CommandsRefI = IndexByFile.find(NativeFilePath); 169 if (CommandsRefI == IndexByFile.end()) 170 return std::vector<CompileCommand>(); 171 const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue(); 172 std::vector<CompileCommand> Commands; 173 for (int I = 0, E = CommandsRef.size(); I != E; ++I) { 174 llvm::SmallString<8> DirectoryStorage; 175 llvm::SmallString<1024> CommandStorage; 176 Commands.push_back(CompileCommand( 177 // FIXME: Escape correctly: 178 CommandsRef[I].first->getValue(DirectoryStorage), 179 unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage)))); 180 } 181 return Commands; 182} 183 184std::vector<std::string> 185JSONCompilationDatabase::getAllFiles() const { 186 std::vector<std::string> Result; 187 188 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 189 CommandsRefI = IndexByFile.begin(); 190 const llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 191 CommandsRefEnd = IndexByFile.end(); 192 for (; CommandsRefI != CommandsRefEnd; ++CommandsRefI) { 193 Result.push_back(CommandsRefI->first().str()); 194 } 195 196 return Result; 197} 198 199bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { 200 llvm::yaml::document_iterator I = YAMLStream.begin(); 201 if (I == YAMLStream.end()) { 202 ErrorMessage = "Error while parsing YAML."; 203 return false; 204 } 205 llvm::yaml::Node *Root = I->getRoot(); 206 if (Root == NULL) { 207 ErrorMessage = "Error while parsing YAML."; 208 return false; 209 } 210 llvm::yaml::SequenceNode *Array = 211 llvm::dyn_cast<llvm::yaml::SequenceNode>(Root); 212 if (Array == NULL) { 213 ErrorMessage = "Expected array."; 214 return false; 215 } 216 for (llvm::yaml::SequenceNode::iterator AI = Array->begin(), 217 AE = Array->end(); 218 AI != AE; ++AI) { 219 llvm::yaml::MappingNode *Object = 220 llvm::dyn_cast<llvm::yaml::MappingNode>(&*AI); 221 if (Object == NULL) { 222 ErrorMessage = "Expected object."; 223 return false; 224 } 225 llvm::yaml::ScalarNode *Directory = NULL; 226 llvm::yaml::ScalarNode *Command = NULL; 227 llvm::yaml::ScalarNode *File = NULL; 228 for (llvm::yaml::MappingNode::iterator KVI = Object->begin(), 229 KVE = Object->end(); 230 KVI != KVE; ++KVI) { 231 llvm::yaml::Node *Value = (*KVI).getValue(); 232 if (Value == NULL) { 233 ErrorMessage = "Expected value."; 234 return false; 235 } 236 llvm::yaml::ScalarNode *ValueString = 237 llvm::dyn_cast<llvm::yaml::ScalarNode>(Value); 238 if (ValueString == NULL) { 239 ErrorMessage = "Expected string as value."; 240 return false; 241 } 242 llvm::yaml::ScalarNode *KeyString = 243 llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey()); 244 if (KeyString == NULL) { 245 ErrorMessage = "Expected strings as key."; 246 return false; 247 } 248 llvm::SmallString<8> KeyStorage; 249 if (KeyString->getValue(KeyStorage) == "directory") { 250 Directory = ValueString; 251 } else if (KeyString->getValue(KeyStorage) == "command") { 252 Command = ValueString; 253 } else if (KeyString->getValue(KeyStorage) == "file") { 254 File = ValueString; 255 } else { 256 ErrorMessage = ("Unknown key: \"" + 257 KeyString->getRawValue() + "\"").str(); 258 return false; 259 } 260 } 261 if (!File) { 262 ErrorMessage = "Missing key: \"file\"."; 263 return false; 264 } 265 if (!Command) { 266 ErrorMessage = "Missing key: \"command\"."; 267 return false; 268 } 269 if (!Directory) { 270 ErrorMessage = "Missing key: \"directory\"."; 271 return false; 272 } 273 llvm::SmallString<8> FileStorage; 274 llvm::SmallString<128> NativeFilePath; 275 llvm::sys::path::native(File->getValue(FileStorage), NativeFilePath); 276 IndexByFile[NativeFilePath].push_back( 277 CompileCommandRef(Directory, Command)); 278 } 279 return true; 280} 281 282} // end namespace tooling 283} // end namespace clang 284