From 7870e88f0653e6ac93c1f2e123aa9826778be376 Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Mon, 25 Feb 2019 22:43:51 +0100 Subject: make: implemented optional sass support using libsass pending: - tests for sass support - build tests on ci with and without libsass - documentation - support sass options in blogcfile --- src/blogc-make/ctx.c | 14 ++++ src/blogc-make/exec-native.c | 27 +++++--- src/blogc-make/exec-native.h | 1 + src/blogc-make/main.c | 6 +- src/blogc-make/rules.c | 17 +++++ src/blogc-make/sass.c | 148 +++++++++++++++++++++++++++++++++++++++++++ src/blogc-make/sass.h | 18 ++++++ src/blogc-make/settings.c | 4 ++ src/blogc-make/settings.h | 1 + 9 files changed, 227 insertions(+), 9 deletions(-) create mode 100644 src/blogc-make/sass.c create mode 100644 src/blogc-make/sass.h (limited to 'src') diff --git a/src/blogc-make/ctx.c b/src/blogc-make/ctx.c index 423b334..408b6f2 100644 --- a/src/blogc-make/ctx.c +++ b/src/blogc-make/ctx.c @@ -6,6 +6,10 @@ * See the file LICENSE. */ +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + #include #include #include @@ -192,6 +196,16 @@ bm_ctx_new(bm_ctx_t *base, const char *settings_file, const char *argv0, } free(content); +#ifndef USE_LIBSASS + if (settings->sass != NULL) { + *err = bc_error_new_printf(BLOGC_MAKE_ERROR_SETTINGS, + "[sass] section found in %s, but blogc-make was built without " + "libsass support", settings_file); + bm_settings_free(settings); + return NULL; + } +#endif + const char *template_dir = bc_trie_lookup(settings->settings, "template_dir"); if (template_dir == NULL) template_dir = ""; diff --git a/src/blogc-make/exec-native.c b/src/blogc-make/exec-native.c index 179216a..2417173 100644 --- a/src/blogc-make/exec-native.c +++ b/src/blogc-make/exec-native.c @@ -24,15 +24,9 @@ int -bm_exec_native_cp(bm_filectx_t *source, bm_filectx_t *dest, bool verbose) +bm_exec_native_mkdir_p(const char *filename) { - if (verbose) - printf("Copying '%s' to '%s'\n", source->path, dest->path); - else - printf(" COPY %s\n", dest->short_path); - fflush(stdout); - - char *fname = bc_strdup(dest->path); + char *fname = bc_strdup(filename); for (char *tmp = fname; *tmp != '\0'; tmp++) { if (*tmp != '/' && *tmp != '\\') continue; @@ -51,6 +45,23 @@ bm_exec_native_cp(bm_filectx_t *source, bm_filectx_t *dest, bool verbose) } free(fname); + return 0; +} + + +int +bm_exec_native_cp(bm_filectx_t *source, bm_filectx_t *dest, bool verbose) +{ + if (verbose) + printf("Copying '%s' to '%s'\n", source->path, dest->path); + else + printf(" COPY %s\n", dest->short_path); + fflush(stdout); + + int rv = bm_exec_native_mkdir_p(dest->path); + if (rv != 0) + return rv; + int fd_from = open(source->path, O_RDONLY); if (fd_from < 0) { fprintf(stderr, "blogc-make: error: failed to open source file to copy " diff --git a/src/blogc-make/exec-native.h b/src/blogc-make/exec-native.h index 56a1da1..0dfefcf 100644 --- a/src/blogc-make/exec-native.h +++ b/src/blogc-make/exec-native.h @@ -13,6 +13,7 @@ #include "../common/error.h" #include "ctx.h" +int bm_exec_native_mkdir_p(const char *filename); int bm_exec_native_cp(bm_filectx_t *source, bm_filectx_t *dest, bool verbose); bool bm_exec_native_is_empty_dir(const char *dir, bc_error_t **err); int bm_exec_native_rm(const char *output_dir, bm_filectx_t *dest, bool verbose); diff --git a/src/blogc-make/main.c b/src/blogc-make/main.c index 5b4a030..f18b9df 100644 --- a/src/blogc-make/main.c +++ b/src/blogc-make/main.c @@ -75,7 +75,11 @@ main(int argc, char **argv) print_help(); goto cleanup; case 'v': - printf("%s\n", PACKAGE_STRING); + printf(PACKAGE_STRING +#ifdef USE_LIBSASS + "+libsass" +#endif + "\n"); goto cleanup; case 'D': dev = true; diff --git a/src/blogc-make/rules.c b/src/blogc-make/rules.c index 06223c0..41e363d 100644 --- a/src/blogc-make/rules.c +++ b/src/blogc-make/rules.c @@ -6,6 +6,10 @@ * See the file LICENSE. */ +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + #include #include #include @@ -22,6 +26,10 @@ #include "utils.h" #include "rules.h" +#ifdef USE_LIBSASS +#include "./sass.h" +#endif + static void posts_ordering(bm_ctx_t *ctx, bc_trie_t *variables, const char *variable) @@ -850,6 +858,15 @@ const bm_rule_t rules[] = { .exec_func = copy_exec, .generate_files = true, }, +#ifdef USE_LIBSASS + { + .name = "sass", + .help = "build CSS stylesheets from Sass (.scss) sources", + .outputlist_func = bm_sass_outputlist, + .exec_func = bm_sass_exec, + .generate_files = true, + }, +#endif { .name = "clean", .help = "clean built files and empty directories in output directory", diff --git a/src/blogc-make/sass.c b/src/blogc-make/sass.c new file mode 100644 index 0000000..da4b8b6 --- /dev/null +++ b/src/blogc-make/sass.c @@ -0,0 +1,148 @@ +/* + * blogc: A blog compiler. + * Copyright (C) 2014-2019 Rafael G. Martins + * + * This program can be distributed under the terms of the BSD License. + * See the file LICENSE. + */ + +#include +#include +#include +#include +#include +#include +#include "ctx.h" +#include "exec-native.h" +#include "rules.h" +#include "./sass.h" +#include "../common/utils.h" + + +bc_slist_t* +bm_sass_outputlist(bm_ctx_t *ctx) +{ + if (ctx == NULL || ctx->settings == NULL || ctx->settings->sass == NULL) + return NULL; + + bc_slist_t *rv = NULL; + + for (size_t i = 0; ctx->settings->sass[i] != NULL; i++) { + char *t = bc_strdup(ctx->settings->sass[i]); + for (int i = strlen(t); i >= 0 ; i--) { + if (t[i] == '.') { + t[i] = '\0'; + break; + } + } + + char *f = bc_strdup_printf("%s/%s.css", ctx->short_output_dir, t); + free(t); + rv = bc_slist_append(rv, bm_filectx_new(ctx, f, NULL, NULL)); + free(f); + } + + return rv; +} + + +int +bm_sass_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bc_trie_t *args) +{ + if (ctx == NULL || ctx->settings == NULL || ctx->settings->sass == NULL) + return 0; + + int rv = 0; + + size_t i = 0; + bc_slist_t *o = outputs; + + for (; ctx->settings->sass[i] != NULL, o != NULL; i++, o = o->next) { + bm_filectx_t *o_fctx = o->data; + if (o_fctx == NULL) + continue; + + if (bc_str_starts_with(basename(ctx->settings->sass[i]), "_")) { + fprintf(stderr, "blogc-make: error: sass: Mixins must be included " + "by other files (%s)\n", ctx->settings->sass[i]); + return 3; + } + if (!bc_str_ends_with(ctx->settings->sass[i], ".scss")) { + fprintf(stderr, "blogc-make: error: sass: Only .scss Sass sources " + "are supported (%s)\n", ctx->settings->sass[i]); + return 3; + } + + bm_filectx_t *f = bm_filectx_new(ctx, ctx->settings->sass[i], NULL, NULL); + + struct Sass_File_Context *fctx = sass_make_file_context(f->path); + struct Sass_Context *sctx = sass_file_context_get_context(fctx); + struct Sass_Compiler *comp = sass_make_file_compiler(fctx); + struct Sass_Options *opts = sass_context_get_options(sctx); + sass_option_set_output_style(opts, SASS_STYLE_COMPRESSED); + char *sass_dir = bc_strdup_printf("%s/sass", ctx->root_dir); + sass_option_push_include_path(opts, sass_dir); + free(sass_dir); + sass_dir = bc_strdup_printf("%s/_sass", ctx->root_dir); + sass_option_push_include_path(opts, sass_dir); + free(sass_dir); + + bc_slist_t *sources = NULL; + + rv = sass_compiler_parse(comp); + if (rv != 0 || 0 != sass_context_get_error_status(sctx)) { + fprintf(stderr, "blogc-make: error: sass: %s\n%s", + o_fctx->path, sass_context_get_error_message(sctx)); + rv = 3; + goto clean; + } + + char **inc = sass_context_get_included_files(sctx); + for (size_t i = 0; inc != NULL && inc[i] != NULL; i++) { + sources = bc_slist_append(sources, bm_filectx_new(ctx, inc[i], NULL, NULL)); + } + + if (!bm_rule_need_rebuild(sources, ctx->settings_fctx, NULL, NULL, o_fctx, false)) + goto clean; + + if (ctx->verbose) + printf("Compiling '%s' to '%s'\n", f->path, o_fctx->path); + else + printf(" SASS %s\n", o_fctx->short_path); + fflush(stdout); + + rv = sass_compiler_execute(comp); + if (rv != 0 || 0 != sass_context_get_error_status(sctx)) { + fprintf(stderr, "blogc-make: error: sass: %s\n%s", + o_fctx->path, sass_context_get_error_message(sctx)); + rv = 3; + goto clean; + } + + rv = bm_exec_native_mkdir_p(o_fctx->path); + if (rv != 0) + goto clean; + + FILE *fp = fopen(o_fctx->path, "w"); + if (fp == NULL) { + fprintf(stderr, "blogc-make: error: sass: failed to open output " + "file (%s): %s\n", o_fctx->path, strerror(errno)); + rv = 3; + goto clean; + } + + fputs(sass_context_get_output_string(sctx), fp); + fclose(fp); + +clean: + bm_filectx_free(f); + bc_slist_free_full(sources, (bc_free_func_t) bm_filectx_free); + sass_delete_compiler(comp); + sass_delete_file_context(fctx); + + if (rv != 0) + return rv; + } + + return 0; +} diff --git a/src/blogc-make/sass.h b/src/blogc-make/sass.h new file mode 100644 index 0000000..351b736 --- /dev/null +++ b/src/blogc-make/sass.h @@ -0,0 +1,18 @@ +/* + * blogc: A blog compiler. + * Copyright (C) 2014-2019 Rafael G. Martins + * + * This program can be distributed under the terms of the BSD License. + * See the file LICENSE. + */ + +#ifndef _MAKE_SASS_H +#define _MAKE_SASS_H + +#include "ctx.h" +#include "../common/utils.h" + +bc_slist_t* bm_sass_outputlist(bm_ctx_t *ctx); +int bm_sass_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bc_trie_t *args); + +#endif /* _MAKE_SASS_H */ diff --git a/src/blogc-make/settings.c b/src/blogc-make/settings.c index be976e3..f13d5b2 100644 --- a/src/blogc-make/settings.c +++ b/src/blogc-make/settings.c @@ -72,6 +72,7 @@ static const char* list_sections[] = { "copy", "copy_files", // backward compatibility "tags", + "sass", NULL, }; @@ -97,6 +98,7 @@ bm_settings_parse(const char *content, size_t content_len, bc_error_t **err) rv->pages = NULL; rv->copy = NULL; rv->tags = NULL; + rv->sass = NULL; // this is some code for compatibility with the [environment] section, // even if I never released a version with it, but some people is using @@ -159,6 +161,7 @@ bm_settings_parse(const char *content, size_t content_len, bc_error_t **err) rv->posts = bc_config_get_list(config, "posts"); rv->pages = bc_config_get_list(config, "pages"); rv->tags = bc_config_get_list(config, "tags"); + rv->sass = bc_config_get_list(config, "sass"); // this is for backward compatibility too. rv->copy = bc_config_get_list(config, "copy"); @@ -184,5 +187,6 @@ bm_settings_free(bm_settings_t *settings) bc_strv_free(settings->pages); bc_strv_free(settings->copy); bc_strv_free(settings->tags); + bc_strv_free(settings->sass); free(settings); } diff --git a/src/blogc-make/settings.h b/src/blogc-make/settings.h index 69fdbb6..8060963 100644 --- a/src/blogc-make/settings.h +++ b/src/blogc-make/settings.h @@ -20,6 +20,7 @@ typedef struct { char **pages; char **copy; char **tags; + char **sass; } bm_settings_t; bm_settings_t* bm_settings_parse(const char *content, size_t content_len, -- cgit v1.2.3-18-g5258