aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael G. Martins <rafael@rafaelmartins.eng.br>2017-02-12 23:44:52 +0100
committerRafael G. Martins <rafael@rafaelmartins.eng.br>2017-02-12 23:44:52 +0100
commit4ad218b5710659899ba842da5a597db5052f2d4d (patch)
treebdcc163184030f05513ab77f8779ea8c6f7d1a86
parentcbeba5720d1b98cd7db013038b741b1a6ac9e48c (diff)
downloadblogc-4ad218b5710659899ba842da5a597db5052f2d4d.tar.gz
blogc-4ad218b5710659899ba842da5a597db5052f2d4d.tar.bz2
blogc-4ad218b5710659899ba842da5a597db5052f2d4d.zip
blogc-make: implemented automatic reloader for runserver rule
this is not the best implementation possible, but it works for most use cases
-rw-r--r--src/blogc-make/ctx.c110
-rw-r--r--src/blogc-make/ctx.h6
-rw-r--r--src/blogc-make/main.c2
-rw-r--r--src/blogc-make/rules.c68
4 files changed, 180 insertions, 6 deletions
diff --git a/src/blogc-make/ctx.c b/src/blogc-make/ctx.c
index 39f1bf4..2dd4f97 100644
--- a/src/blogc-make/ctx.c
+++ b/src/blogc-make/ctx.c
@@ -50,6 +50,49 @@ bm_filectx_new(bm_ctx_t *ctx, const char *filename)
}
+bool
+bm_filectx_changed(bm_filectx_t *ctx, struct timespec *ts)
+{
+ if (ctx == NULL)
+ return false;
+
+ struct stat buf;
+
+ if (0 == stat(ctx->path, &buf)) {
+ if (buf.st_mtim.tv_sec == ctx->timestamp.tv_sec) {
+ if (buf.st_mtim.tv_nsec > ctx->timestamp.tv_nsec) {
+ if (ts != NULL)
+ *ts = buf.st_mtim;
+ return true;
+ }
+ }
+ else if (buf.st_mtim.tv_sec > ctx->timestamp.tv_sec) {
+ if (ts != NULL)
+ *ts = buf.st_mtim;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+void
+bm_filectx_reload(bm_filectx_t *ctx)
+{
+ if (ctx == NULL)
+ return;
+
+ struct timespec ts;
+
+ if (!bm_filectx_changed(ctx, &ts))
+ return;
+
+ ctx->timestamp = ts;
+ ctx->readable = true;
+}
+
+
void
bm_filectx_free(bm_filectx_t *fctx)
{
@@ -62,7 +105,7 @@ bm_filectx_free(bm_filectx_t *fctx)
bm_ctx_t*
-bm_ctx_new(const char *settings_file, bc_error_t **err)
+bm_ctx_new(bm_ctx_t *base, const char *settings_file, bc_error_t **err)
{
if (settings_file == NULL || err == NULL || *err != NULL)
return NULL;
@@ -92,7 +135,14 @@ bm_ctx_new(const char *settings_file, bc_error_t **err)
return NULL;
}
- bm_ctx_t *rv = bc_malloc(sizeof(bm_ctx_t));
+ bm_ctx_t *rv = NULL;
+ if (base == NULL) {
+ rv = bc_malloc(sizeof(bm_ctx_t));
+ }
+ else {
+ bm_ctx_free_internal(base);
+ rv = base;
+ }
rv->settings = settings;
char *real_filename = realpath(settings_file, NULL);
@@ -108,6 +158,8 @@ bm_ctx_new(const char *settings_file, bc_error_t **err)
rv->output_dir = bc_strdup_printf("%s/%s", rv->root_dir, output_dir);
}
+ // can't return null and set error after this!
+
const char *template_dir = bc_trie_lookup(settings->settings,
"template_dir");
@@ -158,25 +210,77 @@ bm_ctx_new(const char *settings_file, bc_error_t **err)
void
-bm_ctx_free(bm_ctx_t *ctx)
+bm_ctx_reload(bm_ctx_t *ctx)
+{
+ if (ctx == NULL || ctx->settings_fctx == NULL)
+ return;
+
+ if (bm_filectx_changed(ctx->settings_fctx, NULL)) {
+ // reload everything! we could just reload settings_fctx, as this
+ // would force rebuilding everything, but we need to know new/deleted
+ // files
+
+ // needs to dup path, because it may be freed when reloading.
+ char *tmp = bc_strdup(ctx->settings_fctx->path);
+ bc_error_t *err = NULL;
+ ctx = bm_ctx_new(ctx, tmp, &err);
+ free(tmp);
+ if (err != NULL) { // failed to reload, keep old ctx
+ bc_error_print(err, "blogc-make");
+ bc_error_free(err);
+ }
+ return;
+ }
+
+ bm_filectx_reload(ctx->main_template_fctx);
+ bm_filectx_reload(ctx->atom_template_fctx);
+
+ for (bc_slist_t *tmp = ctx->posts_fctx; tmp != NULL; tmp = tmp->next)
+ bm_filectx_reload((bm_filectx_t*) tmp->data);
+
+ for (bc_slist_t *tmp = ctx->pages_fctx; tmp != NULL; tmp = tmp->next)
+ bm_filectx_reload((bm_filectx_t*) tmp->data);
+
+ for (bc_slist_t *tmp = ctx->copy_files_fctx; tmp != NULL; tmp = tmp->next)
+ bm_filectx_reload((bm_filectx_t*) tmp->data);
+}
+
+
+void
+bm_ctx_free_internal(bm_ctx_t *ctx)
{
if (ctx == NULL)
return;
bm_settings_free(ctx->settings);
+ ctx->settings = NULL;
free(ctx->root_dir);
+ ctx->root_dir = NULL;
free(ctx->output_dir);
+ ctx->output_dir = NULL;
bm_atom_destroy(ctx->atom_template_fctx->path);
bm_filectx_free(ctx->main_template_fctx);
+ ctx->main_template_fctx = NULL;
bm_filectx_free(ctx->atom_template_fctx);
+ ctx->atom_template_fctx = NULL;
bm_filectx_free(ctx->settings_fctx);
+ ctx->settings_fctx = NULL;
bc_slist_free_full(ctx->posts_fctx, (bc_free_func_t) bm_filectx_free);
+ ctx->posts_fctx = NULL;
bc_slist_free_full(ctx->pages_fctx, (bc_free_func_t) bm_filectx_free);
+ ctx->pages_fctx = NULL;
bc_slist_free_full(ctx->copy_files_fctx, (bc_free_func_t) bm_filectx_free);
+ ctx->copy_files_fctx = NULL;
+}
+
+void
+bm_ctx_free(bm_ctx_t *ctx)
+{
+ bm_ctx_free_internal(ctx);
free(ctx);
}
diff --git a/src/blogc-make/ctx.h b/src/blogc-make/ctx.h
index 05e85fc..7b68965 100644
--- a/src/blogc-make/ctx.h
+++ b/src/blogc-make/ctx.h
@@ -38,8 +38,12 @@ typedef struct {
} bm_ctx_t;
bm_filectx_t* bm_filectx_new(bm_ctx_t *ctx, const char *filename);
+bool bm_filectx_changed(bm_filectx_t *ctx, struct timespec *ts);
+void bm_filectx_reload(bm_filectx_t *ctx);
void bm_filectx_free(bm_filectx_t *fctx);
-bm_ctx_t* bm_ctx_new(const char *filename, bc_error_t **err);
+bm_ctx_t* bm_ctx_new(bm_ctx_t *base, const char *settings_file, bc_error_t **err);
+void bm_ctx_reload(bm_ctx_t *ctx);
+void bm_ctx_free_internal(bm_ctx_t *ctx);
void bm_ctx_free(bm_ctx_t *ctx);
#endif /* _MAKE_CTX_H */
diff --git a/src/blogc-make/main.c b/src/blogc-make/main.c
index 1e43580..98d4cc7 100644
--- a/src/blogc-make/main.c
+++ b/src/blogc-make/main.c
@@ -104,7 +104,7 @@ main(int argc, char **argv)
rules = bc_slist_append(rules, bc_strdup("all"));
}
- ctx = bm_ctx_new(blogcfile ? blogcfile : "blogcfile", &err);
+ ctx = bm_ctx_new(NULL, blogcfile ? blogcfile : "blogcfile", &err);
if (err != NULL) {
bc_error_print(err, "blogc-make");
rv = 3;
diff --git a/src/blogc-make/rules.c b/src/blogc-make/rules.c
index 711809a..1f18d5e 100644
--- a/src/blogc-make/rules.c
+++ b/src/blogc-make/rules.c
@@ -12,6 +12,9 @@
#include <stdlib.h>
#include <time.h>
#include <math.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <signal.h>
#include "../common/utils.h"
#include "ctx.h"
#include "exec.h"
@@ -552,14 +555,77 @@ static int all_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bool verbose);
// RUNSERVER RULE
+
+typedef struct {
+ bm_ctx_t *ctx;
+ bool verbose;
+} runserver_args_t;
+
+static runserver_args_t *args = NULL;
+
+static void
+runserver_sig_handler(int sig)
+{
+ free(args);
+ args = NULL;
+}
+
+static void*
+runserver_thread(void *arg)
+{
+ signal(SIGINT, runserver_sig_handler);
+ signal(SIGTERM, runserver_sig_handler);
+ args = arg;
+ while (true) {
+ bm_ctx_reload(args->ctx);
+ if (args->ctx == NULL) {
+ fprintf(stderr, "blogc-make: error: failed to reload context. "
+ "reloader disabled!\n");
+ goto runserver_cleanup;
+ }
+ if (0 != all_exec(args->ctx, NULL, args->verbose)) {
+ fprintf(stderr, "blogc-make: error: failed to rebuild website. "
+ "reloader disabled!\n");
+ goto runserver_cleanup;
+ }
+ sleep(1);
+ }
+
+runserver_cleanup:
+ free(args);
+ args = NULL;
+
+ return NULL;
+}
+
static int
runserver_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bool verbose)
{
+ // first 'all' call is syncronous, to do a 'sanity check'
int rv = all_exec(ctx, NULL, verbose);
if (rv != 0)
return rv;
- return bm_exec_blogc_runserver(ctx->settings, verbose);
+ runserver_args_t *args = bc_malloc(sizeof(runserver_args_t));
+ args->ctx = ctx;
+ args->verbose = verbose;
+
+ pthread_t thread;
+
+ if (0 != pthread_create(&thread, NULL, runserver_thread, args)) {
+ fprintf(stderr, "blogc-make: error: failed to create blogc-runserver "
+ "thread!\n");
+ return 3;
+ }
+
+ if (0 != pthread_detach(thread)) {
+ fprintf(stderr, "blogc-make: error: failed to detach blogc-runserver "
+ "thread!\n");
+ return 3;
+ }
+
+ bm_exec_blogc_runserver(ctx->settings, verbose);
+ return 0;
}