clang API Documentation
00001 //===--- JSONCompilationDatabase.cpp - ------------------------------------===// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // This file contains the implementation of the JSONCompilationDatabase. 00011 // 00012 //===----------------------------------------------------------------------===// 00013 00014 #include "clang/Tooling/JSONCompilationDatabase.h" 00015 #include "clang/Tooling/CompilationDatabase.h" 00016 #include "clang/Tooling/CompilationDatabasePluginRegistry.h" 00017 #include "clang/Tooling/Tooling.h" 00018 #include "llvm/ADT/SmallString.h" 00019 #include "llvm/Support/Path.h" 00020 #include <system_error> 00021 00022 namespace clang { 00023 namespace tooling { 00024 00025 namespace { 00026 00027 /// \brief A parser for escaped strings of command line arguments. 00028 /// 00029 /// Assumes \-escaping for quoted arguments (see the documentation of 00030 /// unescapeCommandLine(...)). 00031 class CommandLineArgumentParser { 00032 public: 00033 CommandLineArgumentParser(StringRef CommandLine) 00034 : Input(CommandLine), Position(Input.begin()-1) {} 00035 00036 std::vector<std::string> parse() { 00037 bool HasMoreInput = true; 00038 while (HasMoreInput && nextNonWhitespace()) { 00039 std::string Argument; 00040 HasMoreInput = parseStringInto(Argument); 00041 CommandLine.push_back(Argument); 00042 } 00043 return CommandLine; 00044 } 00045 00046 private: 00047 // All private methods return true if there is more input available. 00048 00049 bool parseStringInto(std::string &String) { 00050 do { 00051 if (*Position == '"') { 00052 if (!parseDoubleQuotedStringInto(String)) return false; 00053 } else if (*Position == '\'') { 00054 if (!parseSingleQuotedStringInto(String)) return false; 00055 } else { 00056 if (!parseFreeStringInto(String)) return false; 00057 } 00058 } while (*Position != ' '); 00059 return true; 00060 } 00061 00062 bool parseDoubleQuotedStringInto(std::string &String) { 00063 if (!next()) return false; 00064 while (*Position != '"') { 00065 if (!skipEscapeCharacter()) return false; 00066 String.push_back(*Position); 00067 if (!next()) return false; 00068 } 00069 return next(); 00070 } 00071 00072 bool parseSingleQuotedStringInto(std::string &String) { 00073 if (!next()) return false; 00074 while (*Position != '\'') { 00075 String.push_back(*Position); 00076 if (!next()) return false; 00077 } 00078 return next(); 00079 } 00080 00081 bool parseFreeStringInto(std::string &String) { 00082 do { 00083 if (!skipEscapeCharacter()) return false; 00084 String.push_back(*Position); 00085 if (!next()) return false; 00086 } while (*Position != ' ' && *Position != '"' && *Position != '\''); 00087 return true; 00088 } 00089 00090 bool skipEscapeCharacter() { 00091 if (*Position == '\\') { 00092 return next(); 00093 } 00094 return true; 00095 } 00096 00097 bool nextNonWhitespace() { 00098 do { 00099 if (!next()) return false; 00100 } while (*Position == ' '); 00101 return true; 00102 } 00103 00104 bool next() { 00105 ++Position; 00106 return Position != Input.end(); 00107 } 00108 00109 const StringRef Input; 00110 StringRef::iterator Position; 00111 std::vector<std::string> CommandLine; 00112 }; 00113 00114 std::vector<std::string> unescapeCommandLine( 00115 StringRef EscapedCommandLine) { 00116 CommandLineArgumentParser parser(EscapedCommandLine); 00117 return parser.parse(); 00118 } 00119 00120 class JSONCompilationDatabasePlugin : public CompilationDatabasePlugin { 00121 std::unique_ptr<CompilationDatabase> 00122 loadFromDirectory(StringRef Directory, std::string &ErrorMessage) override { 00123 SmallString<1024> JSONDatabasePath(Directory); 00124 llvm::sys::path::append(JSONDatabasePath, "compile_commands.json"); 00125 std::unique_ptr<CompilationDatabase> Database( 00126 JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage)); 00127 if (!Database) 00128 return nullptr; 00129 return Database; 00130 } 00131 }; 00132 00133 } // end namespace 00134 00135 // Register the JSONCompilationDatabasePlugin with the 00136 // CompilationDatabasePluginRegistry using this statically initialized variable. 00137 static CompilationDatabasePluginRegistry::Add<JSONCompilationDatabasePlugin> 00138 X("json-compilation-database", "Reads JSON formatted compilation databases"); 00139 00140 // This anchor is used to force the linker to link in the generated object file 00141 // and thus register the JSONCompilationDatabasePlugin. 00142 volatile int JSONAnchorSource = 0; 00143 00144 std::unique_ptr<JSONCompilationDatabase> 00145 JSONCompilationDatabase::loadFromFile(StringRef FilePath, 00146 std::string &ErrorMessage) { 00147 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> DatabaseBuffer = 00148 llvm::MemoryBuffer::getFile(FilePath); 00149 if (std::error_code Result = DatabaseBuffer.getError()) { 00150 ErrorMessage = "Error while opening JSON database: " + Result.message(); 00151 return nullptr; 00152 } 00153 std::unique_ptr<JSONCompilationDatabase> Database( 00154 new JSONCompilationDatabase(std::move(*DatabaseBuffer))); 00155 if (!Database->parse(ErrorMessage)) 00156 return nullptr; 00157 return Database; 00158 } 00159 00160 std::unique_ptr<JSONCompilationDatabase> 00161 JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString, 00162 std::string &ErrorMessage) { 00163 std::unique_ptr<llvm::MemoryBuffer> DatabaseBuffer( 00164 llvm::MemoryBuffer::getMemBuffer(DatabaseString)); 00165 std::unique_ptr<JSONCompilationDatabase> Database( 00166 new JSONCompilationDatabase(std::move(DatabaseBuffer))); 00167 if (!Database->parse(ErrorMessage)) 00168 return nullptr; 00169 return Database; 00170 } 00171 00172 std::vector<CompileCommand> 00173 JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { 00174 SmallString<128> NativeFilePath; 00175 llvm::sys::path::native(FilePath, NativeFilePath); 00176 00177 std::string Error; 00178 llvm::raw_string_ostream ES(Error); 00179 StringRef Match = MatchTrie.findEquivalent(NativeFilePath.str(), ES); 00180 if (Match.empty()) 00181 return std::vector<CompileCommand>(); 00182 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 00183 CommandsRefI = IndexByFile.find(Match); 00184 if (CommandsRefI == IndexByFile.end()) 00185 return std::vector<CompileCommand>(); 00186 std::vector<CompileCommand> Commands; 00187 getCommands(CommandsRefI->getValue(), Commands); 00188 return Commands; 00189 } 00190 00191 std::vector<std::string> 00192 JSONCompilationDatabase::getAllFiles() const { 00193 std::vector<std::string> Result; 00194 00195 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 00196 CommandsRefI = IndexByFile.begin(); 00197 const llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 00198 CommandsRefEnd = IndexByFile.end(); 00199 for (; CommandsRefI != CommandsRefEnd; ++CommandsRefI) { 00200 Result.push_back(CommandsRefI->first().str()); 00201 } 00202 00203 return Result; 00204 } 00205 00206 std::vector<CompileCommand> 00207 JSONCompilationDatabase::getAllCompileCommands() const { 00208 std::vector<CompileCommand> Commands; 00209 for (llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator 00210 CommandsRefI = IndexByFile.begin(), CommandsRefEnd = IndexByFile.end(); 00211 CommandsRefI != CommandsRefEnd; ++CommandsRefI) { 00212 getCommands(CommandsRefI->getValue(), Commands); 00213 } 00214 return Commands; 00215 } 00216 00217 void JSONCompilationDatabase::getCommands( 00218 ArrayRef<CompileCommandRef> CommandsRef, 00219 std::vector<CompileCommand> &Commands) const { 00220 for (int I = 0, E = CommandsRef.size(); I != E; ++I) { 00221 SmallString<8> DirectoryStorage; 00222 SmallString<1024> CommandStorage; 00223 Commands.push_back(CompileCommand( 00224 // FIXME: Escape correctly: 00225 CommandsRef[I].first->getValue(DirectoryStorage), 00226 unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage)))); 00227 } 00228 } 00229 00230 bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { 00231 llvm::yaml::document_iterator I = YAMLStream.begin(); 00232 if (I == YAMLStream.end()) { 00233 ErrorMessage = "Error while parsing YAML."; 00234 return false; 00235 } 00236 llvm::yaml::Node *Root = I->getRoot(); 00237 if (!Root) { 00238 ErrorMessage = "Error while parsing YAML."; 00239 return false; 00240 } 00241 llvm::yaml::SequenceNode *Array = dyn_cast<llvm::yaml::SequenceNode>(Root); 00242 if (!Array) { 00243 ErrorMessage = "Expected array."; 00244 return false; 00245 } 00246 for (llvm::yaml::SequenceNode::iterator AI = Array->begin(), 00247 AE = Array->end(); 00248 AI != AE; ++AI) { 00249 llvm::yaml::MappingNode *Object = dyn_cast<llvm::yaml::MappingNode>(&*AI); 00250 if (!Object) { 00251 ErrorMessage = "Expected object."; 00252 return false; 00253 } 00254 llvm::yaml::ScalarNode *Directory = nullptr; 00255 llvm::yaml::ScalarNode *Command = nullptr; 00256 llvm::yaml::ScalarNode *File = nullptr; 00257 for (llvm::yaml::MappingNode::iterator KVI = Object->begin(), 00258 KVE = Object->end(); 00259 KVI != KVE; ++KVI) { 00260 llvm::yaml::Node *Value = (*KVI).getValue(); 00261 if (!Value) { 00262 ErrorMessage = "Expected value."; 00263 return false; 00264 } 00265 llvm::yaml::ScalarNode *ValueString = 00266 dyn_cast<llvm::yaml::ScalarNode>(Value); 00267 if (!ValueString) { 00268 ErrorMessage = "Expected string as value."; 00269 return false; 00270 } 00271 llvm::yaml::ScalarNode *KeyString = 00272 dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey()); 00273 if (!KeyString) { 00274 ErrorMessage = "Expected strings as key."; 00275 return false; 00276 } 00277 SmallString<8> KeyStorage; 00278 if (KeyString->getValue(KeyStorage) == "directory") { 00279 Directory = ValueString; 00280 } else if (KeyString->getValue(KeyStorage) == "command") { 00281 Command = ValueString; 00282 } else if (KeyString->getValue(KeyStorage) == "file") { 00283 File = ValueString; 00284 } else { 00285 ErrorMessage = ("Unknown key: \"" + 00286 KeyString->getRawValue() + "\"").str(); 00287 return false; 00288 } 00289 } 00290 if (!File) { 00291 ErrorMessage = "Missing key: \"file\"."; 00292 return false; 00293 } 00294 if (!Command) { 00295 ErrorMessage = "Missing key: \"command\"."; 00296 return false; 00297 } 00298 if (!Directory) { 00299 ErrorMessage = "Missing key: \"directory\"."; 00300 return false; 00301 } 00302 SmallString<8> FileStorage; 00303 StringRef FileName = File->getValue(FileStorage); 00304 SmallString<128> NativeFilePath; 00305 if (llvm::sys::path::is_relative(FileName)) { 00306 SmallString<8> DirectoryStorage; 00307 SmallString<128> AbsolutePath( 00308 Directory->getValue(DirectoryStorage)); 00309 llvm::sys::path::append(AbsolutePath, FileName); 00310 llvm::sys::path::native(AbsolutePath.str(), NativeFilePath); 00311 } else { 00312 llvm::sys::path::native(FileName, NativeFilePath); 00313 } 00314 IndexByFile[NativeFilePath].push_back( 00315 CompileCommandRef(Directory, Command)); 00316 MatchTrie.insert(NativeFilePath.str()); 00317 } 00318 return true; 00319 } 00320 00321 } // end namespace tooling 00322 } // end namespace clang