First drop the game's core mechanic that compiles.
This commit is contained in:
		
						commit
						5f87d9846c
					
				
					 11 changed files with 607 additions and 0 deletions
				
			
		
							
								
								
									
										12
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | ||||||
|  | .*.sw* | ||||||
|  | .DS_Store | ||||||
|  | *.sqlite3 | ||||||
|  | *.sqlite3-wal | ||||||
|  | *.sqlite3-shm | ||||||
|  | debug | ||||||
|  | coverage/ | ||||||
|  | .coverage | ||||||
|  | builddir | ||||||
|  | subprojects | ||||||
|  | *.csv | ||||||
|  | *.exe | ||||||
							
								
								
									
										1
									
								
								LICENSE
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								LICENSE
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | Copyright (C) 2024 Zed A. Shaw | ||||||
							
								
								
									
										38
									
								
								README.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								README.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,38 @@ | ||||||
|  | A game I'm working. It's a weird one. | ||||||
|  | 
 | ||||||
|  | Right now I've got a command that watches your files for changes, and runs a build command. It's | ||||||
|  | entirely overengineered for this purpose, but it has what I'll need later.  To build it do this: | ||||||
|  | 
 | ||||||
|  | ```shell | ||||||
|  | ./scripts/reset_build.ps1 | ||||||
|  | meson compile -C builddir | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | If you get a bad compile because of libgit2's `src/util/process.h` then do this: | ||||||
|  | 
 | ||||||
|  | ```shell | ||||||
|  | cp patches/process.h subprojects/libgit2/src/util/ | ||||||
|  | meson compile -C builddir | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | I don't know why it fails at this, and only on Windows but I've talked to them repeatedly and it's | ||||||
|  | mostly "works for me" responses.  I also can't figure out how Meson exactly applies patches.  I've | ||||||
|  | generated every possible patch I can and Meson just can't apply them. | ||||||
|  | 
 | ||||||
|  | Once it's running, we have even more annoying BS to deal with, and that's because Meson doesn't | ||||||
|  | actually statically compile efsw or libgit2.  Even though I said `--default-librart=static --prefer-static` in the setup it just ignores that and makes DLLs.  The "fix" for this asinine stupidity is this: | ||||||
|  | 
 | ||||||
