bug-make
[Top][All Lists]
Advanced

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

RE: Cygwin make thinks a statement can be neither true nor false....


From: Dave Korn
Subject: RE: Cygwin make thinks a statement can be neither true nor false....
Date: Tue, 20 Apr 2004 18:30:40 +0100

> -----Original Message-----
> From: Paul Smith On Behalf Of Paul D. Smith
> Sent: 20 April 2004 16:44

[  This is getting off topic for the cygwin list, and unless I've managed to
spot any *real* bugs yet, it's not very OT for the bug-make list either; if
we want to carry on further we should perhaps take it to private mail or to
the help-make list, though I'm not subbed to any of the make lists.  ]

> %% "Dave Korn" <address@hidden> writes:
> 
>   >> I would've expected it to complain about a bad 
> substition reference,
>   >> ie. it's missing an "=".
> 
>   dk>   Or at least do anything, rather than nothing!
> 
> If you enable --warn-undefined-variables then you'll get a warning.

  Ah, thanks!  That's such a good option I think I'm about to alias it right
into all my make commands.  BTW, did I discuss the difficulty in determining
whether a variable is undefined or empty?  That may not seem like a
meaningful concept, but I want to know whether my makefile was correctly
invoked but the variable definition was empty, such as

make -f makefile FOO= all

or whether the invocation was incorrect by forgetting to specify a value for
FOO,

make -f makefile all

  At the moment, "ifndef FOO" succeeds equally in both cases.

> Well, this has been true of every version of make since make was
> invented 30+ years ago, not to mention required by the POSIX standard,
> so... a little late to worry about it now :).

  Heh, I know.  But any changes that could make less damn obfuscatory than
it already is would be great.  It's really in the error of diagnostics and
reporting that make has serious problems, presumably at least some of which
could be fixed without altering its behaviour?

  I mean, look at this:  (I'm not going to call it a bug _just_ yet, because
things that seem utterly unreasonable to me *keep* on turning out to be the
expected and desired behaviour!)

-------------------------------------------
address@hidden /davek/test/mk-test/test5> ls
makefile1  makefile2
address@hidden /davek/test/mk-test/test5> cat makefile1

$(warning one one)
ifne ($(VARIABLE), anything)
$(warning two two)
BUILDDIR_EXTRA=-boot
else
$(warning three three)
endif

all:
        echo "So what's that all about then eh ?"

address@hidden /davek/test/mk-test/test5> cat makefile2

$(warning one one)
ifeq ($(VARIABLE), anything)
$(warning two two)
BUILDDIR_EXTRA=-boot
else
$(warning three three)
endif

all:
        echo "So what's that all about then eh ?"

address@hidden /davek/test/mk-test/test5> make -f makefile1
makefile1:2: one one
makefile1:3: *** missing separator.  Stop.
address@hidden /davek/test/mk-test/test5> make -f makefile1 VARIABLE=anything
makefile1:2: one one
makefile1:3: *** missing separator.  Stop.
address@hidden /davek/test/mk-test/test5> make -f makefile1 
VARIABLE=anything_else
makefile1:2: one one
makefile1:3: *** missing separator.  Stop.
address@hidden /davek/test/mk-test/test5> make -f makefile2
makefile2:2: one one
makefile2:7: three three
echo "So what's that all about then eh ?"
So what's that all about then eh ?
address@hidden /davek/test/mk-test/test5> make -f makefile2 VARIABLE=anything
makefile2:2: one one
makefile2:4: two two
echo "So what's that all about then eh ?"
So what's that all about then eh ?
address@hidden /davek/test/mk-test/test5> make -f makefile2 
VARIABLE=anything_else
makefile2:2: one one
makefile2:7: three three
echo "So what's that all about then eh ?"
So what's that all about then eh ?
address@hidden /davek/test/mk-test/test5>
-------------------------------------------

  Now where's the sense in that?  How can it be that the semantics of the
conditional operator affects the validity of the otherwise-identical syntax?
Every time make gives me that same old error message, I just want to scream
at it

 " WHAT THE HELL KIND OF SEPARATOR ARE YOU EVEN TALKING ABOUT, YOU
