Working prototype of an auto builder that watches for changes and runs the build after getting the git status. It's missing the necessary next step of matching up changes to git files, but it does the most of the build already.

master
Zed A. Shaw 6 months ago
parent 39f89ecbf2
commit 96b1297c62
  1. 2
      PPP3/Makefile
  2. 10
      PPP3/efsw.wrap
  3. 1
      PPP3/ex01.cpp
  4. 1
      PPP3/goc.cpp
  5. 12
      PPP3/libgit2.wrap
  6. 22
      PPP3/meson.build
  7. 3
      PPP3/reset_build.ps1
  8. 3
      PPP3/reset_build.sh
  9. 1
      PPP3/setup.sh
  10. 206
      PPP3/watchgit.cpp

@ -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

@ -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

@ -4,7 +4,6 @@ using namespace std;
int main()
{
constexpr double pi = 3.14159;
double d = 0;
while(cin >> d) {
int i = d;

@ -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(); });

@ -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

@ -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)

@ -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

@ -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

@ -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

@ -0,0 +1,206 @@
#include <iostream>
#include <iomanip>
#include <fstream>
#include <fmt/core.h>
#include <regex>
#include <string>
#include <iterator>
#include <ctime>
#include "dbc.h"
#include <unistd.h>
#include <stdio.h>
#include <git2/sys/errors.h>
#include <git2.h>
#include <efsw/efsw.hpp>
#include <regex>
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<string> &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<string> 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();
}
Loading…
Cancel
Save