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