#include "cmake.h" #include "filesystem.h" #include "dialogs.h" #include "config.h" #include "terminal.h" #include #include "compile_commands.h" CMake::CMake(const boost::filesystem::path &path) { const auto find_cmake_project=[this](const boost::filesystem::path &cmake_path) { for(auto &line: filesystem::read_lines(cmake_path)) { const static std::regex project_regex("^ *project *\\(.*$", std::regex::icase); std::smatch sm; if(std::regex_match(line, sm, project_regex)) return true; } return false; }; auto search_path=boost::filesystem::is_directory(path)?path:path.parent_path(); while(true) { auto search_cmake_path=search_path/"CMakeLists.txt"; if(boost::filesystem::exists(search_cmake_path)) { paths.emplace(paths.begin(), search_cmake_path); if(find_cmake_project(search_cmake_path)) { project_path=search_path; break; } } if(search_path==search_path.root_directory()) break; search_path=search_path.parent_path(); } } bool CMake::update_default_build(const boost::filesystem::path &default_build_path, bool force) { if(project_path.empty() || !boost::filesystem::exists(project_path/"CMakeLists.txt") || default_build_path.empty()) return false; if(!boost::filesystem::exists(default_build_path)) { boost::system::error_code ec; boost::filesystem::create_directories(default_build_path, ec); if(ec) { Terminal::get().print("Error: could not create "+default_build_path.string()+": "+ec.message()+"\n", true); return false; } } if(!force && boost::filesystem::exists(default_build_path/"compile_commands.json")) return true; auto compile_commands_path=default_build_path/"compile_commands.json"; Dialog::Message message("Creating/updating default build"); auto exit_status=Terminal::get().process(Config::get().project.cmake.command+' '+ filesystem::escape_argument(project_path.string())+" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON", default_build_path); message.hide(); if(exit_status==EXIT_SUCCESS) { #ifdef _WIN32 //Temporary fix to MSYS2's libclang auto compile_commands_file=filesystem::read(compile_commands_path); auto replace_drive = [&compile_commands_file](const std::string& param) { size_t pos=0; auto param_size = param.length(); while((pos=compile_commands_file.find(param+"/", pos))!=std::string::npos) { if(pos+param_size+1 cmake_executables; for(auto ¶meter: parameters) { if(parameter.second.size()>1 && parameter.second[0].size()>0 && parameter.second[0].compare(0, 2, "${")!=0) { auto executable=(parameter.first.parent_path()/parameter.second[0]).string(); auto project_path_str=project_path.string(); size_t pos=executable.find(project_path_str); if(pos!=std::string::npos) executable.replace(pos, project_path_str.size(), build_path.string()); cmake_executables.emplace_back(executable); } } CompileCommands compile_commands(build_path); std::vector> command_files_and_maybe_executables; for(auto &command: compile_commands.commands) { auto command_file=filesystem::get_normal_path(command.file); auto values=command.parameter_values("-o"); if(!values.empty()) { size_t pos; values[0].erase(0, 11); if((pos=values[0].find(".dir"))!=std::string::npos) { auto executable=command.directory/values[0].substr(0, pos); command_files_and_maybe_executables.emplace_back(command_file, executable); } } } size_t best_match_size=-1; boost::filesystem::path best_match_executable; for(auto &cmake_executable: cmake_executables) { for(auto &command_file_and_maybe_executable: command_files_and_maybe_executables) { auto &command_file=command_file_and_maybe_executable.first; auto &maybe_executable=command_file_and_maybe_executable.second; if(cmake_executable==maybe_executable) { if(command_file==file_path) return maybe_executable; auto command_file_directory=command_file.parent_path(); if(filesystem::file_in_path(file_path, command_file_directory)) { auto size=static_cast(std::distance(command_file_directory.begin(), command_file_directory.end())); if(best_match_size==static_cast(-1) || best_match_size(std::distance(command_file_directory.begin(), command_file_directory.end())); if(best_match_size==static_cast(-1) || best_match_size(-1)) last_char=data[pos]; pos++; } for(auto &var: variables) { auto pos=data.find("${"+var.first+'}'); while(pos!=std::string::npos) { data.replace(pos, var.first.size()+3, var.second); pos=data.find("${"+var.first+'}'); } } //Remove variables we do not know: pos=data.find("${"); auto pos_end=data.find("}", pos+2); while(pos!=std::string::npos && pos_end!=std::string::npos) { data.erase(pos, pos_end-pos+1); pos=data.find("${"); pos_end=data.find("}", pos+2); } } void CMake::parse() { read_files(); remove_tabs(); remove_comments(); remove_newlines_inside_parentheses(); parsed=true; } std::vector CMake::get_function_parameters(std::string &data) { std::vector parameters; size_t pos=0; size_t parameter_pos=0; bool inside_quote=false; char last_char=0; while(pos(-1)) last_char=data[pos]; pos++; } parameters.emplace_back(data.substr(parameter_pos)); for(auto &var: variables) { for(auto ¶meter: parameters) { auto pos=parameter.find("${"+var.first+'}'); while(pos!=std::string::npos) { parameter.replace(pos, var.first.size()+3, var.second); pos=parameter.find("${"+var.first+'}'); } } } return parameters; } std::vector > > CMake::get_functions_parameters(const std::string &name) { const std::regex function_regex("^ *"+name+" *\\( *(.*)\\) *$", std::regex::icase); variables.clear(); if(!parsed) parse(); std::vector > > functions; for(size_t c=0;cstart_line) { auto line=files[c].substr(start_line, end_line-start_line); std::smatch sm; const static std::regex set_regex("^ *set *\\( *([A-Za-z_][A-Za-z_0-9]*) +(.*)\\) *$", std::regex::icase); const static std::regex project_regex("^ *project *\\( *([^ ]+).*\\) *$", std::regex::icase); if(std::regex_match(line, sm, set_regex)) { auto data=sm[2].str(); while(data.size()>0 && data.back()==' ') data.pop_back(); parse_variable_parameters(data); variables[sm[1].str()]=data; } else if(std::regex_match(line, sm, project_regex)) { auto data=sm[1].str(); parse_variable_parameters(data); variables["CMAKE_PROJECT_NAME"]=data; //TODO: is this variable deprecated/non-standard? variables["PROJECT_NAME"]=data; } if(std::regex_match(line, sm, function_regex)) { auto data=sm[1].str(); while(data.size()>0 && data.back()==' ') data.pop_back(); auto parameters=get_function_parameters(data); functions.emplace_back(paths[c], parameters); } } pos=end_line+1; } } return functions; }