Add basic compilation database parsing
This commit is contained in:
parent
1053429b00
commit
d14965b24e
4 changed files with 283 additions and 29 deletions
|
|
@ -7,6 +7,8 @@
|
|||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include <clang-c/CXCompilationDatabase.h>
|
||||
|
||||
#include "libclang_visitor.hpp"
|
||||
#include "raii_wrapper.hpp"
|
||||
#include "parse_error.hpp"
|
||||
|
|
@ -33,6 +35,22 @@ const std::vector<std::string>& detail::libclang_compile_config_access::flags(
|
|||
return config.get_flags();
|
||||
}
|
||||
|
||||
libclang_compilation_database::libclang_compilation_database(const std::string& build_directory)
|
||||
{
|
||||
static_assert(std::is_same<database, CXCompilationDatabase>::value, "forgot to update type");
|
||||
|
||||
auto error = CXCompilationDatabase_NoError;
|
||||
database_ = clang_CompilationDatabase_fromDirectory(build_directory.c_str(), &error);
|
||||
if (error != CXCompilationDatabase_NoError)
|
||||
throw libclang_error("unable to load compilation database");
|
||||
}
|
||||
|
||||
libclang_compilation_database::~libclang_compilation_database()
|
||||
{
|
||||
if (database_)
|
||||
clang_CompilationDatabase_dispose(database_);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
int parse_number(const char*& str)
|
||||
|
|
@ -65,6 +83,123 @@ libclang_compile_config::libclang_compile_config() : compile_config({})
|
|||
define_macro("__cppast_version_minor__", CPPAST_VERSION_MINOR);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
struct cxcompile_commands_deleter
|
||||
{
|
||||
void operator()(CXCompileCommands cmds)
|
||||
{
|
||||
clang_CompileCommands_dispose(cmds);
|
||||
}
|
||||
};
|
||||
|
||||
using cxcompile_commands = detail::raii_wrapper<CXCompileCommands, cxcompile_commands_deleter>;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
bool is_flag(const detail::cxstring& str)
|
||||
{
|
||||
return str.length() > 1u && str[0] == '-';
|
||||
}
|
||||
|
||||
const char* find_flag_arg_sep(const std::string& last_flag)
|
||||
{
|
||||
if (last_flag[1] == 'D')
|
||||
// no separator, equal is part of the arg
|
||||
return nullptr;
|
||||
return std::strchr(last_flag.c_str(), '=');
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
void parse_flags(CXCompileCommand cmd, Func callback)
|
||||
{
|
||||
auto no_args = clang_CompileCommand_getNumArgs(cmd);
|
||||
std::string last_flag;
|
||||
for (auto i = 1u /* 0 is compiler executable */; i != no_args; ++i)
|
||||
{
|
||||
detail::cxstring str(clang_CompileCommand_getArg(cmd, i));
|
||||
if (is_flag(str))
|
||||
{
|
||||
if (!last_flag.empty())
|
||||
{
|
||||
// process last flag
|
||||
std::string args;
|
||||
if (auto ptr = find_flag_arg_sep(last_flag))
|
||||
{
|
||||
auto pos = std::size_t(ptr - last_flag.c_str());
|
||||
++ptr;
|
||||
while (*ptr)
|
||||
args += *ptr++;
|
||||
last_flag.erase(pos);
|
||||
}
|
||||
else if (last_flag.size() > 2u)
|
||||
{
|
||||
// assume two character flag
|
||||
args = last_flag.substr(2u);
|
||||
last_flag.erase(2u);
|
||||
}
|
||||
|
||||
callback(std::move(last_flag), std::move(args));
|
||||
}
|
||||
|
||||
last_flag = str.std_str();
|
||||
}
|
||||
else if (!last_flag.empty())
|
||||
{
|
||||
// we have flags + args
|
||||
callback(std::move(last_flag), str.std_str());
|
||||
last_flag.clear();
|
||||
}
|
||||
// else skip argument
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
libclang_compile_config::libclang_compile_config(const libclang_compilation_database& database,
|
||||
const std::string& file)
|
||||
: libclang_compile_config()
|
||||
{
|
||||
auto cxcommands =
|
||||
clang_CompilationDatabase_getCompileCommands(database.database_, file.c_str());
|
||||
if (cxcommands == nullptr)
|
||||
throw libclang_error(detail::format("no compile commands specified for file '", file, "'"));
|
||||
cxcompile_commands commands(cxcommands);
|
||||
|
||||
auto size = clang_CompileCommands_getSize(commands.get());
|
||||
for (auto i = 0u; i != size; ++i)
|
||||
{
|
||||
auto cmd = clang_CompileCommands_getCommand(commands.get(), i);
|
||||
auto dir = detail::cxstring(clang_CompileCommand_getDirectory(cmd));
|
||||
parse_flags(cmd, [&](std::string flag, std::string args) {
|
||||
if (flag == "-I")
|
||||
{
|
||||
if (args.front() == '/' || args.front() == '\\')
|
||||
{
|
||||
add_flag(std::move(flag) + std::move(args));
|
||||
}
|
||||
else
|
||||
{
|
||||
// path relative to the directory
|
||||
if (dir[dir.length() - 1] != '/' && dir[dir.length() - 1] != '\\')
|
||||
add_flag(std::move(flag) + dir.std_str() + '/' + std::move(args));
|
||||
else
|
||||
add_flag(std::move(flag) + dir.std_str() + std::move(args));
|
||||
}
|
||||
}
|
||||
else if (flag == "-D" || flag == "-U")
|
||||
// preprocessor options
|
||||
this->add_flag(std::move(flag) + std::move(args));
|
||||
else if (flag == "-std")
|
||||
// standard
|
||||
this->add_flag(std::move(flag) + "=" + std::move(args));
|
||||
else if (flag == "-f" && (args == "ms-compatibility" || args == "ms-extensions"))
|
||||
// other options
|
||||
this->add_flag(std::move(flag) + std::move(args));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void libclang_compile_config::do_set_flags(cpp_standard standard, compile_flags flags)
|
||||
{
|
||||
switch (standard)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue