+## Establish a default target. We'll sort out what it does later.
+all:
+.PHONY: all
+
+## Things to clean.
+CLEANFILES =
+CLEANDIRS =
+REALCLEANFILES = $(CLEANFILES)
+REALCLEANDIRS = $(CLEANDIRS)
+
+## We work in terms of `zonesets'. Each one corresponds to a Lisp source
+## file to be passed to `zone'. A zoneset has a number of different nets
+## associated with it, in the variable zoneset_NETS, and we must run it
+## through `zone' once for each net. The zoneset will make a number of
+## zones, listed in zoneset_ZONES.
+ZONESETS =
+
+###--------------------------------------------------------------------------
+### The distorted.org.uk zones.
+
+ZONESETS += distorted
+
+distorted_VIEWS = inside outside
+distorted_outside_NETS = dmz jump
+distorted_inside_NETS = any unsafe colo vpn
+
+distorted_all_ZONES += distorted.org.uk
+distorted_all_ZONES += 199.29.172.in-addr.arpa
+
+###--------------------------------------------------------------------------
+### Other zones.
+
+## binswood.org.uk
+ZONESETS += binswood
+binswood_VIEWS = outside
+binswood_all_ZONES += binswood.org.uk
+binswood_all_ZONES += 27.165.10.in-addr.arpa
+
+## odin.gg
+ZONESETS += odin
+odin_VIEWS = outside
+odin_all_ZONES = odin.gg
+
+###--------------------------------------------------------------------------
+### Zone construction machinery.
+
+ZONE = zone
+V_ZONE = $(call v_tag,ZONE)$(ZONE)
+
+.SECONDEXPANSION: #sorry
+
+## For each net/zoneset pair, we make a stamp file net/zoneset.stamp to
+## remember that we've made the corresponding zones.
+ALL_ZONESTAMPS = $(foreach s,$(ZONESETS), \
+ $(patsubst %,%/$s.zonestamp,$($s_VIEWS)))
+$(ALL_ZONESTAMPS) : %.zonestamp : $$(notdir $$*).lisp hosts.lisp
+ $(V_AT)mkdir -p $(dir $*)
+ $(V_ZONE) -d$(dir $*) -fview/$(call dir-nosl,$*)$(hack \
+ hack) $(addprefix -s, \
+ $($(notdir $*)_$(call dir-nosl,$*)_NETS)) $<
+ $(V_AT)touch $@
+all: $(ALL_ZONESTAMPS)
+CLEANFILES += $(sort $(foreach s,$(ZONESETS), \
+ $(foreach v,$($s_VIEWS), \
+ $v/*.zonestamp $v/*.zone)))
+REALCLEANFILES += $(sort $(foreach s,$(ZONESETS), \
+ $(foreach v,$($s_VIEWS), \
+ $v/*.serial)))
+REALCLEANDIRS += $(sort $(foreach s,$(ZONESETS),$($s_VIEWS)))
+
+## Now explain that each generated zone file depends on the corresponding
+## zonestamp. This is where things start getting a little hairy.
+$(foreach s,$(ZONESETS), \
+ $(foreach v,$($s_VIEWS), \
+ $(foreach z,$($s_all_ZONES) $($s_$v_ZONES), \
+ $(eval $v/$z.zone: $v/$s.zonestamp))))
+
+## Now we have to check the individual zone files.
+ALL_ZONECHECKS = $(foreach s,$(ZONESETS), \
+ $(foreach v,$($s_VIEWS), \
+ $(foreach z,$($s_all_ZONES) $($s_$v_ZONES), \
+ $v/$z.check)))
+$(ALL_ZONECHECKS) : %.check : %.zone
+ $(call v_tag,CHECK)\
+ { $(CHECKZONE) $(notdir $*) $^ || kill $$$$; } | \
+ { grep -Ev 'loaded serial|OK' || :; }
+check: $(ALL_ZONECHECKS)
+.PHONY: check $(ALL_ZONECHECKS)
+
+## Finally we have to install the zone files.
+ALL_INSTALLS = $(foreach s,$(ZONESETS), \
+ $(foreach v,$($s_VIEWS), \
+ $(foreach z,$($s_all_ZONES) $($s_$v_ZONES), \
+ $v/$z.inst)))
+$(ALL_INSTALLS) : %.inst : %.check
+ $(call v_tag,INST)$(ZONEINST) \
+ $(call dir-nosl,$*) $(notdir $*) <$*.zone
+install: $(ALL_INSTALLS)
+.PHONY: install $(ALL_INSTALLS)
+
+## Files to clean.
+clean:
+ rm -f $(CLEANFILES)
+ [ "$(CLEANDIRS)x" = x ] || rmdir $(CLEANDIRS) || :
+realclean:
+ rm -f $(REALCLEANFILES)
+ [ "$(REALCLEANDIRS)x" = x ] || rmdir $(REALCLEANDIRS) || :
+.PHONY: clean realclean
+
+###----- That's all, folks --------------------------------------------------