shellmakefilebsdmake

How to define subroutines in a Makefile


I am working on a Makefile which has a¹ receipt producing some file using M4. It uses some complex shell constructions to compute macro values which have to be passed to M4. How can I organize code to avoid redundant declarations displayed in the following example?

M4TOOL= m4
M4TOOL+= -D PACKAGE=$$(cd ${PROJECTBASEDIR} && ${MAKE} -V PACKAGE)
M4TOOL+= -D VERSION=$$(cd ${PROJECTBASEDIR} && ${MAKE} -V VERSION)
M4TOOL+= -D AUTHOR=$$(cd ${PROJECTBASEDIR} && ${MAKE} -V AUTHOR)
M4TOOL+= -D RDC960=$$(openssl rdc960 ${DISTFILE} | cut -d ' ' -f 2)
M4TOOL+= -D SHA256=$$(openssl sha256 ${DISTFILE} | cut -d ' ' -f 2)

Portfile: Portfile.m4
    ${M4TOOL} ${.ALLSRC} > ${.TARGET}

¹ Actually a lot!


Solution

  • You should define pseudo-commands using the -c option of the shell, like this:

    PROJECTVARIABLE=sh -c 'cd ${PROJECTBASEDIR} && ${MAKE} -V $$1' PROJECTVARIABLE
    OPENSSLHASH=sh -c 'openssl $$1 $$2 | cut -d " " -f 2' OPENSSLHASH
    

    Note the use of $ or $$ to use bsdmake variable expansion or shell variable expansion. With these defintions you can reorganise your code like this:

    M4TOOLS+= -D PACKAGE=$$(${PROJECTVARIABLE} PACKAGE)
    M4TOOLS+= -D VERSION=$$(${PROJECTVARIABLE} VERSION)
    M4TOOLS+= -D AUTHOR=$$(${PROJECTVARIABLE} AUTHOR)
    M4TOOLS+= -D RMD160=$$(${OPENSSLHASH} rmd160 ${DISTFILE})
    M4TOOLS+= -D SHA256=$$(${OPENSSLHASH} sha256 ${DISTFILE})
    

    The result is arguably easier to read and maintain. When you write such scripts, remember to use error codes and stderr to report errors.

    PS: You can take a look at the COPYTREE_SHARE macro in /usr/ports/Mk/bsd.port.mk on a FreeBSD system. It illustrates well the technique.