Git+CPP as a ‘CMS’

Posted on April 20, 2012

Note 2016-06-28

These days, I use hakyll to publish this static site. At the time of writing, I felt like writing a ‘sufficient’ hack using ubiquitious tools. Now, I choose a more reliable and powerful tool called Nix, that solves the tool installation problem as well. Such a solution will not depend on the API stability of GCC (however good that may be), but will depend on appropriate tools in an explicit, machine-usable way.


Original article, 2012

When I decided to update roberthensing.nl, I felt like using something very simple, and that is what I did.

Ingredients

Procedure

You might have a feeling of where this is going now… The Unix nature of the C preprocessor makes it useful for more than just its primary purpose. Though it is not always implemented as a separate tool, it was design to be able to work this way. The GNU C Compiler provides a cpp binary, which, with a few special flags, gives us a general purpose preprocessor.

In order to manage building the site, I chose make. Some boring work had to be done get the dependencies right, but it is accurate now. Here is what I wrote:

% : %.in
	@echo "    CPP      $@"
	@cpp $(CPPFLAGS) <"$<" >"$@" || rm -f "$@"

install: installable
	@test ! -z $T || (echo "specify T=<target-dir>"; return 1)
	for i in $(INSTALL); do echo "Installing $$i."; cp -u "$$i" "$T/$$i"; done

.deps.make: $(shell find . -type f -name "*.in") $(shell find include -type f)
	@echo "Makefile: Computing dependencies..."
	@rm -f $@.tmp
    	@for i in $^; do cpp $(CPPFLAGS) -M -MG $$i -MF $@.tmp2; cat $@.tmp2 >> $@.tmp; done
	@sed <$@.tmp >$@ -e 's/\.o:/:/'
	@rm -f $@.tmp $@.tmp2

include .deps.make

At this point, I had a build/install script, which is good, but getting the site onto the server was still much error prone work.

Enter git, my versioning software of choice.

Git has a “hooks” feature that executes scripts at various occasions, one of which is proved very useful to me: post-receive. Create a script with this name in your .git/hooks/ on the server (assuming you do not have a bare repository). Write a script like this:

#!/bin/sh
make install T=/srv/www/yoursite

and you can push to your site.

Disadvantages

Although this works great for me now, consider the following:

Extras

To make life more comfortable, I added the following rules:

SVG rendering

This needs librsvg2-bin on Debian (or derived) distributions.

%.png : %.svg
	@echo "    RSVG     $@"
	@rsvg-convert "$<" -o "$@"

Read size from png image

This one may break. file may change, and its output is intended for human reading… Oh, and it is ugly, but very useful.

%.png.size : %.png
	@echo "    Measure  $@"
	@file "$<" | egrep -o '[0-9]* x [0-9]*' | awk '{ print "#define $(shell echo "$<" | sed -e s/[^a-z0-9A-Z]/_/g)_size width=\""$$1"\" height=\""$$3"\""}' > "$@" || (rm -f "$@"; exit 1)

Auto-build

Save file and refresh, just like when editing regular html files. If you use relative paths and Epiphany as your browser, you don’t even need to refresh! Tidier to write in a shell script, but I also want to avoid polluting my directories with small files. Run make auto while editing your html to instantly update your local copy of the website.

auto:
	while inotifywait -q -e modify,create Makefile $$(find . -name "*.in" -not -name ".#*") $$(find include -type f -not -name ".#*"); do make --no-print-directory all && echo done || echo failed; done

Minimal local test server

If you use absolute paths on your site, you will want to run a webserver for testing. Here is a minimal configuration for lighttpd. Run it as user in your git repo: lighttpd -f lighttpd.test.conf. Your pages appear at http://localhost:8080

lighttpd.test.conf:

server.document-root = var.CWD
server.errorlog = "/dev/stderr"
server.port = 8080
index-file.names = ( "index.html" )
include_shell "/usr/share/lighttpd/create-mime.assign.pl"

The mime types configuration script might be Debian-specific. Look at your default lighttpd.conf, somewhere in /etc for an example.