-
-
Save emilbjorklund/77cb39aafd04d60ef1f5 to your computer and use it in GitHub Desktop.
# 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? |
Oh, I left the final
make target in there for reference, but it's of course not actually necessary to achieve this. I just keep it there and run make final
manually, which adds some flags to the call to sass
resulting in minified CSS files without source maps.
Same goes for the clean
target. It's not required to make the compilation work, but I like to clean before I re-build, especially in the final
case where you want the source maps to be removed. This particular clean
target might not work for your use case though, depending on your output folder structure.
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.
I usually do a variation of you want to do (and the exact flavor varies from project to project). I adapted a Makefile from a recent project to do what you want to do:
This will create a list of targets in the
CSS_TARGET
based on the files that exist in the assets/scss folder. For eachfile.scss
, afile.css
target is created. Sometimes I hard-code these, but this insane wildcard/pathsubst pattern will achieve automatic look-up.It then contains a wildcard .css target, which will consume the SCSS file and produce the CSS file in the output folder. If you don't know how these work, RTFM (because I don't know how to explain it any shorter than that anyway). ;)
Note that this is just a Make target to compile your SCSS files. It doesn't run automatically by itself. To make that happen you need some sort of file system watcher. I use
watchmedo
from the Pythonwatchdog
package, which you can install withpip install watchdog
. Here's a command that would probably work for your use case:It will look for all files matching the
*.scss
pattern in the assets folder recursively, and run the command "make" whenever one changes. The -W flag makes it wait until the command finishes before executing another one. This is useful because OSX dispatches several file system events for a single file change causing the command to be invoked redundantly ifwatchmedo
is not told to wait for it to finish.