CompilationDatabase.cpp revision 068040ede2b2e6449d84273cc16e62fcd5e7e541
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 25namespace clang { 26namespace tooling { 27 28namespace { 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(...)). 34class 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 106std::vector<std::string> unescapeCommandLine( 107 StringRef EscapedCommandLine) { 108 CommandLineArgumentParser parser(EscapedCommandLine); 109 return parser.parse(); 110} 111 112} // end namespace 113 114CompilationDatabase::~CompilationDatabase() {} 115 116CompilationDatabase * 117CompilationDatabase::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 129static CompilationDatabase * 130findCompilationDatabaseFromDirectory(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 148CompilationDatabase * 149CompilationDatabase::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 162CompilationDatabase * 163CompilationDatabase::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 175FixedCompilationDatabase * 176FixedCompilationDatabase::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 187FixedCompilationDatabase:: 188FixedCompilationDatabase(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 195std::vector<CompileCommand> 196FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const { 197 std::vector<CompileCommand> Result(CompileCommands); 198 Result[0].CommandLine.push_back(FilePath); 199 return Result; 200} 201 202JSONCompilationDatabase * 203JSONCompilationDatabase::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 219JSONCompilationDatabase * 220JSONCompilationDatabase::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 231std::vector<CompileCommand> 232JSONCompilationDatabase::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 252bool 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