Blag

He's not dead, he's resting

GNU ‘make’ Target Specific Variables are Dumb

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…

5 responses to “GNU ‘make’ Target Specific Variables are Dumb

  1. ant July 22, 2008 at 10:16 pm

    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.

  2. Ciaran McCreesh July 22, 2008 at 10:23 pm

    You can’t even use them in automake since automake doesn’t support GNU make syntax…

  3. ant July 23, 2008 at 12:33 am

    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.

  4. Dodo November 15, 2008 at 4:35 pm

    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.

  5. Rahul October 12, 2009 at 7:23 am

    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)

Leave a reply to Ciaran McCreesh Cancel reply