DELUSIONAL MANIAC? "

>   dk> Considering the close conceptual relationship between shell
>   dk> variables and make variables, and the way they get exported and
>   dk> imported to each other, it just seems like a mistake to try and
>   dk> pretend they're decoupled to such an extent they could be
>   dk> incompatibly named.
> 
> I don't agree that there is a close conceptual relationship.  
> In fact, I
> think it's very important to not view make variables that way.

  Well, IMO there is a confused and unclear relationship.  In some ways
they're tightly coupled, and in other ways the links are broken.  

> Make does _NOT_ export every variable into the environment.  

  I never said that; I said that the two do not map to clearly distinct
concepts, but to overlapping sets.

> Make treats
> the environment variable space and its internal variable space as two
> distinct entities, and makefile writers are well advised to consider
> them that way as well.
> 
> When make starts up it "imports" all of the environment variables as
> make variables, yes.  And, the user can request that certain variables
> be exported from the make variable space into sub-processes' 
> environment
> variable space, through "export".
> 
> But no variable which is not so treated will be sent to subprocesses.

  So, it treats the environment variable space and it's internal space as
'distinct', but hardly separate: because the internalvars are initiated from
the env vars at program startup, it's natural to think of the internal vars
as a kind of 'derived class' from the environment, with some 'overridden
members'.  Overall, ISTM that make treats its variables in the same way as a
shell: it imports whatever is handed down to it from the parent, but it
doesn't export them unless told.

  So the way that a) it inherits them from its parent, just like a
(sub)shell; b) it allows them to be set new values, just like you may in a
sub shell; c) it exports them to a subprocess when told to but not when not
told to, just like a shell; and d) it exports them to subprocesses by
converting them into shell variables, all make me think that the two
concepts are really very similar indeed.  The divergences between their
behaviour are minor, the similarities major, the implementation of
internalvars is dependent on the underlying shell vars to function, and the
namespace between them is shared, at least at program startup time.

  However, this is all diverging into rather abstract discussion of
architectural concepts and structural separation to no great purpose, so
I'll leave it there.

>   dk> -------> snip!<-------
>   dk>    Likewise variables defined on the command line are 
> passed to the
>   dk> sub-`make' through `MAKEFLAGS'.  Words in the value of 
> `MAKEFLAGS' that
>   dk> contain `=', `make' treats as variable definitions just 
> as if they
>   dk> appeared on the command line.  *Note Overriding 
> Variables: Overriding.
>   dk> -------> snip!<-------
> 
>   dk> Oh no it doesn't: neither for variables defined on the initial
>   dk> make command line, nor for variables passed to a recursive
>   dk> submake.  Here's my sample makefile:
> 
> Your test doesn't test the behavior the manual is discussing.
> 
> All this portion of the manual says is that if you do this:
> 
>   $ MAKEFLAGS='FOO=bar' make
> 
> or if you have this:
> 
>     submake: ; $(MAKE) MAKEFLAGS='FOO=bar'
> 
> that the variable FOO will be set to "bar" in the make that gets
> invoked.

  Ah, that's clearer, but the docs are misleading.  When it says "variables
defined on the command line are passed to the sub-make through MAKEFLAGS",
it leads me to expect that the sub-make will actually be able to *find*
those definitions in MAKEFLAGS.  It should perhaps say something like

" Likewise, variables defined on the command line are passed to the sub-make
by their temporary inclusion in 'MAKEFLAGS'.  At startup time, before the
(sub-)makefile is parsed, make searches 'MAKEFLAGS' for words that contain
'='; these words are treated as variable definitions, just as if they
appeared on the command line, and then their definitions are stripped out
from the MAKEFLAGS variable before the makefile is read. "

  The thing is that 'MAKEFLAGS' is pretty unique in not having the same
value in the submake that it was passed down with from the parent make.  To
say that these definitions are passed in MAKEFLAGS without mentioning "But
they aren't ever visible in MAKEFLAGS by the time you get to see it, unlike
everything else that is passed down in other variables, and unlike anything
else that is passed down in 'MAKEFLAGS'" confuses me.  IIUIC, 'MAKEFLAGS' is
pretty unique in being altered by make so that the value it takes during
reading the makefile isn't the same as the value it had when the subprocess
was launched.  There's no warning that the value will have been tampered
with.  The existing documentation also makes no sense where it says:

