# Makefile for /var/www
# Copyright Peter Maydell <pmaydell@chiark.greenend.org.uk> 09/1998
#
# The primary purpose of this makefile is to keep this directory
# and the public website on chiark in sync. 
# The directory $(MIRROR) is a place to keep timestamps which
# are updated when the files are copied to chiark.
# Unless the .tstamp file exists no copying will occur...
# The secondary purpose is to see how clever we can be with Make
# (is it Turing-complete, incidentally?)

# Usage:
# make FILE=filename add
#	marks the file for adding to the remote tree. It will be transferred
#	at the next 'make all'. NB: only files can be added; directories
#	will be added as required if you say 'make FILE=dir/dir/filename add'
# make FILE=filename remove
#	removes the local copy of the file and marks it for deletion from
#	the remote tree at the next 'make all'. You can specify directories
# 	here: they will be recursively deleted! Caution is recommended.
# make FILE=filename nomirror
#	like remove, but doesn't delete the local copy of the file.
#	The file will not be mirrored in future.
# make lint
# 	run weblint on any *.html files being mirrored.
#	(Actually, there's a hack which means that excludes bookmarks.html...)
# make FILE=filename lint
#	run weblint only on the specified file or files
# make
# make all
#	synchronise remote tree with local tree. This is the only command
#	that requires communication with the remote host. 
# make disablesync
#	causes 'make all' and 'make' to fail without syncing. This is 
#	useful if you automatically run make when a PPP connection is 
#	made, and want to temporarily disable this.
# make enablesync
#	allow sync to work again
#
# NB: mixing add and remove on a single file without calling make all in
# the middle might have odd effects(?)

# This makefile is sufficiently complex that I don't guarantee that
# it will work with anything other than GNU Make :->

# Improvements: perhaps rather than running ssh many times, we could
# add commands to a script file and then
# cat script | ssh 'cat >/tmp/script && chmod 755 /tmp/script && /tmp/script'
# (this is no longer so vital now we don't repeat mkdir all the time)
# Allow 'make help' to print a usage message
# Make it easier to work with remote systems where we can't do mkdir -p
# and rm -rf equivalents by having two versions of add/remove?

### configuration variables ###
# name of the subdirectory used to hold timestamp files
MIRROR := chiarkmirror
# Args to pass to weblint. These are very pedantic indeed :->
WEBLINTARGS := -pedantic -e upper-case
# Files not to bother weblinting. Netscape's bookmarks file is 
# never going to pass :->
DONTLINT := bookmarks.html taomarks.html

# These three are only used by the ssh macros below
RHOSTNAME := login.chiark.greenend.org.uk
REMOTEHOST := pmaydell@$(RHOSTNAME)
REMOTEDIR := public-html

# These variables define how to talk to the remote system.
# Here are settings for using ssh/scp:
# (you can say SSHPROTO=-1 if you need to)
# Start the connection; nothing needed here for ssh
RSTARTUP=true
# Ensure that the directory hierarchy $* exists:
RADDDIR=ssh $(SSHPROTO) $(REMOTEHOST) 'mkdir -p $(REMOTEDIR)/$*'
# Ensure that $* (may be file or directory tree) is removed:
RRMTREE=ssh $(SSHPROTO) $(REMOTEHOST) 'rm -rf $(REMOTEDIR)/$*'
# Copy $< to the remote system:
RCPFILE=scp $(SSHPROTO) $< $(REMOTEHOST):$(REMOTEDIR)/$<
# Check that we can contact the remote system; must be a command
# that returns true only if we can talk to the remote system.
# If you don't have fping then just say 'RPING=true' to disable the test.
# This is a 1-second timeout
RPING=fping -t1000 -q $(RHOSTNAME)
# Tidy up the connection and complete anything outstanding; no-op for ssh
RSHUTDOWN=true

#### end of configuration variables ####

# We do some sanity checking on the user-specified goals.
# add and remove may only be used as the single goal, and
# if they are used then the user must set FILE.
# We set ERROR and then the errorcheck target succeeds or fails accordingly.
ifneq (,$(filter add,$(MAKECMDGOALS)))
	# add is a goal: must be only one, FILE must be set
	ifndef FILE
		ERROR="FILE not given: use 'make FILE=filename add'"
	endif
	ifneq ($(MAKECMDGOALS),add)
		ERROR="add must be the only goal specified"
	endif
endif
ifneq (,$(filter remove,$(MAKECMDGOALS)))
	# remove is a goal: must be only one, FILE must be set
	ifndef FILE
		ERROR="FILE not given: use 'make FILE=filename remove'"
	endif
	ifneq ($(MAKECMDGOALS),remove)
		ERROR="remove must be the only goal specified"
	endif
endif 

# Set TSTAMPS to a list of all the .tstamp files in the mirror directory
# and HTMLFILES to a list of .html files to be weblinted.
TSTAMPS := $(shell find $(MIRROR) -name '*.tstamp')
HTMLFILES := $(filter $(MIRROR)/%.html.tstamp, $(TSTAMPS))
HTMLFILES := $(patsubst $(MIRROR)/%.html.tstamp,%.html,$(HTMLFILES))
# Remove the files on the excludes-list:
HTMLFILES := $(filter-out $(DONTLINT), $(HTMLFILES))

