Skip to content

Instantly share code, notes, and snippets.

@emilbjorklund
Last active January 21, 2020 22:27
Show Gist options
  • Save emilbjorklund/77cb39aafd04d60ef1f5 to your computer and use it in GitHub Desktop.
Save emilbjorklund/77cb39aafd04d60ef1f5 to your computer and use it in GitHub Desktop.
Makefile for Sass?
# This is probably some pseudo-makefile syntax, but basically I want to do this:
# - The `assets/scss/` dir has a bunch of "top level" Sass files to
# be compiled - foo.scss, bar.scss etc.
# - Note: these files will each generate one resulting .css file of the
# same name as the source inside the build dir. foo.scss -> foo.css etc.
# - The build needs to be re-run any time any partial inside of a
# subdir in the scss folder changes: if `assets/scss/baz/_baz.scss` changes,
# I want to recompile all of the "root" .scss files.
# I.e. all of the partials in subdirs are prerequisites.
# So I want to run something like this for all the "root" .scss files, using the partials as a prerequisite.
build/css/foo.css: assets/scss/foo.scss assets/scss/*/*.scss
sassc -t compressed assets/scss/foo.scss > build/css/foo.css
# How do I generalize this in a sane way using variables in make?
@emilbjorklund
Copy link
Author

@mattpr
Copy link

mattpr commented Sep 10, 2016

Came across this as I was looking to start using make to build my static sites (jekyll hasn't been flexible enough and since I am not a ruby guy...the whole compass ecosystem isn't my thing either).

I noticed that your approach above causes every css to be rebuilt every time even if the corresponding scss hasn't changed. This is because the "target" %.css never exists, so make runs the rule every time. The file that exists is $(CSS_OUT)/%.css so that needs to be the target if you want make to be clever and not rebuild everything.

Additionally if you want to trigger a rebuild of the top scss file(s) when any of the includes change...they also need to be in the dependency list.

Here's my tweaks (I'm no make expert so happy to get feedback/improvements).

BUILD=build

#
# css
#
CSS_C=sass
CSS_FLAGS=-I $(SCSS_INCLUDES)
CSS_SRC=css
SCSS_INCLUDES=$(CSS_SRC)/includes
CSS_OUT=$(BUILD)/css
CSS_TARGETS=$(patsubst $(CSS_SRC)/%.scss,$(CSS_OUT)/%.css,$(wildcard $(CSS_SRC)/*.scss))

.PHONY: all final css clean

all: clean css

final: CSS_FLAGS += -t compact --sourcemap=none
final: all

clean:
    rm -rf $(BUILD)/*

#
# CSS
#

css: $(CSS_TARGETS)

$(CSS_OUT):
    mkdir -p $(CSS_OUT)

$(CSS_TARGETS): $(CSS_OUT)/%.css : $(CSS_SRC)/%.scss $(SCSS_INCLUDES)/_*.scss | $(CSS_OUT)
    $(CSS_C) $(CSS_FLAGS) $< $@

I'm using static pattern rule instead of the second expansion because it feels cleaner to me.

Changes in detail...

  • CSS_TARGETS now are paths to the actual output files (e.g. build/css/foo.css)
    • This ensures that we only rebuild if the file is out of date (not there, older timestamp than dependencies)
  • We use the middle pattern ($(CSS_OUT)/%.css) to extract the name of the target (e.g. "foo")
  • This is then injected as the first dep ($(CSS_SRC)/%.scss)
    • Ensures that we only run target when the source scss file changed
  • Added all includes as deps ($(SCSS_INCLUDES)/_*.scss) ensures any changes to an include will cause all CSS_TARGETS to rebuild (whereas a change to top-level scss file will only cause a single css file to be rebuilt).
  • Added an ordered prereq to make sure our output directory exists (| $(CSS_OUT))

https://www.gnu.org/software/make/manual/html_node/Static-Usage.html#Static-Usage

Anyway, I learned some stuff from the comment here so thought I would share back.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment