# Mage Build Tool by Matt McCutchen
# http://www.kepreon.com/~matt/mage/
+
# Remember the original default goal so we can restore it at the end of mage.mk.
mg-orig-default-goal:=$(.DEFAULT_GOAL)
# TEXT UTILITIES
empty:=
bs:=\$(empty)
+hash:=\#
pct:=%
define nl
sq='$(subst ','\'',$1)'
# Make-quote: a$\nb => a$$$(nl)b
# This is enough to assign the value, *but not to use it as an argument!*
-mqas=$(subst $(nl),$$(nl),$(subst $$,$$$$,$1))
+mqas=$(subst $(hash),$$(hash),$(subst $(nl),$$(nl),$(subst $$,$$$$,$1)))
# Return nonempty if the strings are equal, empty otherwise.
# If the strings have only nice characters, you can do $(filter x$1,x$2).
streq=$(findstring x$1,$(findstring x$2,x$1))
# Replaces references to automatic variables with references to their Mage-ized
# counterparts. There might be false matches, e.g., $$@ => $$(mg@) ;
# to prevent that, write $$$(empty)@ instead. (c.f. autoconf empty quadrigraph)
-define mg-translate-cmd
-$(subst $$?,$$(mg?),$(subst $$<,$$(mg<),$(subst $$^,$$(mg^),$(subst $$+,$$(mg+),$1))))
-endef
+mg-translate-cmd=$(subst $$t,$$@.tmp,$(subst $$?,$$(mg?),$(subst $$<,$$(mg<),$(subst $$^,$$(mg^),$(subst $$+,$$(mg+),$1)))))
-# $(call prereq-predict,target,prerequisites)
+# $(call mg-prereq-predict,target,prerequisites)
# Expands to code that does the following at second-expansion time:
# 1. Computes the actual prerequisites from the given prerequisite patterns by
# translating % to $* and prepending a directory if appropriate.
# Factor out set-gdeps because make gets confused if the macro for the
# prerequisites has a colon. Make replaces the first % with the stem *before
# expansion*, so use $(pct) to protect some %s.
-set-gdeps=$(eval $@@gdeps:=$1)
-prereq-predict=$$$$(call set-gdeps,$$$$+ $(if $(findstring /,$1),$$$$(subst $$$$(pct),$$$*,$2),$$$$(addprefix $$$$(dir $$$$@),$$$$(subst $$$$(pct),$$$$*,$2))))
+mg-set-gdeps=$(eval $@@gdeps:=$1)
+mg-prereq-predict=$$$$(call mg-set-gdeps,$$$$+ $(if $(findstring /,$1),$$$$(subst $$$$(pct),$$$*,$2),$$$$(addprefix $$$$(dir $$$$@),$$$$(subst $$$$(pct),$$$$*,$2))))
# Used to make the rule for bar always run so we can act based on $(bar@gdeps)
# and check for command change.
# $(call mg-define-rule,target,prerequisites,cmd)
# Defines a rule. cmd is expanded again when it is run, at which time
# Mage-ized automatic variables are available.
-define mg-define-rule
-$(eval
-
-# Store the new command.
-$1: cmd=$(call mg-translate-cmd,$3)
-# Provide variable for the temporary file. FIX
-$1: t=$$@.tmp
-
+#
+# I eradicated the target-specific variables because they fail when there are
+# multiple implicit rules with the same target pattern.
+#
# Rule for the target. Set $(bar@gdeps) to all prerequisites. Apply the new
# prerequisites. The command script always runs. Depend on bar.g to get
# $(bar@gq).
-$1: $(call prereq-predict,$1,$2) $2 MG-FORCE $(mg-genfile-oid)$(aname)$$$$@.g
- $$(mg-rule-cmd)
-)
+define mg-define-rule
+$(eval $1: $(call mg-prereq-predict,$1,$2) $2 MG-FORCE $(mg-genfile-oid)$(aname)$$$$@.g
+ $$(call mg-rule-cmd,$(call mg-translate-cmd,$3)))
endef
# TODO Provide a way to define static pattern rules.
-# Procedure to generate bar. Remember, $@ is bar.g.
+# Procedure to generate bar. Now $@ is bar, so we say $@.g for bar.g.
# If an override:
# 1. Complain. (Should we stop "`bar' is up to date" using @:; ?)
-# 2. Clear out the warnings so we don't replay them.
+# 2. This would be the place to clear bar's metadata if we wanted to.
# Otherwise, check prereqs, command, and nonexistence to decide whether bar
# needs to be regenerated. If so:
-# 1. Set a flag so Mage knows to reread the genfile when make runs target
-# "bar".
+# 1. Note that the genfile is about to change.
# 2. Echo the command being run.
# On error, skip to 8:
# 3. Open the new genfile bar.g.tmp for writing.
-# 4. Store the command in bar.g.tmp.
-# 5. Run the command to bar.tmp, storing warnings in bar.g.tmp.
+# 4. Run the command to bar.tmp, storing warnings in bar.g.tmp.
+# 5. Store the command in bar.g.tmp. Do it here to ensure that bar.g.tmp
+# is at least as new as bar.tmp.
# 6. If bar.tmp differs from bar:
# a. Clear and touch bar.g (so bar doesn't become an override if we're
# killed between steps 6b and 7).
# 1. If there were warnings, replay them. (HMMM To stderr?)
# HMMM .tmp in displayed command looks ugly
define mg-rule-cmd
- $(foreach x,$@ $(mg+),$(eval $x@checked:=1))
$(if $(filter $(mg-scout-oid)$(aname)$@,$($@@gq)),\
$(info Mage: warning: Manually created/modified file at $@ overrides rule.)\
- ,$(call gload,$@)$(if $($@@gq)$(if $(wildcard $@),,x)$(mg-check-cmd),\
- $(eval $@@gloaded:=)$(info $(cmd))\
+ ,$(call gload,$@)$(if $($@@gq)$(if $(wildcard $@),,TARGET-DNE)$(mg-check-cmd),\
+ $(eval $@@gloaded:=)$(info $1)\
@trap 'rm -f $@.tmp $@.g.tmp' EXIT &&\
- exec 3>$@.g.tmp && $(mg-assign-cmd) >&3 &&\
+ exec 3>$@.g.tmp &&\
set -o pipefail && { $(mg-run-cmd) | tee /dev/fd/4 | $(mg-wrap-warnings) >&3; } 4>&1 &&\
- $(mg-maybe-move-target) && mv -f $@.g.tmp $@.g\
+ $(mg-assign-cmd) >&3 &&\
+ $(mg-maybe-move-target) &&\
+ mv -f $@.g.tmp $@.g\
,$(if $($@@warnings),\
$(info $($@@cmd) # Mage warning replay$(nl)$($@@warnings))\
)))
endef
# If the command changed, we must regenerate.
-mg-check-cmd=$(if $(call streq,$(cmd),$($@@cmd)),,x)
+mg-check-cmd=$(if $(call streq,$1,$($@@cmd)),,COMMAND-CHANGED)
# Pieces of mg-generate that I factored out to make mg-generate more readable.
-mg-assign-cmd=echo $(call sq,$(call fmt-make-assignment,$@@cmd,$(cmd)))
-mg-run-cmd={ ($(cmd)) 2>&1 && { [ -r $@.tmp ] || { echo 'Mage: error: Command for $@ succeeded without creating it!'; false; }; }; }
+mg-assign-cmd=echo $(call sq,$(call fmt-make-assignment,$@@cmd,$1))
+mg-run-cmd={ ($1) 2>&1 && { [ -r $@.tmp ] || { echo 'Mage: error: Command for $@ succeeded without creating it!'; false; }; }; }
mg-wrap-warnings=sed -re '1s/^/$@@warnings:=$$(empty)/; 1!s/^/$@@warnings+=$$(nl)/'
-mg-maybe-move-target={ cmp -s $@ $@.tmp || echo >$@.g && mv -f $@.tmp $@; }
+# Drat bash's lack of precedence between || and &&. Extra braces necessary.
+mg-maybe-move-target={ cmp -s $@ $@.tmp || { echo >$@.g && mv -f $@.tmp $@; }; }
# Just add additional prerequisites. This cannot add prerequisite patterns to
# an implicit rule, but it can add specific prerequisites to an individual use
# of an implicit rule. Currently, Mage picks up the target's prerequisites from
# make, so this just attaches the given prerequisites to the target, but the
# implementation might change in the future.
-define mg-define-prereq
-$(eval $1: $2)
+mg-define-prereq=$(eval $1: $2)
+
+
+# DEPENDENCY-LOGGING COMMANDS
+
+mg-dlc-static-run-oid:=$(mg-genfile-oid)
+
+# $(call mg-define-rule-dlc,target,static-prerequisites,cmd,dep-converter)
+# Analogue of mg-define-rule for a dependency-logging command.
+# I haven't decided on the format for the dep-converter yet.
+define mg-define-rule-dlc
+$(eval
+
+# FINISH
+
+# Rule for the target. Set $(bar@gdeps) to all prerequisites. Apply the new
+# prerequisites. The command script always runs. Finally, do the static run
+# and then the first dynamic check.
+$1: $(call mg-prereq-predict,$1,$2) $2 MG-FORCE $(mg-dlc-static-run-oid)$(aname)$$$$@.g $$$$(call dlc-next-dchk,$$$$(target))
+# TODO: Move the finished file into place or something
+# $$(call mg-rule-cmd,$3)
+)
endef
-# END
+#mg-dlc-next-dchk=$(call mg-dlc-next-dchk-1,$1,$(newoid))
+#mg-dlc-next-dchk-1=$(eval $2@opr=$$(call mg-dlc-dchk-pr,$1,$2))$2
+#
+## $(call mg-dlc-dchk-pr,target,oid)
+#mg-dlc-dchk-pr=$(call mg-dlc-dchk-pr-1,$1,$2,$(call next-unchecked-prereq,$($1@deps)))
+## $(call mg-next-unchecked-prereq,foo.c bar.h baz.h)
+#mg-next-unchecked-prereq=$(firstword $(foreach p,$1,$(if $($p@checked),,$p)))
+#define mg-dlc-dchk-pr-1
+#$(if $3,$(call mg-dlc-drun,$1,$2,$3) $(call mg-dlc-next-dchk,$1),)
+#endef
+#
+## $(call mg-dlc-drun,target,oid,prereq). FINISH
+#define mg-dlc-drun
+#$(eval
+#$2$(aname)$3@opr:=$3
+#$2$(aname)$3@ocmd:=
+#)$2$(aname)$3
+#endef
+#
+#
+## END
# We don't want anything we defined to become the default goal.
.DEFAULT_GOAL := $(mg-orig-default-goal)