XMake DEVELOP

XMake version: 0.93a
Release Date: 08/17/05
Copyright (c) 2003 Gregory Keranen. All rights reserved.

Contents

Gnu Make Issues Escaping Makefile Variables Escaped of Newlines in a Makefile are Translated to a Single Space Exported Makefile Variables Are Not Exported to $(shell ...) Chaining of Pattern Rules $(filter-out $(pattern),$(aList)) doesn't work with '%' in $(pattern) Trailing Spaces are Significant! Leading Spaces in Variable Assignments Piping Makefile Input to Make Make Updates Included Makefiles in the REVERSE Order of Appearance

Gnu Make Issues

There are many tricks to writing good makefiles that can only be learned by experience. Beyond studying the Gnu Make documentation, try the help-make email list: http://savannah.gnu.org/mail/?group=make In this section, I offer a few things I have learned about writing makefiles, and some limitations of Gnu Make that are not documented well, or at all, presently.

Escaping Makefile Variables

One of the trickier parts is understanding how $(eval ...) works and how to properly escape variables inside define statements and the commands to rules. If you write myMacro and use it with $(eval $(call myMacro,...)), then beware that you must escape makefile variables assigned and later referenced within the scope of the define statement; otherwise the assigned value will not be evaluated properly: define myMacro _XM_externalTargets:= ...$$(_XM_externalTargets)... endef Another case comes up when you try to reference automatic makefile variables that have shell equivalents, such as '$?', in the commands of a rule: myRule: @myShellCommand;\ if ! test $$$$? -eq 0 ; then\ exit 1;\ fi;\ # ...

Escaped of Newlines in a Makefile are Translated to a Single Space

The POSIX specification states that escaped newlines should be translated to a single space, EXCEPT when they occur in the commands of a makefile rule. Gnu Make 3.80 has a bug #1332 in this regard: newlines in commands ARE translated to spaces. The following makefile example illustrates what to beware of and how to strip extra spaces. # Makefile .PHONY: test test: @echo beware="$(call beware,<- leading space)"; @echo workaround="$(call workaround,<- no leading space)"; define beware $(if $(1),\ $(1)) endef define workaround $(strip $(if $(1),\ $(1))) endef #eof [greg@p3 junk]$ make beware= <- leading space workaround=<- no leading space

Exported Makefile Variables Are Not Exported to $(shell ...)

It is documented that exported makefiles are exported to 'sub-makes' I was expecting that export directive also exports values to $(shell ... ) but apparently this is not the case. # Makefile export FOO1:=1 result:=$(shell echo $$FOO1) $(warning FOO1=$(result)) result:=$(shell export FOO2=2;echo $$FOO2) $(warning FOO2=$(result)) foo:; #eof [greg@p3 junk]$ make Makefile:5: FOO1= Makefile:8: FOO2=2 make: `foo' is up to date.

Chaining of Pattern Rules

This subject is not well covered in the Gnu Make documentation and it is unclear if Gnu Make 3.80 behaves properly in this regard. It appears that rule chaining only works one level deep. According to Gnu Make documentation, the way make works in deciding if a pattern rule applies is: if all prerequisites exist or can be made, then the rule applies if several rules 'can' apply, the one used is the first appearing in the makefile order If this is correct, then I expect make to use my explicit rule for 'foo.b' instead of the match-anything rule which comes later. I am unable to build target 'foo' in the makefile below. #makefile %: %.a touch $@ %.a: %.b touch $@ foo.b: touch $@ %: $(error No rule for target: $@)) #eof [greg@p3 src]$ make foo Makefile:12: *** No rule for target: foo. Stop. If I eliminate the middle pattern rule, it works fine. #makefile %: %.a touch $@ foo.a: touch $@ %: $(error No rule for target: $@)) #eof [greg@p3 src]$ make foo touch foo.a touch foo From the manual: > Ordinarily, a file cannot be intermediate if it is mentioned in the makefile as a target or prerequisite. However, you can explicitly mark a file as intermediate by listing it as a prerequisite of the special target `.INTERMEDIATE'. This takes effect even if the file is mentioned explicitly in some other way. Here is a solution using: .INTERMEDIATE: foo.a This is confusing since 'foo.a' is not mentioned in my makefile as a 'target or prerequisite'. Here's the modified makefile that works. #makefile %: %.a touch $@ %.a: %.b touch $@ foo.b: touch $@ %: $(error No rule for target: $@)) .INTERMEDIATE: foo.a #eof [greg@p3 src]$ make foo touch foo.b touch foo.a touch foo rm foo.a

