Wednesday, 20 July 2011

Vanish decades of newbie make pain

I've just cured years of makefile hell with this single line:

   SHELL:=bash -c 'exec $(SHELL) "$${@//\\$$'\''\012'\''/$$'\''\012'\''}"' --

It means that make recipes that use \ at the end of a line for multi-line recipes will now have the \ stripped off - at last you can have PROPER multi-line recipes!!!


Previously, if you had normal looking multi-line shell clauses in your make recipe, you had to do something like this:

    if blah ; \
    then update-file ; \
    else create-file ; \
    fi


Which is a bit of a hack, and is actually treated as a single line shell statement with semi-colons.

And that is fair enough unless you have a multi-line sed statement as part of your recipe - which is easy to do by accident if you are using literate programming (http://new.fangled.org) with auto-syntax-quoting as one chunk is included into another. (Auto-syntax quoting is when your shell chunk included in a makefile has the $ converted to $$ automatically [something the banking industry has been doing for years])

Because make will start a new shell command every time it finds a newline in the command string (even if you hide it in a mid-line shell variable) there is no way that a backslash-newline can occur except when the human deliberately escapes it.

The single-line SHELL re-definition of mine uses bash to replace backslash-newline with newline and then passes the command to your regular shell.

And I get extra points for using exec


Now my literate programming tools can automatically and nestedly invoke sed in a bash context and then that bash in a make context with automatic quoting and have it all work nicely!

(As long as the makefile author inserts that sticking plaster at the top of the makefile)

And the newbie who uses this can use backslash-newline in a makefile in the proper intuitive way.

    if blah \
    then update-file \
    else create-file \
    fi