I have a Makefile which needs to run Python 2 and 3 with same recipe for identical process. The only difference should only be the command names of Python versions. Since they are virtually the same, I rather not having two recipes just for sake of different command names.

Yesterday, I pushed an update that wasn’t quite okay by me, but I was working on a fix for a bug. Although it’s not really an urgent situation, still, the feeling of getting everything done and pushing out to the world as soon as possible got to me pretty good.

I am okay with using subst just not the part of expanding variable in target name. I feared that could be running into troubles someday. I am not good at writing Makefile, only learn as I go. Today, time finally allowed me sitting down and starting with an empty Makefile to study how it could be done better.

Here is the final result I am okay with:

py2=python2
py3=python3

if: if_py2 if_py3 if_unknown
if_py2 if_py3 if_unknown:
        @echo target is $@
        $(eval py_cmd = \
                $(if $(findstring py2,$@),\
                        $(py2),\
                        $(if $(findstring py3,$@),\
                                $(py3),\
                                $(error Do not know what to do \
                                        with $@)\
                        )\
                )\
        )
        @echo '$$py_cmd' is set to $(py_cmd)
        @echo

At first glance, it looks like terribly long, indeed, it is. The output would be:

$ make
target is if_py2
$py_cmd is set to python2

target is if_py3
$py_cmd is set to python3

Makefile:9: *** Do not know what to do with if_unknown.  Stop.

If could be just one-liner, but I prefer to have that error even the target names are specifically typed.

Another solution I could come up with is double expansion:

py2=python2
py3=python3

double_expand: double_expand_py2 double_expand_py3
double_expand_py2 double_expand_py3:
        @echo target is $@
        $(eval py = $(subst double_expand_,,$@))
        @echo '$$py' is set to $(py)
        $(eval py_cmd = $($(py)))
        @echo '$$py_cmd' is set to $(py_cmd)
        @echo

The variable py is expanded twice, first to get the name of the variable containing Python command, then expand it to get the command name. The output:

$ make
target is double_expand_py2
$py is set to py2
$py_cmd is set to python2

target is double_expand_py3
$py is set to py3
$py_cmd is set to python3

When I first tried to simplify during that commit, I was trying to get ifeq to work for me, but never could do that with eval. I think if that’s for shell variable, it would do, but I’d like to set to Make’s variable.