#include "cmake.h" #include "filesystem.h" #include "dialogs.h" #include "config.h" #include "terminal.h" #include 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 boost::regex project_regex("^ *project *\\(.*$"); boost::smatch sm; if(boost::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); else break; 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(); } } boost::filesystem::path CMake::get_default_build_path() { boost::filesystem::path default_build_path=Config::get().project.default_build_path; const std::string path_variable_project_directory_name=""; size_t pos=0; auto default_build_path_string=default_build_path.string(); auto path_filename_string=project_path.filename().string(); while((pos=default_build_path_string.find(path_variable_project_directory_name, pos))!=std::string::npos) { default_build_path_string.replace(pos, path_variable_project_directory_name.size(), path_filename_string); pos+=path_filename_string.size(); } if(pos!=0) default_build_path=default_build_path_string; if(default_build_path.is_relative()) default_build_path=project_path/default_build_path; return default_build_path; } boost::filesystem::path CMake::get_debug_build_path() { boost::filesystem::path debug_build_path=Config::get().project.debug_build_path; const std::string path_variable_project_directory_name=""; size_t pos=0; auto debug_build_path_string=debug_build_path.string(); auto path_filename_string=project_path.filename().string(); while((pos=debug_build_path_string.find(path_variable_project_directory_name, pos))!=std::string::npos) { debug_build_path_string.replace(pos, path_variable_project_directory_name.size(), path_filename_string); pos+=path_filename_string.size(); } if(pos!=0) debug_build_path=debug_build_path_string; const std::string path_variable_default_build_path=""; pos=0; debug_build_path_string=debug_build_path.string(); auto default_build_path=Config::get().project.default_build_path; while((pos=debug_build_path_string.find(path_variable_default_build_path, pos))!=std::string::npos) { debug_build_path_string.replace(pos, path_variable_default_build_path.size(), default_build_path); pos+=default_build_path.size(); } if(pos!=0) debug_build_path=debug_build_path_string; if(debug_build_path.is_relative()) debug_build_path=project_path/debug_build_path; return debug_build_path; } bool CMake::update_default_build(bool force) { if(project_path.empty()) return false; if(!boost::filesystem::exists(project_path/"CMakeLists.txt")) return false; auto default_build_path=get_default_build_path(); if(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)+" -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); size_t pos=0; while((pos=compile_commands_file.find("-I/", pos))!=std::string::npos) { if(pos+3 message; message=std::unique_ptr(new Dialog::Message("Creating/updating debug build")); auto exit_status=Terminal::get().process(Config::get().project.cmake_command+" "+ filesystem::escape_argument(project_path)+" -DCMAKE_BUILD_TYPE=Debug", debug_build_path); if(message) message->hide(); if(exit_status==EXIT_SUCCESS) return true; return false; } boost::filesystem::path CMake::get_executable(const boost::filesystem::path &file_path) { auto executables = get_functions_parameters("add_executable"); //Attempt to find executable based add_executable files and opened tab boost::filesystem::path executable_path; if(!file_path.empty()) { for(auto &executable: executables) { if(executable.second.size()>1) { for(size_t c=1;c0 && executables[0].second.size()>0) executable_path=executables[0].first.parent_path()/executables[0].second[0]; return executable_path; } void CMake::read_files() { for(auto &path: paths) files.emplace_back(filesystem::read(path)); } void CMake::remove_tabs() { for(auto &file: files) { for(auto &chr: file) { if(chr=='\t') chr=' '; } } } void CMake::remove_comments() { for(auto &file: files) { size_t pos=0; size_t comment_start; bool inside_comment=false; while(posstart_line) { auto line=file.substr(start_line, end_line-start_line); boost::smatch sm; const boost::regex set_regex("^ *set *\\( *([A-Za-z_][A-Za-z_0-9]*) +(.*)\\) *$"); if(boost::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 { const boost::regex project_regex("^ *project *\\( *([^ ]+).*\\) *$"); if(boost::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; } } } pos=end_line+1; } } } void CMake::parse_variable_parameters(std::string &data) { size_t pos=0; bool inside_quote=false; char last_char=0; while(pos 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 > > CMake::get_functions_parameters(const std::string &name) { if(!parsed) parse(); std::vector > > functions; size_t file_c=0; for(auto &file: files) { size_t pos=0; while(posstart_line) { auto line=file.substr(start_line, end_line-start_line); const boost::regex function_regex("^ *"+name+" *\\( *(.*)\\) *$"); boost::smatch sm; if(boost::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(functions.begin(), paths[file_c], parameters); } } pos=end_line+1; } file_c++; } return functions; }