Using conditional rules in a makefile

2020-05-20 09:02发布

问题:

I capture the intent of the Makefile in pseudo code, then indicate the issues I have. I'm looking for a Makefile which is more user friendly in a test environment. The correct usage of the Makefile is one of the below.

    make CATEGORY=parser TEST=basic.
    make ALL

If a user gives "JUST" the commands as indicated below, it should print a message saying "CATEGORY defined TEST undefined" and vice-versa

    make CATEGORY=parser
    make TEST=basic

I tried writing the Makefile in following ways, but it errors out:

    help:
        echo"Usage: make CATEGORY=<advanced|basic> TEST=<test-case>
        echo"       make ALL

    ifdef CATEGORY
        ifdef TEST
            CATEGORY_TEST_DEFINED = 1
        else
             echo "TEST not defined"
    else
        echo "CATEGORY not defined"
    endif



    ifeq ($(CATEGORY_TEST_DEFINED), 1)
    $(CATEGORY):
        cd $(PROJ)/$(CATEGORY)
        make -f test.mk $(TEST)
    endif

    ifdef ALL
    $(ALL):
         for i in `ls`
         cd $$(i)
         make all
    endif

The questions I have are:

  1. Whether the rules in a Makefile can be selective (using ifdef to select the rules and targets).

  2. echo doesn't work. echo should help the user with correct usage.

回答1:

The problem is that echo belongs to the shell; Make can pass it to the shell in a command, but Make cannot execute it. Use info instead:

ifdef CATEGORY
$(info CATEGORY defined)
else
$(info CATEGORY undefined)
endif

If you want the rules to be conditional:

ifdef CATEGORY
ifdef TEST
$(CATEGORY):
    whatever
else
$(info TEST not defined)
else
$(info CATEGORY not defined)
endif


回答2:

These lines are dubious:

help:
    echo"Usage: make CATEGORY=<advanced|basic> TEST=<test-case>
    echo"       make ALL

You need a space between echo and the string, and the string needs to be terminated:

help:
    echo "Usage: make CATEGORY=<advanced|basic> TEST=<test-case>"
    echo "       make ALL"

These lines are dubious:

ifdef CATEGORY
    ifdef TEST
        CATEGORY_TEST_DEFINED = 1
    else
         echo "TEST not defined"
else
    echo "CATEGORY not defined"
endif

Surely you need an endif before the second else? (Even if it is not syntactically mandatory, I'd recommend it.)

ifdef CATEGORY
    ifdef TEST
        CATEGORY_TEST_DEFINED = 1
    else
         echo "TEST not defined"
    endif
else
    echo "CATEGORY not defined"
endif

Additionally, make only executes commands such as echo is supposed to be when processing a target (rule). It won't execute echo there; it will simply object that you cannot define commands without them being actions for a target. Despite everything that GNU Make adds to a makefile, the language in a makefile is a declarative language and not a procedural language.


Another way of handling this is to define default values for the macros:

CATEGORY = advanced
TEST     = all

Define default values that do something semi-reasonable; let the user override the default if they want to. You can have a rule such as:

${CATEGORY}/${TEST}: ...dependencies...
    ...actions...

You can leave help as the first rule. I have some directories where the first rule is:

null:
    @echo "You must specify a target with this makefile!"

This is equivalent to what you have (except that make does not echo the command before running it, so I only see the message instead of the echo command line and the message; that's the @ at work). The makefile this comes from also has a rule all which is otherwise usually the most sensible first (default) rule.