You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

192 lines
7.7 KiB

#include "compile_commands.h"
#include "clangmm.h"
#include <boost/property_tree/json_parser.hpp>
#include <regex>
std::vector<std::string> CompileCommands::Command::parameter_values(const std::string &parameter_name) const {
std::vector<std::string> parameter_values;
bool found_argument = false;
for(auto &parameter : parameters) {
if(found_argument) {
parameter_values.emplace_back(parameter);
found_argument = false;
}
else if(parameter == parameter_name)
found_argument = true;
}
return parameter_values;
}
CompileCommands::CompileCommands(const boost::filesystem::path &build_path) {
try {
boost::property_tree::ptree root_pt;
boost::property_tree::json_parser::read_json((build_path / "compile_commands.json").string(), root_pt);
auto commands_pt = root_pt.get_child("");
for(auto &command : commands_pt) {
boost::filesystem::path directory = command.second.get<std::string>("directory");
auto parameters_str = command.second.get<std::string>("command");
boost::filesystem::path file = command.second.get<std::string>("file");
std::vector<std::string> parameters;
bool backslash = false;
bool single_quote = false;
bool double_quote = false;
size_t parameter_start_pos = std::string::npos;
size_t parameter_size = 0;
auto add_parameter = [&parameters, &parameters_str, &parameter_start_pos, &parameter_size] {
auto parameter = parameters_str.substr(parameter_start_pos, parameter_size);
// Remove escaping
for(size_t c = 0; c < parameter.size() - 1; ++c) {
if(parameter[c] == '\\')
parameter.replace(c, 2, std::string() + parameter[c + 1]);
}
parameters.emplace_back(parameter);
};
for(size_t c = 0; c < parameters_str.size(); ++c) {
if(backslash)
backslash = false;
else if(parameters_str[c] == '\\')
backslash = true;
else if((parameters_str[c] == ' ' || parameters_str[c] == '\t') && !backslash && !single_quote && !double_quote) {
if(parameter_start_pos != std::string::npos) {
add_parameter();
parameter_start_pos = std::string::npos;
parameter_size = 0;
}
continue;
}
else if(parameters_str[c] == '\'' && !backslash && !double_quote) {
single_quote = !single_quote;
continue;
}
else if(parameters_str[c] == '\"' && !backslash && !single_quote) {
double_quote = !double_quote;
continue;
}
if(parameter_start_pos == std::string::npos)
parameter_start_pos = c;
++parameter_size;
}
if(parameter_start_pos != std::string::npos)
add_parameter();
commands.emplace_back(Command{directory, parameters, boost::filesystem::absolute(file, build_path)});
}
}
catch(...) {
}
}
std::vector<std::string> CompileCommands::get_arguments(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) {
std::string default_std_argument = "-std=c++1y";
auto extension = file_path.extension().string();
bool is_header = CompileCommands::is_header(file_path) || extension.empty(); // Include std C++ headers that are without extensions
std::vector<std::string> arguments;
if(!build_path.empty()) {
clangmm::CompilationDatabase db(build_path.string());
if(db) {
clangmm::CompileCommands commands(file_path.string(), db);
auto cmds = commands.get_commands();
for(auto &cmd : cmds) {
auto cmd_arguments = cmd.get_arguments();
bool ignore_next = false;
for(size_t c = 1; c < cmd_arguments.size(); c++) {
if(ignore_next) {
ignore_next = false;
continue;
}
else if(cmd_arguments[c] == "-o" || cmd_arguments[c] == "-c" ||
cmd_arguments[c] == "-x" || // Remove language arguments since some tools add languages not understood by clang
(is_header && cmd_arguments[c] == "-include-pch") || // Header files should not use precompiled headers
cmd_arguments[c] == "-MF") { // Exclude dependency file generation
ignore_next = true;
continue;
}
arguments.emplace_back(cmd_arguments[c]);
}
}
}
else
arguments.emplace_back(default_std_argument);
}
else
arguments.emplace_back(default_std_argument);
auto clang_version_string = clangmm::to_string(clang_getClangVersion());
const static std::regex clang_version_regex(R"(^[A-Za-z ]+([0-9.]+).*$)");
std::smatch sm;
if(std::regex_match(clang_version_string, sm, clang_version_regex)) {
auto clang_version = sm[1].str();
arguments.emplace_back("-I/usr/lib/clang/" + clang_version + "/include");
arguments.emplace_back("-I/usr/lib64/clang/" + clang_version + "/include"); // For Fedora
#if defined(__APPLE__) && CINDEX_VERSION_MAJOR == 0 && CINDEX_VERSION_MINOR < 32 // TODO: remove during 2018 if llvm3.7 is no longer in homebrew (CINDEX_VERSION_MINOR=32 equals clang-3.8 I think)
arguments.emplace_back("-I/usr/local/Cellar/llvm/" + clang_version + "/lib/clang/" + clang_version + "/include");
arguments.emplace_back("-I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1");
arguments.emplace_back("-I/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1"); //Added for OS X 10.11
#endif
#ifdef _WIN32
auto env_msystem_prefix = std::getenv("MSYSTEM_PREFIX");
if(env_msystem_prefix != nullptr)
arguments.emplace_back("-I" + (boost::filesystem::path(env_msystem_prefix) / "lib/clang" / clang_version / "include").string());
#endif
}
arguments.emplace_back("-fretain-comments-from-system-headers");
if(is_header) {
arguments.emplace_back("-Wno-pragma-once-outside-header");
arguments.emplace_back("-Wno-pragma-system-header-outside-header");
arguments.emplace_back("-Wno-include-next-outside-header");
}
if(extension == ".cu" || extension == ".cuh") {
arguments.emplace_back("-xcuda");
arguments.emplace_back("-D__CUDACC__");
arguments.emplace_back("-include");
arguments.emplace_back("cuda_runtime.h");
arguments.emplace_back("-ferror-limit=1000"); // CUDA headers redeclares some std functions
}
else if(extension == ".cl") {
arguments.emplace_back("-xcl");
arguments.emplace_back("-cl-std=CL2.0");
arguments.emplace_back("-Xclang");
arguments.emplace_back("-finclude-default-header");
arguments.emplace_back("-Wno-gcc-compat");
}
else if(is_header)
arguments.emplace_back("-xc++");
if(!build_path.empty()) {
arguments.emplace_back("-working-directory");
arguments.emplace_back(build_path.string());
}
return arguments;
}
bool CompileCommands::is_header(const boost::filesystem::path &path) {
auto ext = path.extension();
if(ext == ".h" || // c headers
ext == ".hh" || ext == ".hp" || ext == ".hpp" || ext == ".h++" || ext == ".tcc" || // c++ headers
ext == ".cuh") // CUDA headers
return true;
else
return false;
}
bool CompileCommands::is_source(const boost::filesystem::path &path) {
auto ext = path.extension();
if(ext == ".c" || // c sources
ext == ".cpp" || ext == ".cxx" || ext == ".cc" || ext == ".C" || ext == ".c++" || // c++ sources
ext == ".cu" || // CUDA sources
ext == ".cl") // OpenCL sources
return true;
else
return false;
}