CompilationDatabase.cpp revision 1a8d6861278051b2109c98baf6a7478a6f3f98aa
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 20namespace clang { 21namespace tooling { 22 23namespace { 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(...)). 29class 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 101std::vector<std::string> unescapeCommandLine( 102 StringRef EscapedCommandLine) { 103 CommandLineArgumentParser parser(EscapedCommandLine); 104 return parser.parse(); 105} 106 107} // end namespace 108 109CompilationDatabase::~CompilationDatabase() {} 110 111CompilationDatabase * 112CompilationDatabase::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 124FixedCompilationDatabase * 125FixedCompilationDatabase::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 136FixedCompilationDatabase:: 137FixedCompilationDatabase(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 144std::vector<CompileCommand> 145FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const { 146 std::vector<CompileCommand> Result(CompileCommands); 147 Result[0].CommandLine.push_back(FilePath); 148 return Result; 149} 150 151JSONCompilationDatabase * 152JSONCompilationDatabase::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 168JSONCompilationDatabase * 169JSONCompilationDatabase::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 180std::vector<CompileCommand> 181JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { 182 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 183 CommandsRefI = IndexByFile.find(FilePath); 184 if (CommandsRefI == IndexByFile.end()) 185 return std::vector<CompileCommand>(); 186 const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue(); 187 std::vector<CompileCommand> Commands; 188 for (int I = 0, E = CommandsRef.size(); I != E; ++I) { 189 llvm::SmallString<8> DirectoryStorage; 190 llvm::SmallString<1024> CommandStorage; 191 Commands.push_back(CompileCommand( 192 // FIXME: Escape correctly: 193 CommandsRef[I].first->getValue(DirectoryStorage), 194 unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage)))); 195 } 196 return Commands; 197} 198 199bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { 200 llvm::yaml::document_iterator I = YAMLStream.begin(); 201 if (I == YAMLStream.end()) { 202 ErrorMessage = "Error while parsing YAML."; 203 return false; 204 } 205 llvm::yaml::Node *Root = I->getRoot(); 206 if (Root == NULL) { 207 ErrorMessage = "Error while parsing YAML."; 208 return false; 209 } 210 llvm::yaml::SequenceNode *Array = 211 llvm::dyn_cast<llvm::yaml::SequenceNode>(Root); 212 if (Array == NULL) { 213 ErrorMessage = "Expected array."; 214 return false; 215 } 216 for (llvm::yaml::SequenceNode::iterator AI = Array->begin(), 217 AE = Array->end(); 218 AI != AE; ++AI) { 219 llvm::yaml::MappingNode *Object = 220 llvm::dyn_cast<llvm::yaml::MappingNode>(&*AI); 221 if (Object == NULL) { 222 ErrorMessage = "Expected object."; 223 return false; 224 } 225 llvm::yaml::ScalarNode *Directory = NULL; 226 llvm::yaml::ScalarNode *Command = NULL; 227 llvm::yaml::ScalarNode *File = NULL; 228 for (llvm::yaml::MappingNode::iterator KVI = Object->begin(), 229 KVE = Object->end(); 230 KVI != KVE; ++KVI) { 231 llvm::yaml::Node *Value = (*KVI).getValue(); 232 if (Value == NULL) { 233 ErrorMessage = "Expected value."; 234 return false; 235 } 236 llvm::yaml::ScalarNode *ValueString = 237 llvm::dyn_cast<llvm::yaml::ScalarNode>(Value); 238 if (ValueString == NULL) { 239 ErrorMessage = "Expected string as value."; 240 return false; 241 } 242 llvm::yaml::ScalarNode *KeyString = 243 llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey()); 244 if (KeyString == NULL) { 245 ErrorMessage = "Expected strings as key."; 246 return false; 247 } 248 llvm::SmallString<8> KeyStorage; 249 if (KeyString->getValue(KeyStorage) == "directory") { 250 Directory = ValueString; 251 } else if (KeyString->getValue(KeyStorage) == "command") { 252 Command = ValueString; 253 } else if (KeyString->getValue(KeyStorage) == "file") { 254 File = ValueString; 255 } else { 256 ErrorMessage = ("Unknown key: \"" + 257 KeyString->getRawValue() + "\"").str(); 258 return false; 259 } 260 } 261 if (!File) { 262 ErrorMessage = "Missing key: \"file\"."; 263 return false; 264 } 265 if (!Command) { 266 ErrorMessage = "Missing key: \"command\"."; 267 return false; 268 } 269 if (!Directory) { 270 ErrorMessage = "Missing key: \"directory\"."; 271 return false; 272 } 273 llvm::SmallString<8> FileStorage; 274 IndexByFile[File->getValue(FileStorage)].push_back( 275 CompileCommandRef(Directory, Command)); 276 } 277 return true; 278} 279 280} // end namespace tooling 281} // end namespace clang 282 283