HTML/Makefile Hybrids

4 views
Skip to first unread message

Sean B. Palmer

unread,
Aug 17, 2010, 12:07:42 PM8/17/10
to Gallimaufry of Whits
The bowels of Apache changed once again and many of the pages on my
site don't work because of some mod_rewrite problem that's so obscure
that it's taken more attention than I wanted to really give it. Given
that I'm not really maintaining my site much at the moment and am
concentrating instead on static content and externally hosted projects
such as the current Gallimaufry of Whits, this has got me thinking
about dynamic content in a static setting.

The problem is roughly as follows. Say you want to host a site on
Dropbox, which only allows static files and doesn't do anything nifty
like allowing you to access files without their file extension, such
as you get from using MultiViews in Apache. It also doesn't do
directory index generation. So you might want to have some kind of
elaborate setup that keeps an HTML directory index up to date for each
folder that you have.

I'd been doing this with a small shell script:

#!/bin/sh
echo '<title>Directory Contents</title>'
for fn in *.txt
do echo "<li><a href='$fn'>$(head -n 1 $fn | sed 's/^# //')</a>"
done
for fn in *.html
do echo "<li><a href='$fn'>$(sed -n '/<title>/{s/<[^>]*>//g;p;q;}' $fn)</a>"
done

Though it would be difficult to get much more simple than this, two
things really bugged me about this. The first is that I don't like
maintaining files that create or modify other files. The second is
that I like to customise each of these content generators, which
mainly just amplifies the syncing problem as I might forget that a
file is automatically generated, edit the output, and then find the
script later and overwrite my edits.

So I thought, hey, there must be some way that an HTML script can also
act as a shell script right? HTML is totally flexible, there must be
some syntactic intersection that makes me trick bash into thinking
that I can run the HTML file and make it update itself. Only after
some consideration I decided that there wasn't, so I thought about sed
instead. After looking in boggling desperation at the sed manual, I
figured that only good old make could possibly work. But, to my great
surprise admittedly, it really did work. You really can produce an
HTML file which is also a makefile. Here's an example:

<!DOCTYPE html><!--:o
define -->
<title>Self-Updating HTML Makefile</title>
<h1>Self-Updating HTML Makefile</h1>
<p>Tue 17 Aug 2010 16:55:01 BST
<!--
endef
# This is a makefile. Use make -f example.html
o:;@sed -i '' "s/^<p>.*/<p>$(shell date)/" example.html; echo updated
# -->

Thanks to Noah for pointing out that one can use "define var" to get
make to ignore a big section of text until the closing endef,
effectively acting like a heredoc. Noah tried his hand as his own
version of this:

http://groups.google.com/group/tumbolia/t/ce9234d265857072

His is a bit more elaborate, and I'm aiming for brutal simplicity. One
thing that is still causing a problem is that old versions of make
don't seem to like the string quoting in the directory updating script
that I made:

* * *

define python

import re, glob;

r_ul = re.compile(r'(?ims)<ul>(.*?)</ul>');
r_title = re.compile(r'<title>(.*?)</title>');
names = sorted(glob.glob('*.html'));

chunks = [(name, open(name).read(512)) for name in names];
titles = [(a, r_title.search(b).group(1)) for a, b in chunks];

link = '<a href=' + chr(0x22) + '%s' + chr(0x22) + '>%s</a>';
items = ['<li>' + link % (a, b) for a, b in titles];
list = chr(0x0A).join(['<ul>'] + items + ['</ul>']);

bytes = open('index.html').read();
bytes = r_ul.sub(list, bytes, 1);
open('index.html', 'w').write(bytes)

endef

o: ;
@python -c "$(shell echo "$(python)")"
@echo updated

* * *

Presumably this was either an old undesired behaviour or a bug, since
it's no problem in the more recent version of make, where by "recent"
I mean just GNU Make 3.81, from 2006. But it would be nice to get it
working on older versions too, and the string quoting is a bit gnarly
in general so this is a sign that perhaps the define and shell echo to
an argument method is not the best way of invoking a script embedded
in a makefile: note that because I couldn't use double quotes in the
code, I had to do silly things like using chr(0x22).

I'm not sure that it's good as a serious solution to the file sync
problem on static sites, but it's a pretty fun bit of code!

Noah Slater

unread,
Aug 17, 2010, 3:21:23 PM8/17/10
to wh...@googlegroups.com

On 17 Aug 2010, at 17:07, Sean B. Palmer wrote:

> Presumably this was either an old undesired behaviour or a bug, since
> it's no problem in the more recent version of make, where by "recent"
> I mean just GNU Make 3.81, from 2006. But it would be nice to get it
> working on older versions too, and the string quoting is a bit gnarly
> in general so this is a sign that perhaps the define and shell echo to
> an argument method is not the best way of invoking a script embedded
> in a makefile: note that because I couldn't use double quotes in the
> code, I had to do silly things like using chr(0x22).

I had a bit of a think about this.

After realising there would be no easy way to pass newlines through to the shell using the $(shell) function, I suddenly had a brainwave. What if you could set the shell of the Makefile to Python itself, then just specify the programme using regular rules. Well, that worked! But every line ended in "; \" so that there was continuity between lines. A bit more reading, and I discovered that in GNU Make 3.82 you can set ONERULE and mitigate this problem.

I posted an updated file here:

http://groups.google.com/group/tumbolia/msg/fd0b9e58c3027f54

Reply all
Reply to author
Forward
0 new messages