From d80c9f9e2415baa337c1745532a8aae8747364d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Lien=20Sell=C3=A6g?= Date: Mon, 3 Aug 2015 10:50:23 +0200 Subject: [PATCH 01/22] Update install.md --- docs/install.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/install.md b/docs/install.md index 3e06b5e..345a815 100644 --- a/docs/install.md +++ b/docs/install.md @@ -6,7 +6,7 @@ First dependencies: ```sh $ sudo apt-get install libboost-python-dev libboost-filesystem-dev libboost-log-dev libboost-test-dev libboost-thread-dev libboost-system-dev libgtkmm-3.0-dev libgtksourceview2.0-dev libgtksourceviewmm-3.0-dev -libpython-dev libclang-dev make cmake gcc +libpython-dev libclang-dev make cmake gcc g++ ``` Install the project: ```sh From 08027264d075ba153293e2711ab965c60e709947 Mon Sep 17 00:00:00 2001 From: eidheim Date: Mon, 3 Aug 2015 15:34:01 +0200 Subject: [PATCH 02/22] Now searches for project path if a clang-file is opened without a directory opened. Some minor fixes also. --- src/notebook.cc | 71 ++++++++++++++++++++++++++++++++++++------------- src/notebook.h | 3 ++- 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/src/notebook.cc b/src/notebook.cc index b0226cf..3a39842 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -3,6 +3,7 @@ #include "sourcefile.h" #include "singletons.h" #include +#include #include //TODO: remove using namespace std; //TODO: remove @@ -50,19 +51,29 @@ void Notebook::open(std::string path) { } can_read.close(); - auto tmp_project_path=project_path; - if(tmp_project_path=="") { - tmp_project_path=boost::filesystem::path(path).parent_path().string(); - } auto language=Source::guess_language(path); if(language && (language->get_id()=="chdr" || language->get_id()=="c" || language->get_id()=="cpp" || language->get_id()=="objc")) { - if(boost::filesystem::exists(tmp_project_path+"/CMakeLists.txt") && !boost::filesystem::exists(tmp_project_path+"/compile_commands.json")) { - make_compile_commands(); + auto view_project_path=project_path; + if(view_project_path=="") { + view_project_path=boost::filesystem::path(path).parent_path().string(); + auto found_project_path=find_project_path(view_project_path); + if(found_project_path!="") { + view_project_path=found_project_path; + Singleton::terminal()->print("Project path for "+path+" set to "+view_project_path+"\n"); + } + else + Singleton::terminal()->print("Error: could not find project path for "+path+"\n"); } - source_views.emplace_back(new Source::ClangView(path, tmp_project_path)); + if(boost::filesystem::exists(view_project_path+"/CMakeLists.txt") && !boost::filesystem::exists(view_project_path+"/compile_commands.json")) + make_compile_commands(view_project_path); + source_views.emplace_back(new Source::ClangView(path, view_project_path)); + } + else { + auto view_project_path=project_path; + if(view_project_path=="") + view_project_path=boost::filesystem::path(path).parent_path().string(); + source_views.emplace_back(new Source::GenericView(path, view_project_path, language)); } - else - source_views.emplace_back(new Source::GenericView(path, tmp_project_path, language)); scrolled_windows.emplace_back(new Gtk::ScrolledWindow()); hboxes.emplace_back(new Gtk::HBox()); @@ -101,6 +112,32 @@ void Notebook::open(std::string path) { }; } +std::string Notebook::find_project_path(const std::string &path) { + auto find_cmake_project=[this](const boost::filesystem::path &path) { + auto cmake_path=path; + cmake_path+="/CMakeLists.txt"; + for(auto &line: juci::filesystem::read_lines(cmake_path)) { + const std::regex cmake_project("^ *project *\\(.*$"); + std::smatch sm; + if(std::regex_match(line, sm, cmake_project)) { + return true; + } + } + return false; + }; + + auto boost_path=boost::filesystem::path(path); + if(find_cmake_project(boost_path)) + return boost_path.string(); + do { + boost_path=boost_path.parent_path(); + if(find_cmake_project(boost_path)) + return boost_path.string(); + } while(boost_path!=boost_path.root_directory()); + + return ""; +} + bool Notebook::save(int page) { if(page>=size()) return false; @@ -112,7 +149,7 @@ bool Notebook::save(int page) { //If CMakeLists.txt have been modified: if(boost::filesystem::path(view->file_path).filename().string()=="CMakeLists.txt") { - if(make_compile_commands()) { + if(project_path!="" && make_compile_commands(project_path)) { for(auto source_view: source_views) { if(auto source_clang_view=dynamic_cast(source_view)) { if(project_path==source_view->project_path) { @@ -133,14 +170,12 @@ bool Notebook::save(int page) { return false; } -bool Notebook::make_compile_commands() { - if(project_path.size()>0) { - Singleton::terminal()->print("Creating "+boost::filesystem::path(project_path+"/compile_commands.json").string()+"\n"); - //TODO: Windows... - if(Singleton::terminal()->execute(project_path, "cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 2>&1")) { - //TODO: refresh directories - return true; - } +bool Notebook::make_compile_commands(const std::string &path) { + Singleton::terminal()->print("Creating "+boost::filesystem::path(path+"/compile_commands.json").string()+"\n"); + //TODO: Windows... + if(Singleton::terminal()->execute(path, "cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 2>&1")) { + //TODO: refresh directories + return true; } return false; } diff --git a/src/notebook.h b/src/notebook.h index 43254f6..2c9d980 100644 --- a/src/notebook.h +++ b/src/notebook.h @@ -22,7 +22,8 @@ public: std::string project_path; private: - bool make_compile_commands(); + std::string find_project_path(const std::string &path); + bool make_compile_commands(const std::string &path); bool save_modified_dialog(); std::vector source_views; //Is NOT freed in destructor, this is intended for quick program exit. std::vector > scrolled_windows; From a6ad9f2d2b1ba7e7a9554cf39375dc7f4c81e38d Mon Sep 17 00:00:00 2001 From: eidheim Date: Mon, 3 Aug 2015 16:01:13 +0200 Subject: [PATCH 03/22] Minor fix: Added const to a std::function. --- src/notebook.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/notebook.cc b/src/notebook.cc index 3a39842..1ccbca7 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -113,7 +113,7 @@ void Notebook::open(std::string path) { } std::string Notebook::find_project_path(const std::string &path) { - auto find_cmake_project=[this](const boost::filesystem::path &path) { + const auto find_cmake_project=[this](const boost::filesystem::path &path) { auto cmake_path=path; cmake_path+="/CMakeLists.txt"; for(auto &line: juci::filesystem::read_lines(cmake_path)) { From 28ddd8ba9509b61a539721647bfb33db89d75ea6 Mon Sep 17 00:00:00 2001 From: eidheim Date: Tue, 4 Aug 2015 09:13:44 +0200 Subject: [PATCH 04/22] Minor fixes. --- src/notebook.cc | 2 +- src/source.cc | 64 ++++++++++++++++++++++++------------------------- src/source.h | 4 ++-- 3 files changed, 34 insertions(+), 36 deletions(-) diff --git a/src/notebook.cc b/src/notebook.cc index 1ccbca7..c113daa 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -203,7 +203,7 @@ bool Notebook::close_current_page() { scrolled_windows.erase(scrolled_windows.begin()+page); hboxes.erase(hboxes.begin()+page); if(auto source_clang_view=dynamic_cast(source_view)) - source_clang_view->delete_object(); + source_clang_view->async_delete(); else delete source_view; } diff --git a/src/source.cc b/src/source.cc index b78c6f9..abbd4c9 100644 --- a/src/source.cc +++ b/src/source.cc @@ -308,6 +308,34 @@ Source::View(file_path, project_path) { //TODO: clear tag_class and param_spec? parsing_in_progress=Singleton::terminal()->print_in_progress("Parsing "+file_path); + //GTK-calls must happen in main thread, so the parse_thread + //sends signals to the main thread that it is to call the following functions: + parse_start.connect([this]{ + if(parse_thread_buffer_map_mutex.try_lock()) { + parse_thread_buffer_map=get_buffer_map(); + parse_thread_mapped=true; + parse_thread_buffer_map_mutex.unlock(); + } + parse_thread_go=true; + }); + parse_done.connect([this](){ + if(parse_thread_mapped) { + if(parsing_mutex.try_lock()) { + INFO("Updating syntax"); + update_syntax(); + update_diagnostics(); + update_types(); + clang_readable=true; + set_status(""); + parsing_mutex.unlock(); + INFO("Syntax updated"); + } + parsing_in_progress->done("done"); + } + else { + parse_thread_go=true; + } + }); init_parse(); get_buffer()->signal_changed().connect([this]() { @@ -349,37 +377,7 @@ void Source::ClangViewParse::init_parse() { start_offset, end_offset); update_syntax(); - - //GTK-calls must happen in main thread, so the parse_thread - //sends signals to the main thread that it is to call the following functions: - parse_start.connect([this]{ - if(parse_thread_buffer_map_mutex.try_lock()) { - parse_thread_buffer_map=get_buffer_map(); - parse_thread_mapped=true; - parse_thread_buffer_map_mutex.unlock(); - } - parse_thread_go=true; - }); - - parse_done.connect([this](){ - if(parse_thread_mapped) { - if(parsing_mutex.try_lock()) { - INFO("Updating syntax"); - update_syntax(); - update_diagnostics(); - update_types(); - clang_readable=true; - set_status(""); - parsing_mutex.unlock(); - INFO("Syntax updated"); - } - parsing_in_progress->done("done"); - } - else { - parse_thread_go=true; - } - }); - + set_status("parsing..."); if(parse_thread.joinable()) parse_thread.join(); @@ -546,7 +544,7 @@ void Source::ClangViewParse::update_types() { bool Source::ClangViewParse::on_motion_notify_event(GdkEventMotion* event) { delayed_tooltips_connection.disconnect(); - if(clang_readable) { + if(clang_readable && event->state==0) { Gdk::Rectangle rectangle(event->x, event->y, 1, 1); Tooltips::init(); type_tooltips.show(rectangle); @@ -1068,7 +1066,7 @@ Source::ClangView::ClangView(const std::string& file_path, const std::string& pr }); } -void Source::ClangView::delete_object() { +void Source::ClangView::async_delete() { parsing_in_progress->cancel("canceled, freeing resources in the background"); parse_thread_stop=true; delete_thread=std::thread([this](){ diff --git a/src/source.h b/src/source.h index 4c6b8cc..7bec853 100644 --- a/src/source.h +++ b/src/source.h @@ -125,7 +125,7 @@ namespace Source { bool on_scroll_event(GdkEventScroll* event); static clang::Index clang_index; std::vector get_compilation_commands(); - + Glib::Dispatcher parse_done; Glib::Dispatcher parse_start; std::map parse_thread_buffer_map; @@ -169,7 +169,7 @@ namespace Source { class ClangView : public ClangViewRefactor { public: ClangView(const std::string& file_path, const std::string& project_path); - void delete_object(); + void async_delete(); bool restart_parse(); private: Glib::Dispatcher do_delete_object; From 017d4480a7a2b379a8577670098f3535c49d538a Mon Sep 17 00:00:00 2001 From: eidheim Date: Tue, 4 Aug 2015 09:25:59 +0200 Subject: [PATCH 05/22] Now refreshes directories after running cmake for compilation_commands.json. --- src/notebook.cc | 5 +++-- src/notebook.h | 4 +++- src/window.cc | 2 +- src/window.h | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/notebook.cc b/src/notebook.cc index c113daa..4e18c1f 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -12,7 +12,7 @@ namespace sigc { SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE } -Notebook::Notebook() : Gtk::Notebook() { +Notebook::Notebook(Directories &directories) : Gtk::Notebook(), directories(directories) { Gsv::init(); } @@ -174,7 +174,8 @@ bool Notebook::make_compile_commands(const std::string &path) { Singleton::terminal()->print("Creating "+boost::filesystem::path(path+"/compile_commands.json").string()+"\n"); //TODO: Windows... if(Singleton::terminal()->execute(path, "cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 2>&1")) { - //TODO: refresh directories + if(project_path!="") + directories.open_folder(project_path); return true; } return false; diff --git a/src/notebook.h b/src/notebook.h index 2c9d980..ef95143 100644 --- a/src/notebook.h +++ b/src/notebook.h @@ -8,10 +8,11 @@ #include #include #include +#include "directories.h" class Notebook : public Gtk::Notebook { public: - Notebook(); + Notebook(Directories &directories); Source::View* get_view(int page); int size(); Source::View* get_current_view(); @@ -25,6 +26,7 @@ private: std::string find_project_path(const std::string &path); bool make_compile_commands(const std::string &path); bool save_modified_dialog(); + Directories &directories; std::vector source_views; //Is NOT freed in destructor, this is intended for quick program exit. std::vector > scrolled_windows; std::vector > hboxes; diff --git a/src/window.cc b/src/window.cc index b999717..1c00711 100644 --- a/src/window.cc +++ b/src/window.cc @@ -12,7 +12,7 @@ namespace sigc { SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE } -Window::Window() : box(Gtk::ORIENTATION_VERTICAL) { +Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories) { INFO("Create Window"); set_title("juCi++"); set_default_size(600, 400); diff --git a/src/window.h b/src/window.h index 44c6afb..7100de6 100644 --- a/src/window.h +++ b/src/window.h @@ -10,8 +10,8 @@ class Window : public Gtk::Window { public: Window(); - Notebook notebook; Directories directories; + Notebook notebook; protected: bool on_key_press_event(GdkEventKey *event); bool on_delete_event (GdkEventAny *event); From f83237275253047eebbe9a35687daae04150ab24 Mon Sep 17 00:00:00 2001 From: eidheim Date: Wed, 5 Aug 2015 14:42:27 +0200 Subject: [PATCH 06/22] Cleanup of terminal.*, and added cmake.* to parse CMakeLists.txt. Also fixed compile, and compile and run. Remember to remove ~/.juci/config/config.json to get this version to work. --- src/CMakeLists.txt | 4 +- src/cmake.cc | 260 +++++++++++++++++++++++++++++++++++++++++++++ src/cmake.h | 28 +++++ src/config.cc | 12 --- src/config.h | 1 - src/directories.cc | 76 ++----------- src/directories.h | 3 +- src/files.h | 21 ---- src/notebook.cc | 54 ++-------- src/notebook.h | 3 +- src/singletons.cc | 1 - src/singletons.h | 2 - src/terminal.cc | 117 ++++++++++---------- src/terminal.h | 27 ++--- src/window.cc | 60 ++++++----- src/window.h | 4 +- 16 files changed, 409 insertions(+), 264 deletions(-) create mode 100644 src/cmake.cc create mode 100644 src/cmake.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 54c9549..5f892eb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -79,7 +79,9 @@ if(${validation}) tooltips.h tooltips.cc singletons.h - singletons.cc) + singletons.cc + cmake.h + cmake.cc) add_library(${module} SHARED api diff --git a/src/cmake.cc b/src/cmake.cc new file mode 100644 index 0000000..9095e73 --- /dev/null +++ b/src/cmake.cc @@ -0,0 +1,260 @@ +#include "cmake.h" +#include "sourcefile.h" +#include +#include "singletons.h" + +#include //TODO: remove +using namespace std; //TODO: remove + +CMake::CMake(const boost::filesystem::path &path) { + const auto find_cmake_project=[this](const boost::filesystem::path &cmake_path) { + for(auto &line: juci::filesystem::read_lines(cmake_path)) { + const std::regex project_regex("^ *project *\\(.*$"); + std::smatch sm; + if(std::regex_match(line, sm, project_regex)) { + return true; + } + } + return false; + }; + + auto search_path=boost::filesystem::path(path); + auto search_cmake_path=search_path; + search_cmake_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; + else { + do { + search_path=search_path.parent_path(); + search_cmake_path=search_path; + search_cmake_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; + } + } while(search_path!=search_path.root_directory()); + } + if(project_path!="") { + if(boost::filesystem::exists(project_path.string()+"/CMakeLists.txt") && !boost::filesystem::exists(project_path.string()+"/compile_commands.json")) + create_compile_commands(project_path.string()); + } +} + +bool CMake::create_compile_commands(const std::string &path) { + Singleton::terminal()->print("Creating "+boost::filesystem::path(path+"/compile_commands.json").string()+"\n"); + //TODO: Windows... + if(Singleton::terminal()->execute("cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 2>&1", path)) + return true; + return false; +} + +void CMake::read_files() { + for(auto &path: paths) + files.emplace_back(juci::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); + const std::regex set_regex("^ *set *\\( *([A-Za-z_][A-Za-z_0-9]*) +(.*)\\) *$"); + std::smatch sm; + if(std::regex_match(line, sm, set_regex)) { + std::string data=sm[2]; + while(data.size()>0 && data.back()==' ') + data.pop_back(); + parse_variable_parameters(data); + variables[sm[1]]=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 std::regex function_regex("^ *"+name+" *\\( *(.*)\\) *$"); + std::smatch sm; + if(std::regex_match(line, sm, function_regex)) { + std::string data=sm[1]; + 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; +} diff --git a/src/cmake.h b/src/cmake.h new file mode 100644 index 0000000..f300f15 --- /dev/null +++ b/src/cmake.h @@ -0,0 +1,28 @@ +#ifndef JUCI_CMAKE_H_ +#define JUCI_CMAKE_H_ +#include +#include +#include + +class CMake { +public: + CMake(const boost::filesystem::path &path); + std::vector > > get_functions_parameters(const std::string &name); + static bool create_compile_commands(const std::string &path); + + std::vector paths; + std::vector files; + boost::filesystem::path project_path; + std::unordered_map variables; +private: + void read_files(); + void remove_tabs(); + void remove_comments(); + void remove_newlines_inside_parentheses(); + void find_variables(); + void parse_variable_parameters(std::string &data); + void parse(); + std::vector get_function_parameters(std::string &data); + bool parsed=false; +}; +#endif //JUCI_CMAKE_H_ diff --git a/src/config.cc b/src/config.cc index fd40399..c2bf32f 100644 --- a/src/config.cc +++ b/src/config.cc @@ -11,7 +11,6 @@ MainConfig::MainConfig(Menu &menu) : menu(menu) { GenerateSource(); GenerateKeybindings(); GenerateDirectoryFilter(); - GenerateTerminalCommands(); } void MainConfig::find_or_create_config_files() { @@ -59,17 +58,6 @@ void MainConfig::GenerateSource() { DEBUG("Source cfg fetched"); } -void MainConfig::GenerateTerminalCommands() { - auto terminal_cfg=Singleton::Config::terminal(); - boost::property_tree::ptree source_json = cfg.get_child("project"); - boost::property_tree::ptree compile_commands_json = source_json.get_child("compile_commands"); - boost::property_tree::ptree run_commands_json = source_json.get_child("run_commands"); - for (auto &i : compile_commands_json) - terminal_cfg->compile_commands.emplace_back(i.second.get_value()); - for (auto &i : run_commands_json) - terminal_cfg->run_command=(i.second.get_value()); //TODO: run_commands array->one run_command? -} - void MainConfig::GenerateKeybindings() { boost::filesystem::path path(Singleton::config_dir() + "menu.xml"); if (!boost::filesystem::is_regular_file(path)) { diff --git a/src/config.h b/src/config.h index d1ea90b..46f5142 100644 --- a/src/config.h +++ b/src/config.h @@ -12,7 +12,6 @@ public: void GenerateSource(); void GenerateKeybindings(); void GenerateDirectoryFilter(); - void GenerateTerminalCommands(); private: boost::property_tree::ptree cfg; boost::property_tree::ptree key_tree; diff --git a/src/directories.cc b/src/directories.cc index edac27b..587ce60 100644 --- a/src/directories.cc +++ b/src/directories.cc @@ -5,6 +5,9 @@ #include #include "boost/algorithm/string.hpp" +#include //TODO: remove +using namespace std; //TODO: remove + namespace sigc { SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE } @@ -47,7 +50,14 @@ void Directories::open_folder(const boost::filesystem::path& dir_path) { } tree_store->clear(); - tree_view.get_column(0)->set_title(get_cmakelists_variable(dir_path, "project")); + + if(last_dir_path!=dir_path) + cmake=std::unique_ptr(new CMake(dir_path)); + auto project=cmake->get_functions_parameters("project"); + if(project.size()>0 && project[0].second.size()>0) + tree_view.get_column(0)->set_title(project[0].second[0]); + else + tree_view.get_column(0)->set_title(""); add_paths(dir_path, Gtk::TreeModel::Row(), 0); for(auto &path: expanded_paths) @@ -118,67 +128,3 @@ void Directories::add_paths(const boost::filesystem::path& dir_path, const Gtk:: } } } - -std::string Directories::get_cmakelists_variable(const boost::filesystem::path& dir_path, std::string command_name) { - INFO("fetches cmake variable value for: "+command_name); - std::string project_name; - std::string project_name_var; - boost::filesystem::directory_iterator end_itr; - for (boost::filesystem::directory_iterator itr( dir_path );itr != end_itr;++itr ) { - if (itr->path().filename().string() == "CMakeLists.txt") { - for (auto &line : juci::filesystem::read_lines(itr->path())) { - if (line.find(command_name+"(", 0) != std::string::npos - || line.find(command_name+" (", 0) != std::string::npos ) { - size_t variable_start = line.find("{", 0); - size_t variable_end = line.find("}", variable_start); - project_name_var = line.substr(variable_start+1, - (variable_end)-variable_start-1); - boost::algorithm::trim(project_name_var); - if (variable_start == std::string::npos) { // not a variabel - variable_start = line.find("(", 0); - - variable_end = line.find(' ', variable_start); - if(variable_end != std::string::npos){ - return line.substr(variable_start+1, - (variable_end)-variable_start-1); - } - variable_end = line.find("#", variable_start); - if(variable_end != std::string::npos){ - return line.substr(variable_start+1, - (variable_end)-variable_start-1); - } - variable_end = line.find(")", variable_start); - return line.substr(variable_start+1, - (variable_end)-variable_start-1); - if (variable_start == std::string::npos) { // not a variable - variable_start = line.find("(", 0); - variable_end = line.find(")", variable_start); - INFO("Wasn't a variable, returning value"); - return line.substr(variable_start+1, - (variable_end)-variable_start-1); - } - break; - } - } - } - for (auto &line : juci::filesystem::read_lines(itr->path())) { - if (line.find("set(", 0) != std::string::npos - || line.find("set (", 0) != std::string::npos) { - if( line.find(project_name_var, 0) != std::string::npos) { - size_t variable_start = line.find(project_name_var, 0) - +project_name_var.length(); - size_t variable_end = line.find(")", variable_start); - project_name = line.substr(variable_start+1, - variable_end-variable_start-1); - boost::algorithm::trim(project_name); - INFO("found variable, returning value"); - return project_name; - } - } - } - break; - } - } - INFO("Couldn't find value in CMakeLists.txt"); - return "no project name"; -} diff --git a/src/directories.h b/src/directories.h index ba545ec..9f688fe 100644 --- a/src/directories.h +++ b/src/directories.h @@ -5,6 +5,7 @@ #include #include #include "boost/filesystem.hpp" +#include "cmake.h" class Directories : public Gtk::ScrolledWindow { public: @@ -29,9 +30,9 @@ public: Directories(); void open_folder(const boost::filesystem::path& dir_path); void select_path(const std::string &path); - std::string get_cmakelists_variable(const boost::filesystem::path& dir_path, std::string command_name); std::function on_row_activated; + std::unique_ptr cmake; private: void add_paths(const boost::filesystem::path& dir_path, const Gtk::TreeModel::Row &row, unsigned depth); diff --git a/src/files.h b/src/files.h index 6a34e45..14a3610 100644 --- a/src/files.h +++ b/src/files.h @@ -68,27 +68,6 @@ const std::string configjson = " \"cmakelists.txt\",\n" " \"in-lowercase.pls\"\n" " ]\n" -" },\n" -" \"project\": {\n" -" \"run_commands\": [\n" -" \"./.build/\"\n" -" ],\n" -" \"compile_commands\": [\n" -" \"rm -rf ./.build\",\n" -" \"mkdir ./.build\",\n" -" \"cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -B./.build -H.\",\n" -" \"cd ./.build/; make\",\n" -" \"cp ./.build/compile_commands.json compile_commands.json\"\n" -" ]\n" -" },\n" -" \"example\": {\n" -" \"key\": \"value\",\n" -" \"key2\": [\n" -" \"val1\",\n" -" \"val2\",\n" -" 3\n" -" ],\n" -" \"key3\": \"value\"\n" " }\n" "}\n"; diff --git a/src/notebook.cc b/src/notebook.cc index 4e18c1f..c66630f 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -4,6 +4,7 @@ #include "singletons.h" #include #include +#include "cmake.h" #include //TODO: remove using namespace std; //TODO: remove @@ -54,18 +55,19 @@ void Notebook::open(std::string path) { auto language=Source::guess_language(path); if(language && (language->get_id()=="chdr" || language->get_id()=="c" || language->get_id()=="cpp" || language->get_id()=="objc")) { auto view_project_path=project_path; + if(directories.cmake && directories.cmake->project_path!="") + view_project_path=directories.cmake->project_path.string(); if(view_project_path=="") { - view_project_path=boost::filesystem::path(path).parent_path().string(); - auto found_project_path=find_project_path(view_project_path); - if(found_project_path!="") { - view_project_path=found_project_path; + auto parent_path=boost::filesystem::path(path).parent_path(); + view_project_path=parent_path.string(); + CMake cmake(parent_path); + if(cmake.project_path!="") { + view_project_path=cmake.project_path.string(); Singleton::terminal()->print("Project path for "+path+" set to "+view_project_path+"\n"); } else Singleton::terminal()->print("Error: could not find project path for "+path+"\n"); } - if(boost::filesystem::exists(view_project_path+"/CMakeLists.txt") && !boost::filesystem::exists(view_project_path+"/compile_commands.json")) - make_compile_commands(view_project_path); source_views.emplace_back(new Source::ClangView(path, view_project_path)); } else { @@ -112,32 +114,6 @@ void Notebook::open(std::string path) { }; } -std::string Notebook::find_project_path(const std::string &path) { - const auto find_cmake_project=[this](const boost::filesystem::path &path) { - auto cmake_path=path; - cmake_path+="/CMakeLists.txt"; - for(auto &line: juci::filesystem::read_lines(cmake_path)) { - const std::regex cmake_project("^ *project *\\(.*$"); - std::smatch sm; - if(std::regex_match(line, sm, cmake_project)) { - return true; - } - } - return false; - }; - - auto boost_path=boost::filesystem::path(path); - if(find_cmake_project(boost_path)) - return boost_path.string(); - do { - boost_path=boost_path.parent_path(); - if(find_cmake_project(boost_path)) - return boost_path.string(); - } while(boost_path!=boost_path.root_directory()); - - return ""; -} - bool Notebook::save(int page) { if(page>=size()) return false; @@ -149,7 +125,8 @@ bool Notebook::save(int page) { //If CMakeLists.txt have been modified: if(boost::filesystem::path(view->file_path).filename().string()=="CMakeLists.txt") { - if(project_path!="" && make_compile_commands(project_path)) { + if(project_path!="" && directories.cmake && directories.cmake->project_path!="" && CMake::create_compile_commands(directories.cmake->project_path.string())) { + directories.open_folder(project_path); for(auto source_view: source_views) { if(auto source_clang_view=dynamic_cast(source_view)) { if(project_path==source_view->project_path) { @@ -170,17 +147,6 @@ bool Notebook::save(int page) { return false; } -bool Notebook::make_compile_commands(const std::string &path) { - Singleton::terminal()->print("Creating "+boost::filesystem::path(path+"/compile_commands.json").string()+"\n"); - //TODO: Windows... - if(Singleton::terminal()->execute(path, "cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 2>&1")) { - if(project_path!="") - directories.open_folder(project_path); - return true; - } - return false; -} - bool Notebook::save_current() { INFO("Notebook save current file"); if(get_current_page()==-1) diff --git a/src/notebook.h b/src/notebook.h index ef95143..848c50c 100644 --- a/src/notebook.h +++ b/src/notebook.h @@ -20,10 +20,9 @@ public: void open(std::string filename); bool save(int page); bool save_current(); - std::string project_path; + std::string project_path; //TODO: remove, and also remove Source::View::project_path (project_path only needed in Source::ClangView) private: - std::string find_project_path(const std::string &path); bool make_compile_commands(const std::string &path); bool save_modified_dialog(); Directories &directories; diff --git a/src/singletons.cc b/src/singletons.cc index 076bf46..ace0668 100644 --- a/src/singletons.cc +++ b/src/singletons.cc @@ -1,7 +1,6 @@ #include "singletons.h" std::unique_ptr Singleton::Config::source_=std::unique_ptr(new Source::Config()); -std::unique_ptr Singleton::Config::terminal_=std::unique_ptr(new Terminal::Config()); std::unique_ptr Singleton::Config::directories_=std::unique_ptr(new Directories::Config()); std::unique_ptr Singleton::terminal_=std::unique_ptr(); diff --git a/src/singletons.h b/src/singletons.h index d305e85..e39453c 100644 --- a/src/singletons.h +++ b/src/singletons.h @@ -14,11 +14,9 @@ public: class Config { public: static Source::Config *source() {return source_.get();} - static Terminal::Config *terminal() {return terminal_.get();} static Directories::Config *directories() {return directories_.get();} private: static std::unique_ptr source_; - static std::unique_ptr terminal_; static std::unique_ptr directories_; }; static std::string config_dir() { return std::string(getenv("HOME")) + "/.juci/config/"; } diff --git a/src/terminal.cc b/src/terminal.cc index de42e7c..d406656 100644 --- a/src/terminal.cc +++ b/src/terminal.cc @@ -48,72 +48,82 @@ Terminal::Terminal() { scrolled_window.add(text_view); add(scrolled_window); - change_folder_command = ""; text_view.signal_size_allocate().connect([this](Gtk::Allocation& allocation){ auto end=text_view.get_buffer()->create_mark(text_view.get_buffer()->end()); text_view.scroll_to(end); text_view.get_buffer()->delete_mark(end); }); + + async_execute_print.connect([this](){ + print(async_execute_print_string); + async_execute_print_finished=true; + }); } -bool Terminal::execute(const std::string &path, const std::string &command) { +bool Terminal::execute(const std::string &command, const std::string &path) { boost::filesystem::path boost_path; - if(path=="") - boost_path=boost::filesystem::current_path(); - else + std::string cd_path_and_command; + if(path!="") { boost_path=boost::filesystem::path(path); - //TODO: Windows... - auto cd_path_and_command="cd "+boost_path.string()+" 2>&1 && "+command; + //TODO: Windows... + cd_path_and_command="cd "+boost_path.string()+" 2>&1 && "+command; + } + else + cd_path_and_command=command; - FILE* p = NULL; + FILE* p; p = popen(cd_path_and_command.c_str(), "r"); if (p == NULL) { print("Error: Failed to run command" + command + "\n"); return false; } else { - char buffer[1028]; - while (fgets(buffer, 1028, p) != NULL) { + char buffer[1024]; + while (fgets(buffer, 1024, p) != NULL) { print(buffer); + while(gtk_events_pending()) + gtk_main_iteration(); } - int exit_code=pclose(p); - if(exit_code==0) - return true; - else - return false; - } -} - -void Terminal::async_execute(const std::string &path, const std::string &command) { - -} - -void Terminal::set_change_folder_command(boost::filesystem::path CMake_path) { - INFO("Terminal: set_change_folder_command"); - path = CMake_path.string(); - change_folder_command = "cd "+ path + "; "; -} - -void Terminal::compile() { - INFO("Terminal: compile"); - text_view.get_buffer()->set_text(""); - DEBUG("Terminal: compile: running cmake command"); - std::vector commands = Singleton::Config::terminal()->compile_commands; - for (size_t it = 0; it < commands.size(); ++it) { - execute_command(commands.at(it), "r"); + return pclose(p)==0; } - print("\n"); - DEBUG("Terminal: compile: compile done"); } -void Terminal::run(std::string executable) { - INFO("Terminal: run"); - print("juCi++ execute: " + executable + "\n"); - DEBUG("Terminal: compile: running run command: "); - DEBUG_VAR(executable); - execute_command("cd "+Singleton::Config::terminal()->run_command + "; ./"+executable, "r"); - print("\n"); +void Terminal::async_execute(const std::string &command, const std::string &path, std::function callback) { + std::thread async_execute_thread([this, command, path, callback](){ + boost::filesystem::path boost_path; + std::string cd_path_and_command; + if(path!="") { + boost_path=boost::filesystem::path(path); + + //TODO: Windows... + cd_path_and_command="cd "+boost_path.string()+" 2>&1 && "+command; + } + else + cd_path_and_command=command; + + FILE* p; + p = popen(cd_path_and_command.c_str(), "r"); + if (p == NULL) { + async_execute_print_string="Error: Failed to run command" + command + "\n"; + async_execute_print_finished=false; + async_execute_print(); + } + else { + char buffer[1024]; + while (fgets(buffer, 1024, p) != NULL) { + async_execute_print_string=buffer; + async_execute_print_finished=false; + async_execute_print(); + while(!async_execute_print_finished) + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + int exit_code=pclose(p); + if(callback) + callback(exit_code==0); + } + }); + async_execute_thread.detach(); } int Terminal::print(std::string message){ @@ -134,22 +144,3 @@ std::shared_ptr Terminal::print_in_progress(std::string st std::shared_ptr in_progress=std::shared_ptr(new Terminal::InProgress(start_msg)); return in_progress; } - -void Terminal::execute_command(std::string command, std::string mode) { - INFO("Terminal: execute_command"); - command = change_folder_command+command; - DEBUG("Terminal: PrintMessage: running command"); - FILE* p = NULL; - std::cout << command << std::endl; - p = popen(command.c_str(), mode.c_str()); - if (p == NULL) { - print("juCi++ ERROR: Failed to run command" + command + "\n"); - } - else { - char buffer[1028]; - while (fgets(buffer, 1028, p) != NULL) { - print(buffer); - } - pclose(p); - } -} diff --git a/src/terminal.h b/src/terminal.h index d7865c5..fe7c4f0 100644 --- a/src/terminal.h +++ b/src/terminal.h @@ -9,13 +9,7 @@ #include class Terminal : public Gtk::HBox { -public: - class Config { - public: - std::vector compile_commands; - std::string run_command; - }; - +public: class InProgress { public: InProgress(const std::string& start_msg); @@ -31,25 +25,18 @@ public: }; Terminal(); - bool execute(const std::string &path, const std::string &command); - void async_execute(const std::string &path, const std::string &command); - void set_change_folder_command(boost::filesystem::path CMake_path); //TODO: remove - void run(std::string executable); //TODO: remove - void compile(); //TODO: remove + bool execute(const std::string &command, const std::string &path=""); + void async_execute(const std::string &command, const std::string &path="", std::function callback=nullptr); int print(std::string message); void print(int line_nr, std::string message); std::shared_ptr print_in_progress(std::string start_msg); private: - void execute_command(std::string command, std::string mode); //TODO: remove - Gtk::TextView text_view; Gtk::ScrolledWindow scrolled_window; - - std::string change_folder_command; //TODO: remove - std::string path; //TODO: remove - const std::string cmake_sucsess = "Build files have been written to:"; //TODO: remove - const std::string make_built = "Built target"; //TODO: remove - const std::string make_executable = "Linking CXX executable"; //TODO: remove + + Glib::Dispatcher async_execute_print; + std::string async_execute_print_string; + std::atomic async_execute_print_finished; }; #endif // JUCI_TERMINAL_H_ diff --git a/src/window.cc b/src/window.cc index 1c00711..ec369f9 100644 --- a/src/window.cc +++ b/src/window.cc @@ -12,7 +12,7 @@ namespace sigc { SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE } -Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories) { +Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compiling(false) { INFO("Create Window"); set_title("juCi++"); set_default_size(600, 400); @@ -194,41 +194,43 @@ void Window::create_menu() { }); menu.action_group->add(Gtk::Action::create("ProjectCompileAndRun", "Compile And Run"), Gtk::AccelKey(menu.key_map["compile_and_run"]), [this]() { - if(notebook.get_current_page()==-1) + if(notebook.get_current_page()==-1 || compiling) return; - notebook.save_current(); - if (running.try_lock()) { - std::thread execute([this]() { - std::string path = notebook.get_current_view()->file_path; - size_t pos = path.find_last_of("/\\"); - if(pos != std::string::npos) { - path.erase(path.begin()+pos,path.end()); - Singleton::terminal()->set_change_folder_command(path); - } - Singleton::terminal()->compile(); - std::string executable = directories.get_cmakelists_variable(path,"add_executable"); - Singleton::terminal()->run(executable); - running.unlock(); + CMake cmake(notebook.get_current_view()->file_path); + auto executables = cmake.get_functions_parameters("add_executable"); + std::string executable; + boost::filesystem::path path; + if(executables.size()>0 && executables[0].second.size()>0) { + executable=executables[0].second[0]; + path=executables[0].first.parent_path(); + path+="/"+executables[0].second[0]; + } + if(cmake.project_path!="") { + compiling=true; + if(path!="") + Singleton::terminal()->print("Compiling and executing "+path.string()+"\n"); + else + Singleton::terminal()->print("Could not find an executable, please use add_executable in CMakeLists.txt\n"); + //TODO: Windows... + Singleton::terminal()->async_execute("make 2>&1", cmake.project_path.string(), [this, path](int exit_code){ + compiling=false; + if(path!="") + //TODO: Windows... + Singleton::terminal()->async_execute(path.string()+" 2>&1", path.parent_path().string()); }); - execute.detach(); } }); menu.action_group->add(Gtk::Action::create("ProjectCompile", "Compile"), Gtk::AccelKey(menu.key_map["compile"]), [this]() { - if(notebook.get_current_page()==-1) + if(notebook.get_current_page()==-1 || compiling) return; - notebook.save_current(); - if (running.try_lock()) { - std::thread execute([this]() { - std::string path = notebook.get_current_view()->file_path; - size_t pos = path.find_last_of("/\\"); - if(pos != std::string::npos){ - path.erase(path.begin()+pos,path.end()); - Singleton::terminal()->set_change_folder_command(path); - } - Singleton::terminal()->compile(); - running.unlock(); + CMake cmake(notebook.get_current_view()->file_path); + if(cmake.project_path!="") { + compiling=true; + Singleton::terminal()->print("Compiling project "+cmake.project_path.string()+"\n"); + //TODO: Windows... + Singleton::terminal()->async_execute("make 2>&1", cmake.project_path.string(), [this](int exit_code){ + compiling=false; }); - execute.detach(); } }); diff --git a/src/window.h b/src/window.h index 7100de6..42ee93c 100644 --- a/src/window.h +++ b/src/window.h @@ -6,6 +6,7 @@ #include "entrybox.h" #include "notebook.h" #include "menu.h" +#include class Window : public Gtk::Window { public: @@ -23,8 +24,8 @@ private: Gtk::VBox terminal_vbox; Gtk::HBox status_hbox; EntryBox entry_box; - std::mutex running; Menu menu; + std::atomic compiling; void create_menu(); void hide(); @@ -41,7 +42,6 @@ private: bool case_sensitive_search=true; bool regex_search=false; bool search_entry_shown=false; - }; #endif // JUCI_WINDOW_H From 82ea15ea9f3ff1de9875b0effb36b34c5e88a02e Mon Sep 17 00:00:00 2001 From: eidheim Date: Wed, 5 Aug 2015 16:16:26 +0200 Subject: [PATCH 07/22] project_path cleanup. --- src/directories.cc | 18 +++++++++++++----- src/directories.h | 4 ++-- src/juci.cc | 1 - src/notebook.cc | 28 ++++++++++++---------------- src/notebook.h | 1 - src/source.cc | 7 +++---- src/source.h | 6 +++--- src/window.cc | 25 ++++++++++++------------- 8 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/directories.cc b/src/directories.cc index 587ce60..1d2d735 100644 --- a/src/directories.cc +++ b/src/directories.cc @@ -41,9 +41,16 @@ Directories::Directories() { } void Directories::open_folder(const boost::filesystem::path& dir_path) { + auto new_path=dir_path; INFO("Open folder"); + if(new_path=="") { + if(current_path=="") + return; + new_path=current_path; + } + std::vector expanded_paths; - if(last_dir_path==dir_path) { + if(current_path==new_path) { tree_view.map_expanded_rows([&expanded_paths](Gtk::TreeView* tree_view, const Gtk::TreeModel::Path& path){ expanded_paths.emplace_back(path); }); @@ -51,18 +58,19 @@ void Directories::open_folder(const boost::filesystem::path& dir_path) { tree_store->clear(); - if(last_dir_path!=dir_path) - cmake=std::unique_ptr(new CMake(dir_path)); + if(current_path!=new_path) + cmake=std::unique_ptr(new CMake(new_path)); auto project=cmake->get_functions_parameters("project"); if(project.size()>0 && project[0].second.size()>0) tree_view.get_column(0)->set_title(project[0].second[0]); else tree_view.get_column(0)->set_title(""); - add_paths(dir_path, Gtk::TreeModel::Row(), 0); + add_paths(new_path, Gtk::TreeModel::Row(), 0); for(auto &path: expanded_paths) tree_view.expand_row(path, false); - last_dir_path=dir_path; + + current_path=new_path; DEBUG("Folder opened"); } diff --git a/src/directories.h b/src/directories.h index 9f688fe..322aba6 100644 --- a/src/directories.h +++ b/src/directories.h @@ -28,11 +28,12 @@ public: }; Directories(); - void open_folder(const boost::filesystem::path& dir_path); + void open_folder(const boost::filesystem::path& dir_path=""); void select_path(const std::string &path); std::function on_row_activated; std::unique_ptr cmake; + boost::filesystem::path current_path; private: void add_paths(const boost::filesystem::path& dir_path, const Gtk::TreeModel::Row &row, unsigned depth); @@ -40,7 +41,6 @@ private: Gtk::TreeView tree_view; Glib::RefPtr tree_store; ColumnRecord column_record; - boost::filesystem::path last_dir_path; }; #endif // JUCI_DIRECTORIES_H_ diff --git a/src/juci.cc b/src/juci.cc index 629c22b..83d8f14 100644 --- a/src/juci.cc +++ b/src/juci.cc @@ -36,7 +36,6 @@ void Juci::on_activate() { add_window(*window); window->show(); if(directory!="") { - window->notebook.project_path=directory; window->directories.open_folder(directory); } for(auto &f: files) diff --git a/src/notebook.cc b/src/notebook.cc index c66630f..8ebbb8b 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -54,28 +54,24 @@ void Notebook::open(std::string path) { auto language=Source::guess_language(path); if(language && (language->get_id()=="chdr" || language->get_id()=="c" || language->get_id()=="cpp" || language->get_id()=="objc")) { - auto view_project_path=project_path; + std::string project_path; if(directories.cmake && directories.cmake->project_path!="") - view_project_path=directories.cmake->project_path.string(); - if(view_project_path=="") { + project_path=directories.cmake->project_path.string(); + else { auto parent_path=boost::filesystem::path(path).parent_path(); - view_project_path=parent_path.string(); + project_path=parent_path.string(); CMake cmake(parent_path); if(cmake.project_path!="") { - view_project_path=cmake.project_path.string(); - Singleton::terminal()->print("Project path for "+path+" set to "+view_project_path+"\n"); + project_path=cmake.project_path.string(); + Singleton::terminal()->print("Project path for "+path+" set to "+project_path+"\n"); } else Singleton::terminal()->print("Error: could not find project path for "+path+"\n"); } - source_views.emplace_back(new Source::ClangView(path, view_project_path)); - } - else { - auto view_project_path=project_path; - if(view_project_path=="") - view_project_path=boost::filesystem::path(path).parent_path().string(); - source_views.emplace_back(new Source::GenericView(path, view_project_path, language)); + source_views.emplace_back(new Source::ClangView(path, project_path)); } + else + source_views.emplace_back(new Source::GenericView(path, language)); scrolled_windows.emplace_back(new Gtk::ScrolledWindow()); hboxes.emplace_back(new Gtk::HBox()); @@ -125,11 +121,11 @@ bool Notebook::save(int page) { //If CMakeLists.txt have been modified: if(boost::filesystem::path(view->file_path).filename().string()=="CMakeLists.txt") { - if(project_path!="" && directories.cmake && directories.cmake->project_path!="" && CMake::create_compile_commands(directories.cmake->project_path.string())) { - directories.open_folder(project_path); + if(directories.cmake && directories.cmake->project_path!="" && CMake::create_compile_commands(directories.cmake->project_path.string())) { + directories.open_folder(); for(auto source_view: source_views) { if(auto source_clang_view=dynamic_cast(source_view)) { - if(project_path==source_view->project_path) { + if(directories.cmake->project_path.string()==source_clang_view->project_path) { if(source_clang_view->restart_parse()) Singleton::terminal()->print("Reparsing "+source_clang_view->file_path+"\n"); else diff --git a/src/notebook.h b/src/notebook.h index 848c50c..8ad8480 100644 --- a/src/notebook.h +++ b/src/notebook.h @@ -20,7 +20,6 @@ public: void open(std::string filename); bool save(int page); bool save_current(); - std::string project_path; //TODO: remove, and also remove Source::View::project_path (project_path only needed in Source::ClangView) private: bool make_compile_commands(const std::string &path); diff --git a/src/source.cc b/src/source.cc index abbd4c9..70df906 100644 --- a/src/source.cc +++ b/src/source.cc @@ -36,8 +36,7 @@ Glib::RefPtr Source::guess_language(const std::string &file_path) ////////////// //// View //// ////////////// -Source::View::View(const std::string& file_path, const std::string& project_path): -file_path(file_path), project_path(project_path) { +Source::View::View(const std::string& file_path): file_path(file_path) { set_smart_home_end(Gsv::SMART_HOME_END_BEFORE); set_show_line_numbers(Singleton::Config::source()->show_line_numbers); set_highlight_current_line(Singleton::Config::source()->highlight_current_line); @@ -248,7 +247,7 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { ///////////////////// //// GenericView //// ///////////////////// -Source::GenericView::GenericView(const std::string& file_path, const std::string& project_path, Glib::RefPtr language) : View(file_path, project_path) { +Source::GenericView::GenericView(const std::string& file_path, Glib::RefPtr language) : View(file_path) { auto style_scheme_manager=Gsv::StyleSchemeManager::get_default(); //TODO: add?: style_scheme_manager->prepend_search_path("~/.juci/"); auto scheme=style_scheme_manager->get_scheme("classic"); @@ -278,7 +277,7 @@ Source::GenericView::GenericView(const std::string& file_path, const std::string clang::Index Source::ClangViewParse::clang_index(0, 0); Source::ClangViewParse::ClangViewParse(const std::string& file_path, const std::string& project_path): -Source::View(file_path, project_path) { +Source::View(file_path), project_path(project_path) { override_font(Pango::FontDescription(Singleton::Config::source()->font)); override_background_color(Gdk::RGBA(Singleton::Config::source()->background)); override_background_color(Gdk::RGBA(Singleton::Config::source()->background_selected), Gtk::StateFlags::STATE_FLAG_SELECTED); diff --git a/src/source.h b/src/source.h index 7bec853..ed434cd 100644 --- a/src/source.h +++ b/src/source.h @@ -46,7 +46,7 @@ namespace Source { class View : public Gsv::View { public: - View(const std::string& file_path, const std::string& project_path); + View(const std::string& file_path); ~View(); void search_highlight(const std::string &text, bool case_sensitive, bool regex); @@ -58,7 +58,6 @@ namespace Source { void replace_all(const std::string &replacement); std::string file_path; - std::string project_path; std::function()> get_declaration_location; std::function goto_method; @@ -84,12 +83,13 @@ namespace Source { class GenericView : public View { public: - GenericView(const std::string& file_path, const std::string& project_path, Glib::RefPtr language); + GenericView(const std::string& file_path, Glib::RefPtr language); }; class ClangViewParse : public View { public: ClangViewParse(const std::string& file_path, const std::string& project_path); + std::string project_path; protected: void init_parse(); void start_reparse(); diff --git a/src/window.cc b/src/window.cc index ec369f9..04a2fe3 100644 --- a/src/window.cc +++ b/src/window.cc @@ -294,18 +294,18 @@ void Window::new_file_entry() { entry_box.entries.emplace_back("untitled", [this](const std::string& content){ std::string filename=content; if(filename!="") { - if(notebook.project_path!="" && !boost::filesystem::path(filename).is_absolute()) - filename=notebook.project_path+"/"+filename; + if(directories.current_path!="" && !boost::filesystem::path(filename).is_absolute()) + filename=directories.current_path.string()+"/"+filename; boost::filesystem::path p(filename); if(boost::filesystem::exists(p)) { Singleton::terminal()->print("Error: "+p.string()+" already exists.\n"); } else { if(juci::filesystem::write(p)) { - if(notebook.project_path!="") - directories.open_folder(notebook.project_path); - notebook.open(boost::filesystem::canonical(p).string()); - Singleton::terminal()->print("New file "+p.string()+" created.\n"); + if(directories.current_path!="") + directories.open_folder(); + notebook.open(boost::filesystem::canonical(p).string()); + Singleton::terminal()->print("New file "+p.string()+" created.\n"); } else Singleton::terminal()->print("Error: could not create new file "+p.string()+".\n"); @@ -322,8 +322,8 @@ void Window::new_file_entry() { void Window::open_folder_dialog() { Gtk::FileChooserDialog dialog("Please choose a folder", Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER); - if(notebook.project_path.size()>0) - gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), notebook.project_path.c_str()); + if(directories.current_path!="") + gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), directories.current_path.string().c_str()); else gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str()); dialog.set_transient_for(*this); @@ -335,7 +335,6 @@ void Window::open_folder_dialog() { if(result==Gtk::RESPONSE_OK) { std::string project_path=dialog.get_filename(); - notebook.project_path=project_path; directories.open_folder(project_path); if(notebook.get_current_page()!=-1) directories.select_path(notebook.get_current_view()->file_path); @@ -344,8 +343,8 @@ void Window::open_folder_dialog() { void Window::open_file_dialog() { Gtk::FileChooserDialog dialog("Please choose a file", Gtk::FILE_CHOOSER_ACTION_OPEN); - if(notebook.project_path.size()>0) - gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), notebook.project_path.c_str()); + if(directories.current_path!="") + gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), directories.current_path.string().c_str()); else gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str()); dialog.set_transient_for(*this); @@ -399,8 +398,8 @@ void Window::save_file_dialog() { if(file) { file << notebook.get_current_view()->get_buffer()->get_text(); file.close(); - if(notebook.project_path!="") - directories.open_folder(notebook.project_path); + if(directories.current_path!="") + directories.open_folder(); notebook.open(path); Singleton::terminal()->print("File saved to: " + notebook.get_current_view()->file_path+"\n"); } From d41391c30a393b930102ce43ae6592f0ed9c8e20 Mon Sep 17 00:00:00 2001 From: eidheim Date: Wed, 5 Aug 2015 18:07:11 +0200 Subject: [PATCH 08/22] Minor fix. --- src/notebook.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/notebook.cc b/src/notebook.cc index 8ebbb8b..f33037f 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -121,7 +121,7 @@ bool Notebook::save(int page) { //If CMakeLists.txt have been modified: if(boost::filesystem::path(view->file_path).filename().string()=="CMakeLists.txt") { - if(directories.cmake && directories.cmake->project_path!="" && CMake::create_compile_commands(directories.cmake->project_path.string())) { + if(directories.cmake && directories.cmake->project_path!="" && boost::filesystem::path(view->file_path)>=directories.cmake->project_path && CMake::create_compile_commands(directories.cmake->project_path.string())) { directories.open_folder(); for(auto source_view: source_views) { if(auto source_clang_view=dynamic_cast(source_view)) { From ad42aa3044a0e6f1beb8b8508758b0c17f02f008 Mon Sep 17 00:00:00 2001 From: eidheim Date: Wed, 5 Aug 2015 21:41:45 +0200 Subject: [PATCH 09/22] Minor fixes to cmake.cc --- src/cmake.cc | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/cmake.cc b/src/cmake.cc index 9095e73..b4b2268 100644 --- a/src/cmake.cc +++ b/src/cmake.cc @@ -145,19 +145,15 @@ void CMake::parse_variable_parameters(std::string &data) { while(pos CMake::get_function_parameters(std::string &data) { data.erase(pos, 1); pos--; } - else if(inside_quote && data[pos]!='\\' && last_char=='\\') { - data.erase(pos-1, 1); - pos--; - } else if(!inside_quote && pos+1 CMake::get_function_parameters(std::string &data) { parameters.emplace_back(data.substr(parameter_pos)); for(auto &var: variables) { for(auto ¶meter: parameters) { - auto pos=parameter.find("${"+var.first+'}'); + auto pos=parameter.find("${"+var.first+'}'); //TODO: check if there is a slash in front of $ while(pos!=std::string::npos) { parameter.replace(pos, var.first.size()+3, var.second); pos=parameter.find("${"+var.first+'}'); From 10c5c5fb1b6913bd3598c30c92978c85d53a01c8 Mon Sep 17 00:00:00 2001 From: eidheim Date: Thu, 6 Aug 2015 14:36:42 +0200 Subject: [PATCH 10/22] Can now cancel all applications started in Juci with escape. Also some cleanup and fixes. --- src/cmake.cc | 6 +-- src/directories.cc | 8 +++- src/directories.h | 1 + src/terminal.cc | 110 ++++++++++++++++++++++++++++++++++++++------- src/terminal.h | 7 ++- src/window.cc | 52 ++++++++++++++++----- src/window.h | 2 + 7 files changed, 153 insertions(+), 33 deletions(-) diff --git a/src/cmake.cc b/src/cmake.cc index b4b2268..06d9e07 100644 --- a/src/cmake.cc +++ b/src/cmake.cc @@ -162,7 +162,7 @@ void CMake::parse_variable_parameters(std::string &data) { pos++; } for(auto &var: variables) { - auto pos=data.find("${"+var.first+'}'); //TODO: check if there is a slash in front of $ + 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+'}'); @@ -170,7 +170,7 @@ void CMake::parse_variable_parameters(std::string &data) { } //Remove variables we do not know: - pos=data.find("${"); //TODO: check if there is a slash in front of $ + 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); @@ -221,7 +221,7 @@ std::vector CMake::get_function_parameters(std::string &data) { parameters.emplace_back(data.substr(parameter_pos)); for(auto &var: variables) { for(auto ¶meter: parameters) { - auto pos=parameter.find("${"+var.first+'}'); //TODO: check if there is a slash in front of $ + 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+'}'); diff --git a/src/directories.cc b/src/directories.cc index 1d2d735..088a0f7 100644 --- a/src/directories.cc +++ b/src/directories.cc @@ -58,7 +58,7 @@ void Directories::open_folder(const boost::filesystem::path& dir_path) { tree_store->clear(); - if(current_path!=new_path) + if(dir_path!="") cmake=std::unique_ptr(new CMake(new_path)); auto project=cmake->get_functions_parameters("project"); if(project.size()>0 && project[0].second.size()>0) @@ -69,8 +69,11 @@ void Directories::open_folder(const boost::filesystem::path& dir_path) { for(auto &path: expanded_paths) tree_view.expand_row(path, false); - + current_path=new_path; + + if(selected_path.size()>0) + select_path(selected_path); DEBUG("Folder opened"); } @@ -80,6 +83,7 @@ void Directories::select_path(const std::string &path) { auto tree_path=Gtk::TreePath(iter); tree_view.expand_to_path(tree_path); tree_view.set_cursor(tree_path); + selected_path=path; return true; } return false; diff --git a/src/directories.h b/src/directories.h index 322aba6..57d36da 100644 --- a/src/directories.h +++ b/src/directories.h @@ -41,6 +41,7 @@ private: Gtk::TreeView tree_view; Glib::RefPtr tree_store; ColumnRecord column_record; + std::string selected_path; }; #endif // JUCI_DIRECTORIES_H_ diff --git a/src/terminal.cc b/src/terminal.cc index d406656..87f1427 100644 --- a/src/terminal.cc +++ b/src/terminal.cc @@ -2,6 +2,67 @@ #include #include "logging.h" #include "singletons.h" +#include +#include + +#include //TODO: remove +using namespace std; //TODO: remove + +#define READ 0 +#define WRITE 1 + +//TODO: Windows... +//Coppied partially from http://www.linuxprogrammingblog.com/code-examples/sigaction +void signal_execl_exit(int sig, siginfo_t *siginfo, void *context) { + int status; + while (waitpid(siginfo->si_pid, &status, WNOHANG) > 0) {} + + Singleton::terminal()->async_pid_mutex.lock(); + if(Singleton::terminal()->async_pid_descriptors.find(siginfo->si_pid)!=Singleton::terminal()->async_pid_descriptors.end()) { + Singleton::terminal()->async_pid_status[siginfo->si_pid]=status; + close(Singleton::terminal()->async_pid_descriptors.at(siginfo->si_pid).first); + close(Singleton::terminal()->async_pid_descriptors.at(siginfo->si_pid).second); + Singleton::terminal()->async_pid_descriptors.erase(siginfo->si_pid); + } + Singleton::terminal()->async_pid_mutex.unlock(); +} + +//TODO: Windows... +//Copied from http://stackoverflow.com/questions/12778672/killing-process-that-has-been-created-with-popen2 +pid_t popen2(const char *command, int *input_descriptor, int *output_descriptor) { + pid_t pid; + int p_stdin[2], p_stdout[2]; + + if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0) + return -1; + + pid = fork(); + + if (pid < 0) + return pid; + else if (pid == 0) { + close(p_stdin[WRITE]); + dup2(p_stdin[READ], READ); + close(p_stdout[READ]); + dup2(p_stdout[WRITE], WRITE); + + execl("/bin/sh", "sh", "-c", command, NULL); + perror("execl"); + exit(1); + } + + if (input_descriptor == NULL) + close(p_stdin[WRITE]); + else + *input_descriptor = p_stdin[WRITE]; + + if (output_descriptor == NULL) + close(p_stdout[READ]); + else + *output_descriptor = p_stdout[READ]; + + return pid; +} Terminal::InProgress::InProgress(const std::string& start_msg): stop(false) { waiting_print.connect([this](){ @@ -55,9 +116,20 @@ Terminal::Terminal() { }); async_execute_print.connect([this](){ - print(async_execute_print_string); - async_execute_print_finished=true; + async_execute_print_string_mutex.lock(); + if(async_execute_print_string.size()>0) { + print(async_execute_print_string); + async_execute_print_string=""; + } + async_execute_print_string_mutex.unlock(); }); + + //Coppied from http://www.linuxprogrammingblog.com/code-examples/sigaction + struct sigaction act; + memset (&act, '\0', sizeof(act)); + act.sa_sigaction = &signal_execl_exit; + act.sa_flags = SA_SIGINFO; + sigaction(SIGCHLD, &act, NULL); } bool Terminal::execute(const std::string &command, const std::string &path) { @@ -97,28 +169,36 @@ void Terminal::async_execute(const std::string &command, const std::string &path boost_path=boost::filesystem::path(path); //TODO: Windows... - cd_path_and_command="cd "+boost_path.string()+" 2>&1 && "+command; + cd_path_and_command="cd "+boost_path.string()+" 2>&1 && exec "+command; } else cd_path_and_command=command; - FILE* p; - p = popen(cd_path_and_command.c_str(), "r"); - if (p == NULL) { - async_execute_print_string="Error: Failed to run command" + command + "\n"; - async_execute_print_finished=false; + int input_descriptor, output_descriptor; + async_pid_mutex.lock(); + auto pid=popen2(cd_path_and_command.c_str(), &input_descriptor, &output_descriptor); + async_pid_descriptors[pid]={input_descriptor, output_descriptor}; + async_pid_mutex.unlock(); + if (pid<=0) { + async_execute_print_string_mutex.lock(); + async_execute_print_string+="Error: Failed to run command" + command + "\n"; + async_execute_print_string_mutex.unlock(); async_execute_print(); } else { char buffer[1024]; - while (fgets(buffer, 1024, p) != NULL) { - async_execute_print_string=buffer; - async_execute_print_finished=false; + ssize_t n; + while ((n=read(output_descriptor, buffer, 1024)) > 0) { + async_execute_print_string_mutex.lock(); + for(int c=0;cinsert(text_view.get_buffer()->end(), "> "+message); + text_view.get_buffer()->insert(text_view.get_buffer()->end(), message); return text_view.get_buffer()->end().get_line(); } diff --git a/src/terminal.h b/src/terminal.h index fe7c4f0..493ffbd 100644 --- a/src/terminal.h +++ b/src/terminal.h @@ -7,6 +7,7 @@ #include #include #include +#include class Terminal : public Gtk::HBox { public: @@ -27,6 +28,10 @@ public: Terminal(); bool execute(const std::string &command, const std::string &path=""); void async_execute(const std::string &command, const std::string &path="", std::function callback=nullptr); + std::unordered_map > async_pid_descriptors; + std::unordered_map async_pid_status; + std::mutex async_pid_mutex; + int print(std::string message); void print(int line_nr, std::string message); std::shared_ptr print_in_progress(std::string start_msg); @@ -36,7 +41,7 @@ private: Glib::Dispatcher async_execute_print; std::string async_execute_print_string; - std::atomic async_execute_print_finished; + std::mutex async_execute_print_string_mutex; }; #endif // JUCI_TERMINAL_H_ diff --git a/src/window.cc b/src/window.cc index 04a2fe3..9f8ecd2 100644 --- a/src/window.cc +++ b/src/window.cc @@ -88,6 +88,17 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compil notebook.signal_page_removed().connect([this](Gtk::Widget* page, guint page_num) { entry_box.hide(); }); + + compile_success.connect([this](){ + directories.open_folder(); + }); + run_success.connect([this](){ + Singleton::terminal()->print("Execution returned: success\n"); + }); + run_error.connect([this](){ + Singleton::terminal()->print("Execution returned: error\n"); + }); + INFO("Window created"); } // Window constructor @@ -197,6 +208,7 @@ void Window::create_menu() { if(notebook.get_current_page()==-1 || compiling) return; CMake cmake(notebook.get_current_view()->file_path); + directories.open_folder(); auto executables = cmake.get_functions_parameters("add_executable"); std::string executable; boost::filesystem::path path; @@ -207,29 +219,40 @@ void Window::create_menu() { } if(cmake.project_path!="") { compiling=true; - if(path!="") + if(path!="") { Singleton::terminal()->print("Compiling and executing "+path.string()+"\n"); + //TODO: Windows... + Singleton::terminal()->async_execute("make 2>&1", cmake.project_path.string(), [this, path](bool success){ + compiling=false; + if(success) { + compile_success(); + //TODO: Windows... + Singleton::terminal()->async_execute(path.string()+" 2>&1", path.parent_path().string(), [this](bool success){ + if(success) + run_success(); + else + run_error(); + }); + } + }); + } else Singleton::terminal()->print("Could not find an executable, please use add_executable in CMakeLists.txt\n"); - //TODO: Windows... - Singleton::terminal()->async_execute("make 2>&1", cmake.project_path.string(), [this, path](int exit_code){ - compiling=false; - if(path!="") - //TODO: Windows... - Singleton::terminal()->async_execute(path.string()+" 2>&1", path.parent_path().string()); - }); } }); menu.action_group->add(Gtk::Action::create("ProjectCompile", "Compile"), Gtk::AccelKey(menu.key_map["compile"]), [this]() { if(notebook.get_current_page()==-1 || compiling) return; CMake cmake(notebook.get_current_view()->file_path); + directories.open_folder(); if(cmake.project_path!="") { compiling=true; Singleton::terminal()->print("Compiling project "+cmake.project_path.string()+"\n"); //TODO: Windows... - Singleton::terminal()->async_execute("make 2>&1", cmake.project_path.string(), [this](int exit_code){ + Singleton::terminal()->async_execute("make 2>&1", cmake.project_path.string(), [this](bool success){ compiling=false; + if(success) + compile_success(); }); } }); @@ -243,8 +266,15 @@ void Window::create_menu() { } bool Window::on_key_press_event(GdkEventKey *event) { - if(event->keyval==GDK_KEY_Escape) + if(event->keyval==GDK_KEY_Escape) { + if(entry_box.entries.size()==0) { + Singleton::terminal()->async_pid_mutex.lock(); + for(auto &pid: Singleton::terminal()->async_pid_descriptors) + kill(pid.first, SIGTERM); + Singleton::terminal()->async_pid_mutex.unlock(); + } entry_box.hide(); + } #ifdef __APPLE__ //For Apple's Command-left, right, up, down keys else if((event->state & GDK_META_MASK)>0) { if(event->keyval==GDK_KEY_Left) { @@ -336,8 +366,6 @@ void Window::open_folder_dialog() { if(result==Gtk::RESPONSE_OK) { std::string project_path=dialog.get_filename(); directories.open_folder(project_path); - if(notebook.get_current_page()!=-1) - directories.select_path(notebook.get_current_view()->file_path); } } diff --git a/src/window.h b/src/window.h index 42ee93c..ea53f6b 100644 --- a/src/window.h +++ b/src/window.h @@ -26,6 +26,8 @@ private: EntryBox entry_box; Menu menu; std::atomic compiling; + Glib::Dispatcher compile_success; + Glib::Dispatcher run_success, run_error; void create_menu(); void hide(); From fbe7a58886190174ee2b71b697bd4fa132936d38 Mon Sep 17 00:00:00 2001 From: eidheim Date: Thu, 6 Aug 2015 14:53:31 +0200 Subject: [PATCH 11/22] Minor fix. --- src/terminal.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/terminal.cc b/src/terminal.cc index 87f1427..d3562a3 100644 --- a/src/terminal.cc +++ b/src/terminal.cc @@ -199,6 +199,8 @@ void Terminal::async_execute(const std::string &command, const std::string &path int exit_code=async_pid_status.at(pid); async_pid_status.erase(pid); async_pid_mutex.unlock(); + close(input_descriptor); + close(output_descriptor); if(callback) callback(exit_code==0); } From 925a318fc4d1ece7da6a3f2612d99fc9861e0bf3 Mon Sep 17 00:00:00 2001 From: eidheim Date: Thu, 6 Aug 2015 16:33:03 +0200 Subject: [PATCH 12/22] Minor fixes and cleanup of terminal.*. --- src/cmake.cc | 2 +- src/terminal.cc | 52 +++++++++++++++++++++++++++---------------------- src/terminal.h | 15 +++++++------- src/window.cc | 21 ++++++-------------- src/window.h | 1 - 5 files changed, 44 insertions(+), 47 deletions(-) diff --git a/src/cmake.cc b/src/cmake.cc index 06d9e07..0d9fefc 100644 --- a/src/cmake.cc +++ b/src/cmake.cc @@ -47,7 +47,7 @@ CMake::CMake(const boost::filesystem::path &path) { bool CMake::create_compile_commands(const std::string &path) { Singleton::terminal()->print("Creating "+boost::filesystem::path(path+"/compile_commands.json").string()+"\n"); //TODO: Windows... - if(Singleton::terminal()->execute("cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 2>&1", path)) + if(Singleton::terminal()->execute("cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 2>&1", path)==EXIT_SUCCESS) return true; return false; } diff --git a/src/terminal.cc b/src/terminal.cc index d3562a3..d558706 100644 --- a/src/terminal.cc +++ b/src/terminal.cc @@ -115,13 +115,13 @@ Terminal::Terminal() { text_view.get_buffer()->delete_mark(end); }); - async_execute_print.connect([this](){ - async_execute_print_string_mutex.lock(); - if(async_execute_print_string.size()>0) { - print(async_execute_print_string); - async_execute_print_string=""; + async_print_dispatcher.connect([this](){ + async_print_string_mutex.lock(); + if(async_print_string.size()>0) { + print(async_print_string); + async_print_string=""; } - async_execute_print_string_mutex.unlock(); + async_print_string_mutex.unlock(); }); //Coppied from http://www.linuxprogrammingblog.com/code-examples/sigaction @@ -132,7 +132,7 @@ Terminal::Terminal() { sigaction(SIGCHLD, &act, NULL); } -bool Terminal::execute(const std::string &command, const std::string &path) { +int Terminal::execute(const std::string &command, const std::string &path) { boost::filesystem::path boost_path; std::string cd_path_and_command; if(path!="") { @@ -148,7 +148,7 @@ bool Terminal::execute(const std::string &command, const std::string &path) { p = popen(cd_path_and_command.c_str(), "r"); if (p == NULL) { print("Error: Failed to run command" + command + "\n"); - return false; + return -1; } else { char buffer[1024]; @@ -157,11 +157,11 @@ bool Terminal::execute(const std::string &command, const std::string &path) { while(gtk_events_pending()) gtk_main_iteration(); } - return pclose(p)==0; + return pclose(p); } } -void Terminal::async_execute(const std::string &command, const std::string &path, std::function callback) { +void Terminal::async_execute(const std::string &command, const std::string &path, std::function callback) { std::thread async_execute_thread([this, command, path, callback](){ boost::filesystem::path boost_path; std::string cd_path_and_command; @@ -179,21 +179,16 @@ void Terminal::async_execute(const std::string &command, const std::string &path auto pid=popen2(cd_path_and_command.c_str(), &input_descriptor, &output_descriptor); async_pid_descriptors[pid]={input_descriptor, output_descriptor}; async_pid_mutex.unlock(); - if (pid<=0) { - async_execute_print_string_mutex.lock(); - async_execute_print_string+="Error: Failed to run command" + command + "\n"; - async_execute_print_string_mutex.unlock(); - async_execute_print(); - } + if (pid<=0) + async_print("Error: Failed to run command" + command + "\n"); else { char buffer[1024]; ssize_t n; while ((n=read(output_descriptor, buffer, 1024)) > 0) { - async_execute_print_string_mutex.lock(); + std::string message; for(int c=0;cinsert(text_view.get_buffer()->end(), message); return text_view.get_buffer()->end().get_line(); } -void Terminal::print(int line_nr, std::string message){ +void Terminal::print(int line_nr, const std::string &message){ INFO("Terminal: PrintMessage at line " << line_nr); auto iter=text_view.get_buffer()->get_iter_at_line(line_nr); while(!iter.ends_line()) @@ -226,3 +221,14 @@ std::shared_ptr Terminal::print_in_progress(std::string st std::shared_ptr in_progress=std::shared_ptr(new Terminal::InProgress(start_msg)); return in_progress; } + +void Terminal::async_print(const std::string &message) { + async_print_string_mutex.lock(); + bool dispatch=true; + if(async_print_string.size()>0) + dispatch=false; + async_print_string+=message; + async_print_string_mutex.unlock(); + if(dispatch) + async_print_dispatcher(); +} diff --git a/src/terminal.h b/src/terminal.h index 493ffbd..c7d477f 100644 --- a/src/terminal.h +++ b/src/terminal.h @@ -26,22 +26,23 @@ public: }; Terminal(); - bool execute(const std::string &command, const std::string &path=""); - void async_execute(const std::string &command, const std::string &path="", std::function callback=nullptr); + int execute(const std::string &command, const std::string &path=""); + void async_execute(const std::string &command, const std::string &path="", std::function callback=nullptr); std::unordered_map > async_pid_descriptors; std::unordered_map async_pid_status; std::mutex async_pid_mutex; - int print(std::string message); - void print(int line_nr, std::string message); + int print(const std::string &message); + void print(int line_nr, const std::string &message); std::shared_ptr print_in_progress(std::string start_msg); + void async_print(const std::string &message); private: Gtk::TextView text_view; Gtk::ScrolledWindow scrolled_window; - Glib::Dispatcher async_execute_print; - std::string async_execute_print_string; - std::mutex async_execute_print_string_mutex; + Glib::Dispatcher async_print_dispatcher; + std::string async_print_string; + std::mutex async_print_string_mutex; }; #endif // JUCI_TERMINAL_H_ diff --git a/src/window.cc b/src/window.cc index 9f8ecd2..e297239 100644 --- a/src/window.cc +++ b/src/window.cc @@ -92,12 +92,6 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compil compile_success.connect([this](){ directories.open_folder(); }); - run_success.connect([this](){ - Singleton::terminal()->print("Execution returned: success\n"); - }); - run_error.connect([this](){ - Singleton::terminal()->print("Execution returned: error\n"); - }); INFO("Window created"); } // Window constructor @@ -222,16 +216,13 @@ void Window::create_menu() { if(path!="") { Singleton::terminal()->print("Compiling and executing "+path.string()+"\n"); //TODO: Windows... - Singleton::terminal()->async_execute("make 2>&1", cmake.project_path.string(), [this, path](bool success){ + Singleton::terminal()->async_execute("make 2>&1", cmake.project_path.string(), [this, path](int exit_code){ compiling=false; - if(success) { + if(exit_code==EXIT_SUCCESS) { compile_success(); //TODO: Windows... - Singleton::terminal()->async_execute(path.string()+" 2>&1", path.parent_path().string(), [this](bool success){ - if(success) - run_success(); - else - run_error(); + Singleton::terminal()->async_execute(path.string()+" 2>&1", path.parent_path().string(), [this, path](int exit_code){ + Singleton::terminal()->async_print(path.string()+" returned: "+std::to_string(exit_code)+'\n'); }); } }); @@ -249,9 +240,9 @@ void Window::create_menu() { compiling=true; Singleton::terminal()->print("Compiling project "+cmake.project_path.string()+"\n"); //TODO: Windows... - Singleton::terminal()->async_execute("make 2>&1", cmake.project_path.string(), [this](bool success){ + Singleton::terminal()->async_execute("make 2>&1", cmake.project_path.string(), [this](int exit_code){ compiling=false; - if(success) + if(exit_code==EXIT_SUCCESS) compile_success(); }); } diff --git a/src/window.h b/src/window.h index ea53f6b..f14f731 100644 --- a/src/window.h +++ b/src/window.h @@ -27,7 +27,6 @@ private: Menu menu; std::atomic compiling; Glib::Dispatcher compile_success; - Glib::Dispatcher run_success, run_error; void create_menu(); void hide(); From f0a0577526673ad3f1032a12685af44c591b28de Mon Sep 17 00:00:00 2001 From: eidheim Date: Thu, 6 Aug 2015 16:55:32 +0200 Subject: [PATCH 13/22] Now destroys running applications when juci exits. --- src/terminal.cc | 7 +++++++ src/terminal.h | 1 + src/window.cc | 9 +++------ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/terminal.cc b/src/terminal.cc index d558706..86e77ae 100644 --- a/src/terminal.cc +++ b/src/terminal.cc @@ -203,6 +203,13 @@ void Terminal::async_execute(const std::string &command, const std::string &path async_execute_thread.detach(); } +void Terminal::kill_executing() { + async_pid_mutex.lock(); + for(auto &pid: async_pid_descriptors) + kill(pid.first, SIGTERM); + async_pid_mutex.unlock(); +} + int Terminal::print(const std::string &message){ INFO("Terminal: PrintMessage"); text_view.get_buffer()->insert(text_view.get_buffer()->end(), message); diff --git a/src/terminal.h b/src/terminal.h index c7d477f..24c57a7 100644 --- a/src/terminal.h +++ b/src/terminal.h @@ -31,6 +31,7 @@ public: std::unordered_map > async_pid_descriptors; std::unordered_map async_pid_status; std::mutex async_pid_mutex; + void kill_executing(); int print(const std::string &message); void print(int line_nr, const std::string &message); diff --git a/src/window.cc b/src/window.cc index e297239..bba0803 100644 --- a/src/window.cc +++ b/src/window.cc @@ -258,12 +258,8 @@ void Window::create_menu() { bool Window::on_key_press_event(GdkEventKey *event) { if(event->keyval==GDK_KEY_Escape) { - if(entry_box.entries.size()==0) { - Singleton::terminal()->async_pid_mutex.lock(); - for(auto &pid: Singleton::terminal()->async_pid_descriptors) - kill(pid.first, SIGTERM); - Singleton::terminal()->async_pid_mutex.unlock(); - } + if(entry_box.entries.size()==0) + Singleton::terminal()->kill_executing(); entry_box.hide(); } #ifdef __APPLE__ //For Apple's Command-left, right, up, down keys @@ -307,6 +303,7 @@ void Window::hide() { if(!notebook.close_current_page()) return; } + Singleton::terminal()->kill_executing(); Gtk::Window::hide(); } From 9cdc787a9e23c171614f0fb0c53e7819eec725f2 Mon Sep 17 00:00:00 2001 From: eidheim Date: Thu, 6 Aug 2015 18:08:44 +0200 Subject: [PATCH 14/22] Minor fixes to terminal.cc. --- src/terminal.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/terminal.cc b/src/terminal.cc index 86e77ae..92ee282 100644 --- a/src/terminal.cc +++ b/src/terminal.cc @@ -11,6 +11,8 @@ using namespace std; //TODO: remove #define READ 0 #define WRITE 1 +int execute_status=-1; + //TODO: Windows... //Coppied partially from http://www.linuxprogrammingblog.com/code-examples/sigaction void signal_execl_exit(int sig, siginfo_t *siginfo, void *context) { @@ -24,6 +26,8 @@ void signal_execl_exit(int sig, siginfo_t *siginfo, void *context) { close(Singleton::terminal()->async_pid_descriptors.at(siginfo->si_pid).second); Singleton::terminal()->async_pid_descriptors.erase(siginfo->si_pid); } + else + execute_status=status; Singleton::terminal()->async_pid_mutex.unlock(); } @@ -157,7 +161,10 @@ int Terminal::execute(const std::string &command, const std::string &path) { while(gtk_events_pending()) gtk_main_iteration(); } - return pclose(p); + int exit_code=pclose(p); + if(exit_code!=-1) + return exit_code; + return execute_status; } } From 84df7c5fda32c8fdd11d54adf3e3908ee9d92cc1 Mon Sep 17 00:00:00 2001 From: eidheim Date: Thu, 6 Aug 2015 18:50:23 +0200 Subject: [PATCH 15/22] Minor fix. --- src/terminal.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/terminal.cc b/src/terminal.cc index 92ee282..7e32eeb 100644 --- a/src/terminal.cc +++ b/src/terminal.cc @@ -201,8 +201,6 @@ void Terminal::async_execute(const std::string &command, const std::string &path int exit_code=async_pid_status.at(pid); async_pid_status.erase(pid); async_pid_mutex.unlock(); - close(input_descriptor); - close(output_descriptor); if(callback) callback(exit_code); } From 8098f90b779185bc414f254440660411746bdc8e Mon Sep 17 00:00:00 2001 From: eidheim Date: Thu, 6 Aug 2015 19:33:36 +0200 Subject: [PATCH 16/22] Some changes to terminal.*. Execution should now be stable on all unix-like systems independent of pclose-implementations. --- src/terminal.cc | 44 +++++++++++++++++++++++++------------------- src/terminal.h | 4 ---- src/window.cc | 2 +- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/terminal.cc b/src/terminal.cc index 7e32eeb..d80b26a 100644 --- a/src/terminal.cc +++ b/src/terminal.cc @@ -4,6 +4,7 @@ #include "singletons.h" #include #include +#include #include //TODO: remove using namespace std; //TODO: remove @@ -12,6 +13,9 @@ using namespace std; //TODO: remove #define WRITE 1 int execute_status=-1; +std::mutex execute_status_mutex; +std::unordered_map > async_execute_pid_descriptors; +std::unordered_map async_execute_pid_status; //TODO: Windows... //Coppied partially from http://www.linuxprogrammingblog.com/code-examples/sigaction @@ -19,16 +23,16 @@ void signal_execl_exit(int sig, siginfo_t *siginfo, void *context) { int status; while (waitpid(siginfo->si_pid, &status, WNOHANG) > 0) {} - Singleton::terminal()->async_pid_mutex.lock(); - if(Singleton::terminal()->async_pid_descriptors.find(siginfo->si_pid)!=Singleton::terminal()->async_pid_descriptors.end()) { - Singleton::terminal()->async_pid_status[siginfo->si_pid]=status; - close(Singleton::terminal()->async_pid_descriptors.at(siginfo->si_pid).first); - close(Singleton::terminal()->async_pid_descriptors.at(siginfo->si_pid).second); - Singleton::terminal()->async_pid_descriptors.erase(siginfo->si_pid); + execute_status_mutex.lock(); + if(async_execute_pid_descriptors.find(siginfo->si_pid)!=async_execute_pid_descriptors.end()) { + async_execute_pid_status[siginfo->si_pid]=status; + close(async_execute_pid_descriptors.at(siginfo->si_pid).first); + close(async_execute_pid_descriptors.at(siginfo->si_pid).second); + async_execute_pid_descriptors.erase(siginfo->si_pid); } else execute_status=status; - Singleton::terminal()->async_pid_mutex.unlock(); + execute_status_mutex.unlock(); } //TODO: Windows... @@ -161,9 +165,11 @@ int Terminal::execute(const std::string &command, const std::string &path) { while(gtk_events_pending()) gtk_main_iteration(); } + execute_status_mutex.lock(); int exit_code=pclose(p); - if(exit_code!=-1) - return exit_code; + if(exit_code==-1) + exit_code=execute_status; + execute_status_mutex.unlock(); return execute_status; } } @@ -182,10 +188,10 @@ void Terminal::async_execute(const std::string &command, const std::string &path cd_path_and_command=command; int input_descriptor, output_descriptor; - async_pid_mutex.lock(); + execute_status_mutex.lock(); auto pid=popen2(cd_path_and_command.c_str(), &input_descriptor, &output_descriptor); - async_pid_descriptors[pid]={input_descriptor, output_descriptor}; - async_pid_mutex.unlock(); + async_execute_pid_descriptors[pid]={input_descriptor, output_descriptor}; + execute_status_mutex.unlock(); if (pid<=0) async_print("Error: Failed to run command" + command + "\n"); else { @@ -197,10 +203,10 @@ void Terminal::async_execute(const std::string &command, const std::string &path message+=buffer[c]; async_print(message); } - async_pid_mutex.lock(); - int exit_code=async_pid_status.at(pid); - async_pid_status.erase(pid); - async_pid_mutex.unlock(); + execute_status_mutex.lock(); + int exit_code=async_execute_pid_status.at(pid); + async_execute_pid_status.erase(pid); + execute_status_mutex.unlock(); if(callback) callback(exit_code); } @@ -209,10 +215,10 @@ void Terminal::async_execute(const std::string &command, const std::string &path } void Terminal::kill_executing() { - async_pid_mutex.lock(); - for(auto &pid: async_pid_descriptors) + execute_status_mutex.lock(); + for(auto &pid: async_execute_pid_descriptors) kill(pid.first, SIGTERM); - async_pid_mutex.unlock(); + execute_status_mutex.unlock(); } int Terminal::print(const std::string &message){ diff --git a/src/terminal.h b/src/terminal.h index 24c57a7..5857fd8 100644 --- a/src/terminal.h +++ b/src/terminal.h @@ -7,7 +7,6 @@ #include #include #include -#include class Terminal : public Gtk::HBox { public: @@ -28,9 +27,6 @@ public: Terminal(); int execute(const std::string &command, const std::string &path=""); void async_execute(const std::string &command, const std::string &path="", std::function callback=nullptr); - std::unordered_map > async_pid_descriptors; - std::unordered_map async_pid_status; - std::mutex async_pid_mutex; void kill_executing(); int print(const std::string &message); diff --git a/src/window.cc b/src/window.cc index bba0803..ecbf8f0 100644 --- a/src/window.cc +++ b/src/window.cc @@ -212,8 +212,8 @@ void Window::create_menu() { path+="/"+executables[0].second[0]; } if(cmake.project_path!="") { - compiling=true; if(path!="") { + compiling=true; Singleton::terminal()->print("Compiling and executing "+path.string()+"\n"); //TODO: Windows... Singleton::terminal()->async_execute("make 2>&1", cmake.project_path.string(), [this, path](int exit_code){ From 89b6d7e9dcd2c5d30df7a920219cf0c771feddd6 Mon Sep 17 00:00:00 2001 From: eidheim Date: Thu, 6 Aug 2015 19:38:37 +0200 Subject: [PATCH 17/22] Changed some variable names in terminal.cc. --- src/terminal.cc | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/terminal.cc b/src/terminal.cc index d80b26a..0c0d787 100644 --- a/src/terminal.cc +++ b/src/terminal.cc @@ -13,9 +13,9 @@ using namespace std; //TODO: remove #define WRITE 1 int execute_status=-1; -std::mutex execute_status_mutex; -std::unordered_map > async_execute_pid_descriptors; -std::unordered_map async_execute_pid_status; +std::mutex async_and_sync_execute_mutex; +std::unordered_map > async_execute_descriptors; +std::unordered_map async_execute_status; //TODO: Windows... //Coppied partially from http://www.linuxprogrammingblog.com/code-examples/sigaction @@ -23,16 +23,16 @@ void signal_execl_exit(int sig, siginfo_t *siginfo, void *context) { int status; while (waitpid(siginfo->si_pid, &status, WNOHANG) > 0) {} - execute_status_mutex.lock(); - if(async_execute_pid_descriptors.find(siginfo->si_pid)!=async_execute_pid_descriptors.end()) { - async_execute_pid_status[siginfo->si_pid]=status; - close(async_execute_pid_descriptors.at(siginfo->si_pid).first); - close(async_execute_pid_descriptors.at(siginfo->si_pid).second); - async_execute_pid_descriptors.erase(siginfo->si_pid); + async_and_sync_execute_mutex.lock(); + if(async_execute_descriptors.find(siginfo->si_pid)!=async_execute_descriptors.end()) { + async_execute_status[siginfo->si_pid]=status; + close(async_execute_descriptors.at(siginfo->si_pid).first); + close(async_execute_descriptors.at(siginfo->si_pid).second); + async_execute_descriptors.erase(siginfo->si_pid); } else execute_status=status; - execute_status_mutex.unlock(); + async_and_sync_execute_mutex.unlock(); } //TODO: Windows... @@ -165,11 +165,11 @@ int Terminal::execute(const std::string &command, const std::string &path) { while(gtk_events_pending()) gtk_main_iteration(); } - execute_status_mutex.lock(); + async_and_sync_execute_mutex.lock(); int exit_code=pclose(p); if(exit_code==-1) exit_code=execute_status; - execute_status_mutex.unlock(); + async_and_sync_execute_mutex.unlock(); return execute_status; } } @@ -188,10 +188,10 @@ void Terminal::async_execute(const std::string &command, const std::string &path cd_path_and_command=command; int input_descriptor, output_descriptor; - execute_status_mutex.lock(); + async_and_sync_execute_mutex.lock(); auto pid=popen2(cd_path_and_command.c_str(), &input_descriptor, &output_descriptor); - async_execute_pid_descriptors[pid]={input_descriptor, output_descriptor}; - execute_status_mutex.unlock(); + async_execute_descriptors[pid]={input_descriptor, output_descriptor}; + async_and_sync_execute_mutex.unlock(); if (pid<=0) async_print("Error: Failed to run command" + command + "\n"); else { @@ -203,10 +203,10 @@ void Terminal::async_execute(const std::string &command, const std::string &path message+=buffer[c]; async_print(message); } - execute_status_mutex.lock(); - int exit_code=async_execute_pid_status.at(pid); - async_execute_pid_status.erase(pid); - execute_status_mutex.unlock(); + async_and_sync_execute_mutex.lock(); + int exit_code=async_execute_status.at(pid); + async_execute_status.erase(pid); + async_and_sync_execute_mutex.unlock(); if(callback) callback(exit_code); } @@ -215,10 +215,10 @@ void Terminal::async_execute(const std::string &command, const std::string &path } void Terminal::kill_executing() { - execute_status_mutex.lock(); - for(auto &pid: async_execute_pid_descriptors) + async_and_sync_execute_mutex.lock(); + for(auto &pid: async_execute_descriptors) kill(pid.first, SIGTERM); - execute_status_mutex.unlock(); + async_and_sync_execute_mutex.unlock(); } int Terminal::print(const std::string &message){ From c841c7ff11fc0f8422bfb0130a9590caddb0576b Mon Sep 17 00:00:00 2001 From: eidheim Date: Thu, 6 Aug 2015 19:44:06 +0200 Subject: [PATCH 18/22] Minor fix to terminal.cc. --- src/terminal.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/terminal.cc b/src/terminal.cc index 0c0d787..b6ba748 100644 --- a/src/terminal.cc +++ b/src/terminal.cc @@ -20,10 +20,10 @@ std::unordered_map async_execute_status; //TODO: Windows... //Coppied partially from http://www.linuxprogrammingblog.com/code-examples/sigaction void signal_execl_exit(int sig, siginfo_t *siginfo, void *context) { + async_and_sync_execute_mutex.lock(); int status; while (waitpid(siginfo->si_pid, &status, WNOHANG) > 0) {} - async_and_sync_execute_mutex.lock(); if(async_execute_descriptors.find(siginfo->si_pid)!=async_execute_descriptors.end()) { async_execute_status[siginfo->si_pid]=status; close(async_execute_descriptors.at(siginfo->si_pid).first); From 5a32a2700f00ef8381a2f39ae3ccdfdf17c15159 Mon Sep 17 00:00:00 2001 From: eidheim Date: Thu, 6 Aug 2015 19:49:20 +0200 Subject: [PATCH 19/22] Minor fix again to terminal.cc. --- src/terminal.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/terminal.cc b/src/terminal.cc index b6ba748..d634244 100644 --- a/src/terminal.cc +++ b/src/terminal.cc @@ -170,7 +170,7 @@ int Terminal::execute(const std::string &command, const std::string &path) { if(exit_code==-1) exit_code=execute_status; async_and_sync_execute_mutex.unlock(); - return execute_status; + return exit_code; } } From f9416ff9756417feda205996f77c39665e9b71a6 Mon Sep 17 00:00:00 2001 From: eidheim Date: Fri, 7 Aug 2015 11:15:28 +0200 Subject: [PATCH 20/22] Fixed terminal.*. Now kill works without leaving zombies from time to time. --- src/terminal.cc | 113 ++++++++++++++++++++++++++++++++---------------- src/terminal.h | 11 ++--- src/window.cc | 4 +- 3 files changed, 83 insertions(+), 45 deletions(-) diff --git a/src/terminal.cc b/src/terminal.cc index d634244..8f7b2b8 100644 --- a/src/terminal.cc +++ b/src/terminal.cc @@ -9,25 +9,25 @@ #include //TODO: remove using namespace std; //TODO: remove -#define READ 0 -#define WRITE 1 - int execute_status=-1; std::mutex async_and_sync_execute_mutex; -std::unordered_map > async_execute_descriptors; +std::unordered_map > async_execute_descriptors; std::unordered_map async_execute_status; //TODO: Windows... //Coppied partially from http://www.linuxprogrammingblog.com/code-examples/sigaction void signal_execl_exit(int sig, siginfo_t *siginfo, void *context) { async_and_sync_execute_mutex.lock(); + if(async_execute_descriptors.find(siginfo->si_pid)!=async_execute_descriptors.end()) { + close(async_execute_descriptors.at(siginfo->si_pid)[0]); + close(async_execute_descriptors.at(siginfo->si_pid)[1]); + close(async_execute_descriptors.at(siginfo->si_pid)[2]); + } int status; while (waitpid(siginfo->si_pid, &status, WNOHANG) > 0) {} if(async_execute_descriptors.find(siginfo->si_pid)!=async_execute_descriptors.end()) { async_execute_status[siginfo->si_pid]=status; - close(async_execute_descriptors.at(siginfo->si_pid).first); - close(async_execute_descriptors.at(siginfo->si_pid).second); async_execute_descriptors.erase(siginfo->si_pid); } else @@ -36,12 +36,13 @@ void signal_execl_exit(int sig, siginfo_t *siginfo, void *context) { } //TODO: Windows... -//Copied from http://stackoverflow.com/questions/12778672/killing-process-that-has-been-created-with-popen2 -pid_t popen2(const char *command, int *input_descriptor, int *output_descriptor) { +//TODO: Someone who knows this stuff see if I have done something stupid. +//Copied partially from http://stackoverflow.com/questions/12778672/killing-process-that-has-been-created-with-popen2 +pid_t popen3(const char *command, int *input_descriptor, int *output_descriptor, int *error_descriptor) { pid_t pid; - int p_stdin[2], p_stdout[2]; + int p_stdin[2], p_stdout[2], p_stderr[2]; - if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0) + if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0 || pipe(p_stderr) != 0) return -1; pid = fork(); @@ -49,10 +50,12 @@ pid_t popen2(const char *command, int *input_descriptor, int *output_descriptor) if (pid < 0) return pid; else if (pid == 0) { - close(p_stdin[WRITE]); - dup2(p_stdin[READ], READ); - close(p_stdout[READ]); - dup2(p_stdout[WRITE], WRITE); + close(p_stdin[1]); + dup2(p_stdin[0], 0); + close(p_stdout[0]); + dup2(p_stdout[1], 1); + close(p_stderr[0]); + dup2(p_stderr[1], 2); execl("/bin/sh", "sh", "-c", command, NULL); perror("execl"); @@ -60,14 +63,19 @@ pid_t popen2(const char *command, int *input_descriptor, int *output_descriptor) } if (input_descriptor == NULL) - close(p_stdin[WRITE]); + close(p_stdin[1]); else - *input_descriptor = p_stdin[WRITE]; + *input_descriptor = p_stdin[1]; if (output_descriptor == NULL) - close(p_stdout[READ]); + close(p_stdout[0]); + else + *output_descriptor = p_stdout[0]; + + if (error_descriptor == NULL) + close(p_stderr[0]); else - *output_descriptor = p_stdout[READ]; + *error_descriptor = p_stderr[0]; return pid; } @@ -123,13 +131,16 @@ Terminal::Terminal() { text_view.get_buffer()->delete_mark(end); }); + bold_tag=text_view.get_buffer()->create_tag(); + bold_tag->property_weight()=PANGO_WEIGHT_BOLD; async_print_dispatcher.connect([this](){ - async_print_string_mutex.lock(); - if(async_print_string.size()>0) { - print(async_print_string); - async_print_string=""; + async_print_strings_mutex.lock(); + if(async_print_strings.size()>0) { + for(auto &string_bold: async_print_strings) + print(string_bold.first, string_bold.second); + async_print_strings.clear(); } - async_print_string_mutex.unlock(); + async_print_strings_mutex.unlock(); }); //Coppied from http://www.linuxprogrammingblog.com/code-examples/sigaction @@ -182,19 +193,30 @@ void Terminal::async_execute(const std::string &command, const std::string &path boost_path=boost::filesystem::path(path); //TODO: Windows... - cd_path_and_command="cd "+boost_path.string()+" 2>&1 && exec "+command; + cd_path_and_command="cd "+boost_path.string()+" && exec "+command; } else cd_path_and_command=command; - int input_descriptor, output_descriptor; + int input_descriptor, output_descriptor, error_descriptor; async_and_sync_execute_mutex.lock(); - auto pid=popen2(cd_path_and_command.c_str(), &input_descriptor, &output_descriptor); - async_execute_descriptors[pid]={input_descriptor, output_descriptor}; + auto pid=popen3(cd_path_and_command.c_str(), &input_descriptor, &output_descriptor, &error_descriptor); + async_execute_descriptors[pid]={input_descriptor, output_descriptor, error_descriptor}; async_and_sync_execute_mutex.unlock(); if (pid<=0) async_print("Error: Failed to run command" + command + "\n"); else { + std::thread error_thread([this, error_descriptor](){ + char buffer[1024]; + ssize_t n; + while ((n=read(error_descriptor, buffer, 1024)) > 0) { + std::string message; + for(int c=0;c 0) { @@ -206,6 +228,8 @@ void Terminal::async_execute(const std::string &command, const std::string &path async_and_sync_execute_mutex.lock(); int exit_code=async_execute_status.at(pid); async_execute_status.erase(pid); + if(async_execute_descriptors.find(pid)!=async_execute_descriptors.end()) //cleanup in case signal_execl_exit is not called + async_execute_descriptors.erase(pid); async_and_sync_execute_mutex.unlock(); if(callback) callback(exit_code); @@ -216,23 +240,36 @@ void Terminal::async_execute(const std::string &command, const std::string &path void Terminal::kill_executing() { async_and_sync_execute_mutex.lock(); - for(auto &pid: async_execute_descriptors) - kill(pid.first, SIGTERM); + for(auto &pid: async_execute_descriptors) { + kill(pid.first, SIGTERM); //signal_execl_exit is not always called after kill! Hence the following lines: + close(async_execute_descriptors.at(pid.first)[0]); + close(async_execute_descriptors.at(pid.first)[1]); + close(async_execute_descriptors.at(pid.first)[2]); + int status; + while (waitpid(pid.first, &status, WNOHANG) > 0) {} + async_execute_status[pid.first]=status; + } async_and_sync_execute_mutex.unlock(); } -int Terminal::print(const std::string &message){ +int Terminal::print(const std::string &message, bool bold){ INFO("Terminal: PrintMessage"); - text_view.get_buffer()->insert(text_view.get_buffer()->end(), message); + if(bold) + text_view.get_buffer()->insert_with_tag(text_view.get_buffer()->end(), message, bold_tag); + else + text_view.get_buffer()->insert(text_view.get_buffer()->end(), message); return text_view.get_buffer()->end().get_line(); } -void Terminal::print(int line_nr, const std::string &message){ +void Terminal::print(int line_nr, const std::string &message, bool bold){ INFO("Terminal: PrintMessage at line " << line_nr); auto iter=text_view.get_buffer()->get_iter_at_line(line_nr); while(!iter.ends_line()) iter++; - text_view.get_buffer()->insert(iter, message); + if(bold) + text_view.get_buffer()->insert_with_tag(iter, message, bold_tag); + else + text_view.get_buffer()->insert(iter, message); } std::shared_ptr Terminal::print_in_progress(std::string start_msg) { @@ -240,13 +277,13 @@ std::shared_ptr Terminal::print_in_progress(std::string st return in_progress; } -void Terminal::async_print(const std::string &message) { - async_print_string_mutex.lock(); +void Terminal::async_print(const std::string &message, bool bold) { + async_print_strings_mutex.lock(); bool dispatch=true; - if(async_print_string.size()>0) + if(async_print_strings.size()>0) dispatch=false; - async_print_string+=message; - async_print_string_mutex.unlock(); + async_print_strings.emplace_back(message, bold); + async_print_strings_mutex.unlock(); if(dispatch) async_print_dispatcher(); } diff --git a/src/terminal.h b/src/terminal.h index 5857fd8..a9b9475 100644 --- a/src/terminal.h +++ b/src/terminal.h @@ -29,17 +29,18 @@ public: void async_execute(const std::string &command, const std::string &path="", std::function callback=nullptr); void kill_executing(); - int print(const std::string &message); - void print(int line_nr, const std::string &message); + int print(const std::string &message, bool bold=false); + void print(int line_nr, const std::string &message, bool bold=false); std::shared_ptr print_in_progress(std::string start_msg); - void async_print(const std::string &message); + void async_print(const std::string &message, bool bold=false); private: Gtk::TextView text_view; Gtk::ScrolledWindow scrolled_window; Glib::Dispatcher async_print_dispatcher; - std::string async_print_string; - std::mutex async_print_string_mutex; + std::vector > async_print_strings; + std::mutex async_print_strings_mutex; + Glib::RefPtr bold_tag; }; #endif // JUCI_TERMINAL_H_ diff --git a/src/window.cc b/src/window.cc index ecbf8f0..5f1ff4e 100644 --- a/src/window.cc +++ b/src/window.cc @@ -216,12 +216,12 @@ void Window::create_menu() { compiling=true; Singleton::terminal()->print("Compiling and executing "+path.string()+"\n"); //TODO: Windows... - Singleton::terminal()->async_execute("make 2>&1", cmake.project_path.string(), [this, path](int exit_code){ + Singleton::terminal()->async_execute("make", cmake.project_path.string(), [this, path](int exit_code){ compiling=false; if(exit_code==EXIT_SUCCESS) { compile_success(); //TODO: Windows... - Singleton::terminal()->async_execute(path.string()+" 2>&1", path.parent_path().string(), [this, path](int exit_code){ + Singleton::terminal()->async_execute(path.string(), path.parent_path().string(), [this, path](int exit_code){ Singleton::terminal()->async_print(path.string()+" returned: "+std::to_string(exit_code)+'\n'); }); } From 7be6c3e91de918152fe58c8e13d08963b7c7fe8a Mon Sep 17 00:00:00 2001 From: eidheim Date: Fri, 7 Aug 2015 14:32:53 +0200 Subject: [PATCH 21/22] Gave up on being able to call more complex commands. Should work without crashes now. --- src/notebook.cc | 1 + src/terminal.cc | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/notebook.cc b/src/notebook.cc index f33037f..e77e000 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -120,6 +120,7 @@ bool Notebook::save(int page) { Singleton::terminal()->print("File saved to: " +view->file_path+"\n"); //If CMakeLists.txt have been modified: + //TODO: recreate cmake even without directories open? if(boost::filesystem::path(view->file_path).filename().string()=="CMakeLists.txt") { if(directories.cmake && directories.cmake->project_path!="" && boost::filesystem::path(view->file_path)>=directories.cmake->project_path && CMake::create_compile_commands(directories.cmake->project_path.string())) { directories.open_folder(); diff --git a/src/terminal.cc b/src/terminal.cc index 8f7b2b8..7bbf668 100644 --- a/src/terminal.cc +++ b/src/terminal.cc @@ -57,6 +57,7 @@ pid_t popen3(const char *command, int *input_descriptor, int *output_descriptor, close(p_stderr[0]); dup2(p_stderr[1], 2); + //setpgid(0, 0); //TODO: get this working so we can execute without calling exec from sh -c (in that way we can call more complex commands) execl("/bin/sh", "sh", "-c", command, NULL); perror("execl"); exit(1); @@ -240,15 +241,19 @@ void Terminal::async_execute(const std::string &command, const std::string &path void Terminal::kill_executing() { async_and_sync_execute_mutex.lock(); + int status; for(auto &pid: async_execute_descriptors) { - kill(pid.first, SIGTERM); //signal_execl_exit is not always called after kill! Hence the following lines: close(async_execute_descriptors.at(pid.first)[0]); close(async_execute_descriptors.at(pid.first)[1]); close(async_execute_descriptors.at(pid.first)[2]); - int status; - while (waitpid(pid.first, &status, WNOHANG) > 0) {} + kill(pid.first, SIGTERM); //signal_execl_exit is not always called after kill! + while(waitpid(pid.first, &status, WNOHANG) > 0) {} async_execute_status[pid.first]=status; } + /*for(auto &pid: async_execute_descriptors) { + kill(-pid.first, SIGTERM); + while(waitpid(-pid.first, &status, WNOHANG) > 0) {} + }*/ async_and_sync_execute_mutex.unlock(); } From 5cd2e4909c7237b1fc278dfc003f61342815b270 Mon Sep 17 00:00:00 2001 From: eidheim Date: Fri, 7 Aug 2015 17:31:17 +0200 Subject: [PATCH 22/22] Solved the whole zombie/crash issue. Though, for some reason, files/pipes/something are kept open, after a while one gets the message that too many files are open. --- src/terminal.cc | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/terminal.cc b/src/terminal.cc index 7bbf668..8103af4 100644 --- a/src/terminal.cc +++ b/src/terminal.cc @@ -57,7 +57,7 @@ pid_t popen3(const char *command, int *input_descriptor, int *output_descriptor, close(p_stderr[0]); dup2(p_stderr[1], 2); - //setpgid(0, 0); //TODO: get this working so we can execute without calling exec from sh -c (in that way we can call more complex commands) + setpgid(0, 0); execl("/bin/sh", "sh", "-c", command, NULL); perror("execl"); exit(1); @@ -194,7 +194,7 @@ void Terminal::async_execute(const std::string &command, const std::string &path boost_path=boost::filesystem::path(path); //TODO: Windows... - cd_path_and_command="cd "+boost_path.string()+" && exec "+command; + cd_path_and_command="cd "+boost_path.string()+" && "+command; } else cd_path_and_command=command; @@ -204,8 +204,11 @@ void Terminal::async_execute(const std::string &command, const std::string &path auto pid=popen3(cd_path_and_command.c_str(), &input_descriptor, &output_descriptor, &error_descriptor); async_execute_descriptors[pid]={input_descriptor, output_descriptor, error_descriptor}; async_and_sync_execute_mutex.unlock(); - if (pid<=0) - async_print("Error: Failed to run command" + command + "\n"); + if (pid<=0) { + async_print("Error: Failed to run command: " + command + "\n"); + if(callback) + callback(-1); + } else { std::thread error_thread([this, error_descriptor](){ char buffer[1024]; @@ -246,14 +249,10 @@ void Terminal::kill_executing() { close(async_execute_descriptors.at(pid.first)[0]); close(async_execute_descriptors.at(pid.first)[1]); close(async_execute_descriptors.at(pid.first)[2]); - kill(pid.first, SIGTERM); //signal_execl_exit is not always called after kill! - while(waitpid(pid.first, &status, WNOHANG) > 0) {} + kill(-pid.first, SIGINT); //signal_execl_exit is not always called after kill! + while(waitpid(-pid.first, &status, WNOHANG) > 0) {} async_execute_status[pid.first]=status; } - /*for(auto &pid: async_execute_descriptors) { - kill(-pid.first, SIGTERM); - while(waitpid(-pid.first, &status, WNOHANG) > 0) {} - }*/ async_and_sync_execute_mutex.unlock(); }