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.
This commit is contained in:
parent
39f89ecbf2
commit
96b1297c62
10 changed files with 254 additions and 7 deletions
|
@ -4,7 +4,7 @@ all:
|
||||||
meson compile -C . 2>&1 | ${ROOT_DIR}/builddir/goc
|
meson compile -C . 2>&1 | ${ROOT_DIR}/builddir/goc
|
||||||
|
|
||||||
build:
|
build:
|
||||||
meson compile -C .
|
meson compile -C ${ROOT_DIR}/builddir
|
||||||
|
|
||||||
test:
|
test:
|
||||||
@echo "../ex08.cpp:7:10: error: use of undeclared identifier \'oops\'" | ${ROOT_DIR}/builddir/goc
|
@echo "../ex08.cpp:7:10: error: use of undeclared identifier \'oops\'" | ${ROOT_DIR}/builddir/goc
|
||||||
|
|
10
PPP3/efsw.wrap
Normal file
10
PPP3/efsw.wrap
Normal file
|
@ -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()
|
int main()
|
||||||
{
|
{
|
||||||
constexpr double pi = 3.14159;
|
|
||||||
double d = 0;
|
double d = 0;
|
||||||
while(cin >> d) {
|
while(cin >> d) {
|
||||||
int i = d;
|
int i = d;
|
||||||
|
|
|
@ -22,7 +22,6 @@ int main()
|
||||||
auto t = time(nullptr);
|
auto t = time(nullptr);
|
||||||
auto tm = *std::gmtime(&t);
|
auto tm = *std::gmtime(&t);
|
||||||
|
|
||||||
dbc::log("TEST 1 of the logging thing");
|
|
||||||
dbc::check(stats_out.good(), "Error opening stats.csv file.");
|
dbc::check(stats_out.good(), "Error opening stats.csv file.");
|
||||||
dbc::pre("simple test", [&]() { return stats_out.good(); });
|
dbc::pre("simple test", [&]() { return stats_out.good(); });
|
||||||
|
|
||||||
|
|
12
PPP3/libgit2.wrap
Normal file
12
PPP3/libgit2.wrap
Normal file
|
@ -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',
|
project('lcppthw', 'cpp',
|
||||||
default_options: ['cpp_std=c++20'])
|
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 = [
|
dependencies = [
|
||||||
dependency('fmt'),
|
dependency('fmt'),
|
||||||
dependency('sqlite3'),
|
libgit2package_dep,
|
||||||
dependency('sqlitecpp')
|
efsw_dep,
|
||||||
]
|
]
|
||||||
|
|
||||||
executable('goc', 'goc.cpp',
|
executable('goc', 'goc.cpp',
|
||||||
|
cpp_args: '-DFMT_HEADER_ONLY',
|
||||||
|
dependencies: [dependency('fmt')])
|
||||||
|
|
||||||
|
executable('watchgit', 'watchgit.cpp',
|
||||||
cpp_args: '-DFMT_HEADER_ONLY',
|
cpp_args: '-DFMT_HEADER_ONLY',
|
||||||
dependencies: dependencies)
|
dependencies: dependencies)
|
||||||
|
|
||||||
|
|
|
@ -7,4 +7,5 @@ meson wrap install fmt
|
||||||
meson wrap install sqlite3
|
meson wrap install sqlite3
|
||||||
meson wrap install sqlitecpp
|
meson wrap install sqlitecpp
|
||||||
meson wrap install ftxui
|
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 sqlite3
|
||||||
meson wrap install sqlitecpp
|
meson wrap install sqlitecpp
|
||||||
meson wrap install ftxui
|
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 sqlite3
|
||||||
meson wrap install sqlitecpp
|
meson wrap install sqlitecpp
|
||||||
meson wrap install ftxui
|
meson wrap install ftxui
|
||||||
|
cp *.wrap subprojects
|
||||||
meson setup builddir
|
meson setup builddir
|
||||||
|
|
206
PPP3/watchgit.cpp
Normal file
206
PPP3/watchgit.cpp
Normal file
|
@ -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…
Add table
Add a link
Reference in a new issue