diff --git a/Makefile b/Makefile index ac433bf..412f641 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,7 @@ sources = \ target.c \ targetset.c \ targetcache.c \ + targetwatch.c \ util.c \ vfs.c diff --git a/cache.c b/cache.c index c2b4c8a..4a4b97a 100644 --- a/cache.c +++ b/cache.c @@ -168,6 +168,16 @@ void cache_close() { sqlite3_close(Cache); } +void cache_bump_version() { + ++CurrentVersion; + printf("CurrentVersion = %d\n", CurrentVersion); + char Buffer[100]; + sprintf(Buffer, "REPLACE INTO info(key, value) VALUES('version', %d)", CurrentVersion); + if (sqlite3_exec(Cache, Buffer, 0, 0, 0) != SQLITE_OK) { + printf("Sqlite error: %s\n", sqlite3_errmsg(Cache)); + exit(1); + } +} void cache_hash_get(target_t *Target, int *LastUpdated, int *LastChecked, time_t *FileTime, BYTE Hash[SHA256_BLOCK_SIZE]) { sqlite3_bind_text(HashGetStatement, 1, Target->Id, Target->IdLength, SQLITE_STATIC); diff --git a/cache.h b/cache.h index 7bb08a8..1815c2d 100644 --- a/cache.h +++ b/cache.h @@ -26,5 +26,6 @@ ml_value_t *cache_expr_get(target_t *Target); void cache_expr_set(target_t *Target, ml_value_t *Value); extern int CurrentVersion; +void cache_bump_version(); #endif diff --git a/minilang b/minilang index db85fad..6200b08 160000 --- a/minilang +++ b/minilang @@ -1 +1 @@ -Subproject commit db85fadf0f014e4ea80ecf89d14486d63b88486a +Subproject commit 6200b0881cc90b9bd098a943c156f70e2b991fd9 diff --git a/rabs.c b/rabs.c index 51320e4..f1bca0e 100644 --- a/rabs.c +++ b/rabs.c @@ -8,6 +8,7 @@ #include #include #include "target.h" +#include "targetwatch.h" #include "context.h" #include "util.h" #include "cache.h" @@ -22,10 +23,11 @@ const char *SystemName = "/_minibuild_"; const char *RootPath = 0; ml_value_t *AppendMethod; static int EchoCommands = 0; -extern int StatusUpdates; static stringmap_t Globals[1] = {STRINGMAP_INIT}; static stringmap_t Defines[1] = {STRINGMAP_INIT}; +static int SavedArgc; +static char **SavedArgv; static ml_value_t *rabs_ml_get(void *Data, const char *Name) { ml_value_t *Value = context_symb_get(CurrentContext, Name); @@ -55,6 +57,7 @@ static ml_value_t *rabs_ml_global(void *Data, const char *Name) { } static void load_file(const char *FileName) { + if (MonitorFiles) targetwatch_add(FileName, (target_t *)-1); ml_value_t *Closure = ml_load(rabs_ml_global, NULL, FileName); if (Closure->Type == MLErrorT) { printf("\e[31mError: %s\n\e[0m", ml_error_message(Closure)); @@ -335,7 +338,14 @@ static ml_value_t *debug(void *Data, int Count, ml_value_t **Args) { return MLNil; } -int main(int Argc, const char **Argv) { +void restart() { + cache_close(); + execv(SavedArgv[0], SavedArgv); +} + +int main(int Argc, char **Argv) { + SavedArgc = Argc; + SavedArgv = Argv; GC_INIT(); ml_init(); AppendMethod = ml_method("append"); @@ -366,6 +376,7 @@ int main(int Argc, const char **Argv) { int QueryOnly = 0; int ListTargets = 0; int NumThreads = 1; + int InteractiveMode = 0; for (int I = 1; I < Argc; ++I) { if (Argv[I][0] == '-') { switch (Argv[I][1]) { @@ -420,6 +431,15 @@ int main(int Argc, const char **Argv) { } break; } + case 'i': { + InteractiveMode = 1; + break; + } + case 'w': { + targetwatch_init(); + MonitorFiles = 1; + break; + } case 't': { GC_disable(); break; @@ -448,7 +468,11 @@ int main(int Argc, const char **Argv) { context_push(""); context_symb_set(CurrentContext, "VERSION", ml_integer(CurrentVersion)); - target_threads_start(NumThreads); + if (InteractiveMode || MonitorFiles) { + + } else { + target_threads_start(NumThreads); + } load_file(concat(RootPath, SystemName, NULL)); target_t *Target; @@ -478,6 +502,13 @@ int main(int Argc, const char **Argv) { } else { target_update(Target); target_threads_wait(NumThreads); + if (InteractiveMode) { + target_interactive_start(NumThreads); + ml_console(rabs_ml_global, Globals); + } else if (MonitorFiles) { + target_interactive_start(NumThreads); + targetwatch_wait(); + } } return 0; } diff --git a/rabs.h b/rabs.h index 1b81227..c05b157 100644 --- a/rabs.h +++ b/rabs.h @@ -4,8 +4,8 @@ #include #include "minilang.h" -extern pthread_mutex_t GlobalLock[1]; extern const char *RootPath, *SystemName; extern __thread const char *CurrentPath; +void restart(); #endif diff --git a/target.c b/target.c index d6d45c2..af2a828 100644 --- a/target.c +++ b/target.c @@ -3,6 +3,7 @@ #include "util.h" #include "context.h" #include "targetcache.h" +#include "targetwatch.h" #include "cache.h" #include #include @@ -35,6 +36,7 @@ typedef struct target_symb_t target_symb_t; extern const char *RootPath; static int QueuedTargets = 0, BuiltTargets = 0, NumTargets = 0; int StatusUpdates = 0; +int MonitorFiles = 0; pthread_mutex_t GlobalLock[1] = {PTHREAD_MUTEX_INITIALIZER}; static pthread_cond_t TargetAvailable[1] = {PTHREAD_COND_INITIALIZER}; @@ -117,7 +119,7 @@ static void target_do_build(int ThreadIndex, target_t *Target) { if (StatusUpdates) printf("\e[35m%d / %d\e[0m #%d Built \e[32m%s\e[0m at version %d\n", BuiltTargets, QueuedTargets, ThreadIndex, Target->Id, Target->LastUpdated); targetset_foreach(Target->Affects, Target, (void *)depends_updated_fn); memset(Target->Depends, 0, sizeof(Target->Depends)); - memset(Target->Affects, 0, sizeof(Target->Affects)); + //memset(Target->Affects, 0, sizeof(Target->Affects)); pthread_cond_broadcast(TargetAvailable); } @@ -130,6 +132,7 @@ int depends_update_fn(target_t *Depend, target_t *Target) { if (Depend->LastUpdated == STATE_QUEUED) { if (!targetset_insert(Depend->Affects, Target)) ++Target->WaitCount; } else { + targetset_insert(Depend->Affects, Target); if (Depend->LastUpdated > Target->DependsLastUpdated) { Target->DependsLastUpdated = Depend->LastUpdated; } @@ -137,6 +140,17 @@ int depends_update_fn(target_t *Depend, target_t *Target) { return 0; } +static int affects_refresh_fn(target_t *Affect, target_t *Target) { + printf("%s -> %s\n", Target->Id, Affect->Id); + if (Affect->LastUpdated != STATE_QUEUED) { + Affect->LastUpdated = STATE_QUEUED; + ++QueuedTargets; + targetset_foreach(Affect->Affects, Affect, (void *)affects_refresh_fn); + } + if (!targetset_insert(Affect->Depends, Target)) ++Affect->WaitCount; + return 0; +} + void target_update(target_t *Target) { if (Target->LastUpdated == STATE_CHECKING) { printf("\e[31mError: build cycle with %s\e[0m\n", Target->Id); @@ -167,6 +181,16 @@ void target_update(target_t *Target) { } } +void target_recheck(target_t *Target) { + if (Target == (target_t *)-1) restart(); + printf("Rechecking %s\n", Target->Id); + cache_bump_version(); + ++QueuedTargets; + targetset_foreach(Target->Affects, Target, (void *)affects_refresh_fn); + target_queue_build(Target); + pthread_cond_broadcast(TargetAvailable); +} + int depends_query_fn(const char *Id, target_t *Depends, int *DependsLastUpdated) { target_query(Depends); if (Depends->LastUpdated > *DependsLastUpdated) *DependsLastUpdated = Depends->LastUpdated; @@ -297,6 +321,9 @@ static time_t target_file_hash(target_file_t *Target, time_t PreviousTime, BYTE sha256_final(Ctx, Target->Hash); } pthread_mutex_lock(GlobalLock); + if (MonitorFiles && !Target->Build) { + targetwatch_add(FileName, (target_t *)Target); + } return Stat->st_mtime; } @@ -826,12 +853,18 @@ ml_value_t *target_set_build(void *Data, int Count, ml_value_t **Args) { return Args[0]; } +ml_value_t *target_recheck_value(void *Data, int Count, ml_value_t **Args) { + target_recheck((target_t *)Args[0]); + return Args[0]; +} + struct target_scan_t { TARGET_FIELDS const char *Name; target_t *Source; scan_results_t *Results; ml_value_t *Scan, *Rebuild; + int Recursive; }; static ml_type_t *ScanTargetT; @@ -843,6 +876,12 @@ struct scan_results_t { static ml_type_t *ScanResultsT; +static int scan_results_affects_fn(target_t *Target, target_t *Scan) { + targetset_insert(Target->Affects, Scan); + targetset_insert(Scan->Depends, Target); + return 0; +} + static time_t target_scan_hash(target_scan_t *Target, time_t PreviousTime, BYTE PreviousHash[SHA256_BLOCK_SIZE]) { targetset_t *Scans = cache_scan_get((target_t *)Target->Results); if (Scans) targetset_foreach(Scans, Target->Results, (void *)depends_update_fn); @@ -900,6 +939,9 @@ static void target_scan_build(target_scan_t *Target) { targetset_t Scans[1] = {TARGETSET_INIT}; ml_list_foreach(Result, Scans, (void *)build_scan_target_list); cache_depends_set((target_t *)Target->Results, Scans); + if (Target->Recursive) { + targetset_foreach(Scans, Target, (void *)scan_results_affects_fn); + } cache_scan_set((target_t *)Target->Results, Scans); } @@ -954,8 +996,8 @@ static int scan_depends_update_fn(target_t *Depend, scan_results_t *Target) { return 0; } -static scan_results_t *scan_results_new(target_t *ParentTarget, const char *Name, target_t **Slot) { - const char *Id = concat("results:", ParentTarget->Id, "::", Name, NULL); +static scan_results_t *scan_results_new(target_t *ParentTarget, const char *Name, target_t **Slot, int Recursive) { + const char *Id = concat(Recursive ? "results!:" : "results:", ParentTarget->Id, "::", Name, NULL); if (!Slot) Slot = targetcache_lookup(Id); scan_results_t *Target = (scan_results_t *)Slot[0]; if (!Target) { @@ -973,6 +1015,7 @@ static scan_results_t *scan_results_new(target_t *ParentTarget, const char *Name ScanTarget->Source = ParentTarget; ScanTarget->Results = Target; ScanTarget->Rebuild = ml_function(ScanTarget, (void *)scan_target_rebuild); + ScanTarget->Recursive = Recursive; targetset_insert(Target->Depends, (target_t *)ScanTarget); } targetset_t *Scans = cache_scan_get((target_t *)Target); @@ -981,7 +1024,7 @@ static scan_results_t *scan_results_new(target_t *ParentTarget, const char *Name } ml_value_t *target_scan_new(void *Data, int Count, ml_value_t **Args) { - return (ml_value_t *)scan_results_new((target_t *)Args[0], ml_string_value(Args[1]), 0); + return (ml_value_t *)scan_results_new((target_t *)Args[0], ml_string_value(Args[1]), 0, (Count > 2) && Args[2] != MLNil); } struct target_symb_t { @@ -1157,6 +1200,19 @@ target_t *target_find(const char *Id) { target_expr_t *Target = target_new(target_expr_t, ExprTargetT, Id, Slot); return (target_t *)Target; } + if (!memcmp(Id, "results!", 8)) { + const char *Name; + for (Name = Id + strlen(Id) - 1; --Name > Id + 9;) { + if (Name[0] == ':' && Name[1] == ':') break; + } + size_t ParentIdLength = Name - Id - 9; + char *ParentId = snew(ParentIdLength + 1); + memcpy(ParentId, Id + 9, ParentIdLength); + ParentId[ParentIdLength] = 0; + target_t *ParentTarget = target_find(ParentId); + Name += 2; + return (target_t *)scan_results_new(ParentTarget, Name, Slot, 1); + } if (!memcmp(Id, "results", 7)) { const char *Name; for (Name = Id + strlen(Id) - 1; --Name > Id + 8;) { @@ -1168,7 +1224,7 @@ target_t *target_find(const char *Id) { ParentId[ParentIdLength] = 0; target_t *ParentTarget = target_find(ParentId); Name += 2; - return (target_t *)scan_results_new(ParentTarget, Name, Slot); + return (target_t *)scan_results_new(ParentTarget, Name, Slot, 0); } return 0; } @@ -1194,7 +1250,7 @@ struct build_thread_t { }; static build_thread_t *BuildThreads = 0; -int RunningThreads = 1, LastThread = 0; +int RunningThreads = 0, LastThread = 0; static void *target_thread_fn(void *Arg) { int Index = (int)(ptrdiff_t)Arg; @@ -1227,7 +1283,33 @@ static void *target_thread_fn(void *Arg) { return 0; } +static void *active_mode_thread_fn(void *Arg) { + int Index = (int)(ptrdiff_t)Arg; + printf("Starting build thread #%d\n", (int)Index); + pthread_mutex_lock(GlobalLock); + ++RunningThreads; + for (;;) { + while (!BuildQueue) { + //printf("[%d]: No target in build queue, %d threads running\n", Index, RunningThreads); + pthread_cond_wait(TargetAvailable, GlobalLock); + } + target_t *Target = BuildQueue; + BuildQueue = Target->Next; + Target->Next = 0; + target_do_build(Index, Target); + if (SpareThreads) { + --RunningThreads; + --SpareThreads; + pthread_cond_signal(TargetAvailable); + pthread_mutex_unlock(GlobalLock); + return 0; + } + } + return 0; +} + void target_threads_start(int NumThreads) { + RunningThreads = 1; pthread_mutex_init(GlobalLock, NULL); pthread_mutex_lock(GlobalLock); pthread_cond_init(TargetAvailable, NULL); @@ -1270,6 +1352,21 @@ static int print_unbuilt_target(const char *Id, target_t *Target, void *Data) { return 0; } +void target_interactive_start(int NumThreads) { + RunningThreads = 0; + pthread_mutex_init(GlobalLock, NULL); + pthread_mutex_lock(GlobalLock); + pthread_cond_init(TargetAvailable, NULL); + for (LastThread = 0; LastThread < NumThreads; ++LastThread) { + build_thread_t *BuildThread = new(build_thread_t); + GC_pthread_create(&BuildThread->Handle, 0, active_mode_thread_fn, (void *)(ptrdiff_t)LastThread); + BuildThread->Next = BuildThreads; + BuildThreads = BuildThread; + } + pthread_cond_broadcast(TargetAvailable); + pthread_mutex_unlock(GlobalLock); +} + void target_threads_wait(int NumThreads) { --RunningThreads; pthread_cond_broadcast(TargetAvailable); @@ -1307,6 +1404,7 @@ void target_init() { ml_method_by_name("string", 0, target_file_to_string, FileTargetT, NULL); ml_method_by_name("string", 0, target_expr_to_string, ExprTargetT, NULL); ml_method_by_name("=>", 0, target_set_build, TargetT, MLAnyT, NULL); + ml_method_by_name("refresh", 0, target_recheck_value, TargetT, NULL); ml_method_by_name("=>", 0, scan_results_set_build, ScanResultsT, MLAnyT, NULL); ml_method_by_name("/", 0, target_file_div, FileTargetT, MLStringT, NULL); ml_method_by_name("%", 0, target_file_mod, FileTargetT, MLStringT, NULL); diff --git a/target.h b/target.h index b66709d..f1155c3 100644 --- a/target.h +++ b/target.h @@ -27,6 +27,10 @@ struct target_t { TARGET_FIELDS }; +extern int StatusUpdates; +extern int MonitorFiles; +extern pthread_mutex_t GlobalLock[1]; + void target_init(); ml_value_t *target_dir_new(void *Data, int Count, ml_value_t **Args); @@ -47,5 +51,7 @@ void target_list(); target_t *target_file_check(const char *Path, int Absolute); void target_threads_start(int NumThreads); void target_threads_wait(int NumThreads); +void target_interactive_start(int NumThreads); +void target_recheck(target_t *Target); #endif diff --git a/targetwatch.c b/targetwatch.c new file mode 100644 index 0000000..0840055 --- /dev/null +++ b/targetwatch.c @@ -0,0 +1,74 @@ +#include "targetwatch.h" +#include "target.h" +#include "minilang/stringmap.h" +#include +#include +#include +#include +#include +#include + +#define INITIAL_SIZE 256 + +typedef struct directory_t { + const char *Path; + stringmap_t Files[1]; +} directory_t; + +static directory_t **DirectoriesByHandle = 0; +static size_t MaxHandles = 0; +static stringmap_t DirectoriesByName[1] = {STRINGMAP_INIT}; +static int WatchHandle; + +void targetwatch_init() { + MaxHandles = INITIAL_SIZE; + DirectoriesByHandle = anew(directory_t *, MaxHandles); + WatchHandle = inotify_init(); +} + +void targetwatch_add(const char *FilePath, target_t *Target) { + int Length = strlen(FilePath); + char *DirectoryPath = snew(Length + 1); + char *FileName = stpcpy(DirectoryPath, FilePath); + while (FileName[-1] != '/') --FileName; + FileName[-1] = 0; + printf("Adding watch on %s in %s\n", FileName, DirectoryPath); + directory_t **Slot = (directory_t **)stringmap_slot(DirectoriesByName, DirectoryPath); + directory_t *Directory = Slot[0]; + if (!Directory) { + Directory = Slot[0] = new(directory_t); + Directory->Path = DirectoryPath; + int Handle = inotify_add_watch(WatchHandle, DirectoryPath, IN_CLOSE_WRITE); + if (Handle > MaxHandles) { + directory_t **NewWatches = anew(directory_t *, Handle + 1); + memcpy(NewWatches, DirectoriesByHandle, MaxHandles * sizeof(directory_t *)); + DirectoriesByHandle = NewWatches; + MaxHandles = Handle; + } + DirectoriesByHandle[Handle] = Directory; + } + stringmap_insert(Directory->Files, FileName, Target); +} + +#define BUFFER_SIZE (sizeof(struct inotify_event) + NAME_MAX + 1) + +void targetwatch_wait() { + char Buffer[BUFFER_SIZE]; + for (;;) { + ssize_t Bytes = read(WatchHandle, Buffer, BUFFER_SIZE); + char *Next = Buffer, *Limit = Buffer + Bytes; + pthread_mutex_lock(GlobalLock); + do { + struct inotify_event *Event = (struct inotify_event *)Next; + directory_t *Directory = DirectoriesByHandle[Event->wd]; + if (Directory) { + printf("Event %x for %s in %s\n", Event->mask, Event->name, Directory->Path); + target_t *Target = stringmap_search(Directory->Files, Event->name); + if (Target) target_recheck(Target); + } + Next += sizeof(struct inotify_event) + Event->len; + } while (Next < Limit); + pthread_mutex_unlock(GlobalLock); + } + +} diff --git a/targetwatch.h b/targetwatch.h new file mode 100644 index 0000000..ece7181 --- /dev/null +++ b/targetwatch.h @@ -0,0 +1,10 @@ +#ifndef TARGETWATCH_H +#define TARGETWATCH_H + +typedef struct target_t target_t; + +void targetwatch_init(); +void targetwatch_add(const char *FilePath, target_t *Target); +void targetwatch_wait(); + +#endif diff --git a/test/_minibuild_ b/test/_minibuild_ index e5b0ff0..0363018 100644 --- a/test/_minibuild_ +++ b/test/_minibuild_ @@ -63,7 +63,7 @@ c_program := fun(Executable, Objects, Libraries) do var Source := Object % Extension if Source:exists then Sources:put(Source) - var Scan := Source:scan("INCLUDES") => Functions[1] + var Scan := Source:scan("INCLUDES", :true) => Functions[1] Object[Source, Scan] => Functions[2] exit end diff --git a/test/src/_minibuild_ b/test/src/_minibuild_ index 8f843c8..5f6012e 100644 --- a/test/src/_minibuild_ +++ b/test/src/_minibuild_ @@ -42,7 +42,7 @@ var ScanTest := Generated:scan("ScanTest") => fun() do var Objects := [] for Source := file("gen"):ls(".*.c") do var Object := Source % "o" - var Scan := Source:scan("INCLUDES") => c_includes + var Scan := Source:scan("INCLUDES", :true) => c_includes Object[Source, Scan] => c_compile Objects:put(Object) end diff --git a/test/src/test.c b/test/src/test.c index 8a8e177..d3332a7 100644 --- a/test/src/test.c +++ b/test/src/test.c @@ -4,5 +4,5 @@ int main() { printf("Hello world!\n"); - printf("%d + %d = %d\n", 2, 3, add(2, 3)); + printf("%d + %d = %d\n", 2, 6, add(2, 6)); }