|  | ```shell | ||||||
|  | meson devenv -C builddir | ||||||
|  | cd .. | ||||||
|  | cp builddir/watchgit . | ||||||
|  | ./watchgit . "meson compile -C builddir | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Do all that garbage and yay, this little program runs. Finally. | ||||||
|  | 
 | ||||||
|  | ## Future Changes | ||||||
|  | 
 | ||||||
|  | 1. If I can't figure out why libgit2 has compilation errors in `src/util/process.h` then I may just rip it out and just shell out to `git` directly. | ||||||
|  | 2. Ultimately I can jsut use efsw to watch the directory for all changes, but I wanted to only focus on what's in git.  Oh well. | ||||||
|  | 3. I also have to find out why efsw and libgit2 refuse to compile statically.  Once I do that I can static compile `watchgit` and then work on the next part of the game. | ||||||
							
								
								
									
										49
									
								
								dbc.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								dbc.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | ||||||
|  | #include <string> | ||||||
|  | #include <fmt/core.h> | ||||||
|  | 
 | ||||||
|  | using namespace std; | ||||||
|  | 
 | ||||||
|  | namespace dbc { | ||||||
|  |   class Error { | ||||||
|  |     public: | ||||||
|  |       const string message; | ||||||
|  |       Error(string m) : message{m} {} | ||||||
|  |       Error(const char *m) : message{m} {} | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   class CheckError : public Error {}; | ||||||
|  | 
 | ||||||
|  |   class SentinelError : public Error {}; | ||||||
|  |   class PreCondError : public Error {}; | ||||||
|  |   class PostCondError : public Error {}; | ||||||
|  | 
 | ||||||
|  |   void log(const string &message) { | ||||||
|  |     fmt::print("{}\n", message); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void sentinel(const string &message) { | ||||||
|  |     string err = fmt::format("[SENTINEL!] {}\n", message); | ||||||
|  |     throw SentinelError{err}; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void pre(const string &message, std::function<bool()> tester) { | ||||||
|  |     if(!tester()) { | ||||||
|  |       string err = fmt::format("[PRE!] {}\n", message); | ||||||
|  |       throw PreCondError{err}; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void post(const string &message, std::function<bool()> tester) { | ||||||
|  |     if(!tester()) { | ||||||
|  |       string err = fmt::format("[POST!] {}\n", message); | ||||||
|  |       throw PostCondError{err}; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void check(bool test, const string &message) { | ||||||
|  |     if(!test) { | ||||||
|  |       string err = fmt::format("[CHECK!] {}\n", message); | ||||||
|  |       throw CheckError{err}; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										10
									
								
								efsw.wrap
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								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 | ||||||
							
								
								
									
										12
									
								
								libgit2.wrap
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								libgit2.wrap
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | ||||||
|  | [wrap-file] | ||||||
|  | directory = libgit2-1.8.1 | ||||||
|  | source_url = https://github.com/libgit2/libgit2/archive/refs/tags/v1.8.1.tar.gz | ||||||
|  | source_filename = v1.8.1.tar.gz | ||||||
|  | source_hash = 8c1eaf0cf07cba0e9021920bfba9502140220786ed5d8a8ec6c7ad9174522f8e | ||||||
|  | wrapdb_version = 2.4.1-3 | ||||||
|  | # patch_filename = | ||||||
|  | # patch_hash = | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | [provide] | ||||||
|  | libgit2 = libgit2_dep | ||||||
							
								
								
									
										26
									
								
								meson.build
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								meson.build
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | ||||||
|  | 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': false, | ||||||
|  |   'USE_NTLMCLIENT': false, | ||||||
|  |   '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') | ||||||
|  | 
 | ||||||
|  | fmt = dependency('fmt') | ||||||
|  | 
 | ||||||
|  | dependencies = [ | ||||||
|  |   fmt, libgit2package_dep, efsw_dep, | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | executable('watchgit', 'watchgit.cpp', | ||||||
|  |   dependencies: dependencies) | ||||||
							
								
								
									
										223
									
								
								patches/process.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								patches/process.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,223 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (C) the libgit2 contributors. All rights reserved. | ||||||
|  |  * | ||||||
|  |  * This file is part of libgit2, distributed under the GNU GPL v2 with | ||||||
|  |  * a Linking Exception. For full terms see the included COPYING file. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef INCLUDE_process_h__ | ||||||
|  | #define INCLUDE_process_h__ | ||||||
|  | 
 | ||||||
|  | typedef struct git_str git_str; | ||||||
|  | typedef struct git_process git_process; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  | 	unsigned int capture_in  : 1, | ||||||
|  | 	             capture_out : 1, | ||||||
|  | 	             capture_err : 1, | ||||||
|  | 	             exclude_env : 1; | ||||||
|  | 
 | ||||||
|  | 	char *cwd; | ||||||
|  | } git_process_options; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  | 	GIT_PROCESS_STATUS_NONE, | ||||||
|  | 	GIT_PROCESS_STATUS_NORMAL, | ||||||
|  | 	GIT_PROCESS_STATUS_ERROR | ||||||
|  | } git_process_result_status; | ||||||
|  | 
 | ||||||
|  | #define GIT_PROCESS_RESULT_INIT { GIT_PROCESS_STATUS_NONE } | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  | 	git_process_result_status status; | ||||||
|  | 	int exitcode; | ||||||
|  | 	int signal; | ||||||
|  | } git_process_result; | ||||||
|  | 
 | ||||||
|  | #define GIT_PROCESS_OPTIONS_INIT { 0 } | ||||||
|  | 
 | ||||||
|  | #ifdef GIT_WIN32 | ||||||
|  | # define p_pid_t DWORD | ||||||
|  | #else | ||||||
|  | # define p_pid_t pid_t | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Create a new process.  The command to run should be specified as the | ||||||
|  |  * element of the `arg` array, execv-style. This should be the full path | ||||||
|  |  * to the command to run, the PATH is not obeyed. | ||||||
|  |  * | ||||||
|  |  * This function will add the given environment variables (in `env`) | ||||||
|  |  * to the current environment.  Operations on environment variables | ||||||
|  |  * are not thread safe, so you may not modify the environment during | ||||||
|  |  * this call.  You can avoid this by setting `exclude_env` in the | ||||||
|  |  * options and providing the entire environment yourself. | ||||||
|  |  * | ||||||
|  |  * @param out location to store the process | ||||||
|  |  * @param args the command (with arguments) to run | ||||||
|  |  * @param args_len the length of the args array | ||||||
|  |  * @param env environment variables to add (or NULL) | ||||||
|  |  * @param env_len the length of the env len | ||||||
|  |  * @param opts the options for creating the process | ||||||
|  |  * @return 0 or an error code | ||||||
|  |  */ | ||||||
|  | extern int git_process_new( | ||||||
|  | 	git_process **out, | ||||||
|  | 	const char **args, | ||||||
|  | 	size_t args_len, | ||||||
|  | 	const char **env, | ||||||
|  | 	size_t env_len, | ||||||
|  | 	git_process_options *opts); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Create a new process. The command to run should be specified as the | ||||||
|  |  * `cmdline` option - which is the full text of the command line as it | ||||||
|  |  * would be specified or run by a user. The command to run will be | ||||||
|  |  * looked up in the PATH. | ||||||
|  |  * | ||||||
|  |  * On Unix, this will be executed by the system's shell (`/bin/sh`) | ||||||
|  |  * and may contain _Bourne-style_ shell quoting rules. On Windows, | ||||||
|  |  * this will be passed to `CreateProcess`, and similarly, may | ||||||
|  |  * contain _Windows-style_ shell quoting rules. | ||||||
|  |  * | ||||||
|  |  * This function will add the given environment variables (in `env`) | ||||||
|  |  * to the current environment.  Operations on environment variables | ||||||
|  |  * are not thread safe, so you may not modify the environment during | ||||||
|  |  * this call.  You can avoid this by setting `exclude_env` in the | ||||||
|  |  * options and providing the entire environment yourself. | ||||||
|  |  */ | ||||||
|  | extern int git_process_new_from_cmdline( | ||||||
|  | 	git_process **out, | ||||||
|  | 	const char *cmdline, | ||||||
|  | 	const char **env, | ||||||
|  | 	size_t env_len, | ||||||
|  | 	git_process_options *opts); | ||||||
|  | 
 | ||||||
|  | #ifdef GIT_WIN32 | ||||||
|  | 
 | ||||||
|  | extern int git_process__appname( | ||||||
|  | 	git_str *out, | ||||||
|  | 	const char *cmdline); | ||||||
|  | 
 | ||||||
|  | /* Windows path parsing is tricky; this helper function is for testing. */ | ||||||
|  | extern int git_process__cmdline( | ||||||
|  | 	git_str *out, | ||||||
|  | 	const char **in, | ||||||
|  | 	size_t in_len); | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Whether the given string looks like a command line option (starts | ||||||
|  |  * with a dash). This is useful for examining strings that will become | ||||||
|  |  * cmdline arguments to ensure that they are not erroneously treated | ||||||
|  |  * as an option. For example, arguments to `ssh`. | ||||||
|  |  */ | ||||||
|  | static __inline__ bool git_process__is_cmdline_option(const char *str) | ||||||
|  | { | ||||||
|  | 	return (str && str[0] == '-'); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Start the process. | ||||||
|  |  * | ||||||
|  |  * @param process the process to start | ||||||
|  |  * @return 0 or an error code | ||||||
|  |  */ | ||||||
|  | extern int git_process_start(git_process *process); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Returns the process id of the process. | ||||||
|  |  * | ||||||
|  |  * @param out pointer to a pid_t to store the process id | ||||||
|  |  * @param process the process to query | ||||||
|  |  * @return 0 or an error code | ||||||
|  |  */ | ||||||
|  | extern int git_process_id(p_pid_t *out, git_process *process); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Read from the process's stdout.  The process must have been created with | ||||||
|  |  * `capture_out` set to true. | ||||||
|  |  * | ||||||
|  |  * @param process the process to read from | ||||||
|  |  * @param buf the buf to read into | ||||||
|  |  * @param count maximum number of bytes to read | ||||||
|  |  * @return number of bytes read or an error code | ||||||
|  |  */ | ||||||
|  | extern ssize_t git_process_read(git_process *process, void *buf, size_t count); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Read from the process's stderr.  The process must have been created with | ||||||
|  |  * `capture_err` set to true. | ||||||
|  |  * | ||||||
|  |  * @param process the process to read from | ||||||
|  |  * @param buf the buf to read into | ||||||
|  |  * @param count maximum number of bytes to read | ||||||
|  |  * @return number of bytes read or an error code | ||||||
|  |  */ | ||||||
|  | extern ssize_t git_process_read_err(git_process *process, void *buf, size_t count); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Write to the process's stdin.  The process must have been created with | ||||||
|  |  * `capture_in` set to true. | ||||||
|  |  * | ||||||
|  |  * @param process the process to write to | ||||||
|  |  * @param buf the buf to write | ||||||
|  |  * @param count maximum number of bytes to write | ||||||
|  |  * @return number of bytes written or an error code | ||||||
|  |  */ | ||||||
|  | extern ssize_t git_process_write(git_process *process, const void *buf, size_t count); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Wait for the process to finish. | ||||||
|  |  * | ||||||
|  |  * @param result the result of the process or NULL | ||||||
|  |  * @param process the process to wait on | ||||||
|  |  */ | ||||||
|  | extern int git_process_wait(git_process_result *result, git_process *process); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Close the input pipe from the child. | ||||||
|  |  * | ||||||
|  |  * @param process the process to close the pipe on | ||||||
|  |  */ | ||||||
|  | extern int git_process_close_in(git_process *process); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Close the output pipe from the child. | ||||||
|  |  * | ||||||
|  |  * @param process the process to close the pipe on | ||||||
|  |  */ | ||||||
|  | extern int git_process_close_out(git_process *process); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Close the error pipe from the child. | ||||||
|  |  * | ||||||
|  |  * @param process the process to close the pipe on | ||||||
|  |  */ | ||||||
|  | extern int git_process_close_err(git_process *process); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Close all resources that are used by the process.  This does not | ||||||
|  |  * wait for the process to complete. | ||||||
|  |  * | ||||||
|  |  * @parma process the process to close | ||||||
|  |  */ | ||||||
|  | extern int git_process_close(git_process *process); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Place a human-readable error message in the given git buffer. | ||||||
|  |  * | ||||||
|  |  * @param msg the buffer to store the message | ||||||
|  |  * @param result the process result that produced an error | ||||||
|  |  */ | ||||||
|  | extern int git_process_result_msg(git_str *msg, git_process_result *result); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Free a process structure | ||||||
|  |  * | ||||||
|  |  * @param process the process to free | ||||||
|  |  */ | ||||||
|  | extern void git_process_free(git_process *process); | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										14
									
								
								scripts/reset_build.ps1
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								scripts/reset_build.ps1
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | ||||||
|  | mv .\subprojects\packagecache . | ||||||
|  | rm -recurse -force .\subprojects\,.\builddir\ | ||||||
|  | mkdir subprojects | ||||||
|  | mv .\packagecache .\subprojects\ | ||||||
|  | cp *.wrap subprojects | ||||||
|  | # cp -recurse -force packagefiles subprojects | ||||||
|  | mkdir builddir | ||||||
|  | meson wrap install fmt | ||||||
|  | meson wrap install sqlite3 | ||||||
|  | meson wrap install sqlitecpp | ||||||
|  | meson wrap install ftxui | ||||||
|  | # $env:CC="clang" | ||||||
|  | # $env:CXX="clang++" | ||||||
|  | meson setup --default-library=static --prefer-static builddir | ||||||
							
								
								
									
										14
									
								
								scripts/reset_build.sh
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								scripts/reset_build.sh
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | ||||||
|  | #!/usr/bin/env bash | ||||||
|  | set -e | ||||||
|  | 
 | ||||||
|  | mv -f ./subprojects/packagecache . | ||||||
|  | rm -rf subprojects builddir | ||||||
|  | mkdir subprojects | ||||||
|  | mv packagecache ./subprojects/ | ||||||
|  | mkdir builddir | ||||||
|  | cp *.wrap subprojects | ||||||
|  | meson wrap install fmt | ||||||
|  | meson wrap install sqlite3 | ||||||
|  | meson wrap install sqlitecpp | ||||||
|  | meson wrap install ftxui | ||||||
|  | meson setup builddir | ||||||
							
								
								
									
										208
									
								
								watchgit.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										208
									
								
								watchgit.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,208 @@ | ||||||
|  | #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 "test.hpp" | ||||||
|  | #include <git2.h> | ||||||
|  | #include <efsw/efsw.hpp> | ||||||
|  | #include <regex> | ||||||
|  | 
 | ||||||
|  | using namespace std; | ||||||
|  | using namespace fmt; | ||||||
|  | 
 | ||||||
|  | #define BUF_MAX 1024 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * 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[BUF_MAX]; // BUF_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, BUF_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
	
	 Zed A. Shaw
						Zed A. Shaw