$(filter-out $(pattern),$(aList)) doesn't work with '%' in $(pattern)

I have used variable filter_out_pattern for convenience only; results are the same if you use $(patsubst %,%/\%,$(subDir)) instead. This appears to be a bug since $(filter ...) and $(filter-out ...) do, in general, accept patterns passed as variables. # Makefile dirs:=/dir1 /dir1/dir2 subDir:= /dir1 filter_out_pattern:=$(patsubst %,%/\%,$(subDir)) foo: @echo dirs=$(dirs) @echo filter_out_pattern=$(filter_out_pattern) @echo after filtering: dirs=$(filter-out $(filter_out_pattern),$(dirs)) @echo should be: dirs=$(filter-out /dir1/%,$(dirs)) #eof [greg@p3 junk]$ make dirs=/dir1 /dir1/dir2 filter_out_pattern=/dir1/% after filtering: dirs=/dir1 /dir1/dir2 should be: dirs=/dir1

Trailing Spaces are Significant!

It is easy to assign variables with invisible trailing spaces If you always end assignment statements with a comment, it makes it easier to catch # TRAILING SPACE: XM_externalTargets+=/foo # # NO TRAILING SPACE: XM_externalTargets+=/foo#

Leading Spaces in Variable Assignments

Makefile variable assignments ignore any leading space on the right hand of the '=' sign while retaining all white-space characters up to the end of a line. To assign a variable to a single space, do this: x:=# space:=$(x) # Leading spaces in command-line variable assignments are lost, just as makefile variables behave. There is no way to fix this, unlike the above trick for use inside a makefile. make FOO=' ' # FOO is considered 'empty'

Piping Makefile Input to Make

This kind of thing can be useful sometimes, to query makefile variables from the shell: echo '$(shell echo $(MAKEFLAGS) 1>&2)' | make -f- <options> echo 'x:;@echo $(XM_outfiles)' | xmake -f-

Make Updates Included Makefiles in the REVERSE Order of Appearance

