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 --- Makefile.am | 25 ++++++++ blogc.spec.in | 3 +- configure.ac | 12 ++++ 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 + 12 files changed, 266 insertions(+), 10 deletions(-) create mode 100644 src/blogc-make/sass.c create mode 100644 src/blogc-make/sass.h diff --git a/Makefile.am b/Makefile.am index 040f291..5fae160 100644 --- a/Makefile.am +++ b/Makefile.am @@ -60,6 +60,7 @@ noinst_HEADERS = \ src/blogc-make/httpd.h \ src/blogc-make/reloader.h \ src/blogc-make/rules.h \ + src/blogc-make/sass.h \ src/blogc-make/settings.h \ src/blogc-make/utils.h \ src/blogc-runserver/httpd.h \ @@ -235,6 +236,16 @@ blogc_make_LDADD = \ libblogc_make.la \ libblogc_common.la \ $(NULL) + +if USE_LIBSASS +blogc_make_CFLAGS += \ + $(LIBSASS_CFLAGS) \ + $(NULL) + +blogc_make_LDADD += \ + $(LIBSASS_LIBS) \ + $(NULL) +endif endif if BUILD_MAKE_LIB @@ -259,6 +270,20 @@ libblogc_make_la_LIBADD = \ $(PTHREAD_LIBS) \ libblogc_common.la \ $(NULL) + +if USE_LIBSASS +libblogc_make_la_SOURCES += \ + src/blogc-make/sass.c \ + $(NULL) + +libblogc_make_la_CFLAGS += \ + $(LIBSASS_CFLAGS) \ + $(NULL) + +libblogc_make_la_LIBADD += \ + $(LIBSASS_LIBS) \ + $(NULL) +endif endif diff --git a/blogc.spec.in b/blogc.spec.in index 067ba89..f842cb9 100644 --- a/blogc.spec.in +++ b/blogc.spec.in @@ -6,7 +6,7 @@ Group: Applications/Text Summary: A blog compiler URL: @PACKAGE_URL@ Source0: https://github.com/blogc/blogc/releases/download/v@PACKAGE_VERSION@/blogc-@PACKAGE_VERSION@.tar.xz -BuildRequires: gcc, bash, coreutils, diffutils +BuildRequires: gcc, bash, coreutils, diffutils, libsass-devel %if ! 0%{?el6} BuildRequires: git, tar, make %endif @@ -32,6 +32,7 @@ blogc-git-receiver is a simple login shell/git hook to deploy blogc websites. %package make Summary: A simple build tool for blogc Group: Development/Tools +Requires: libsass Requires: %{name} = %{version}-%{release} Requires: %{name}-runserver = %{version}-%{release} diff --git a/configure.ac b/configure.ac index 880acf4..afe2cab 100644 --- a/configure.ac +++ b/configure.ac @@ -102,6 +102,8 @@ AC_ARG_ENABLE([make-embedded], AS_HELP_STRING([--enable-make-embedded], [build blogc-make tool embedded on blogc binary])) AC_ARG_ENABLE([make], AS_HELP_STRING([--enable-make], [build blogc-make tool])) +AC_ARG_WITH([libsass], AS_HELP_STRING([--without-libsass], + [disable blogc-make dependency on libsass])) AS_IF([test "x$enable_make" = "xyes" -o "x$enable_make_embedded" = "xyes"], [ AC_CHECK_HEADERS([dirent.h fcntl.h libgen.h sys/stat.h sys/wait.h time.h unistd.h],, [ AC_MSG_ERROR([blogc-make tool requested but required headers not found]) @@ -118,10 +120,20 @@ AS_IF([test "x$enable_make" = "xyes" -o "x$enable_make_embedded" = "xyes"], [ MAKE_="enabled" have_make=yes ]) + AS_IF([test "x$with_libsass" != "xno"], [ + PKG_CHECK_MODULES([LIBSASS], [libsass], [ + MAKE_="${MAKE_} (with libsass)" + have_libsass=yes + AC_DEFINE([USE_LIBSASS], [], [Build blogc-make with libsass support]) + ], [ + have_libsass=no + ]) + ]) ]) AM_CONDITIONAL([BUILD_MAKE], [test "x$have_make" = "xyes"]) AM_CONDITIONAL([BUILD_MAKE_LIB], [test "x$have_make_lib" = "xyes"]) AM_CONDITIONAL([BUILD_MAKE_EMBEDDED], [test "x$have_make_embedded" = "xyes"]) +AM_CONDITIONAL([USE_LIBSASS], [test "x$have_libsass" = "xyes"]) RUNSERVER="disabled" AC_ARG_ENABLE([runserver], AS_HELP_STRING([--enable-runserver], 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