parent
b9e9119832
commit
e35536c7e3
@ -0,0 +1,130 @@ |
|||||||
|
#include "builder.hpp" |
||||||
|
#include "dbc.hpp" |
||||||
|
#include "watcher.hpp" |
||||||
|
#include "game_engine.hpp" |
||||||
|
#include <chrono> // for milliseconds |
||||||
|
#include <efsw/efsw.hpp> |
||||||
|
#include <fmt/chrono.h> |
||||||
|
#include <fmt/color.h> |
||||||
|
#include <fmt/core.h> |
||||||
|
#include <fmt/core.h> |
||||||
|
#include <fstream> |
||||||
|
#include <git2.h> |
||||||
|
#include <iostream> |
||||||
|
#include <memory> // for allocator, shared_ptr |
||||||
|
#include <regex> |
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> // for EXIT_SUCCESS |
||||||
|
#include <string> // for operator+, to_string |
||||||
|
#include <thread> // for sleep_for |
||||||
|
#include <unistd.h> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
using namespace std; |
||||||
|
using namespace fmt; |
||||||
|
|
||||||
|
#define BUF_MAX 1024 |
||||||
|
|
||||||
|
void Builder::run_build(GameEngine &game, const char* command) { |
||||||
|
regex err_re("(.*?):([0-9]+):([0-9]+):\\s*(.*?):\\s*(.*)\n*"); |
||||||
|
|
||||||
|
char buffer[BUF_MAX]; // BUF_MAX is a define already?
|
||||||
|
ofstream stats_out; |
||||||
|
stats_out.open("stats.csv", ios::out | ios::app); |
||||||
|
std::time_t tstamp = std::time(nullptr); |
||||||
|
|
||||||
|
dbc::check(stats_out.good(), "Error opening stats.csv file."); |
||||||
|
dbc::pre("simple test", [&]() { return stats_out.good(); }); |
||||||
|
|
||||||
|
// need to catch the error message when the command is bad
|
||||||
|
FILE *build_out = popen(command, "r"); |
||||||
|
dbc::check(build_out != nullptr, "Failed to run command."); |
||||||
|
|
||||||
|
int hit_count = 0; |
||||||
|
|
||||||
|
while(fgets(buffer, BUF_MAX, build_out) != nullptr) { |
||||||
|
string line(buffer); // yeah, that's probably a problem
|
||||||
|
|
||||||
|
smatch err; |
||||||
|
bool match = regex_match(line, err, err_re); |
||||||
|
|
||||||
|
if(match) { |
||||||
|
string file_name = err[1].str(); |
||||||
|
string lnumber = err[2].str(); |
||||||
|
string col = err[3].str(); |
||||||
|
string type = err[4].str(); |
||||||
|
string message = err[5].str(); |
||||||
|
|
||||||
|
string result = format("{:%FT%T},{},{},{},{},{}\n", |
||||||
|
fmt::localtime(tstamp), file_name, |
||||||
|
lnumber, col, type, message); |
||||||
|
|
||||||
|
stats_out << result; |
||||||
|
gui.output(format("\nHIT WITH {} @ {}:{}:{} {}", type, file_name, lnumber, col, message)); |
||||||
|
|
||||||
|
game.hit(type); |
||||||
|
++hit_count; |
||||||
|
|
||||||
|
// refactor this
|
||||||
|
if(game.is_dead()) { |
||||||
|
gui.output(format("YOU DIED!\n")); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if(hit_count == 0) { |
||||||
|
game.heal(10); |
||||||
|
} |
||||||
|
|
||||||
|
int rc = pclose(build_out); |
||||||
|
if(rc == 0) { |
||||||
|
gui.output("BUILD FINISHED!"); |
||||||
|
} else { |
||||||
|
gui.output(format("!!! BUILD FAILED. Your command correct? '{}'", command)); |
||||||
|
} |
||||||
|
|
||||||
|
stats_out.close(); |
||||||
|
dbc::post("a post test", [&]() { return !stats_out.is_open(); }); |
||||||
|
} |
||||||
|
|
||||||
|
void Builder::run(const char *git_path, const char *build_cmd) { |
||||||
|
git_repository* repo = nullptr; |
||||||
|
|
||||||
|
try { |
||||||
|
gui.output(format("Using build command: {}", build_cmd)); |
||||||
|
efsw::FileWatcher* fileWatcher = new efsw::FileWatcher(); |
||||||
|
dbc::check(fileWatcher != nullptr, "Failed to create filewatcher."); |
||||||
|
|
||||||
|
git_libgit2_init(); |
||||||
|
|
||||||
|
int err = git_repository_open(&repo, git_path); |
||||||
|
dbc::check(err == 0, git_error_last()->message); |
||||||
|
|
||||||
|
UpdateListener* listener = new UpdateListener(repo); |
||||||
|
dbc::check(listener != nullptr, "Failed to create listener."); |
||||||
|
|
||||||
|
gui.output(format("Watching directory {} for changes...\n", git_path)); |
||||||
|
efsw::WatchID wid = fileWatcher->addWatch(git_path, listener, true); |
||||||
|
|
||||||
|
int rc = gui.main_loop(game, [&] { |
||||||
|
fileWatcher->watch(); |
||||||
|
|
||||||
|
if(listener->changes) { |
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100)); |
||||||
|
gui.output(format("CHANGES! Running build {}", build_cmd)); |
||||||
|
run_build(game, build_cmd); |
||||||
|
listener->reset_state(); |
||||||
|
} |
||||||
|
return 0; |
||||||
|
}); |
||||||
|
|
||||||
|
dbc::check(rc == 0, "Invalid return from main_loop."); |
||||||
|
|
||||||
|
fileWatcher->removeWatch(wid); |
||||||
|
git_libgit2_shutdown(); |
||||||
|
} catch(dbc::Error &err) { |
||||||
|
if(repo != nullptr) git_repository_free(repo); |
||||||
|
git_libgit2_shutdown(); |
||||||
|
throw err; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
#pragma once |
||||||
|
#include "gui.hpp" |
||||||
|
#include "game_engine.hpp" |
||||||
|
|
||||||
|
class Builder { |
||||||
|
GUI gui; |
||||||
|
GameEngine game; |
||||||
|
|
||||||
|
public: |
||||||
|
|
||||||
|
Builder(GUI &g, GameEngine &engine) : gui(g), game(engine) {}; |
||||||
|
|
||||||
|
void run_build(GameEngine &game, const char* command); |
||||||
|
|
||||||
|
void run(const char *git_path, const char *build_cmd); |
||||||
|
|
||||||
|
}; |
@ -1,176 +1,21 @@ |
|||||||
#include "dbc.hpp" |
#include "builder.hpp" |
||||||
#include "gui.hpp" |
|
||||||
#include "game_engine.hpp" |
|
||||||
#include <chrono> // for milliseconds |
|
||||||
#include <efsw/efsw.hpp> |
|
||||||
#include <filesystem> |
|
||||||
#include <fmt/chrono.h> |
|
||||||
#include <fmt/color.h> |
|
||||||
#include <fmt/core.h> |
#include <fmt/core.h> |
||||||
#include <fmt/core.h> |
|
||||||
#include <fstream> |
|
||||||
#include <ftxui/component/event.hpp> // for Event |
|
||||||
#include <ftxui/component/mouse.hpp> // for ftxui |
|
||||||
#include <ftxui/dom/elements.hpp> // for text, separator, Element, operator|, vbox, border |
|
||||||
#include <git2.h> |
|
||||||
#include <iostream> |
|
||||||
#include <memory> // for allocator, shared_ptr |
|
||||||
#include <regex> |
|
||||||
#include <stdio.h> |
|
||||||
#include <stdlib.h> // for EXIT_SUCCESS |
|
||||||
#include <string> // for operator+, to_string |
|
||||||
#include <thread> // for sleep_for |
|
||||||
#include <unistd.h> |
|
||||||
#include <vector> |
|
||||||
|
|
||||||
#include "ftxui/component/component.hpp" // for CatchEvent, Renderer, operator|= |
|
||||||
#include "ftxui/component/loop.hpp" // for Loop |
|
||||||
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive |
|
||||||
|
|
||||||
using namespace ftxui; |
|
||||||
using namespace std; |
|
||||||
|
|
||||||
using namespace std; |
|
||||||
using namespace fmt; |
|
||||||
namespace fs = std::filesystem; |
|
||||||
|
|
||||||
const auto ERROR = fmt::emphasis::bold | fg(fmt::color::red); |
|
||||||
|
|
||||||
#define BUF_MAX 1024 |
|
||||||
|
|
||||||
class UpdateListener : public efsw::FileWatchListener { |
|
||||||
public: |
|
||||||
bool changes = false; |
|
||||||
git_repository* repo = nullptr; |
|
||||||
|
|
||||||
UpdateListener(git_repository *r) : repo(r) {}; |
|
||||||
|
|
||||||
void handleFileAction(efsw::WatchID watchid, |
|
||||||
const std::string& dir, |
|
||||||
const std::string& filename, |
|
||||||
efsw::Action action, |
|
||||||
std::string oldFilename) override |
|
||||||
{ |
|
||||||
|
|
||||||
// this is some gnarly BS here, probably tons
|
|
||||||
// of memory leaks for now but it's working
|
|
||||||
int ignored = 1; |
|
||||||
auto the_path = fs::path(dir) / fs::path(filename); |
|
||||||
string full_path = the_path.lexically_normal().string(); |
|
||||||
|
|
||||||
std::replace(full_path.begin(), |
|
||||||
full_path.end(), '\\', '/'); |
|
||||||
|
|
||||||
int rc = git_ignore_path_is_ignored(&ignored, repo, full_path.c_str()); |
|
||||||
|
|
||||||
dbc::check(rc == 0, "git ignored failed."); |
|
||||||
|
|
||||||
if(!ignored) { |
|
||||||
output(format("\nCHANGE: filename={} .gitignored?={}", full_path.c_str(), ignored)); |
|
||||||
|
|
||||||
changes = changes || !ignored; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void reset_state() { |
|
||||||
changes = false; |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
void run_build(GameEngine &game, const char* command) { |
|
||||||
regex err_re("(.*?):([0-9]+):([0-9]+):\\s*(.*?):\\s*(.*)\n*"); |
|
||||||
|
|
||||||
char buffer[BUF_MAX]; // BUF_MAX is a define already?
|
|
||||||
ofstream stats_out; |
|
||||||
stats_out.open("stats.csv", ios::out | ios::app); |
|
||||||
std::time_t tstamp = std::time(nullptr); |
|
||||||
|
|
||||||
dbc::check(stats_out.good(), "Error opening stats.csv file."); |
|
||||||
dbc::pre("simple test", [&]() { return stats_out.good(); }); |
|
||||||
|
|
||||||
// need to catch the error message when the command is bad
|
|
||||||
FILE *build_out = popen(command, "r"); |
|
||||||
dbc::check(build_out != nullptr, "Failed to run command."); |
|
||||||
|
|
||||||
while(fgets(buffer, BUF_MAX, build_out) != nullptr) { |
|
||||||
string line(buffer); // yeah, that's probably a problem
|
|
||||||
|
|
||||||
smatch err; |
|
||||||
bool match = regex_match(line, err, err_re); |
|
||||||
|
|
||||||
if(match) { |
|
||||||
string file_name = err[1].str(); |
|
||||||
string lnumber = err[2].str(); |
|
||||||
string col = err[3].str(); |
|
||||||
string type = err[4].str(); |
|
||||||
string message = err[5].str(); |
|
||||||
|
|
||||||
string result = format("{:%FT%T},{},{},{},{},{}\n", |
|
||||||
fmt::localtime(tstamp), file_name, |
|
||||||
lnumber, col, type, message); |
|
||||||
|
|
||||||
stats_out << result; |
|
||||||
output(format("\nHIT WITH {} @ {}:{}:{} {}", type, file_name, lnumber, col, message)); |
|
||||||
|
|
||||||
game.hit(type); |
|
||||||
|
|
||||||
// refactor this
|
|
||||||
if(game.is_dead()) { |
|
||||||
output(format("YOU DIED!\n")); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
stats_out.close(); |
|
||||||
dbc::post("a post test", [&]() { return !stats_out.is_open(); }); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) |
int main(int argc, char *argv[]) |
||||||
{ |
{ |
||||||
git_repository* repo = nullptr; |
if(argc != 3) { |
||||||
|
fmt::println("USAGE: watchgit PATH BUILD_CMD"); |
||||||
try { |
return 1; |
||||||
dbc::check(argc == 3, "USAGE: watchgit PATH BUILD_CMD"); |
} else { |
||||||
const char *git_path = argv[1]; |
const char *git_path = argv[1]; |
||||||
const char *build_cmd = argv[2]; |
const char *build_cmd = argv[2]; |
||||||
|
|
||||||
output(format("Using build command: {}", build_cmd)); |
GUI gui; |
||||||
efsw::FileWatcher* fileWatcher = new efsw::FileWatcher(); |
|
||||||
dbc::check(fileWatcher != nullptr, "Failed to create filewatcher."); |
|
||||||
|
|
||||||
git_libgit2_init(); |
|
||||||
|
|
||||||
int err = git_repository_open(&repo, git_path); |
|
||||||
dbc::check(err == 0, git_error_last()->message); |
|
||||||
|
|
||||||
UpdateListener* listener = new UpdateListener(repo); |
|
||||||
dbc::check(listener != nullptr, "Failed to create listener."); |
|
||||||
|
|
||||||
output(format("Watching directory {} for changes...\n", git_path)); |
|
||||||
efsw::WatchID wid = fileWatcher->addWatch(git_path, listener, true); |
|
||||||
|
|
||||||
GameEngine game{100}; |
GameEngine game{100}; |
||||||
|
|
||||||
int rc = main_loop(game, [&] { |
auto builder = Builder(gui, game); |
||||||
fileWatcher->watch(); |
builder.run(git_path, build_cmd); |
||||||
|
|
||||||
if(listener->changes) { |
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100)); |
|
||||||
output(format("CHANGES! Running build {}", build_cmd)); |
|
||||||
run_build(game, build_cmd); |
|
||||||
listener->reset_state(); |
|
||||||
} |
|
||||||
return 0; |
|
||||||
}); |
|
||||||
|
|
||||||
dbc::check(rc == 0, "Invalid return from main_loop."); |
|
||||||
|
|
||||||
git_libgit2_shutdown(); |
|
||||||
} catch(dbc::Error &err) { |
|
||||||
output(format("ERROR: {}\n", err.message)); |
|
||||||
if(repo != nullptr) git_repository_free(repo); |
|
||||||
git_libgit2_shutdown(); |
|
||||||
return 1; |
return 1; |
||||||
} |
} |
||||||
} |
} |
||||||
|
@ -0,0 +1,31 @@ |
|||||||
|
#include "watcher.hpp" |
||||||
|
#include <filesystem> |
||||||
|
using namespace std; |
||||||
|
namespace fs = std::filesystem; |
||||||
|
|
||||||
|
void UpdateListener::handleFileAction(efsw::WatchID watchid, |
||||||
|
const std::string& dir, |
||||||
|
const std::string& filename, |
||||||
|
efsw::Action action, |
||||||
|
std::string oldFilename) |
||||||
|
{ |
||||||
|
|
||||||
|
// this is some gnarly BS here, probably tons
|
||||||
|
// of memory leaks for now but it's working
|
||||||
|
int ignored = 1; |
||||||
|
auto the_path = fs::path(dir) / fs::path(filename); |
||||||
|
string full_path = the_path.lexically_normal().string(); |
||||||
|
|
||||||
|
std::replace(full_path.begin(), |
||||||
|
full_path.end(), '\\', '/'); |
||||||
|
|
||||||
|
int rc = git_ignore_path_is_ignored(&ignored, repo, full_path.c_str()); |
||||||
|
|
||||||
|
if(!ignored) { |
||||||
|
changes = changes || !ignored; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void UpdateListener::reset_state() { |
||||||
|
changes = false; |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
#pragma once |
||||||
|
#include <efsw/efsw.hpp> |
||||||
|
#include <git2.h> |
||||||
|
#include <string> // for operator+, to_string |
||||||
|
|
||||||
|
class UpdateListener : public efsw::FileWatchListener { |
||||||
|
public: |
||||||
|
bool changes = false; |
||||||
|
git_repository* repo = nullptr; |
||||||
|
|
||||||
|
UpdateListener(git_repository *r) : repo(r) {}; |
||||||
|
|
||||||
|
void handleFileAction(efsw::WatchID watchid, |
||||||
|
const std::string& dir, |
||||||
|
const std::string& filename, |
||||||
|
efsw::Action action, |
||||||
|
std::string oldFilename) override; |
||||||
|
|
||||||
|
void reset_state(); |
||||||
|
}; |
Loading…
Reference in new issue