Blag
He's not dead, he's resting
GNU ‘make’ Target Specific Variables are Dumb
July 22, 2008
Posted by on Let’s say we have a Makefile using some target-specific variables:
$ cat Makefile foo = global b : foo = for-b all : d @cat d a : @echo a:$(foo) > a b : a @{ cat a ; echo b:$(foo) ; } > b c : a @{ cat a ; echo c:$(foo) ; } > c d : b c @cat b c > d
Then we can get different results depending upon how we make the ‘all’ target:
$ rm a b c d ; make a:for-b b:for-b a:for-b c:global $ rm a b c d ; make a b c d all a:global b:for-b a:global c:global
This is considered a feature:
There is one more special feature of target-specific variables: when you define a target-specific variable that variable value is also in effect for all prerequisites of this target, and all their prerequisites, etc. (unless those prerequisites override that variable with their own target-specific variable value).
In practice, it’s a pain in the ass. Using Quagmire, you’re supposed to be able to do this:
noinst_SHARED_LIBRARIES = libone.so libtwo.so libthree.so libone.so_SOURCES = one.c libone.so : LDFLAGS = -Wl,-soname,libone.so libone.so_LIBS = libthree.so libtwo.so_SOURCES = two.c libtwo.so : LDFLAGS = -Wl,-soname,libtwo.so libtwo.so_LIBS = libthree.so libthree.so_SOURCES = three.c
Which leads to some rather bizarre behaviour:
$ make clean all &>/dev/null ; scanelf -S libthree.so TYPE SONAME FILE ET_DYN libone.so libthree.so $ make clean libone.so &>/dev/null ; scanelf -S libthree.so TYPE SONAME FILE ET_DYN libone.so libthree.so $ make clean libtwo.so &>/dev/null ; scanelf -S libthree.so TYPE SONAME FILE ET_DYN libtwo.so libthree.so $ make clean libthree.so &>/dev/null ; scanelf -S libthree.so TYPE SONAME FILE ET_DYN libthree.so
I can think of a few not very nice ways of getting around this. Hopefully someone will come up with something better…
Automake reminds me of INTERCAL. I’ve come across some awful unmaintained C code before (gens-gtk) and made it compile, sometimes even work properly, only to give up crying when I entered the bitrotting autohell wasteland that came with it.
You can’t even use them in automake since automake doesn’t support GNU make syntax…
Well there’s the other problem with it, there’s so many separate programs and files for compiling a source tree that it won’t all fit in my tiny mind. After reading the info (urgh) pages for days on end I still haven’t got a clue… maybe I’m not 1337 enough to be doing that sort of thing.
I was able to put the Target Specific Variables to some use as follows for recursively executing various Make commands like make, make clean, make debug.
—
target :=
subdirs := src test
.PHONY: $(subdirs) all clean
#For all target is NULL
all : $(subdirs)
clean : target := clean
clean : $(subdirs)
debug : target := debug
debug : $(subdirs)
#Recursively run the make through the subdirs with the same target
$(subdirs):
$(MAKE) -C $@ $(target)
—
Is there anything to be careful about in such use of TSVs instead of using some combination of
CLEANSUBDIRS = $(addsuffix .clean, $(SUBDIRS))
$(CLEANSUBDIRS):
@cd $(basename $@) ; $(MAKE) clean
for each target to avoid duplication of code.
I just realized one could use MAKECMDGOALS also but it is recommended to use this variable only when necessary:
subdirs := src test
.PHONY: all
all : $(subdirs)
.PHONY: clean
clean : $(subdirs)
.PHONY: debug
debug : $(subdirs)
#Recursively run the make through the subdirs with the same target
.PHONY: $(subdirs)
$(subdirs):
$(Q)$(MAKE) -C $@ $(MAKECMDGOALS)
But this is a little inflexible. I did like to use the TSV approach if it has no issues.
Actually it worked very well for me while trying to compile 32 bit and 64 bit apache modules
build-64: XS=$(APXS64)
build-32: XS=$(APXS)
build-%:
cd $(SRC_DIR)-$* && $(XS) -S $(FLAGS) -o $(MODULE) $(SRC)