1f4a2713aSLionel Sambuc //===--- JSONCompilationDatabase.cpp - ------------------------------------===//
2f4a2713aSLionel Sambuc //
3f4a2713aSLionel Sambuc // The LLVM Compiler Infrastructure
4f4a2713aSLionel Sambuc //
5f4a2713aSLionel Sambuc // This file is distributed under the University of Illinois Open Source
6f4a2713aSLionel Sambuc // License. See LICENSE.TXT for details.
7f4a2713aSLionel Sambuc //
8f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
9f4a2713aSLionel Sambuc //
10f4a2713aSLionel Sambuc // This file contains the implementation of the JSONCompilationDatabase.
11f4a2713aSLionel Sambuc //
12f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
13f4a2713aSLionel Sambuc
14f4a2713aSLionel Sambuc #include "clang/Tooling/JSONCompilationDatabase.h"
15f4a2713aSLionel Sambuc #include "clang/Tooling/CompilationDatabase.h"
16f4a2713aSLionel Sambuc #include "clang/Tooling/CompilationDatabasePluginRegistry.h"
17f4a2713aSLionel Sambuc #include "clang/Tooling/Tooling.h"
18f4a2713aSLionel Sambuc #include "llvm/ADT/SmallString.h"
19f4a2713aSLionel Sambuc #include "llvm/Support/Path.h"
20*0a6a1f1dSLionel Sambuc #include <system_error>
21f4a2713aSLionel Sambuc
22f4a2713aSLionel Sambuc namespace clang {
23f4a2713aSLionel Sambuc namespace tooling {
24f4a2713aSLionel Sambuc
25f4a2713aSLionel Sambuc namespace {
26f4a2713aSLionel Sambuc
27f4a2713aSLionel Sambuc /// \brief A parser for escaped strings of command line arguments.
28f4a2713aSLionel Sambuc ///
29f4a2713aSLionel Sambuc /// Assumes \-escaping for quoted arguments (see the documentation of
30f4a2713aSLionel Sambuc /// unescapeCommandLine(...)).
31f4a2713aSLionel Sambuc class CommandLineArgumentParser {
32f4a2713aSLionel Sambuc public:
CommandLineArgumentParser(StringRef CommandLine)33f4a2713aSLionel Sambuc CommandLineArgumentParser(StringRef CommandLine)
34f4a2713aSLionel Sambuc : Input(CommandLine), Position(Input.begin()-1) {}
35f4a2713aSLionel Sambuc
parse()36f4a2713aSLionel Sambuc std::vector<std::string> parse() {
37f4a2713aSLionel Sambuc bool HasMoreInput = true;
38f4a2713aSLionel Sambuc while (HasMoreInput && nextNonWhitespace()) {
39f4a2713aSLionel Sambuc std::string Argument;
40f4a2713aSLionel Sambuc HasMoreInput = parseStringInto(Argument);
41f4a2713aSLionel Sambuc CommandLine.push_back(Argument);
42f4a2713aSLionel Sambuc }
43f4a2713aSLionel Sambuc return CommandLine;
44f4a2713aSLionel Sambuc }
45f4a2713aSLionel Sambuc
46f4a2713aSLionel Sambuc private:
47f4a2713aSLionel Sambuc // All private methods return true if there is more input available.
48f4a2713aSLionel Sambuc
parseStringInto(std::string & String)49f4a2713aSLionel Sambuc bool parseStringInto(std::string &String) {
50f4a2713aSLionel Sambuc do {
51f4a2713aSLionel Sambuc if (*Position == '"') {
52f4a2713aSLionel Sambuc if (!parseDoubleQuotedStringInto(String)) return false;
53f4a2713aSLionel Sambuc } else if (*Position == '\'') {
54f4a2713aSLionel Sambuc if (!parseSingleQuotedStringInto(String)) return false;
55f4a2713aSLionel Sambuc } else {
56f4a2713aSLionel Sambuc if (!parseFreeStringInto(String)) return false;
57f4a2713aSLionel Sambuc }
58f4a2713aSLionel Sambuc } while (*Position != ' ');
59f4a2713aSLionel Sambuc return true;
60f4a2713aSLionel Sambuc }
61f4a2713aSLionel Sambuc
parseDoubleQuotedStringInto(std::string & String)62f4a2713aSLionel Sambuc bool parseDoubleQuotedStringInto(std::string &String) {
63f4a2713aSLionel Sambuc if (!next()) return false;
64f4a2713aSLionel Sambuc while (*Position != '"') {
65f4a2713aSLionel Sambuc if (!skipEscapeCharacter()) return false;
66f4a2713aSLionel Sambuc String.push_back(*Position);
67f4a2713aSLionel Sambuc if (!next()) return false;
68f4a2713aSLionel Sambuc }
69f4a2713aSLionel Sambuc return next();
70f4a2713aSLionel Sambuc }
71f4a2713aSLionel Sambuc
parseSingleQuotedStringInto(std::string & String)72f4a2713aSLionel Sambuc bool parseSingleQuotedStringInto(std::string &String) {
73f4a2713aSLionel Sambuc if (!next()) return false;
74f4a2713aSLionel Sambuc while (*Position != '\'') {
75f4a2713aSLionel Sambuc String.push_back(*Position);
76f4a2713aSLionel Sambuc if (!next()) return false;
77f4a2713aSLionel Sambuc }
78f4a2713aSLionel Sambuc return next();
79f4a2713aSLionel Sambuc }
80f4a2713aSLionel Sambuc
parseFreeStringInto(std::string & String)81f4a2713aSLionel Sambuc bool parseFreeStringInto(std::string &String) {
82f4a2713aSLionel Sambuc do {
83f4a2713aSLionel Sambuc if (!skipEscapeCharacter()) return false;
84f4a2713aSLionel Sambuc String.push_back(*Position);
85f4a2713aSLionel Sambuc if (!next()) return false;
86f4a2713aSLionel Sambuc } while (*Position != ' ' && *Position != '"' && *Position != '\'');
87f4a2713aSLionel Sambuc return true;
88f4a2713aSLionel Sambuc }
89f4a2713aSLionel Sambuc
skipEscapeCharacter()90f4a2713aSLionel Sambuc bool skipEscapeCharacter() {
91f4a2713aSLionel Sambuc if (*Position == '\\') {
92f4a2713aSLionel Sambuc return next();
93f4a2713aSLionel Sambuc }
94f4a2713aSLionel Sambuc return true;
95f4a2713aSLionel Sambuc }
96f4a2713aSLionel Sambuc
nextNonWhitespace()97f4a2713aSLionel Sambuc bool nextNonWhitespace() {
98f4a2713aSLionel Sambuc do {
99f4a2713aSLionel Sambuc if (!next()) return false;
100f4a2713aSLionel Sambuc } while (*Position == ' ');
101f4a2713aSLionel Sambuc return true;
102f4a2713aSLionel Sambuc }
103f4a2713aSLionel Sambuc
next()104f4a2713aSLionel Sambuc bool next() {
105f4a2713aSLionel Sambuc ++Position;
106f4a2713aSLionel Sambuc return Position != Input.end();
107f4a2713aSLionel Sambuc }
108f4a2713aSLionel Sambuc
109f4a2713aSLionel Sambuc const StringRef Input;
110f4a2713aSLionel Sambuc StringRef::iterator Position;
111f4a2713aSLionel Sambuc std::vector<std::string> CommandLine;
112f4a2713aSLionel Sambuc };
113f4a2713aSLionel Sambuc
unescapeCommandLine(StringRef EscapedCommandLine)114f4a2713aSLionel Sambuc std::vector<std::string> unescapeCommandLine(
115f4a2713aSLionel Sambuc StringRef EscapedCommandLine) {
116f4a2713aSLionel Sambuc CommandLineArgumentParser parser(EscapedCommandLine);
117f4a2713aSLionel Sambuc return parser.parse();
118f4a2713aSLionel Sambuc }
119f4a2713aSLionel Sambuc
120f4a2713aSLionel Sambuc class JSONCompilationDatabasePlugin : public CompilationDatabasePlugin {
121*0a6a1f1dSLionel Sambuc std::unique_ptr<CompilationDatabase>
loadFromDirectory(StringRef Directory,std::string & ErrorMessage)122*0a6a1f1dSLionel Sambuc loadFromDirectory(StringRef Directory, std::string &ErrorMessage) override {
123f4a2713aSLionel Sambuc SmallString<1024> JSONDatabasePath(Directory);
124f4a2713aSLionel Sambuc llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
125*0a6a1f1dSLionel Sambuc std::unique_ptr<CompilationDatabase> Database(
126f4a2713aSLionel Sambuc JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage));
127f4a2713aSLionel Sambuc if (!Database)
128*0a6a1f1dSLionel Sambuc return nullptr;
129*0a6a1f1dSLionel Sambuc return Database;
130f4a2713aSLionel Sambuc }
131f4a2713aSLionel Sambuc };
132f4a2713aSLionel Sambuc
133f4a2713aSLionel Sambuc } // end namespace
134f4a2713aSLionel Sambuc
135f4a2713aSLionel Sambuc // Register the JSONCompilationDatabasePlugin with the
136f4a2713aSLionel Sambuc // CompilationDatabasePluginRegistry using this statically initialized variable.
137f4a2713aSLionel Sambuc static CompilationDatabasePluginRegistry::Add<JSONCompilationDatabasePlugin>
138f4a2713aSLionel Sambuc X("json-compilation-database", "Reads JSON formatted compilation databases");
139f4a2713aSLionel Sambuc
140f4a2713aSLionel Sambuc // This anchor is used to force the linker to link in the generated object file
141f4a2713aSLionel Sambuc // and thus register the JSONCompilationDatabasePlugin.
142f4a2713aSLionel Sambuc volatile int JSONAnchorSource = 0;
143f4a2713aSLionel Sambuc
144*0a6a1f1dSLionel Sambuc std::unique_ptr<JSONCompilationDatabase>
loadFromFile(StringRef FilePath,std::string & ErrorMessage)145f4a2713aSLionel Sambuc JSONCompilationDatabase::loadFromFile(StringRef FilePath,
146f4a2713aSLionel Sambuc std::string &ErrorMessage) {
147*0a6a1f1dSLionel Sambuc llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> DatabaseBuffer =
148*0a6a1f1dSLionel Sambuc llvm::MemoryBuffer::getFile(FilePath);
149*0a6a1f1dSLionel Sambuc if (std::error_code Result = DatabaseBuffer.getError()) {
150f4a2713aSLionel Sambuc ErrorMessage = "Error while opening JSON database: " + Result.message();
151*0a6a1f1dSLionel Sambuc return nullptr;
152f4a2713aSLionel Sambuc }
153*0a6a1f1dSLionel Sambuc std::unique_ptr<JSONCompilationDatabase> Database(
154*0a6a1f1dSLionel Sambuc new JSONCompilationDatabase(std::move(*DatabaseBuffer)));
155f4a2713aSLionel Sambuc if (!Database->parse(ErrorMessage))
156*0a6a1f1dSLionel Sambuc return nullptr;
157*0a6a1f1dSLionel Sambuc return Database;
158f4a2713aSLionel Sambuc }
159f4a2713aSLionel Sambuc
160*0a6a1f1dSLionel Sambuc std::unique_ptr<JSONCompilationDatabase>
loadFromBuffer(StringRef DatabaseString,std::string & ErrorMessage)161f4a2713aSLionel Sambuc JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
162f4a2713aSLionel Sambuc std::string &ErrorMessage) {
163*0a6a1f1dSLionel Sambuc std::unique_ptr<llvm::MemoryBuffer> DatabaseBuffer(
164f4a2713aSLionel Sambuc llvm::MemoryBuffer::getMemBuffer(DatabaseString));
165*0a6a1f1dSLionel Sambuc std::unique_ptr<JSONCompilationDatabase> Database(
166*0a6a1f1dSLionel Sambuc new JSONCompilationDatabase(std::move(DatabaseBuffer)));
167f4a2713aSLionel Sambuc if (!Database->parse(ErrorMessage))
168*0a6a1f1dSLionel Sambuc return nullptr;
169*0a6a1f1dSLionel Sambuc return Database;
170f4a2713aSLionel Sambuc }
171f4a2713aSLionel Sambuc
172f4a2713aSLionel Sambuc std::vector<CompileCommand>
getCompileCommands(StringRef FilePath) const173f4a2713aSLionel Sambuc JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
174f4a2713aSLionel Sambuc SmallString<128> NativeFilePath;
175f4a2713aSLionel Sambuc llvm::sys::path::native(FilePath, NativeFilePath);
176*0a6a1f1dSLionel Sambuc
177f4a2713aSLionel Sambuc std::string Error;
178f4a2713aSLionel Sambuc llvm::raw_string_ostream ES(Error);
179f4a2713aSLionel Sambuc StringRef Match = MatchTrie.findEquivalent(NativeFilePath.str(), ES);
180f4a2713aSLionel Sambuc if (Match.empty())
181f4a2713aSLionel Sambuc return std::vector<CompileCommand>();
182f4a2713aSLionel Sambuc llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
183f4a2713aSLionel Sambuc CommandsRefI = IndexByFile.find(Match);
184f4a2713aSLionel Sambuc if (CommandsRefI == IndexByFile.end())
185f4a2713aSLionel Sambuc return std::vector<CompileCommand>();
186f4a2713aSLionel Sambuc std::vector<CompileCommand> Commands;
187f4a2713aSLionel Sambuc getCommands(CommandsRefI->getValue(), Commands);
188f4a2713aSLionel Sambuc return Commands;
189f4a2713aSLionel Sambuc }
190f4a2713aSLionel Sambuc
191f4a2713aSLionel Sambuc std::vector<std::string>
getAllFiles() const192f4a2713aSLionel Sambuc JSONCompilationDatabase::getAllFiles() const {
193f4a2713aSLionel Sambuc std::vector<std::string> Result;
194f4a2713aSLionel Sambuc
195f4a2713aSLionel Sambuc llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
196f4a2713aSLionel Sambuc CommandsRefI = IndexByFile.begin();
197f4a2713aSLionel Sambuc const llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
198f4a2713aSLionel Sambuc CommandsRefEnd = IndexByFile.end();
199f4a2713aSLionel Sambuc for (; CommandsRefI != CommandsRefEnd; ++CommandsRefI) {
200f4a2713aSLionel Sambuc Result.push_back(CommandsRefI->first().str());
201f4a2713aSLionel Sambuc }
202f4a2713aSLionel Sambuc
203f4a2713aSLionel Sambuc return Result;
204f4a2713aSLionel Sambuc }
205f4a2713aSLionel Sambuc
206f4a2713aSLionel Sambuc std::vector<CompileCommand>
getAllCompileCommands() const207f4a2713aSLionel Sambuc JSONCompilationDatabase::getAllCompileCommands() const {
208f4a2713aSLionel Sambuc std::vector<CompileCommand> Commands;
209f4a2713aSLionel Sambuc for (llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
210f4a2713aSLionel Sambuc CommandsRefI = IndexByFile.begin(), CommandsRefEnd = IndexByFile.end();
211f4a2713aSLionel Sambuc CommandsRefI != CommandsRefEnd; ++CommandsRefI) {
212f4a2713aSLionel Sambuc getCommands(CommandsRefI->getValue(), Commands);
213f4a2713aSLionel Sambuc }
214f4a2713aSLionel Sambuc return Commands;
215f4a2713aSLionel Sambuc }
216f4a2713aSLionel Sambuc
getCommands(ArrayRef<CompileCommandRef> CommandsRef,std::vector<CompileCommand> & Commands) const217f4a2713aSLionel Sambuc void JSONCompilationDatabase::getCommands(
218f4a2713aSLionel Sambuc ArrayRef<CompileCommandRef> CommandsRef,
219f4a2713aSLionel Sambuc std::vector<CompileCommand> &Commands) const {
220f4a2713aSLionel Sambuc for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
221f4a2713aSLionel Sambuc SmallString<8> DirectoryStorage;
222f4a2713aSLionel Sambuc SmallString<1024> CommandStorage;
223f4a2713aSLionel Sambuc Commands.push_back(CompileCommand(
224f4a2713aSLionel Sambuc // FIXME: Escape correctly:
225f4a2713aSLionel Sambuc CommandsRef[I].first->getValue(DirectoryStorage),
226f4a2713aSLionel Sambuc unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage))));
227f4a2713aSLionel Sambuc }
228f4a2713aSLionel Sambuc }
229f4a2713aSLionel Sambuc
parse(std::string & ErrorMessage)230f4a2713aSLionel Sambuc bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
231f4a2713aSLionel Sambuc llvm::yaml::document_iterator I = YAMLStream.begin();
232f4a2713aSLionel Sambuc if (I == YAMLStream.end()) {
233f4a2713aSLionel Sambuc ErrorMessage = "Error while parsing YAML.";
234f4a2713aSLionel Sambuc return false;
235f4a2713aSLionel Sambuc }
236f4a2713aSLionel Sambuc llvm::yaml::Node *Root = I->getRoot();
237*0a6a1f1dSLionel Sambuc if (!Root) {
238f4a2713aSLionel Sambuc ErrorMessage = "Error while parsing YAML.";
239f4a2713aSLionel Sambuc return false;
240f4a2713aSLionel Sambuc }
241f4a2713aSLionel Sambuc llvm::yaml::SequenceNode *Array = dyn_cast<llvm::yaml::SequenceNode>(Root);
242*0a6a1f1dSLionel Sambuc if (!Array) {
243f4a2713aSLionel Sambuc ErrorMessage = "Expected array.";
244f4a2713aSLionel Sambuc return false;
245f4a2713aSLionel Sambuc }
246f4a2713aSLionel Sambuc for (llvm::yaml::SequenceNode::iterator AI = Array->begin(),
247f4a2713aSLionel Sambuc AE = Array->end();
248f4a2713aSLionel Sambuc AI != AE; ++AI) {
249f4a2713aSLionel Sambuc llvm::yaml::MappingNode *Object = dyn_cast<llvm::yaml::MappingNode>(&*AI);
250*0a6a1f1dSLionel Sambuc if (!Object) {
251f4a2713aSLionel Sambuc ErrorMessage = "Expected object.";
252f4a2713aSLionel Sambuc return false;
253f4a2713aSLionel Sambuc }
254*0a6a1f1dSLionel Sambuc llvm::yaml::ScalarNode *Directory = nullptr;
255*0a6a1f1dSLionel Sambuc llvm::yaml::ScalarNode *Command = nullptr;
256*0a6a1f1dSLionel Sambuc llvm::yaml::ScalarNode *File = nullptr;
257f4a2713aSLionel Sambuc for (llvm::yaml::MappingNode::iterator KVI = Object->begin(),
258f4a2713aSLionel Sambuc KVE = Object->end();
259f4a2713aSLionel Sambuc KVI != KVE; ++KVI) {
260f4a2713aSLionel Sambuc llvm::yaml::Node *Value = (*KVI).getValue();
261*0a6a1f1dSLionel Sambuc if (!Value) {
262f4a2713aSLionel Sambuc ErrorMessage = "Expected value.";
263f4a2713aSLionel Sambuc return false;
264f4a2713aSLionel Sambuc }
265f4a2713aSLionel Sambuc llvm::yaml::ScalarNode *ValueString =
266f4a2713aSLionel Sambuc dyn_cast<llvm::yaml::ScalarNode>(Value);
267*0a6a1f1dSLionel Sambuc if (!ValueString) {
268f4a2713aSLionel Sambuc ErrorMessage = "Expected string as value.";
269f4a2713aSLionel Sambuc return false;
270f4a2713aSLionel Sambuc }
271f4a2713aSLionel Sambuc llvm::yaml::ScalarNode *KeyString =
272f4a2713aSLionel Sambuc dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
273*0a6a1f1dSLionel Sambuc if (!KeyString) {
274f4a2713aSLionel Sambuc ErrorMessage = "Expected strings as key.";
275f4a2713aSLionel Sambuc return false;
276f4a2713aSLionel Sambuc }
277f4a2713aSLionel Sambuc SmallString<8> KeyStorage;
278f4a2713aSLionel Sambuc if (KeyString->getValue(KeyStorage) == "directory") {
279f4a2713aSLionel Sambuc Directory = ValueString;
280f4a2713aSLionel Sambuc } else if (KeyString->getValue(KeyStorage) == "command") {
281f4a2713aSLionel Sambuc Command = ValueString;
282f4a2713aSLionel Sambuc } else if (KeyString->getValue(KeyStorage) == "file") {
283f4a2713aSLionel Sambuc File = ValueString;
284f4a2713aSLionel Sambuc } else {
285f4a2713aSLionel Sambuc ErrorMessage = ("Unknown key: \"" +
286f4a2713aSLionel Sambuc KeyString->getRawValue() + "\"").str();
287f4a2713aSLionel Sambuc return false;
288f4a2713aSLionel Sambuc }
289f4a2713aSLionel Sambuc }
290f4a2713aSLionel Sambuc if (!File) {
291f4a2713aSLionel Sambuc ErrorMessage = "Missing key: \"file\".";
292f4a2713aSLionel Sambuc return false;
293f4a2713aSLionel Sambuc }
294f4a2713aSLionel Sambuc if (!Command) {
295f4a2713aSLionel Sambuc ErrorMessage = "Missing key: \"command\".";
296f4a2713aSLionel Sambuc return false;
297f4a2713aSLionel Sambuc }
298f4a2713aSLionel Sambuc if (!Directory) {
299f4a2713aSLionel Sambuc ErrorMessage = "Missing key: \"directory\".";
300f4a2713aSLionel Sambuc return false;
301f4a2713aSLionel Sambuc }
302f4a2713aSLionel Sambuc SmallString<8> FileStorage;
303f4a2713aSLionel Sambuc StringRef FileName = File->getValue(FileStorage);
304f4a2713aSLionel Sambuc SmallString<128> NativeFilePath;
305f4a2713aSLionel Sambuc if (llvm::sys::path::is_relative(FileName)) {
306f4a2713aSLionel Sambuc SmallString<8> DirectoryStorage;
307f4a2713aSLionel Sambuc SmallString<128> AbsolutePath(
308f4a2713aSLionel Sambuc Directory->getValue(DirectoryStorage));
309f4a2713aSLionel Sambuc llvm::sys::path::append(AbsolutePath, FileName);
310f4a2713aSLionel Sambuc llvm::sys::path::native(AbsolutePath.str(), NativeFilePath);
311f4a2713aSLionel Sambuc } else {
312f4a2713aSLionel Sambuc llvm::sys::path::native(FileName, NativeFilePath);
313f4a2713aSLionel Sambuc }
314f4a2713aSLionel Sambuc IndexByFile[NativeFilePath].push_back(
315f4a2713aSLionel Sambuc CompileCommandRef(Directory, Command));
316f4a2713aSLionel Sambuc MatchTrie.insert(NativeFilePath.str());
317f4a2713aSLionel Sambuc }
318f4a2713aSLionel Sambuc return true;
319f4a2713aSLionel Sambuc }
320f4a2713aSLionel Sambuc
321f4a2713aSLionel Sambuc } // end namespace tooling
322f4a2713aSLionel Sambuc } // end namespace clang
323