Today I wrote my first Makefile ever. I realized that keeping my compilation stuff for Poslin just laying around somewhere in some badly-written scripts was a bad idea.
So I came up with all of this:
all: build/poslin0
.PHONY: all
build/poslin0: # unholy amounts of file dependencies
sbcl --noinform --disable-ldb --lose-on-corruption \
--dynamic-space-size 8192 \
--userinit ./sbclinit \
--load ./compile-poslin.lisp
mv -v ./poslin0 build/poslin0
.PHONY: clean
clean:
rm -vf ./build/poslin0
That's pretty straightforward, even if I never wrote a Makefile before.
But an install target is still missing, and of course I want an install target. Having an install target is absolutely crucial, right? *seriousface*
Just as important is for the user of the makefile to specify the prefix for the install, so of course I wanna accommodate that. But there will need to be two startup scripts, to not only load the barebones version of poslin (called "poslin0"), but also the one with the base library loaded (poslin1) and the one where fancy stuff like packages and generic operations and optimization stuff and so on is also defined (poslin).
So the Makefile turns into something like this:
PREFIX ?= /usr/local
BINDIR ?= $(PREFIX)/bin
LIBDIR ?= $(PREFIX)/lib/poslin
P0 = $(BINDIR)/poslin0
P1 = $(BINDIR)/poslin1
P2 = $(BINDIR)/poslin
# see above
.PHONY: install
install: all
mkdir -pv $(BINDIR)
mkdir -pv $(LIBDIR)
cp -rv ./lib/* $(LIBDIR)/
cp -v ./build/poslin0 $(P0)
echo "#!/bin/sh\n" > $(P1)
echo "$(P0) $(LIBDIR)/base.poslin" >> $(P1)
echo "#!/bin/sh\n" > $(P2)
echo "$(P1) $(LIBDIR)/supplemental/package.poslin \\" \
>> $(P2)
echo " $(LIBDIR)/supplemental/generic-op.poslin" >> $(P2)
chmod +x $(P1)
chmod +x $(P2)
But that's not completely correct. The commands in the resulting scripts need to end with a "$*" so the user can supply additional poslin files to load.
The naive version of course doesn't cut it, because those "$*"s would be expanded immediately, which is bunk:
echo "#!/bin/sh\n" > $(P1)
echo "$(P0) $(LIBDIR)/base.poslin $*" >> $(P1)
echo "#!/bin/sh\n" > $(P2)
echo "$(P1) $(LIBDIR)/supplemental/package.poslin \\" \
>> $(P2)
echo " $(LIBDIR)/supplemental/generic-op.poslin $*" >> $(P2)
So I need to escape those "$" characters:
echo "#!/bin/sh\n" > $(P1)
echo "$(P0) $(LIBDIR)/base.poslin \$*" >> $(P1)
echo "#!/bin/sh\n" > $(P2)
echo "$(P1) $(LIBDIR)/supplemental/package.poslin \\" \
>> $(P2)
echo " $(LIBDIR)/supplemental/generic-op.poslin \$*" >> $(P2)
But no, that's not how you escape "$"s in sh – this even complains about unclosed quoted strings or somesuch nonesense. You need to use "$$"!
echo "#!/bin/sh\n" > $(P1)
echo "$(P0) $(LIBDIR)/base.poslin $$*" >> $(P1)
echo "#!/bin/sh\n" > $(P2)
echo "$(P1) $(LIBDIR)/supplemental/package.poslin \\" \
>> $(P2)
echo " $(LIBDIR)/supplemental/generic-op.poslin $$*" >> $(P2)
Hm. That also doesn't work. I don't remember what exactly happened here, but I think those "$$*"s just vanished entirely. Probably because the substitution of variables is done from the right or something, what the heck do I know!
Okay, then maybe make the "*" stay away from the "$$":
echo "#!/bin/sh\n" > $(P1)
echo "$(P0) $(LIBDIR)/base.poslin "$$"*" >> $(P1)
echo "#!/bin/sh\n" > $(P2)
echo "$(P1) $(LIBDIR)/supplemental/package.poslin \\" \
>> $(P2)
echo " $(LIBDIR)/supplemental/generic-op.poslin "$$"*" >> $(P2)
And it works!
Damn, this is ugly. How does anybody get anything done in bash?