Add a testsuite (./testsuite) and dependency-logging command stuff that doesn't
[mgear/mgear.git] / mage.mk
similarity index 76%
rename from src/mage.mk
rename to mage.mk
index 658474d..d9ad5d7 100644 (file)
+++ b/mage.mk
@@ -1,6 +1,7 @@
 # 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)
 
@@ -13,6 +14,7 @@ mg-orig-default-goal:=$(.DEFAULT_GOAL)
 # TEXT UTILITIES
 empty:=
 bs:=\$(empty)
+hash:=\#
 pct:=%
 define nl
 
@@ -22,7 +24,7 @@ endef
 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))
@@ -177,11 +179,9 @@ mg+ = $(filter-out MG-% $(opfx)%,$+)
 # 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.
@@ -190,8 +190,8 @@ endef
 # 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.
@@ -201,37 +201,33 @@ MG-FORCE:
 # $(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).
@@ -243,39 +239,81 @@ endef
 #     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)