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