VERSION_NUMBER := 1.0
TEST_SUITE := test.AllTests

# Location of trees.
SOURCE_DIR := src
ALL_DIR := $(SOURCE_DIR)/org
TEST_DIR := $(SOURCE_DIR)/test
LIB_DIR    := libs
OUTPUT_DIR := classes
DOC_DIR := doc

# Unix tools
AWK := awk
FIND := find
MKDIR := mkdir -p
RM := rm -rf
SHELL := /bin/bash
MKTEMP := mktemp

# Tool Paths
JAVA_HOME := /usr/lib/j2sdk1.4-sun

# Java tools
JAVA := $(JAVA_HOME)/bin/java
JAVAC := $(JAVA_HOME)/bin/javac

JFLAGS := -sourcepath $(SOURCE_DIR) \
	  -d $(OUTPUT_DIR) \
	  -source 1.4

JVMFLAGS := -ea \
	    -esa \
	    -Xfuture

JVM := $(JAVA) $(JVMFLAGS)

JAR := $(JAVA_HOME)/bin/jar
JARFLAGS := cf

JAVADOC := $(JAVA_HOME)/bin/javadoc
JDFLAGS := -sourcepath $(SOURCE_DIR) \
	   -d $(DOC_DIR) \
	   -link http://java.sun.com/j2se/1.4/docs/api

# Jars
LOG4J_JAR := $(LIB_DIR)/log4j.jar
COJEN_JAR := $(LIB_DIR)/cojen.jar
JUNIT_JAR := $(LIB_DIR)/junit.jar

class_path := OUTPUT_DIR \
		LOG4J_JAR \
		COJEN_JAR 

test_class_path := $(class_path) JUNIT_JAR

space := $(empty) $(empty)

define set-classpath
  $(eval export CLASSPATH := $1)
endef

# $(call build-classpath, variable-list)
define build-classpath
$(strip \
  $(patsubst :%,%, \
    $(subst : ,:, \
      $(strip \
        $(foreach j,$1,$(call get-file,$j):)))))
endef

# $(call get-file, variable-name)
define get-file
  $(strip \
    $($1) \
    $(if $(call file-exists-eval,$1),, \
      $(warning The file referenced by variable '$1' ($($1)) cannot be found)))
endef

# $(call file-exists-eval, variable-name)
define file-exists-eval
  $(strip \
    $(if $($1),,$(warning '$1' has no value)) \
    $(wildcard $($1)))
endef

# $(call brief-help, makefile)
define brief-help
  $(AWK) '$$1 ~ /^[^.][-A-Za-z0-9]*:/ \
    { print substr($$1, 1, length($$1)-1) }' $1 | \
  sort | \
  pr -T -w 80 -4
endef

# $(call file-exists, wildcard-pattern)
file-exists = $(wildcard $1)

# $(call check-file, file-list)
define checkfile
  $(foreach f, $1, \
    $(if $(call file-exists, $($f)),,            \
       $(warning $f ($($f)) is missing)))
endef

#(call make-temp-dir, root-opt)
define make-temp-dir
  $(shell $(MKTEMP) -d -t $(if $1,$1,make).XXXXXXXXXX)
endef

# MANIFEST_TEMPLATE - Manifest input to m4 macro processor
MANIFEST_TEMPLATE := manifest.mf.template
TMP_JAR_DIR        := $(call make-temp-dir)
TMP_MANIFEST       := $(TMP_JAR_DIR)/manifest.mf

# $(call add-manifest, jar, jar-name, manifest-file-opt)
define add-manifest
  $(RM) $(dir $(TMP_MANIFEST))
  $(MKDIR) $(dir $(TMP_MANIFEST))
  m4 --define=NAME="$(notdir $2)"                        \
      --define=IMPL_VERSION=$(VERSION_NUMBER)            \
      --define=SPEC_VERSION=$(VERSION_NUMBER)            \
      $(if $3,$3,$(MANIFEST_TEMPLATE))                   \
      > $(TMP_MANIFEST)
  $(JAR) -ufm $1 $(TMP_MANIFEST)
  $(RM) $(dir $(TMP_MANIFEST))
endef

# $(call make-jar,jar-variable-prefix)
define make-jar
  .PHONY: $1 $$($1_name)
  $1: $($1_name)
  $$($1_name):
	cd $(OUTPUT_DIR); \
	$(JAR) $(JARFLAGS) $$(notdir $$@) $$($1_packages)
	$$(call add-manifest, $$@, $$($1_name), $$($1_manifest))
endef

# Set the CLASSPATH
export CLASSPATH := $(call build-classpath, $(class_path))

# make-directories - Ensure output directory exists.
make-directories := $(shell $(MKDIR) $(OUTPUT_DIR) $(DOC_DIR))

# help - The default goal
.PHONY: help
help:
	@$(call brief-help, $(CURDIR)/Makefile)

# all - Perform all tasks for a complete build
.PHONY: all
all: compile jars javadoc

# all_javas - Temp file for holding source file list
all_javas := $(OUTPUT_DIR)/all.javas
test_javas := $(OUTPUT_DIR)/test.javas

# compile - Compile the source
compile: $(all_javas)
	$(JAVAC) $(JFLAGS) @$(all_javas)

# test - Run tests
.PHONY: test
test: compile_tests
	$(JAVA) $(JVMFLAGS) junit.textui.TestRunner $(TEST_SUITE)

compile_tests: $(test_javas) $(properties_jar_name)
	$(call set-classpath,$(call build-classpath, $(test_class_path)))
	$(JAVAC) $(JFLAGS) @$(test_javas)

# all_javas - Gather source file list
.INTERMEDIATE: $(all_javas)
$(all_javas):
	$(FIND) $(ALL_DIR) -name '*.java' > $@

# test_javas - Gather source file list
.INTERMEDIATE: $(test_javas)
$(test_javas):
	$(FIND) $(TEST_DIR) -name '*.java' > $@

# jar_list - List of all jars to create
jar_list := properties_jar

# jars - Create all jars
.PHONY: jars
jars: $(jar_list)
	
# properties_jar - Create the $(properties_jar)
properties_jar_name     := $(OUTPUT_DIR)/properties.jar
properties_jar_manifest := properties.mf
properties_jar_packages := org

# Create an explicit rule for each jar
# $(foreach j, $(jar_list), $(eval $(call make-jar,$j)))
$(eval $(call make-jar,properties_jar))

# javadoc - Generate the Java doc from sources
.PHONY: javadoc
javadoc: $(all_javas)
	$(JAVADOC) $(JDFLAGS) @$<

.PHONY: clean
clean:
	$(RM) $(OUTPUT_DIR) $(DOC_DIR) test-precompiled

.PHONY: classpath
classpath:
	@echo CLASSPATH='$(CLASSPATH)'

.PHONY: check-config
check-config:
	@echo Checking configuration...
	$(call check-file, $(class_path) JAVA_HOME)

.PHONY: print
print:
	$(foreach v, $(V), \
	$(warning $v = $($v)))

