CompilationDatabase.cpp revision c661f1467a84a0a3a83a396b067188b647844ee9
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 124JSONCompilationDatabase * 125JSONCompilationDatabase::loadFromFile(StringRef FilePath, 126 std::string &ErrorMessage) { 127 llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer; 128 llvm::error_code Result = 129 llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer); 130 if (Result != 0) { 131 ErrorMessage = "Error while opening JSON database: " + Result.message(); 132 return NULL; 133 } 134 llvm::OwningPtr<JSONCompilationDatabase> Database( 135 new JSONCompilationDatabase(DatabaseBuffer.take())); 136 if (!Database->parse(ErrorMessage)) 137 return NULL; 138 return Database.take(); 139} 140 141JSONCompilationDatabase * 142JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString, 143 std::string &ErrorMessage) { 144 llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer( 145 llvm::MemoryBuffer::getMemBuffer(DatabaseString)); 146 llvm::OwningPtr<JSONCompilationDatabase> Database( 147 new JSONCompilationDatabase(DatabaseBuffer.take())); 148 if (!Database->parse(ErrorMessage)) 149 return NULL; 150 return Database.take(); 151} 152 153std::vector<CompileCommand> 154JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { 155 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 156 CommandsRefI = IndexByFile.find(FilePath); 157 if (CommandsRefI == IndexByFile.end()) 158 return std::vector<CompileCommand>(); 159 const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue(); 160 std::vector<CompileCommand> Commands; 161 for (int I = 0, E = CommandsRef.size(); I != E; ++I) { 162 llvm::SmallString<8> DirectoryStorage; 163 llvm::SmallString<1024> CommandStorage; 164 Commands.push_back(CompileCommand( 165 // FIXME: Escape correctly: 166 CommandsRef[I].first->getValue(DirectoryStorage), 167 unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage)))); 168 } 169 return Commands; 170} 171 172bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { 173 llvm::yaml::document_iterator I = YAMLStream.begin(); 174 if (I == YAMLStream.end()) { 175 ErrorMessage = "Error while parsing YAML."; 176 return false; 177 } 178 llvm::yaml::Node *Root = I->getRoot(); 179 if (Root == NULL) { 180 ErrorMessage = "Error while parsing YAML."; 181 return false; 182 } 183 llvm::yaml::SequenceNode *Array = 184 llvm::dyn_cast<llvm::yaml::SequenceNode>(Root); 185 if (Array == NULL) { 186 ErrorMessage = "Expected array."; 187 return false; 188 } 189 for (llvm::yaml::SequenceNode::iterator AI = Array->begin(), 190 AE = Array->end(); 191 AI != AE; ++AI) { 192 llvm::yaml::MappingNode *Object = 193 llvm::dyn_cast<llvm::yaml::MappingNode>(&*AI); 194 if (Object == NULL) { 195 ErrorMessage = "Expected object."; 196 return false; 197 } 198 llvm::yaml::ScalarNode *Directory; 199 llvm::yaml::ScalarNode *Command; 200 llvm::SmallString<8> FileStorage; 201 llvm::StringRef File; 202 for (llvm::yaml::MappingNode::iterator KVI = Object->begin(), 203 KVE = Object->end(); 204 KVI != KVE; ++KVI) { 205 llvm::yaml::Node *Value = (*KVI).getValue(); 206 if (Value == NULL) { 207 ErrorMessage = "Expected value."; 208 return false; 209 } 210 llvm::yaml::ScalarNode *ValueString = 211 llvm::dyn_cast<llvm::yaml::ScalarNode>(Value); 212 if (ValueString == NULL) { 213 ErrorMessage = "Expected string as value."; 214 return false; 215 } 216 llvm::yaml::ScalarNode *KeyString = 217 llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey()); 218 llvm::SmallString<8> KeyStorage; 219 if (KeyString->getValue(KeyStorage) == "directory") { 220 Directory = ValueString; 221 } else if (KeyString->getValue(KeyStorage) == "command") { 222 Command = ValueString; 223 } else if (KeyString->getValue(KeyStorage) == "file") { 224 File = ValueString->getValue(FileStorage); 225 } else { 226 ErrorMessage = ("Unknown key: \"" + 227 KeyString->getRawValue() + "\"").str(); 228 return false; 229 } 230 } 231 IndexByFile[File].push_back( 232 CompileCommandRef(Directory, Command)); 233 } 234 return true; 235} 236 237} // end namespace tooling 238} // end namespace clang 239 240