diff --git a/PPP3/Makefile b/PPP3/Makefile index f7542fc..109600f 100644 --- a/PPP3/Makefile +++ b/PPP3/Makefile @@ -4,7 +4,7 @@ all: meson compile -C . 2>&1 | ${ROOT_DIR}/builddir/goc build: - meson compile -C . + meson compile -C ${ROOT_DIR}/builddir test: @echo "../ex08.cpp:7:10: error: use of undeclared identifier \'oops\'" | ${ROOT_DIR}/builddir/goc diff --git a/PPP3/efsw.wrap b/PPP3/efsw.wrap new file mode 100644 index 0000000..4de1f1f --- /dev/null +++ b/PPP3/efsw.wrap @@ -0,0 +1,10 @@ +[wrap-git] +url = https://github.com/SpartanJ/efsw.git +revision = 1.3.1 +depth = 1 +# patch_filename = +# patch_hash = + + +[provide] +efsw = efsw_dep diff --git a/PPP3/ex01.cpp b/PPP3/ex01.cpp index e981151..7160fbe 100644 --- a/PPP3/ex01.cpp +++ b/PPP3/ex01.cpp @@ -4,7 +4,6 @@ using namespace std; int main() { - constexpr double pi = 3.14159; double d = 0; while(cin >> d) { int i = d; diff --git a/PPP3/goc.cpp b/PPP3/goc.cpp index 3febe5e..f2053f2 100644 --- a/PPP3/goc.cpp +++ b/PPP3/goc.cpp @@ -22,7 +22,6 @@ int main() auto t = time(nullptr); auto tm = *std::gmtime(&t); - dbc::log("TEST 1 of the logging thing"); dbc::check(stats_out.good(), "Error opening stats.csv file."); dbc::pre("simple test", [&]() { return stats_out.good(); }); diff --git a/PPP3/libgit2.wrap b/PPP3/libgit2.wrap new file mode 100644 index 0000000..5f70604 --- /dev/null +++ b/PPP3/libgit2.wrap @@ -0,0 +1,12 @@ +[wrap-file] +directory = libgit2-1.8.0 +source_url = https://github.com/libgit2/libgit2/archive/refs/tags/v1.8.0.tar.gz +source_filename = v1.8.0.tar.gz +source_hash = 9e1d6a880d59026b675456fbb1593c724c68d73c34c0d214d6eb848e9bbd8ae4 +wrapdb_version = 2.4.1-3 +# patch_filename = +# patch_hash = + + +[provide] +libgit2 = libgit2_dep diff --git a/PPP3/meson.build b/PPP3/meson.build index 6527ae5..d36bbd1 100644 --- a/PPP3/meson.build +++ b/PPP3/meson.build @@ -1,13 +1,31 @@ project('lcppthw', 'cpp', default_options: ['cpp_std=c++20']) +cmake = import('cmake') +opts = cmake.subproject_options() +opts.add_cmake_defines({ + 'USE_ICONV': false, + 'USE_SSH': 'exec', + 'BUILD_SHARED_LIBS': true, + 'BUILD_TESTS': false, + }) +libgit2_proj = cmake.subproject('libgit2', options: opts) +libgit2package_dep = libgit2_proj.dependency('libgit2package') + +efsw_proj = cmake.subproject('efsw') +efsw_dep = efsw_proj.dependency('efsw') + dependencies = [ dependency('fmt'), - dependency('sqlite3'), - dependency('sqlitecpp') + libgit2package_dep, + efsw_dep, ] executable('goc', 'goc.cpp', + cpp_args: '-DFMT_HEADER_ONLY', + dependencies: [dependency('fmt')]) + +executable('watchgit', 'watchgit.cpp', cpp_args: '-DFMT_HEADER_ONLY', dependencies: dependencies) diff --git a/PPP3/reset_build.ps1 b/PPP3/reset_build.ps1 index e8c5d8b..ad84f0b 100644 --- a/PPP3/reset_build.ps1 +++ b/PPP3/reset_build.ps1 @@ -7,4 +7,5 @@ meson wrap install fmt meson wrap install sqlite3 meson wrap install sqlitecpp meson wrap install ftxui -meson setup -Ddefault_library=static builddir +# meson setup -Ddefault_library=static builddir +meson setup builddir diff --git a/PPP3/reset_build.sh b/PPP3/reset_build.sh index 402a505..4f11bb5 100755 --- a/PPP3/reset_build.sh +++ b/PPP3/reset_build.sh @@ -10,4 +10,5 @@ meson wrap install fmt meson wrap install sqlite3 meson wrap install sqlitecpp meson wrap install ftxui -meson setup builddir +cp *.wrap subprojects +meson setup -Ddefault_library=static builddir diff --git a/PPP3/setup.sh b/PPP3/setup.sh index 52666e8..4ca5cf0 100755 --- a/PPP3/setup.sh +++ b/PPP3/setup.sh @@ -7,4 +7,5 @@ meson wrap install fmt meson wrap install sqlite3 meson wrap install sqlitecpp meson wrap install ftxui +cp *.wrap subprojects meson setup builddir diff --git a/PPP3/watchgit.cpp b/PPP3/watchgit.cpp new file mode 100644 index 0000000..f6b115b --- /dev/null +++ b/PPP3/watchgit.cpp @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "dbc.h" +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace fmt; + +/* + * No idea what the semantics of this are. Will need + * to research git's dumb terminology to figure out why + * they have 4 different versions of the path for status. + */ +const char *unfuck_path(const git_status_entry *entry) { + if(entry->head_to_index != nullptr) { + if(entry->head_to_index->new_file.path) { + return entry->head_to_index->new_file.path; + } else { + return entry->head_to_index->old_file.path; + } + } + + if(entry->index_to_workdir != nullptr) { + if(entry->index_to_workdir->new_file.path) { + return entry->index_to_workdir->new_file.path; + } else { + return entry->index_to_workdir->old_file.path; + } + } + + return nullptr; +} + +void add_status(const git_status_entry *entry, unsigned int status_flags, vector &updates) { + const char *path = unfuck_path(entry); + + if(status_flags & GIT_STATUS_WT_NEW + || status_flags & GIT_STATUS_INDEX_NEW) + { + updates.push_back(string{path}); + } + + if(status_flags & GIT_STATUS_WT_MODIFIED + || status_flags & GIT_STATUS_INDEX_MODIFIED) + { + updates.push_back(string{path}); + } + + // need to confirm this gets the new name + if(status_flags & GIT_STATUS_WT_RENAMED + || status_flags & GIT_STATUS_INDEX_RENAMED) + { + updates.push_back(string{path}); + } +} + +class UpdateListener : public efsw::FileWatchListener { + public: + bool changes = false; + + void handleFileAction(efsw::WatchID watchid, + const std::string& dir, + const std::string& filename, + efsw::Action action, + std::string oldFilename) override + { + changes = true; + + switch(action) { + case efsw::Actions::Add: + print("ADD {} {} {}\n", dir, filename, oldFilename); + break; + case efsw::Actions::Delete: + print("DEL {} {} {}\n", dir, filename, oldFilename); + break; + case efsw::Actions::Modified: + print("MOD {} {} {}\n", dir, filename, oldFilename); + break; + case efsw::Actions::Moved: + print("MOV {} {} {}\n", dir, filename, oldFilename); + break; + default: + dbc::sentinel("Unknown efsw action."); + } + + } + + void reset_state() { + changes = false; + } +}; + +void list_git_changes(git_repository* repo) { + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + git_status_list *statuses = nullptr; + + //TODO: does this leak? + int err = git_status_list_new(&statuses, repo, &opts); + dbc::check(err == 0, git_error_last()->message); + size_t count = git_status_list_entrycount(statuses); + + vector updates; + + for(size_t i=0; i < count; i++) { + const git_status_entry *entry = git_status_byindex(statuses, i); + add_status(entry, entry->status, updates); + } + + for(string path : updates) { + print("PATH {}\n", path); + } +} + +void run_build(const char* command) { + regex err_re("(.*?):([0-9]+):([0-9]+):\\s*(.*?):\\s*(.*)"); + char buffer[LINE_MAX]; // LINE_MAX is a define already? + smatch err; + ofstream stats_out; + stats_out.open("stats.csv", ios::out | ios::app); + auto t = time(nullptr); + auto tm = *std::gmtime(&t); + + dbc::check(stats_out.good(), "Error opening stats.csv file."); + dbc::pre("simple test", [&]() { return stats_out.good(); }); + + FILE *build_out = popen(command, "r"); + dbc::check(build_out != nullptr, "Failed to run command."); + + while(fgets(buffer, LINE_MAX, build_out) != nullptr) { + string line(buffer); // yeah, that's probably a problem + + print("{}\n", line); + if(regex_match(line, err, err_re)) { + string file_name = err[1].str(); + string line = err[2].str(); + string col = err[3].str(); + string type = err[4].str(); + string message = err[5].str(); + + stats_out << put_time(&tm, "%FT%TZ"); + stats_out << format(",{},{},{},{},{}\n", file_name, line, col, type, message); + } + } + + stats_out.close(); + dbc::post("a post test", [&]() { return !stats_out.is_open(); }); +} + + +int main(int argc, char *argv[]) +{ + git_repository* repo = nullptr; + + try { + dbc::check(argc == 3, "USAGE: watchgit PATH BUILD_CMD"); + const char *git_path = argv[1]; + const char *build_cmd = argv[2]; + + print("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(); + dbc::check(listener != nullptr, "Failed to create listener."); + + print("Watching directory {} for changes...\n", git_path); + efsw::WatchID wid = fileWatcher->addWatch(git_path, listener, true); + + while(true) { + fileWatcher->watch(); + + if(listener->changes) { + sleep(1); + list_git_changes(repo); + listener->reset_state(); + run_build(build_cmd); + } + + sleep(1); + } + + git_libgit2_shutdown(); + } catch(dbc::Error &err) { + print("ERROR: {}\n", err.message); + if(repo != nullptr) git_repository_free(repo); + return 1; + } + + git_libgit2_shutdown(); +}