From 485f064c6a4161a188e06559d2965d0a7004cf2a Mon Sep 17 00:00:00 2001 From: Richard Denton Date: Mon, 8 Aug 2022 21:04:51 +1000 Subject: [PATCH 1/8] new: Add de-coupled Process module --- include/Process.h | 66 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 include/Process.h diff --git a/include/Process.h b/include/Process.h new file mode 100644 index 0000000..56d447c --- /dev/null +++ b/include/Process.h @@ -0,0 +1,66 @@ +#ifndef ARCADE_MACHINE_PROCESS_H +#define ARCADE_MACHINE_PROCESS_H + +#include +#include +#include +#include +#include + +namespace Arcade { + class Process { + private: + std::string _command; + std::vector _args; + bool _is_running = false; + + public: + Process(std::string command, std::vector args) { + this->_command = command; + this->_args = args; + } + + bool is_running() const { return this->_is_running; } + + /** + * @brief Executes the process synchronously. + * @return std::string The buffered stream from stdout / stderr + */ + std::string execute_sync() { + // This should never happen, but you never know... + if (this->_is_running) + throw std::runtime_error("Instance of process is already running."); + + std::string command = std::string(this->_command); + for (auto arg : this->_args) + command.append(" " + arg); + + this->_is_running = true; + auto pipe = popen(command.c_str(), "r"); + if (! pipe) + throw std::runtime_error("Error running process `" + command + "`"); + + std::array buffer; + std::string str_buffer; + while (! feof(pipe)) { + if (fgets(buffer.data(), 256, pipe) != nullptr) + str_buffer += buffer.data(); + } + + if (pclose(pipe) != EXIT_SUCCESS) + std::cerr << "Unable to close process, possible memory leak" << std::endl; + + this->_is_running = false; + return str_buffer; + } + + /** + * @brief Executes the process asynchronously in a thread. + */ + void execute_async(std::vector &output_stream) { + throw std::runtime_error("This method is currently not supported."); + } + }; +} + +#endif \ No newline at end of file From 50f49ddf0057e56c894c12bb6ed6f9ed266fea5d Mon Sep 17 00:00:00 2001 From: Richard Denton Date: Mon, 8 Aug 2022 21:13:35 +1000 Subject: [PATCH 2/8] refactor: Use Arcade Process module when not on Win32 --- include/ConfigData.h | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/include/ConfigData.h b/include/ConfigData.h index 57d8b59..8987fbb 100644 --- a/include/ConfigData.h +++ b/include/ConfigData.h @@ -7,6 +7,8 @@ #include #include #include +#include +#include "Process.h" /** * @brief Parses the configuration data from config.txt files to a data object @@ -180,19 +182,28 @@ class ConfigData{ * * @param url git repository url * @param dir directory to clone to - * @return true - * @return false + * @return bool */ bool get_from_git(std::string url, const char* dir) { struct stat info; - - if (stat(dir, &info) != 0){ +#ifdef _WIN32 + // TODO: Replace with Arcade.Process once Windows + // support has been written and tested. + if (stat(dir, &info) != 0) system(("git clone " + url + " " + dir).c_str()); - } else { - std::string d = dir; - system(("git -C " + d + " pull " + url).c_str()); - } + else + system(("git -C " + dir + " pull " + url).c_str()); +#else + std::vector args; + if (stat(dir, &info) != 0) + args = {"clone", url, dir}; + else + args = {"-C", dir, "pull", url}; + + auto p = Arcade::Process("git", args); + p.execute_sync(); +#endif return true; } @@ -225,14 +236,7 @@ class ConfigData{ */ void delete_dir(std::string dir) { - std::string error; - try{ - system(("rmdir -s -q " + dir).c_str()); - throw(error); - } catch (std::string e) { - std::cerr << "Name cannot be changed" << std::endl; - std::cerr << error << std::endl; - } + std::experimental::filesystem::remove_all(dir); } /** From 13cb792aeff86db6f9852d6992400a51dad01fe3 Mon Sep 17 00:00:00 2001 From: Richard Denton Date: Mon, 8 Aug 2022 21:18:03 +1000 Subject: [PATCH 3/8] refactor: remove unused array include --- include/Process.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/Process.h b/include/Process.h index 56d447c..1bc3b82 100644 --- a/include/Process.h +++ b/include/Process.h @@ -1,7 +1,6 @@ #ifndef ARCADE_MACHINE_PROCESS_H #define ARCADE_MACHINE_PROCESS_H -#include #include #include #include From 1fad30da3783c9fc8870e8f6130b0d1a839eb4ce Mon Sep 17 00:00:00 2001 From: Richard Denton Date: Mon, 8 Aug 2022 21:18:57 +1000 Subject: [PATCH 4/8] refactor: add used array include (whoops) --- include/Process.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/Process.h b/include/Process.h index 1bc3b82..56d447c 100644 --- a/include/Process.h +++ b/include/Process.h @@ -1,6 +1,7 @@ #ifndef ARCADE_MACHINE_PROCESS_H #define ARCADE_MACHINE_PROCESS_H +#include #include #include #include From 9692bc9a6da2a94ea1c52d245baac413f5beb400 Mon Sep 17 00:00:00 2001 From: Richard Denton Date: Mon, 8 Aug 2022 21:19:19 +1000 Subject: [PATCH 5/8] refactor: align buffer size --- include/Process.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/Process.h b/include/Process.h index 56d447c..998b3d9 100644 --- a/include/Process.h +++ b/include/Process.h @@ -40,7 +40,7 @@ namespace Arcade { if (! pipe) throw std::runtime_error("Error running process `" + command + "`"); - std::array buffer; + std::array buffer; std::string str_buffer; while (! feof(pipe)) { if (fgets(buffer.data(), 256, pipe) != nullptr) From 2e519ed5130f1dc7a3ff52dfc8ba75ebe3110a3b Mon Sep 17 00:00:00 2001 From: Richard Denton Date: Mon, 8 Aug 2022 22:56:15 +1000 Subject: [PATCH 6/8] enhance: add support for asynchronous, thread-based process launching --- include/Process.h | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/include/Process.h b/include/Process.h index 998b3d9..735a3b4 100644 --- a/include/Process.h +++ b/include/Process.h @@ -6,14 +6,43 @@ #include #include #include +#include namespace Arcade { class Process { private: std::string _command; std::vector _args; + std::thread _execution_context; + bool _is_running = false; + void _execute_async_thread(std::vector &output_stream) { + // Don't allow duplicate threads to execute. + if (this->_is_running) + return; + + std::string command = std::string(this->_command); + for (auto arg : this->_args) + command.append(" " + arg); + + this->_is_running = true; + auto pipe = popen(command.c_str(), "r"); + if (! pipe) + throw std::runtime_error("Error running process (async) `" + command + "`"); + + std::array buffer; + while (! feof(pipe)) { + if (fgets(buffer.data(), 256, pipe) != nullptr) + output_stream.push_back(buffer.data()); + } + + if (pclose(pipe) != EXIT_SUCCESS) + std::cerr << "Unable to close process (async), possible memory leak" << std::endl; + + this->_is_running = false; + } + public: Process(std::string command, std::vector args) { this->_command = command; @@ -58,7 +87,8 @@ namespace Arcade { * @brief Executes the process asynchronously in a thread. */ void execute_async(std::vector &output_stream) { - throw std::runtime_error("This method is currently not supported."); + this->_execution_context = std::thread(&Process::_execute_async_thread, this, std::ref(output_stream)); + this->_execution_context.detach(); } }; } From d39b4181742b41cd807334a86854c0ee05521c8b Mon Sep 17 00:00:00 2001 From: Richard Denton Date: Mon, 29 Aug 2022 20:38:06 +1000 Subject: [PATCH 7/8] refactor: move ArcadeProcess to cpp --- include/ArcadeProcess.h | 27 ++++++++++++ include/Process.h | 96 ----------------------------------------- src/ArcadeProcess.cpp | 73 +++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 96 deletions(-) create mode 100644 include/ArcadeProcess.h delete mode 100644 include/Process.h create mode 100644 src/ArcadeProcess.cpp diff --git a/include/ArcadeProcess.h b/include/ArcadeProcess.h new file mode 100644 index 0000000..714be49 --- /dev/null +++ b/include/ArcadeProcess.h @@ -0,0 +1,27 @@ +#ifndef ARCADE_MACHINE_PROCESS_H +#define ARCADE_MACHINE_PROCESS_H + +#include +#include +#include +#include +#include +#include + +class ArcadeProcess { + private: + std::string _command; + std::vector _args; + std::thread _execution_context; + bool _is_running = false; + void _execute_async_thread(); + + public: + std::vector output_stream; + ArcadeProcess(std::string command, std::vector args); + bool is_running() const { return this->_is_running; } + std::string execute_sync(); + void execute_async(); +}; + +#endif \ No newline at end of file diff --git a/include/Process.h b/include/Process.h deleted file mode 100644 index 735a3b4..0000000 --- a/include/Process.h +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef ARCADE_MACHINE_PROCESS_H -#define ARCADE_MACHINE_PROCESS_H - -#include -#include -#include -#include -#include -#include - -namespace Arcade { - class Process { - private: - std::string _command; - std::vector _args; - std::thread _execution_context; - - bool _is_running = false; - - void _execute_async_thread(std::vector &output_stream) { - // Don't allow duplicate threads to execute. - if (this->_is_running) - return; - - std::string command = std::string(this->_command); - for (auto arg : this->_args) - command.append(" " + arg); - - this->_is_running = true; - auto pipe = popen(command.c_str(), "r"); - if (! pipe) - throw std::runtime_error("Error running process (async) `" + command + "`"); - - std::array buffer; - while (! feof(pipe)) { - if (fgets(buffer.data(), 256, pipe) != nullptr) - output_stream.push_back(buffer.data()); - } - - if (pclose(pipe) != EXIT_SUCCESS) - std::cerr << "Unable to close process (async), possible memory leak" << std::endl; - - this->_is_running = false; - } - - public: - Process(std::string command, std::vector args) { - this->_command = command; - this->_args = args; - } - - bool is_running() const { return this->_is_running; } - - /** - * @brief Executes the process synchronously. - * @return std::string The buffered stream from stdout / stderr - */ - std::string execute_sync() { - // This should never happen, but you never know... - if (this->_is_running) - throw std::runtime_error("Instance of process is already running."); - - std::string command = std::string(this->_command); - for (auto arg : this->_args) - command.append(" " + arg); - - this->_is_running = true; - auto pipe = popen(command.c_str(), "r"); - if (! pipe) - throw std::runtime_error("Error running process `" + command + "`"); - - std::array buffer; - std::string str_buffer; - while (! feof(pipe)) { - if (fgets(buffer.data(), 256, pipe) != nullptr) - str_buffer += buffer.data(); - } - - if (pclose(pipe) != EXIT_SUCCESS) - std::cerr << "Unable to close process, possible memory leak" << std::endl; - - this->_is_running = false; - return str_buffer; - } - - /** - * @brief Executes the process asynchronously in a thread. - */ - void execute_async(std::vector &output_stream) { - this->_execution_context = std::thread(&Process::_execute_async_thread, this, std::ref(output_stream)); - this->_execution_context.detach(); - } - }; -} - -#endif \ No newline at end of file diff --git a/src/ArcadeProcess.cpp b/src/ArcadeProcess.cpp new file mode 100644 index 0000000..288c593 --- /dev/null +++ b/src/ArcadeProcess.cpp @@ -0,0 +1,73 @@ +#include "ArcadeProcess.h" + +#include +#include +#include +#include +#include + + +void ArcadeProcess::_execute_async_thread() { + // Don't allow duplicate threads to execute. + if (this->_is_running) + return; + + std::string command = std::string(this->_command); + for (auto arg : this->_args) + command.append(" " + arg); + + this->_is_running = true; + auto pipe = popen(command.c_str(), "r"); + if (! pipe) + throw std::runtime_error("Error running process (async) `" + command + "`"); + + std::array buffer; + while (! feof(pipe)) { + if (fgets(buffer.data(), 256, pipe) != nullptr) + this->output_stream.push_back(buffer.data()); + } + + if (pclose(pipe) != EXIT_SUCCESS) + std::cerr << "Unable to close process (async), possible memory leak" << std::endl; + + this->_is_running = false; +} + +ArcadeProcess::ArcadeProcess(std::string command, std::vector args) { + this->_command = command; + this->_args = args; + this->output_stream = std::vector(); +} + +std::string ArcadeProcess::execute_sync() { + // This should never happen, but you never know... + if (this->_is_running) + throw std::runtime_error("Instance of process is already running."); + + std::string command = std::string(this->_command); + for (auto arg : this->_args) + command.append(" " + arg); + + this->_is_running = true; + auto pipe = popen(command.c_str(), "r"); + if (! pipe) + throw std::runtime_error("Error running process `" + command + "`"); + + std::array buffer; + std::string str_buffer; + while (! feof(pipe)) { + if (fgets(buffer.data(), 256, pipe) != nullptr) + str_buffer += buffer.data(); + } + + if (pclose(pipe) != EXIT_SUCCESS) + std::cerr << "Unable to close process, possible memory leak" << std::endl; + + this->_is_running = false; + return str_buffer; +} + +void ArcadeProcess::execute_async() { + this->_execution_context = std::thread(&ArcadeProcess::_execute_async_thread, this); + this->_execution_context.detach(); +} From f50fb1960bed4bb20472910d500f1873700b4dbe Mon Sep 17 00:00:00 2001 From: Richard Denton Date: Mon, 29 Aug 2022 20:38:27 +1000 Subject: [PATCH 8/8] fix: remove unused include --- include/ConfigData.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/ConfigData.h b/include/ConfigData.h index e7c7deb..32d87f1 100644 --- a/include/ConfigData.h +++ b/include/ConfigData.h @@ -5,7 +5,6 @@ #include #include #include -#include "Process.h" #include "splashkit.h" /**