bug-make
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[bug #59956] Recipes inside conditionals can break the parser


From: Paul D. Smith
Subject: [bug #59956] Recipes inside conditionals can break the parser
Date: Wed, 27 Jan 2021 16:26:00 -0500 (EST)
User-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36

Follow-up Comment #1, bug #59956 (project make):

There is one real bug here, but everything else is just an understandable
consequence of that same bug.

The real bug is that the "else" that is part of a recipe, is being interpreted
by make as an "else" match for the ifeq.

This is because while make is parsing the "not-taken" side of the
if-statement, it is not interpreting the content it sees.  Because it's not
interpreting that content, it doesn't realize that the "else" here is indented
by a TAB in the context of a rule and it assumes that the "else" is supposed
to be attached to the "ifeq".

Fixing this without some heuristic is basically impossible.  In order to allow
this to work, make would need to be tracking enough of the "not-taken" side of
the if-statement to know whether the "else" is in the context of a recipe or
not.  But, people often use "ifeq" to disable entire sections of a makefile,
some of which might not even be valid make syntax.  Also consider if the
.RECIPEPREFIX was modified _inside_ of the ifeq statement, to something
else...!!

We can "fix" it by adding a simplifying heuristic; perhaps something like:
"conditional statements are only recognized if they are not prefixed by the
recipe prefix in effect when the if-statement started".  That is not perfect
but it's probably good enough.

All the other items mentioned here are not really bugs; if you remove the
lines starting with *ifeq* through *else* (and the *endif* of course) and just
look at the resulting makefile, which is what make is seeing, it's pretty
obvious why you get the behavior you do in all cases.

Adding *.ONESHELL:* makes it "work" because the part of the if-statement after
the else is a interpreted as a recipe attached to *.ONESHELL:* which is not an
error although it is a no-op.  In other words this:


.ONESHELL:
ifeq (1,0)
test:
        @if [ "asd" == "123" ]; then
                echo "true"
        else
                echo "false"
        fi
endif


is equivalent to writing this:


.ONESHELL:
                echo "false"
        fi


which is a valid makefile, because we allow any target including a special
target like *.ONESHELL:* to have a recipe; obviously the recipe is invalid
shell syntax but that doesn't matter since the recipe is never run.

When you add a variable assignment after the *.ONESHELL:* and before the
*ifeq*, now you see this:


.ONESHELL:
aaa = 1
                echo "false"
        fi


which is now clearly an invalid makefile.  And if you add another *.ONESHELL:*
you'll get this:


.ONESHELL:
aaa = 1
.ONESHELL:
                echo "false"
        fi


which is, again, just fine.

If you add a character after *else*, then it's no longer a valid *else*
command for make's *ifeq*, so it's ignored.  In make conditionals are grouped
by _word_ (whitespace separated) so *else:* is considered a single token which
does not equal *else*, it's not considered two tokens, an *else* and a *:*.

If you add a space after the *else* before more text, then it IS a valid
*else* command, and you'll get an error that there's extraneous text after the
*else*.

Finally, adding *.RECIPEPREFIX* doesn't really fix anything.  It only changes
the error you get.  If you write:


.ONESHELL:
.RECIPEPREFIX = >
ifeq (1,0)
test:
        @if [ "asd" == "123" ]; then
                echo "true"
        else
                echo "false"
        fi
endif


then the makefile make sees is:


.ONESHELL:
.RECIPEPREFIX = >
                echo "false"
        fi


You won't get an error about recipe commencing before first target because the
*echo* line is _not a recipe_; it doesn't begin with the *.RECIPEPREFIX*
character.  Instead you'll get a _missing separator_ error because these lines
are not valid make directives.

    _______________________________________________________

Reply to this item at:

  <https://savannah.gnu.org/bugs/?59956>

_______________________________________________
  Message sent via Savannah
  https://savannah.gnu.org/




reply via email to

[Prev in Thread] Current Thread [Next in Thread]