Update materials for building rsync with mgear.
[mgear/mgear.git] / mgear.mk
1 # Mgear Build Tool by Matt McCutchen
2 # http://www.kepreon.com/~matt/mgear/
3
4
5 # Remember the original default goal so we can restore it at the end of mgear.mk.
6 mg-orig-default-goal:=$(.DEFAULT_GOAL)
7
8 # We use second-expansion heavily to dynamically compute prerequisites when they
9 # are needed.  Second-expansion lets us follow make's implicit rule search
10 # instead of trying to anticipate which prerequisites it will need in advance.
11 .SECONDEXPANSION:
12
13 # Deletion of intermediate files messes everything up.
14 # TODO: See if we can relax this requirement.
15 .SECONDARY:
16
17
18 # TEXT UTILITIES
19 empty:=
20 bs:=\$(empty)
21 hash:=\#
22 comma:=,
23 pct:=%
24 define nl
25
26
27 endef
28 # Shell-quote: a'b => 'a'\''b'
29 sq='$(subst ','\'',$1)'
30 # Make-quote: a$\nb => a$$$(nl)b
31 # This is enough to assign the value, *but not to use it as an argument!*
32 mqas=$(subst $(hash),$$(hash),$(subst $(nl),$$(nl),$(subst $$,$$$$,$1)))
33 # Make the value safe to use as an argument without doubling $.
34 # *The implementation is incomplete*; I will improve it as needed.
35 msarg=$(subst $(comma),$$(comma),$1)
36 # Return nonempty if the strings are equal, empty otherwise.
37 # If the strings have only nice characters, you can do $(filter x$1,x$2).
38 streq=$(findstring x$1,$(findstring x$2,x$1))
39
40 # $(call fmt-make-assignment,foo,bar)
41 # Output a make assignment that stores bar in variable foo.
42 # The result is like `foo:=bar' but handles leading spaces, $, and
43 # newlines appearing in bar safely.
44 fmt-make-assignment=$1:=$$(empty)$(call mqas,$2)
45
46
47 # TARGET OBFUSCATION
48
49 # Mgear uses two kinds of "obfuscated targets" (those that are not the simple
50 # names of real files):
51 # - An always-exists target like /.//. is used as a prerequisite of an implicit
52 #   rule.  Since it exists, make doesn't second-expand its own prerequisites
53 #   until it is actually run.  This way, a target's prerequisites can depend on
54 #   the results of previous command scripts.
55 # - An alternative-name target like /.//./proc/self/cwd/bar is used to check the
56 #   mtime of a file without actually building it or introducing a circular
57 #   dependency.
58
59 # $(newoid) allocates and returns a new obfuscation ID (oid).  Example:
60 #    x:=$(newoid)
61 # You can then refer to target $x or $x$(aname)bar (for any file bar).
62 # Prerequisites and commands come from $($x@opr) and $($x@ocmd).
63 # Target-specific variables $(oid) and $(otgt) (if alternate-name) are
64 # available.
65
66 opfx:=/./
67 # TODO If the system doesn't support /proc/self/cwd, use something else.
68 aname:=/proc/self/cwd/
69 nextoid:=/./.
70 newoid=$(nextoid)$(eval nextoid:=$(subst /.//////,//./,$(patsubst /.//////%,/././%,$(nextoid:.=/.))))
71
72 # Target-specific variables for obfuscated targets.
73 $(opfx)%: oid=$(word 1,$(subst $(aname), ,$@))
74 $(opfx)%: otgt=$(word 2,$(subst $(aname), ,$@))
75
76 # High-priority implicit rule for obfuscated targets.  Works for both always-
77 # exists and alternate-name targets.
78 $(opfx)%: $$($$(oid)@opr)
79         $($(oid)@ocmd)
80
81 # Make won't use the same implicit rule more than once on its stack, so provide
82 # a second copy of the rule to allow two obfuscated targets on the stack.
83 # Needed for $(mg-genfile-oid)$(aname)bar.g <- $(mg-scout-oid)$(aname)bar .
84 # The prerequisites must look different before second-expansion so the second
85 # rule isn't discarded as a duplicate.
86 $(opfx)%: $$(empty) $$($$(oid)@opr)
87         $($(oid)@ocmd)
88
89 # MAIN BUILD LOGIC
90
91 # DESIGN:
92 #
93 # - To each generated file bar corresponds a "genfile" bar.g that contains some
94 # information about how bar was generated, including the command (for rebuild on
95 # command change) and the warnings (for replay).
96 #
97 # - bar.g's mtime is the last time mgear verified that bar was up to date.  bar
98 # needs to be regenerated iff a prerequisite is newer than *bar.g* (not bar).
99 # If a prerequisite changes but the command gives the same contents for bar,
100 # bar.g is touched but bar itself is not touched because files that depend on it
101 # need not be regenerated.
102 #
103 # - If bar is newer than bar.g, the user has overridden it, and we should leave
104 # the override in place but warn the user about it.
105 #
106 # - The user's Makefile defines mgear rules by calling mg-define-rule.  Each
107 # mgear rule becomes one underlying make rule with the same target and a little
108 # extra magic.  This is important so that all implicit rule competition takes
109 # place at the same target.  Additionally, the prerequisites are passed to an
110 # obfuscated target for bar.g to see if any are newer than bar.g.  This is done
111 # by the single obfuscated implicit rule, keeping the number of implicit rules
112 # low. Then the command script for bar checks for overrides, command change,
113 # prerequisite change, etc. and acts accordingly.
114 #
115 # Target metadata variables for bar: (* means stored in bar.g)
116 # Now some variables are reused instead of remembered for every target.
117 #
118 # @name:=bar
119 #     Name of the target to which @cmd, @warnings, @deps refer.
120 #
121 # @cmd:=cat foo >bar.tmp
122 #     Generation command as given to the shell.*
123 #
124 # @warnings:=$(empty)yikes!
125 #     Data that the command printed to stdout or stderr, presumably warnings.*
126 #
127 # @deps:=included@x oops@
128 #     If dependency-logging, list of filename@revision used.  Revision is x for
129 #     exists and empty for doesn't exist.  Later perhaps x will be the mtime.*
130 #
131 # bar@gdeps:=foo
132 #     Static dependencies of bar, for checking by bar.g.
133 #
134 # bar@gq:=foo $(mg-scout-oid)$(aname)bar
135 #     $? from the rule for bar.g; used by the rule for bar.
136 #
137 # bar@checked:=1
138 #     Set when mgear determines that a file is up to date or depends on it being
139 #     so determined.  Used to decide which prerequisite to check next for a
140 #     dependency-logging command.
141 #
142 # bar@dlc-ran:=1
143 #     Indicates that mgear is in the middle of building bar using a dependency-
144 #     logging command.  Means that bar.g.tmp, not bar.g, is the most current
145 #     genfile.
146 #
147 ## If the rule for bar is overridden, we clear the information from bar.g so
148 ## that it is as if bar.g didn't exist. -- Not currently needed
149 #
150 # Some make features with which mgear's compatibility has not been investigated:
151 # - Command-line options (especially --dry-run, --question, --touch,
152 #   --always-make, --keep-going, --jobs, --assume-old, and --assume-new)
153 # - Static pattern rules
154 # - Vpath
155
156 # $(call gload,foo.o)
157 # Load foo.o's genfile, if any, into $(@cmd), etc.
158 define gload
159 $(if $(filter $(@name),$1),,$(eval 
160 # TODO: Store and check the version of mgear that wrote the genfile. 
161 #@mgear-version:=
162 @cmd:=
163 @warnings:=
164 @deps:=
165 -include $1.g
166 @name:=$1
167 ))
168 endef
169 @name:=
170
171 # bar.g: scout bar and its dependencies and store $?.  When implicit rules
172 # compete for bar, we depend on the rule make uses being the last one it
173 # second-expands so that $(bar@gdeps) is still correct.
174 mg-genfile-oid:=$(newoid)
175 mg-scout-oid:=$(newoid)
176 $(mg-genfile-oid)@opr=$($(otgt:.g=)@gdeps) $(if $(wildcard $(otgt:.g=)),$(mg-scout-oid)$(aname)$(otgt:.g=),) MG-FORCE
177 $(mg-genfile-oid)@ocmd=$(eval $(otgt:.g=)@gq:=$(filter-out MG-%,$?))
178 $(mg-scout-oid)@opr:=
179 $(mg-scout-oid)@ocmd:=
180
181 # Mgear-ized automatic variables.
182 # For now, if you really want the eventual target, write $$(@).
183 mg@ = $@.tmp
184 # $%: haven't thought about it much, but probably needs no translation
185 mg< = $(firstword $(mg^))
186 mg? = $(filter-out $(opfx)%,$($@@gq))
187 mg^ = $(filter-out MG-% $(opfx)%,$^)
188 mg+ = $(filter-out MG-% $(opfx)%,$+)
189 # $|: needs no translation
190 # $*: needs no translation
191
192 # $(call mg-translate-cmd,cat $$< >$$@)
193 # Replaces references to automatic variables with references to their mgear-ized
194 # counterparts.  There might be false matches, e.g., $$@ => $$(mg@) ;
195 # to prevent that, write $$$(empty)@ instead.  (c.f. autoconf empty quadrigraph)
196 mg-translate-cmd=$(subst $$@,$$(mg@),$(subst $$?,$$(mg?),$(subst $$<,$$(mg<),$(subst $$^,$$(mg^),$(subst $$+,$$(mg+),$1)))))
197
198 # $(call mg-prereq-predict,target,prerequisites)
199 # Expands to code that does the following at second-expansion time:
200 # 1. Computes the actual prerequisites from the given prerequisite patterns by
201 #    translating % to $* and prepending a directory if appropriate.
202 # 2. Saves both existing ($+) and newly computed prerequisites to $(bar@gdeps)
203 #    where the rule for bar.g can get them.
204 # Factor out set-gdeps because make gets confused if the macro for the
205 # prerequisites has a colon.  Make replaces the first % with the stem *before
206 # expansion*, so use $(pct) to protect some %s.
207 mg-set-gdeps=$(eval $@@gdeps:=$1)
208 mg-prereq-predict=$$$$(call mg-set-gdeps,$$$$+ $(if $(findstring /,$1),$$$$(subst $$$$(pct),$$$*,$2),$$$$(addprefix $$$$(dir $$$$@),$$$$(subst $$$$(pct),$$$$*,$2))))
209
210 # Used to make the rule for bar always run so we can act based on $(bar@gdeps)
211 # and check for command change.
212 MG-FORCE:
213 .PHONY: MG-FORCE
214
215 # $(call mg-define-rule,target,prerequisites,cmdvar)
216 # Defines a rule.  cmdvar is *the name of a variable* containing the command.  
217 # The variable is read immediately with $(value) and then expanded in the scope
218 # of mgear-ized automatic variables each time the rule is run.
219 #
220 # I eradicated the target-specific variables because they fail when there are
221 # multiple implicit rules with the same target pattern.
222 #
223 # Rule for the target.  Set $(bar@gdeps) to all prerequisites.  Apply the new
224 # prerequisites.  The command script always runs.  Depend on bar.g to get
225 # $(bar@gq).
226 define mg-define-rule
227 $(eval $1: $(call mg-prereq-predict,$1,$2) $2 MG-FORCE $(mg-genfile-oid)$(aname)$$$$@.g
228         $$(call mg-rule-cmd,$(call msarg,$(call mg-translate-cmd,$(value $3)))))
229 endef
230
231 # TODO Provide a way to define static pattern rules.
232
233 # Procedure to generate bar.  Now $@ is bar, so we say $@.g for bar.g.
234 # If an override:
235 #     1. Complain.  (Should we stop "`bar' is up to date" using @:; ?)
236 #     2. This would be the place to clear bar's metadata if we wanted to.
237 # Otherwise, check prereqs, command, and nonexistence to decide whether bar
238 # needs to be regenerated.  If so:
239 #     1. Note that the genfile is about to change.
240 #     2. Echo the command being run.
241 #     On error, skip to 8:
242 #        3. Open the new genfile bar.g.tmp for writing.
243 #        4. Run the command to bar.tmp, storing warnings in bar.g.tmp.
244 #        5. Store the command in bar.g.tmp.  Do it here to ensure that bar.g.tmp
245 #           is at least as new as bar.tmp.
246 #        6. If bar.tmp differs from bar:
247 #           a. Clear and touch bar.g (so bar doesn't become an override if we're
248 #              killed between steps 6b and 7).
249 #           b. Move in the new bar.
250 #        7. Now that bar is known to be up to date, move in the new bar.g.
251 #     8. Defensively remove both temporary files and exit with the exit code
252 #        from before the removal. (trap EXIT)
253 # If not:
254 #     1. If there were warnings, replay them.  (HMMM To stderr?)
255 # HMMM .tmp in displayed command looks ugly
256 define mg-rule-cmd
257         $(if $(filter $(mg-scout-oid)$(aname)$@,$($@@gq)),\
258                 $(info mgear: warning: Manually created/modified file at $@ overrides rule.)\
259         ,$(call gload,$@)$(if $($@@gq)$(if $(wildcard $@),,TARGET-DNE)$(mg-check-cmd),\
260                 $(eval $@@gloaded:=)$(info $1)\
261                 @trap 'rm -f $@.tmp $@.g.tmp' EXIT &&\
262                 exec 3>$@.g.tmp &&\
263                 set -o pipefail && { $(mg-run-cmd) | tee /dev/fd/4 | $(mg-wrap-warnings) >&3; } 4>&1 &&\
264                 $(mg-assign-cmd) >&3 &&\
265                 $(mg-maybe-move-target) &&\
266                 mv -f $@.g.tmp $@.g\
267         ,$(if $(@warnings),\
268                 $(info $1 # mgear warning replay$(nl)$(@warnings))\
269         )))
270 endef
271
272 # If the command changed, we must regenerate.
273 # HMMM What if the working directory changes?  Most likely, the command will
274 # also change and mgear will do the right thing. 
275 mg-check-cmd=$(if $(call streq,$1,$(@cmd)),,COMMAND-CHANGED)
276
277 # Pieces of mg-generate that I factored out to make mg-generate more readable.
278 mg-assign-cmd=echo $(call sq,$(call fmt-make-assignment,@cmd,$1))
279 mg-run-cmd={ ($1) 2>&1 && { [ -r $@.tmp ] || { echo 'mgear: error: Command for $@ succeeded without creating it!'; false; }; }; }
280 mg-wrap-warnings=sed -re '1s/^/@warnings:=$$(empty)/; 1!s/^/@warnings+=$$(nl)/'
281 # Drat bash's lack of precedence between || and &&.  Extra braces necessary.
282 mg-maybe-move-target={ cmp -s $@ $@.tmp || { echo >$@.g && mv -f $@.tmp $@; }; }
283
284 # Just add additional prerequisites.  This cannot add prerequisite patterns to
285 # an implicit rule, but it can add specific prerequisites to an individual use
286 # of an implicit rule.  Currently, mgear picks up the target's prerequisites
287 # from make, so this just attaches the given prerequisites to the target, but
288 # the implementation might change in the future.
289 mg-define-prereq=$(eval $1: $2)
290
291
292 # CLEAN
293
294 # A command that finds all genfiles in a given directory and deletes them and
295 # the corresponding generated files.  If a file is overridden, only the genfile
296 # is deleted.
297 #
298 # Example:
299 #
300 # clean:
301 #         $(call mg-clean-cmd,.)
302 # .PHONY: clean
303
304 define mg-clean-cmd
305         @exec 3>&1 &&\
306         find $1 -name '*.g' | while read gf; do\
307         f="$${gf%.g}" &&\
308         if ! [ "$$f" -nt "$$gf" ]; then\
309                 echo "rm -f '$$f'" >&3 && echo "$$f";\
310         fi &&\
311         echo "$$gf";\
312         done | xargs rm -f
313 endef
314
315
316 # DEPENDENCY-LOGGING COMMANDS
317
318 mg-dlc-static-run-oid:=$(mg-genfile-oid)
319
320 # $(call mg-define-rule-dlc,target,static-prerequisites,cmd,dep-converter)
321 # Analogue of mg-define-rule for a dependency-logging command.
322 # I haven't decided on the format for the dep-converter yet.
323 define mg-define-rule-dlc
324 $(eval 
325
326 # FINISH
327
328 # Rule for the target.  Set $(bar@gdeps) to all prerequisites.  Apply the new
329 # prerequisites.  The command script always runs.  Finally, do the static run
330 # and then the first dynamic check.
331 $1: $(call mg-prereq-predict,$1,$2) $2 MG-FORCE $(mg-dlc-static-run-oid)$(aname)$$$$@.g  $$$$(call dlc-next-dchk,$$$$(target))
332 # TODO: Move the finished file into place or something
333 #       $$(call mg-rule-cmd,$3)
334 )
335 endef
336
337 #mg-dlc-next-dchk=$(call mg-dlc-next-dchk-1,$1,$(newoid))
338 #mg-dlc-next-dchk-1=$(eval $2@opr=$$(call mg-dlc-dchk-pr,$1,$2))$2
339 #
340 ## $(call mg-dlc-dchk-pr,target,oid)
341 #mg-dlc-dchk-pr=$(call mg-dlc-dchk-pr-1,$1,$2,$(call next-unchecked-prereq,$($1@deps)))
342 ## $(call mg-next-unchecked-prereq,foo.c bar.h baz.h)
343 #mg-next-unchecked-prereq=$(firstword $(foreach p,$1,$(if $($p@checked),,$p)))
344 #define mg-dlc-dchk-pr-1
345 #$(if $3,$(call mg-dlc-drun,$1,$2,$3) $(call mg-dlc-next-dchk,$1),)
346 #endef
347 #
348 ## $(call mg-dlc-drun,target,oid,prereq). FINISH
349 #define mg-dlc-drun
350 #$(eval 
351 #$2$(aname)$3@opr:=$3
352 #$2$(aname)$3@ocmd:=
353 #)$2$(aname)$3
354 #endef
355 #
356 #
357 ## END
358
359 # We don't want anything we defined to become the default goal.
360 .DEFAULT_GOAL := $(mg-orig-default-goal)