Given a Makefile which includes two other generated makefiles in this order: include $(CURDIR)/Makefile1.mko include $(CURDIR)/Makefile2.mko The order of processing is seen with the command: make -d Which produces the following output: Reading makefile `Makefile'... Reading makefile `/usr/local/apache/htdocs/test/Makefile1.mko' (search path) (no ~ expansion)... Reading makefile `/usr/local/apache/htdocs/test/Makefile2.mko' (search path) (no ~ expansion)... Updating makefiles.... Considering target file `/usr/local/apache/htdocs/test/Makefile2.mko'. Looking for an implicit rule for `/usr/local/apache/htdocs/test/Makefile2.mko'. ... No need to remake target `/usr/local/apache/htdocs/test/Makefile2.mko'. Considering target file `/usr/local/apache/htdocs/test/Makefile1.mko'. Looking for an implicit rule for `/usr/local/apache/htdocs/test/Makefile1.mko'. This behavior caused me much confusion trying to figure out why exported variables from custom automake file XMakefile.mks were not being reflected in .mkdo file generation. Example: %.phpx are php source files %.html files are generated from %.html.phpx files %.phpx.mkdo dependency makefiles are generated from %.phpx files %.mko makefiles are generated from %.mks meta-makefiles XMakefile.mks exports the following variable: export XMAKE_LANG:=en XMakefile.mk can be considered the main makefile (actually there are others, but they are not important to this example) which includes other makefiles in this order: # .mko files, auto-generated from .mks files -include $(XM_customAutoMakefiles) # .mkdo files, auto-generated per XMExtension rules -include $(XM_dependencyAutoMakefiles) This is the trace of how make behaves when you try to build target: xmake index.html - INITIAL STATE XMakefile.mks: export XMAKE_LANG:=es index.html.phpx: includes navbar.html navbar.html.phpx: includes file: 'navbar_item_home'.$_ENV['XMAKE_LANG']; navbar_item_home.en: contains the text: 'home' navbar_item_home.es: contains the text: 'origen' THESE FILES DO NOT EXIST: XMakefile.mko index.html.phpx.mkdo navbar.html.phpx.mkdo index.html navbar.html - INITIAL BUILD 0. Makefile.mk is read, which FAILS to include non-existent makefiles in this order: include XMakefile.mko include index.html.phpx.mkdo navbar.html.phpx.mkdo GENERATE included makefiles in REVERSE ORDER READ: 1. XMakefile.mko is generated as PREREQUISITE of navbar.html.phpx.mkdo 2. navbar.html.phpx.mkdo is generated NOTE: $_ENV['XMAKE_LANG'] is not set in makefile environment yet since XMakefile.mko is not yet included The default value 'en' for XMAKE_LANG will be used navbar.html : navbar.html.phpx.mkdo navbar.html.phpx.mkdo : navbar.html.phpx navbar_item_home.en XMakefile.mko 3. index.html.phpx.mkdo is generated index.html : index.html.phpx.mkdo index.html.phpx.mkdo : index.html.phpx navbar.html XMakefile.mko RE-LOAD all makefiles, after generation of included makefiles NOTE: $XMAKE_LANG is now set to 'es' in makefile environment GENERATE PREREQUISITES and TARGETS, IN ORDER 4. PREREQUISITE navbar.html is generated, using navbar_item_home.es: navbar = 'origen' 5. TARGET index.html is generated, using $_ENV['XMAKE_LANG'], including navbar.html lang = 'es' navbar = 'origen' - EDIT XMakefile.mks: export XMAKE_LANG:=en - SECONDARY BUILD 0. Makefile.mk is read, which includes makefiles in this order: include XMakefile.mko (old out of date version) XMAKE_LANG:=es include index.html.phpx.mkdo (old out of date version) index.html : index.html.phpx.mkdo index.html.phpx.mkdo : ... navbar.html XMakefile.mko include navbar.html.phpx.mkdo (old out of date version) navbar.html : navbar.html.phpx.mkdo navbar.html.phpx.mkdo : ... navbar_item_home.es XMakefile.mko GENERATE included makefiles in REVERSE ORDER READ: 1. XMakefile.mko is updated as PREREQUISITE of navbar.html.phpx.mkdo NOTE: makefile is updated but NOT RE-LOADED! export XMAKE_LANG:=es OLD value is still in the current environment! 2. navbar.html.phpx.mkdo is updated as PREREQUISITE of navbar.html (which is prerequisite of index.html.phpx.mkdo) navbar.html.phpx generates .mkdo file using OLD value: $_ENV['XMAKE_LANG'] == 'es' navbar.html : navbar.html.phpx.mkdo navbar.html.phpx.mkdo : ... navbar_item_home.es XMakefile.mko 3. navbar.html is updated as PREREQUISITE of index.html Uses OLD $_ENV['XMAKE_LANG'] == 'es' => navbar_item_home.es navbar = 'origen' THIS IS THE CRUX OF THE PROBLEM: navbar.html is only generated now because it is an intermediate of another makefile: index.html.phpx.mkdo HOWEVER: navbar.html is also a TARGET which should be build in a correct up to date makefile context PROBLEM: since navbar.html was a PREREQUISITE of another included makefile, it will NEVER get built in the correct, up to date makefile environment! 4. index.html.phpx.mkdo is updated Uses OLD $_ENV['XMAKE_LANG'] == 'es' (no significant consequence in this example) 5. RE-LOAD makefiles, generate TARGETS: Re-executing: /usr/local/bin/make -f /usr/local/apache/htdocs/common/xmake/config/XMakefile.mkh -r -d GNU Make 3.80 ... No need to remake target `/usr/local/apache/htdocs/gembroker/www/dev/XMake/common/navbar.html'. THIS IS AN ERROR - FILE SHOULD BE OUT OF DATE! No need to remake target `/usr/local/apache/htdocs/gembroker/www/dev/XMake/common/navbar.html.phpx.mkdo'. THIS IS AN ERROR - FILE SHOULD BE OUT OF DATE! QUESTION: What is the MORAL of this story? I think it is this: Never use a data from an automatically generated makefile as input to the automated generation of other makefiles! SOLUTION: export variables either: 1. From OUTSIDE the makefile environment; i.e., from the shell OR 2. From a MANUALLY generated makefile (such as XMakefile.mko) that will never be 'out of date'!