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 ::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 JSONCompilationDatabase * 203 JSONCompilationDatabase::loadFromFile(StringRef FilePath, 204 std::string &ErrorMessage) { 205 llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer; 206 llvm::error_code Result = 207 llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer); 208 if (Result != 0) { 209 ErrorMessage = "Error while opening JSON database: " + Result.message(); 210 return NULL; 211 } 212 llvm::OwningPtr<JSONCompilationDatabase> Database( 213 new JSONCompilationDatabase(DatabaseBuffer.take())); 214 if (!Database->parse(ErrorMessage)) 215 return NULL; 216 return Database.take(); 217 } 218 219 JSONCompilationDatabase * 220 JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString, 221 std::string &ErrorMessage) { 222 llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer( 223 llvm::MemoryBuffer::getMemBuffer(DatabaseString)); 224 llvm::OwningPtr<JSONCompilationDatabase> Database( 225 new JSONCompilationDatabase(DatabaseBuffer.take())); 226 if (!Database->parse(ErrorMessage)) 227 return NULL; 228 return Database.take(); 229 } 230 231 std::vector<CompileCommand> 232 JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { 233 llvm::SmallString<128> NativeFilePath; 234 llvm::sys::path::native(FilePath, NativeFilePath); 235 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 236 CommandsRefI = IndexByFile.find(NativeFilePath); 237 if (CommandsRefI == IndexByFile.end()) 238 return std::vector<CompileCommand>(); 239 const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue(); 240 std::vector<CompileCommand> Commands; 241 for (int I = 0, E = CommandsRef.size(); I != E; ++I) { 242 llvm::SmallString<8> DirectoryStorage; 243 llvm::SmallString<1024> CommandStorage; 244 Commands.push_back(CompileCommand( 245 // FIXME: Escape correctly: 246 CommandsRef[I].first->getValue(DirectoryStorage), 247 unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage)))); 248 } 249 return Commands; 250 } 251 252 bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { 253 llvm::yaml::document_iterator I = YAMLStream.begin(); 254 if (I == YAMLStream.end()) { 255 ErrorMessage = "Error while parsing YAML."; 256 return false; 257 } 258 llvm::yaml::Node *Root = I->getRoot(); 259 if (Root == NULL) { 260 ErrorMessage = "Error while parsing YAML."; 261 return false; 262 } 263 llvm::yaml::SequenceNode *Array = 264 llvm::dyn_cast<llvm::yaml::SequenceNode>(Root); 265 if (Array == NULL) { 266 ErrorMessage = "Expected array."; 267 return false; 268 } 269 for (llvm::yaml::SequenceNode::iterator AI = Array->begin(), 270 AE = Array->end(); 271 AI != AE; ++AI) { 272 llvm::yaml::MappingNode *Object = 273 llvm::dyn_cast<llvm::yaml::MappingNode>(&*AI); 274 if (Object == NULL) { 275 ErrorMessage = "Expected object."; 276 return false; 277 } 278 llvm::yaml::ScalarNode *Directory = NULL; 279 llvm::yaml::ScalarNode *Command = NULL; 280 llvm::yaml::ScalarNode *File = NULL; 281 for (llvm::yaml::MappingNode::iterator KVI = Object->begin(), 282 KVE = Object->end(); 283 KVI != KVE; ++KVI) { 284 llvm::yaml::Node *Value = (*KVI).getValue(); 285 if (Value == NULL) { 286 ErrorMessage = "Expected value."; 287 return false; 288 } 289 llvm::yaml::ScalarNode *ValueString = 290 llvm::dyn_cast<llvm::yaml::ScalarNode>(Value); 291 if (ValueString == NULL) { 292 ErrorMessage = "Expected string as value."; 293 return false; 294 } 295 llvm::yaml::ScalarNode *KeyString = 296 llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey()); 297 if (KeyString == NULL) { 298 ErrorMessage = "Expected strings as key."; 299 return false; 300 } 301 llvm::SmallString<8> KeyStorage; 302 if (KeyString->getValue(KeyStorage) == "directory") { 303 Directory = ValueString; 304 } else if (KeyString->getValue(KeyStorage) == "command") { 305 Command = ValueString; 306 } else if (KeyString->getValue(KeyStorage) == "file") { 307 File = ValueString; 308 } else { 309 ErrorMessage = ("Unknown key: \"" + 310 KeyString->getRawValue() + "\"").str(); 311 return false; 312 } 313 } 314 if (!File) { 315 ErrorMessage = "Missing key: \"file\"."; 316 return false; 317 } 318 if (!Command) { 319 ErrorMessage = "Missing key: \"command\"."; 320 return false; 321 } 322 if (!Directory) { 323 ErrorMessage = "Missing key: \"directory\"."; 324 return false; 325 } 326 llvm::SmallString<8> FileStorage; 327 llvm::SmallString<128> NativeFilePath; 328 llvm::sys::path::native(File->getValue(FileStorage), NativeFilePath); 329 IndexByFile[NativeFilePath].push_back( 330 CompileCommandRef(Directory, Command)); 331 } 332 return true; 333 } 334 335 } // end namespace tooling 336 } // end namespace clang 337