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.
.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
[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.
export FOO1:=1
result:=$(shell echo $$FOO1)
$(warning FOO1=$(result))
result:=$(shell export FOO2=2;echo $$FOO2)
$(warning FOO2=$(result))
foo:;
[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.
%: %.a
touch $@
%.a: %.b
touch $@
foo.b:
touch $@
%:
$(error No rule for target: $@))
[greg@p3 src]$ make foo
Makefile:12: *** No rule for target: foo. Stop.
If I eliminate the middle pattern rule, it works fine.
%: %.a
touch $@
foo.a:
touch $@
%:
$(error No rule for target: $@))
[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.
%: %.a
touch $@
%.a: %.b
touch $@
foo.b:
touch $@
%:
$(error No rule for target: $@))
.INTERMEDIATE: foo.a
[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.
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))
[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
XM_externalTargets+=/foo #
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=' '
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:
-include $(XM_customAutoMakefiles)
-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'!