#include "compile_commands.hpp" #include "clangmm.hpp" #include "config.hpp" #include "filesystem.hpp" #include "json.hpp" #include "terminal.hpp" #include "utility.hpp" #include #include std::vector CompileCommands::Command::get_argument_values(const std::string &argument_name) const { std::vector argument_values; bool found_argument = false; for(auto &argument : arguments) { if(found_argument) { argument_values.emplace_back(argument); found_argument = false; } else if(argument == argument_name) found_argument = true; } return argument_values; } CompileCommands::CompileCommands(const boost::filesystem::path &build_path) { clangmm::CompilationDatabase db(build_path.string()); if(db) { clangmm::CompileCommands compile_commands({}, db); for(auto &command : compile_commands.get_commands()) commands.emplace_back(Command{clangmm::to_string(clang_CompileCommand_getDirectory(command.cx_command)), command.get_arguments(), filesystem::get_absolute_path(clangmm::to_string(clang_CompileCommand_getFilename(command.cx_command)), build_path)}); } } std::vector CompileCommands::get_arguments(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) { 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 arguments; if(!build_path.empty()) { clangmm::CompilationDatabase db(build_path.string()); if(db) { std::vector> compile_commands_arguments; // If header file, use source file flags if they are in the same folder if(is_header && !extension.empty()) { auto parent_path = file_path.parent_path(); clangmm::CompileCommands compile_commands({}, db); for(auto &command : compile_commands.get_commands()) { boost::filesystem::path file = filesystem::get_absolute_path(clangmm::to_string(clang_CompileCommand_getFilename(command.cx_command)), build_path); if(file.parent_path() == parent_path) compile_commands_arguments.emplace_back(command.get_arguments()); } } if(compile_commands_arguments.empty()) { clangmm::CompileCommands compile_commands(file_path.string(), db); for(auto &command : compile_commands.get_commands()) compile_commands_arguments.emplace_back(command.get_arguments()); } for(auto &command_arguments : compile_commands_arguments) { bool ignore_next = false; for(size_t i = 1; i + 1 < command_arguments.size(); i++) { // Exclude first and last argument if(ignore_next) ignore_next = false; else if(command_arguments[i] == "-o" || command_arguments[i] == "-x" || // Remove language arguments since some tools add languages not understood by clang (is_header && command_arguments[i] == "-include-pch") || // Header files should not use precompiled headers command_arguments[i] == "-MF") { // Exclude dependency file generation ignore_next = true; } else if(command_arguments[i] == "-c") { } else if(command_arguments[i] == "--") // End of command options break; else arguments.emplace_back(command_arguments[i]); } } } } #if defined(__APPLE__) static auto resource_path = []() -> std::string { boost::system::error_code ec; for(boost::filesystem::directory_iterator it(boost::filesystem::path(LIBCLANG_LIBRARY_DIR) / "clang", ec), end; it != end; ++it) return it->path().string(); return {}; }(); if(!resource_path.empty()) { arguments.emplace_back("-resource-dir"); arguments.emplace_back(resource_path); } #elif defined(_WIN32) auto clang_version_string = clangmm::to_string(clang_getClangVersion()); const static std::regex clang_version_regex(R"(^[A-Za-z ]+([0-9.]+).*$)", std::regex::optimize); std::smatch sm; if(std::regex_match(clang_version_string, sm, clang_version_regex)) { auto clang_version = sm[1].str(); auto env_msystem_prefix = std::getenv("MSYSTEM_PREFIX"); if(env_msystem_prefix) arguments.emplace_back("-I" + (boost::filesystem::path(env_msystem_prefix) / "lib/clang" / clang_version / "include").string()); } #else static auto resource_path = []() -> std::string { if(!filesystem::find_executable("clang++").empty()) { std::stringstream stdin_stream, stdout_stream; auto exit_status = Terminal::get().process(stdin_stream, stdout_stream, "clang++ -print-resource-dir"); if(exit_status != 0) return {}; auto path = stdout_stream.str(); if(!path.empty()) path.pop_back(); return path; } else { boost::system::error_code ec; for(boost::filesystem::directory_iterator it(boost::filesystem::path(LIBCLANG_LIBRARY_DIR) / "clang", ec), end; it != end; ++it) return it->path().string(); } return {}; }(); if(!resource_path.empty()) { arguments.emplace_back("-resource-dir"); arguments.emplace_back(resource_path); } #endif // Do not add -fretain-comments-from-system-headers if pch is used, since the pch was most likely made without this flag if(std::none_of(arguments.begin(), arguments.end(), [](const std::string &argument) { return argument == "-include-pch"; })) 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 && extension != ".h") // libclang crashes if it tries to parse .h files as C++ file in a C project arguments.emplace_back("-xc++"); if(!build_path.empty()) { arguments.emplace_back("-working-directory"); arguments.emplace_back(build_path.string()); } if(Config::get().source.clang_tidy_enable) { arguments.emplace_back("-Xclang"); arguments.emplace_back("-add-plugin"); arguments.emplace_back("-Xclang"); arguments.emplace_back("clang-tidy"); if(!Config::get().source.clang_tidy_checks.empty()) { arguments.emplace_back("-Xclang"); arguments.emplace_back("-plugin-arg-clang-tidy"); arguments.emplace_back("-Xclang"); arguments.emplace_back("-checks=" + Config::get().source.clang_tidy_checks); } } 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; }