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