------->snip!<-------
   A similar variable `MFLAGS' exists also, for historical
compatibility.  It has the same value as `MAKEFLAGS' except that it
does not contain the command line variable definitions, 
------->snip!<-------

  Because after all, MAKEFLAGS doesn't "contain the command line variable
definitions" either!  When and where could we ever actually *observe* a
difference between the contents of MAKEFLAGS and of MFLAGS?

  Oh, while we're here, I've been browsing your website (for which much
thanks, you have some good articles there), and I have an observation to
make on the subject of
http://savannah.gnu.org/bugs/?func=detailitem&item_id=1454 "[bugs #1454]
$(if ) cannot parse semi-colons inside arguments" which I stumbled across
independently today: the followup comment says 

------->snip!<-------
The real issue here is that make sees the semicolon as a part of a target
definition, like this: 

all: ; @echo hi 

Instead of noticing that the semicolon is contained within a variable
reference, if it appears on the context where it could be a target
definition make treats it as the end of the target definition. 

Note this won't happen in other contexts, like variable assignement, where
make doesn't treat ";" as a special character.
------->snip!<-------

  I'd just like to point out that it isn't only $shell that is affected:
$error and $warning are also affected.  I also notice that it works fine if
you escape the semicolon:

------->snip!<-------
address@hidden /davek/test/mk-test/test4> ls
makefile1  makefile2  makefile3
address@hidden /davek/test/mk-test/test4> cat makefile1

$(warning Top of makefile show some vars $(PWD) $(PWD))

.PHONY: all
all:
        echo Hi then

address@hidden /davek/test/mk-test/test4> cat makefile2

$(warning Top of makefile show some vars $(PWD) ; $(PWD))

.PHONY: all
all:
        echo Hi then

address@hidden /davek/test/mk-test/test4> cat makefile3

$(warning Top of makefile show some vars $(PWD) \; $(PWD))

.PHONY: all
all:
        echo Hi then

address@hidden /davek/test/mk-test/test4> make -f makefile1
makefile1:2: Top of makefile show some vars /davek/test/mk-test/test4
/davek/tes
t/mk-test/test4
echo Hi then
Hi then
address@hidden /davek/test/mk-test/test4> make -f makefile2
makefile2:2: *** unterminated call to function `warning': missing `)'.
Stop.
address@hidden /davek/test/mk-test/test4> make -f makefile3
makefile3:2: Top of makefile show some vars /davek/test/mk-test/test4 ;
/davek/t
est/mk-test/test4
echo Hi then
Hi then
address@hidden /davek/test/mk-test/test4>
------->snip!<-------

  I had been guessing that this was because the shell echo command was used
to display output and escaping the semicolon prevented the shell getting
confused, but I guess not after all!

  Oh, if you'd like a contribution for your "Rules of makefiles" page,
please permit me to offer:

------->snip!<-------
6.  Keep your imports and exports together.

  It can be very hard to know what variables are set where, exported where,
imported where and used where when you get a number of levels of make
recursion, any of which can define or redefine things.
  For maintainability and clarity of coding style, it's a good idea to begin
every makefile with a set of ifdefs to test that all the required variables
have values supplied, and error out if not; and to bunch together all the
export statements (if any) in one group directly above the first recursive
make invocation (if any) in the file, even if that means splitting out a
line such as "export FOO=bar" into two separate lines, one to assign FOO and
one to export it.
  This technique makes your files semi-self-documenting: you know at once
the entire set of variables that are meaningful to any given sub-level of
makefile, and you know at once what variables it has to set correctly for
the sake of the lower-level makefiles.  By comparing the ifdef tests at the
top of a recursively-invoked makefile with the block of export statements
just before the recursive invocation in the parent makefile, any
discrepancies should jump out at you.
------->snip!<-------

  Errm... that's rather wordy compared to your existing entries, so I'll
leave it for you to summarize in your own words if you want.


    cheers, 
      DaveK
-- 
Can't think of a witty .sigline today....





reply via email to

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