# RMSTAMPS is a list of files/dirs to be deleted from the remote site
RMSTAMPS := $(shell find $(MIRROR) -name '*.remove')
# ADDSTAMPS is a list of directories to be created on the remote site
ADDSTAMPS := $(shell find $(MIRROR) -name '.add')

.PHONY: all lint add remove nomirror rmlocal errorcheck synccheck startup shutdown

# all target requires remote link to be up. First we create any new
# directories, then we mirror the files, then we delete any files that
# are to be removed.
all: errorcheck synccheck startup $(ADDSTAMPS) $(TSTAMPS) $(RMSTAMPS) shutdown

# To make a $(MIRROR)/foobar.tstamp file (which depends on foobar)
# we scp foobar to chiark's public-html directory and 
# touch $(MIRROR)/foobar.tstamp.
$(TSTAMPS) : $(MIRROR)/%.tstamp : %
	$(RCPFILE) && touch $@

# To handle a $(MIRROR)/foobar.remove file we need to 
# ssh $(REMOTEHOST) 'rm -rf $(REMOTEDIR)/file'
# and then remove the .remove file from the mirrordir.
# The errorcheck dependency means the commands are always run.
$(RMSTAMPS) : $(MIRROR)/%.remove : errorcheck
	$(RRMTREE)
	rm $@

# Handle $(MIRROR)/dir/dir/.add by ensuring that 
# the directories exist and deleting the .add file.
# Actual file transfer will be done by the TSTAMP rule.
$(ADDSTAMPS) : $(MIRROR)/%.add : errorcheck
	$(RADDDIR)
	rm $@

# run weblint on all the .html files
lint: errorcheck
ifdef FILE
	weblint $(WEBLINTARGS) $(FILE)
else
	weblint $(WEBLINTARGS) $(HTMLFILES)
endif

# Idea about how to handle removing files: have a target remove
# such that saying 'make remove index.html' does:
#  * rm -rf index.html
#  * if [ -f $(MIRROR).index.html.tstamp ]; then
#      rm $(MIRROR)/index.html.tstamp; else 
#      rm -rf $(MIRROR)/index.html; fi
#     (this is so that we can say 'make remove directory' too)
#  * touch $(MIRROR)/index.html.remove
# And then have the all target look for $(MIRROR)/*.remove
# files, and do a ssh REMOTEHOST 'rm -rf $REMOTEDIR/file'
# and then remove the .remove file so that we don't do it again next time...

# To remove a file, remove from mirror site and then delete the local copy
remove: errorcheck nomirror rmlocal

# don't mirror this file/directory in future. Deletes remote copy.
nomirror: errorcheck
	# remove file/dir from the mirror directory
	if [ -f $(MIRROR)/$(FILE).tstamp ]; then \
		rm $(MIRROR)/$(FILE).tstamp; \
	else rm -rf $(MIRROR)/$(FILE); fi
	touch $(MIRROR)/$(FILE).remove

# Just remove the local copy of the file/directory. Succeeds even if it
# doesn't exist, so we can do things like:
# mv foo bar; make FILE=foo remove; make FILE=bar add
rmlocal: errorcheck
	rm -rf $(FILE)


# Similarly we allow 'make add dir/dir/index.html' which will
#  * create directories if required
#  * touch dir/dir/file.tstamp and file
#  * touch dir/dir/.add
# and make the all target do a mkdir -p on the remote system for
# every .add file in $(MIRROR), and then remove the .add files
add: errorcheck
	mkdir -p $(dir $(FILE))
	mkdir -p $(MIRROR)/$(dir $(FILE))
	touch $(MIRROR)/$(dir $(FILE)).add
# We could improve efficiency by deleting here .add files
# for any parent directories of $(dir $(FILE)), since we'll
# effectively do a mkdir -p anyway.
#
# Note that the tstamp must be older in case the user
# has already created the file before doing the make add...
	touch -d '1 Jan 1971' $(MIRROR)/$(FILE).tstamp
	touch $(FILE)

# Simple rules to allow us to turn the upload on/off
disablesync:
	touch $(MIRROR)/.nosync

enablesync:
	rm -f $(MIRROR)/.nosync

#Trivial startup/shutdown rules
startup:
	$(RSTARTUP)

shutdown:
	$(RSHUTDOWN)

# This rule checks to see if we should be trying to sync the
# remote system or not. It fails with an appropriate error if not.
synccheck:
	@if [ -e $(MIRROR)/.nosync ]; then echo 'Sync disabled'; false; fi
	@if $(RPING); then echo 'Remote host is alive'; else echo 'ERROR: Remote host unreachable'; false; fi

# This rule checks to see if some conditional has set ERROR. If so
# it prints the message and causes make to stop (because false will
# always 'fail'). -k defeats this, of course.
# Unfortunately I can't see how to get make to always do the errorcheck
# target first, so it has to be listed as a dependency of add/all/lint/etc.
errorcheck:
ifdef ERROR
	@echo ERROR: $(ERROR)
	@false
else
	@true
endif
