diff options
author | Manuel Klimek <klimek@google.com> | 2012-04-04 12:07:46 +0000 |
---|---|---|
committer | Manuel Klimek <klimek@google.com> | 2012-04-04 12:07:46 +0000 |
commit | cb971c6726d16e12ecd2a340941d7f5c06698332 (patch) | |
tree | 63f54a02c06faaf2297917a6e475852f1e959747 /lib/Tooling/CompilationDatabase.cpp | |
parent | c9aa9c00fc99ded37a064d607b71815484e20652 (diff) |
Adds a tooling library.
Provides an API to run clang tools (FrontendActions) as standalone tools,
or repeatedly in-memory in a process. This is useful for unit-testing,
map-reduce style applications, source transformation daemons or command line
tools.
The ability to run over multiple translation units with different command
line arguments enables building up refactoring tools that need to apply
transformations across translation unit boundaries.
See tools/clang-check/ClangCheck.cpp for an example.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@154008 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Tooling/CompilationDatabase.cpp')
-rw-r--r-- | lib/Tooling/CompilationDatabase.cpp | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/lib/Tooling/CompilationDatabase.cpp b/lib/Tooling/CompilationDatabase.cpp new file mode 100644 index 0000000000..eea1055f49 --- /dev/null +++ b/lib/Tooling/CompilationDatabase.cpp @@ -0,0 +1,230 @@ +//===--- CompilationDatabase.cpp - ----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains multiple implementations for CompilationDatabases. +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/CompilationDatabase.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/JSONParser.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/system_error.h" + +namespace clang { +namespace tooling { + +namespace { + +/// \brief A parser for JSON escaped strings of command line arguments. +/// +/// Assumes \-escaping for quoted arguments (see the documentation of +/// unescapeJSONCommandLine(...)). +class CommandLineArgumentParser { + public: + CommandLineArgumentParser(StringRef CommandLine) + : Input(CommandLine), Position(Input.begin()-1) {} + + std::vector<std::string> parse() { + bool HasMoreInput = true; + while (HasMoreInput && nextNonWhitespace()) { + std::string Argument; + HasMoreInput = parseStringInto(Argument); + CommandLine.push_back(Argument); + } + return CommandLine; + } + + private: + // All private methods return true if there is more input available. + + bool parseStringInto(std::string &String) { + do { + if (*Position == '"') { + if (!parseQuotedStringInto(String)) return false; + } else { + if (!parseFreeStringInto(String)) return false; + } + } while (*Position != ' '); + return true; + } + + bool parseQuotedStringInto(std::string &String) { + if (!next()) return false; + while (*Position != '"') { + if (!skipEscapeCharacter()) return false; + String.push_back(*Position); + if (!next()) return false; + } + return next(); + } + + bool parseFreeStringInto(std::string &String) { + do { + if (!skipEscapeCharacter()) return false; + String.push_back(*Position); + if (!next()) return false; + } while (*Position != ' ' && *Position != '"'); + return true; + } + + bool skipEscapeCharacter() { + if (*Position == '\\') { + return next(); + } + return true; + } + + bool nextNonWhitespace() { + do { + if (!next()) return false; + } while (*Position == ' '); + return true; + } + + bool next() { + ++Position; + if (Position == Input.end()) return false; + // Remove the JSON escaping first. This is done unconditionally. + if (*Position == '\\') ++Position; + return Position != Input.end(); + } + + const StringRef Input; + StringRef::iterator Position; + std::vector<std::string> CommandLine; +}; + +std::vector<std::string> unescapeJSONCommandLine( + StringRef JSONEscapedCommandLine) { + CommandLineArgumentParser parser(JSONEscapedCommandLine); + return parser.parse(); +} + +} // end namespace + +CompilationDatabase::~CompilationDatabase() {} + +CompilationDatabase * +CompilationDatabase::loadFromDirectory(StringRef BuildDirectory, + std::string &ErrorMessage) { + llvm::SmallString<1024> JSONDatabasePath(BuildDirectory); + llvm::sys::path::append(JSONDatabasePath, "compile_commands.json"); + llvm::OwningPtr<CompilationDatabase> Database( + JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage)); + if (!Database) { + return NULL; + } + return Database.take(); +} + +JSONCompilationDatabase * +JSONCompilationDatabase::loadFromFile(StringRef FilePath, + std::string &ErrorMessage) { + llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer; + llvm::error_code Result = + llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer); + if (Result != 0) { + ErrorMessage = "Error while opening JSON database: " + Result.message(); + return NULL; + } + llvm::OwningPtr<JSONCompilationDatabase> Database( + new JSONCompilationDatabase(DatabaseBuffer.take())); + if (!Database->parse(ErrorMessage)) + return NULL; + return Database.take(); +} + +JSONCompilationDatabase * +JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString, + std::string &ErrorMessage) { + llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer( + llvm::MemoryBuffer::getMemBuffer(DatabaseString)); + llvm::OwningPtr<JSONCompilationDatabase> Database( + new JSONCompilationDatabase(DatabaseBuffer.take())); + if (!Database->parse(ErrorMessage)) + return NULL; + return Database.take(); +} + +std::vector<CompileCommand> +JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { + llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator + CommandsRefI = IndexByFile.find(FilePath); + if (CommandsRefI == IndexByFile.end()) + return std::vector<CompileCommand>(); + const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue(); + std::vector<CompileCommand> Commands; + for (int I = 0, E = CommandsRef.size(); I != E; ++I) { + Commands.push_back(CompileCommand( + // FIXME: Escape correctly: + CommandsRef[I].first, + unescapeJSONCommandLine(CommandsRef[I].second))); + } + return Commands; +} + +bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { + llvm::SourceMgr SM; + llvm::JSONParser Parser(Database->getBuffer(), &SM); + llvm::JSONValue *Root = Parser.parseRoot(); + if (Root == NULL) { + ErrorMessage = "Error while parsing JSON."; + return false; + } + llvm::JSONArray *Array = dyn_cast<llvm::JSONArray>(Root); + if (Array == NULL) { + ErrorMessage = "Expected array."; + return false; + } + for (llvm::JSONArray::const_iterator AI = Array->begin(), AE = Array->end(); + AI != AE; ++AI) { + const llvm::JSONObject *Object = dyn_cast<llvm::JSONObject>(*AI); + if (Object == NULL) { + ErrorMessage = "Expected object."; + return false; + } + StringRef EntryDirectory; + StringRef EntryFile; + StringRef EntryCommand; + for (llvm::JSONObject::const_iterator KVI = Object->begin(), + KVE = Object->end(); + KVI != KVE; ++KVI) { + const llvm::JSONValue *Value = (*KVI)->Value; + if (Value == NULL) { + ErrorMessage = "Expected value."; + return false; + } + const llvm::JSONString *ValueString = + dyn_cast<llvm::JSONString>(Value); + if (ValueString == NULL) { + ErrorMessage = "Expected string as value."; + return false; + } + if ((*KVI)->Key->getRawText() == "directory") { + EntryDirectory = ValueString->getRawText(); + } else if ((*KVI)->Key->getRawText() == "file") { + EntryFile = ValueString->getRawText(); + } else if ((*KVI)->Key->getRawText() == "command") { + EntryCommand = ValueString->getRawText(); + } else { + ErrorMessage = (Twine("Unknown key: \"") + + (*KVI)->Key->getRawText() + "\"").str(); + return false; + } + } + IndexByFile[EntryFile].push_back( + CompileCommandRef(EntryDirectory, EntryCommand)); + } + return true; +} + +} // end namespace tooling +} // end namespace clang + |