147c245a5SManuel Klimek //===--- CompilationDatabase.cpp - ----------------------------------------===//
247c245a5SManuel Klimek //
347c245a5SManuel Klimek //                     The LLVM Compiler Infrastructure
447c245a5SManuel Klimek //
547c245a5SManuel Klimek // This file is distributed under the University of Illinois Open Source
647c245a5SManuel Klimek // License. See LICENSE.TXT for details.
747c245a5SManuel Klimek //
847c245a5SManuel Klimek //===----------------------------------------------------------------------===//
947c245a5SManuel Klimek //
1047c245a5SManuel Klimek //  This file contains multiple implementations for CompilationDatabases.
1147c245a5SManuel Klimek //
1247c245a5SManuel Klimek //===----------------------------------------------------------------------===//
1347c245a5SManuel Klimek 
1447c245a5SManuel Klimek #include "clang/Tooling/CompilationDatabase.h"
1547c245a5SManuel Klimek #include "llvm/ADT/SmallString.h"
1640636c99SManuel Klimek #include "llvm/Support/YAMLParser.h"
1747c245a5SManuel Klimek #include "llvm/Support/Path.h"
1847c245a5SManuel Klimek #include "llvm/Support/system_error.h"
1947c245a5SManuel Klimek 
2047c245a5SManuel Klimek namespace clang {
2147c245a5SManuel Klimek namespace tooling {
2247c245a5SManuel Klimek 
2347c245a5SManuel Klimek namespace {
2447c245a5SManuel Klimek 
2540636c99SManuel Klimek /// \brief A parser for escaped strings of command line arguments.
2647c245a5SManuel Klimek ///
2747c245a5SManuel Klimek /// Assumes \-escaping for quoted arguments (see the documentation of
2840636c99SManuel Klimek /// unescapeCommandLine(...)).
2947c245a5SManuel Klimek class CommandLineArgumentParser {
3047c245a5SManuel Klimek  public:
3147c245a5SManuel Klimek   CommandLineArgumentParser(StringRef CommandLine)
3247c245a5SManuel Klimek       : Input(CommandLine), Position(Input.begin()-1) {}
3347c245a5SManuel Klimek 
3447c245a5SManuel Klimek   std::vector<std::string> parse() {
3547c245a5SManuel Klimek     bool HasMoreInput = true;
3647c245a5SManuel Klimek     while (HasMoreInput && nextNonWhitespace()) {
3747c245a5SManuel Klimek       std::string Argument;
3847c245a5SManuel Klimek       HasMoreInput = parseStringInto(Argument);
3947c245a5SManuel Klimek       CommandLine.push_back(Argument);
4047c245a5SManuel Klimek     }
4147c245a5SManuel Klimek     return CommandLine;
4247c245a5SManuel Klimek   }
4347c245a5SManuel Klimek 
4447c245a5SManuel Klimek  private:
4547c245a5SManuel Klimek   // All private methods return true if there is more input available.
4647c245a5SManuel Klimek 
4747c245a5SManuel Klimek   bool parseStringInto(std::string &String) {
4847c245a5SManuel Klimek     do {
4947c245a5SManuel Klimek       if (*Position == '"') {
5047c245a5SManuel Klimek         if (!parseQuotedStringInto(String)) return false;
5147c245a5SManuel Klimek       } else {
5247c245a5SManuel Klimek         if (!parseFreeStringInto(String)) return false;
5347c245a5SManuel Klimek       }
5447c245a5SManuel Klimek     } while (*Position != ' ');
5547c245a5SManuel Klimek     return true;
5647c245a5SManuel Klimek   }
5747c245a5SManuel Klimek 
5847c245a5SManuel Klimek   bool parseQuotedStringInto(std::string &String) {
5947c245a5SManuel Klimek     if (!next()) return false;
6047c245a5SManuel Klimek     while (*Position != '"') {
6147c245a5SManuel Klimek       if (!skipEscapeCharacter()) return false;
6247c245a5SManuel Klimek       String.push_back(*Position);
6347c245a5SManuel Klimek       if (!next()) return false;
6447c245a5SManuel Klimek     }
6547c245a5SManuel Klimek     return next();
6647c245a5SManuel Klimek   }
6747c245a5SManuel Klimek 
6847c245a5SManuel Klimek   bool parseFreeStringInto(std::string &String) {
6947c245a5SManuel Klimek     do {
7047c245a5SManuel Klimek       if (!skipEscapeCharacter()) return false;
7147c245a5SManuel Klimek       String.push_back(*Position);
7247c245a5SManuel Klimek       if (!next()) return false;
7347c245a5SManuel Klimek     } while (*Position != ' ' && *Position != '"');
7447c245a5SManuel Klimek     return true;
7547c245a5SManuel Klimek   }
7647c245a5SManuel Klimek 
7747c245a5SManuel Klimek   bool skipEscapeCharacter() {
7847c245a5SManuel Klimek     if (*Position == '\\') {
7947c245a5SManuel Klimek       return next();
8047c245a5SManuel Klimek     }
8147c245a5SManuel Klimek     return true;
8247c245a5SManuel Klimek   }
8347c245a5SManuel Klimek 
8447c245a5SManuel Klimek   bool nextNonWhitespace() {
8547c245a5SManuel Klimek     do {
8647c245a5SManuel Klimek       if (!next()) return false;
8747c245a5SManuel Klimek     } while (*Position == ' ');
8847c245a5SManuel Klimek     return true;
8947c245a5SManuel Klimek   }
9047c245a5SManuel Klimek 
9147c245a5SManuel Klimek   bool next() {
9247c245a5SManuel Klimek     ++Position;
9347c245a5SManuel Klimek     return Position != Input.end();
9447c245a5SManuel Klimek   }
9547c245a5SManuel Klimek 
9647c245a5SManuel Klimek   const StringRef Input;
9747c245a5SManuel Klimek   StringRef::iterator Position;
9847c245a5SManuel Klimek   std::vector<std::string> CommandLine;
9947c245a5SManuel Klimek };
10047c245a5SManuel Klimek 
10140636c99SManuel Klimek std::vector<std::string> unescapeCommandLine(
10240636c99SManuel Klimek     StringRef EscapedCommandLine) {
10340636c99SManuel Klimek   CommandLineArgumentParser parser(EscapedCommandLine);
10447c245a5SManuel Klimek   return parser.parse();
10547c245a5SManuel Klimek }
10647c245a5SManuel Klimek 
10747c245a5SManuel Klimek } // end namespace
10847c245a5SManuel Klimek 
10947c245a5SManuel Klimek CompilationDatabase::~CompilationDatabase() {}
11047c245a5SManuel Klimek 
11147c245a5SManuel Klimek CompilationDatabase *
11247c245a5SManuel Klimek CompilationDatabase::loadFromDirectory(StringRef BuildDirectory,
11347c245a5SManuel Klimek                                        std::string &ErrorMessage) {
11447c245a5SManuel Klimek   llvm::SmallString<1024> JSONDatabasePath(BuildDirectory);
11547c245a5SManuel Klimek   llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
11647c245a5SManuel Klimek   llvm::OwningPtr<CompilationDatabase> Database(
11747c245a5SManuel Klimek     JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage));
11847c245a5SManuel Klimek   if (!Database) {
11947c245a5SManuel Klimek     return NULL;
12047c245a5SManuel Klimek   }
12147c245a5SManuel Klimek   return Database.take();
12247c245a5SManuel Klimek }
12347c245a5SManuel Klimek 
124*ff26efceSManuel Klimek FixedCompilationDatabase *
125*ff26efceSManuel Klimek FixedCompilationDatabase::loadFromCommandLine(int &Argc,
126*ff26efceSManuel Klimek                                               const char **Argv,
127*ff26efceSManuel Klimek                                               Twine Directory) {
128*ff26efceSManuel Klimek   const char **DoubleDash = std::find(Argv, Argv + Argc, StringRef("--"));
129*ff26efceSManuel Klimek   if (DoubleDash == Argv + Argc)
130*ff26efceSManuel Klimek     return NULL;
131*ff26efceSManuel Klimek   std::vector<std::string> CommandLine(DoubleDash + 1, Argv + Argc);
132*ff26efceSManuel Klimek   Argc = DoubleDash - Argv;
133*ff26efceSManuel Klimek   return new FixedCompilationDatabase(Directory, CommandLine);
134*ff26efceSManuel Klimek }
135*ff26efceSManuel Klimek 
136*ff26efceSManuel Klimek FixedCompilationDatabase::
137*ff26efceSManuel Klimek FixedCompilationDatabase(Twine Directory, ArrayRef<std::string> CommandLine) {
138*ff26efceSManuel Klimek   std::vector<std::string> ToolCommandLine(1, "clang-tool");
139*ff26efceSManuel Klimek   ToolCommandLine.insert(ToolCommandLine.end(),
140*ff26efceSManuel Klimek                          CommandLine.begin(), CommandLine.end());
141*ff26efceSManuel Klimek   CompileCommands.push_back(CompileCommand(Directory, ToolCommandLine));
142*ff26efceSManuel Klimek }
143*ff26efceSManuel Klimek 
144*ff26efceSManuel Klimek std::vector<CompileCommand>
145*ff26efceSManuel Klimek FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const {
146*ff26efceSManuel Klimek   std::vector<CompileCommand> Result(CompileCommands);
147*ff26efceSManuel Klimek   Result[0].CommandLine.push_back(FilePath);
148*ff26efceSManuel Klimek   return Result;
149*ff26efceSManuel Klimek }
150*ff26efceSManuel Klimek 
15147c245a5SManuel Klimek JSONCompilationDatabase *
15247c245a5SManuel Klimek JSONCompilationDatabase::loadFromFile(StringRef FilePath,
15347c245a5SManuel Klimek                                       std::string &ErrorMessage) {
15447c245a5SManuel Klimek   llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer;
15547c245a5SManuel Klimek   llvm::error_code Result =
15647c245a5SManuel Klimek     llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer);
15747c245a5SManuel Klimek   if (Result != 0) {
15847c245a5SManuel Klimek     ErrorMessage = "Error while opening JSON database: " + Result.message();
15947c245a5SManuel Klimek     return NULL;
16047c245a5SManuel Klimek   }
16147c245a5SManuel Klimek   llvm::OwningPtr<JSONCompilationDatabase> Database(
16247c245a5SManuel Klimek     new JSONCompilationDatabase(DatabaseBuffer.take()));
16347c245a5SManuel Klimek   if (!Database->parse(ErrorMessage))
16447c245a5SManuel Klimek     return NULL;
16547c245a5SManuel Klimek   return Database.take();
16647c245a5SManuel Klimek }
16747c245a5SManuel Klimek 
16847c245a5SManuel Klimek JSONCompilationDatabase *
16947c245a5SManuel Klimek JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
17047c245a5SManuel Klimek                                         std::string &ErrorMessage) {
17147c245a5SManuel Klimek   llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer(
17247c245a5SManuel Klimek       llvm::MemoryBuffer::getMemBuffer(DatabaseString));
17347c245a5SManuel Klimek   llvm::OwningPtr<JSONCompilationDatabase> Database(
17447c245a5SManuel Klimek     new JSONCompilationDatabase(DatabaseBuffer.take()));
17547c245a5SManuel Klimek   if (!Database->parse(ErrorMessage))
17647c245a5SManuel Klimek     return NULL;
17747c245a5SManuel Klimek   return Database.take();
17847c245a5SManuel Klimek }
17947c245a5SManuel Klimek 
18047c245a5SManuel Klimek std::vector<CompileCommand>
18147c245a5SManuel Klimek JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
18247c245a5SManuel Klimek   llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
18347c245a5SManuel Klimek     CommandsRefI = IndexByFile.find(FilePath);
18447c245a5SManuel Klimek   if (CommandsRefI == IndexByFile.end())
18547c245a5SManuel Klimek     return std::vector<CompileCommand>();
18647c245a5SManuel Klimek   const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue();
18747c245a5SManuel Klimek   std::vector<CompileCommand> Commands;
18847c245a5SManuel Klimek   for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
18940636c99SManuel Klimek     llvm::SmallString<8> DirectoryStorage;
19040636c99SManuel Klimek     llvm::SmallString<1024> CommandStorage;
19147c245a5SManuel Klimek     Commands.push_back(CompileCommand(
19247c245a5SManuel Klimek       // FIXME: Escape correctly:
19340636c99SManuel Klimek       CommandsRef[I].first->getValue(DirectoryStorage),
19440636c99SManuel Klimek       unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage))));
19547c245a5SManuel Klimek   }
19647c245a5SManuel Klimek   return Commands;
19747c245a5SManuel Klimek }
19847c245a5SManuel Klimek 
19947c245a5SManuel Klimek bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
20040636c99SManuel Klimek   llvm::yaml::document_iterator I = YAMLStream.begin();
20140636c99SManuel Klimek   if (I == YAMLStream.end()) {
20240636c99SManuel Klimek     ErrorMessage = "Error while parsing YAML.";
20347c245a5SManuel Klimek     return false;
20447c245a5SManuel Klimek   }
20540636c99SManuel Klimek   llvm::yaml::Node *Root = I->getRoot();
20640636c99SManuel Klimek   if (Root == NULL) {
20740636c99SManuel Klimek     ErrorMessage = "Error while parsing YAML.";
20840636c99SManuel Klimek     return false;
20940636c99SManuel Klimek   }
21040636c99SManuel Klimek   llvm::yaml::SequenceNode *Array =
21140636c99SManuel Klimek     llvm::dyn_cast<llvm::yaml::SequenceNode>(Root);
21247c245a5SManuel Klimek   if (Array == NULL) {
21347c245a5SManuel Klimek     ErrorMessage = "Expected array.";
21447c245a5SManuel Klimek     return false;
21547c245a5SManuel Klimek   }
21640636c99SManuel Klimek   for (llvm::yaml::SequenceNode::iterator AI = Array->begin(),
21740636c99SManuel Klimek                                           AE = Array->end();
21847c245a5SManuel Klimek        AI != AE; ++AI) {
21940636c99SManuel Klimek     llvm::yaml::MappingNode *Object =
22040636c99SManuel Klimek       llvm::dyn_cast<llvm::yaml::MappingNode>(&*AI);
22147c245a5SManuel Klimek     if (Object == NULL) {
22247c245a5SManuel Klimek       ErrorMessage = "Expected object.";
22347c245a5SManuel Klimek       return false;
22447c245a5SManuel Klimek     }
22540636c99SManuel Klimek     llvm::yaml::ScalarNode *Directory;
22640636c99SManuel Klimek     llvm::yaml::ScalarNode *Command;
22740636c99SManuel Klimek     llvm::SmallString<8> FileStorage;
22840636c99SManuel Klimek     llvm::StringRef File;
22940636c99SManuel Klimek     for (llvm::yaml::MappingNode::iterator KVI = Object->begin(),
23047c245a5SManuel Klimek                                            KVE = Object->end();
23147c245a5SManuel Klimek          KVI != KVE; ++KVI) {
23240636c99SManuel Klimek       llvm::yaml::Node *Value = (*KVI).getValue();
23347c245a5SManuel Klimek       if (Value == NULL) {
23447c245a5SManuel Klimek         ErrorMessage = "Expected value.";
23547c245a5SManuel Klimek         return false;
23647c245a5SManuel Klimek       }
23740636c99SManuel Klimek       llvm::yaml::ScalarNode *ValueString =
23840636c99SManuel Klimek         llvm::dyn_cast<llvm::yaml::ScalarNode>(Value);
23947c245a5SManuel Klimek       if (ValueString == NULL) {
24047c245a5SManuel Klimek         ErrorMessage = "Expected string as value.";
24147c245a5SManuel Klimek         return false;
24247c245a5SManuel Klimek       }
24340636c99SManuel Klimek       llvm::yaml::ScalarNode *KeyString =
24440636c99SManuel Klimek         llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
24540636c99SManuel Klimek       llvm::SmallString<8> KeyStorage;
24640636c99SManuel Klimek       if (KeyString->getValue(KeyStorage) == "directory") {
24740636c99SManuel Klimek         Directory = ValueString;
24840636c99SManuel Klimek       } else if (KeyString->getValue(KeyStorage) == "command") {
24940636c99SManuel Klimek         Command = ValueString;
25040636c99SManuel Klimek       } else if (KeyString->getValue(KeyStorage) == "file") {
25140636c99SManuel Klimek         File = ValueString->getValue(FileStorage);
25247c245a5SManuel Klimek       } else {
25340636c99SManuel Klimek         ErrorMessage = ("Unknown key: \"" +
25440636c99SManuel Klimek                         KeyString->getRawValue() + "\"").str();
25547c245a5SManuel Klimek         return false;
25647c245a5SManuel Klimek       }
25747c245a5SManuel Klimek     }
25840636c99SManuel Klimek     IndexByFile[File].push_back(
25940636c99SManuel Klimek       CompileCommandRef(Directory, Command));
26047c245a5SManuel Klimek   }
26147c245a5SManuel Klimek   return true;
26247c245a5SManuel Klimek }
26347c245a5SManuel Klimek 
26447c245a5SManuel Klimek } // end namespace tooling
26547c245a5SManuel Klimek } // end namespace clang
26647c245a5SManuel Klimek 
267