makefilegnu-makebsdmake

How to programmatically define targets in GNU Make?


I am not aware of any way to define programatically targets in GNU Make. How is this possible?

Sometimes one can go away with alternate methods. The ability to define programatically targets in Makefiles is however a very important to write and organise complex production rules with make. Examples of complex production rules are found in the build system of FreeBSD or in Makefile libraries such as BSD Owl

The main differences between shell scripts and Makefiles are:

For instance, a very simple and useful pattern is the following:

build: pre-build
build: do-build
build: post-build

This presents the build target as a composite of three targets, one containing the actual instructions do-build and two other that are hooks, executed before and after do-build. This pattern is used by many build systems written for BSD Make, which incidentally allows programmatic definition of targets, so that one can write in a batch:

.for _target in configure build test install
.if !target(${_target})
${_target}: pre-${_target}
${_target}: do-${_target}
${_target}: post-${_target}
.endif
.endfor

The condition introduced by the .if/.endif block enables the user to use its own definition of any ${_target}.

What would be the translation of that snippet for GNU Make?


Solution

  • FWIW here is the make equivalent syntax for

    .for _target in configure build test install
    .if !target(${_target})
    ${_target}: pre-${_target}
    ${_target}: do-${_target}
    ${_target}: post-${_target}
    .endif
    .endfor
    

    Basically, you want make to see something like this snippet:

    build: pre-build
    build: do-build
    build: post-build
    

    and similarly for configure, test and install. This suggests a loop with an eval somewhere:

    define makerule =
      $1: pre-$1
      $1: do-$1
      $1: post-$1
    endef
    
    targets := configure build test install
    
    $(foreach _,${targets},$(eval $(call makerule,$_)))
    

    (to play with this, change eval to info). Careful with those closures!

    FWIW, here's the expansion of the foreach:

    Please note: I have to agree with all the other commenters: your pattern is bad make. If your makefile is not -j safe, then it is broken (missing dependencies).