tech-toolchain archive

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

Re: make -V default behavior change



First, this whole discussion was much too quick - it started at 15:11 UTC
and seems to have been completed with a resolution at 20:00 UTC (all Jun 19)
that's less than 5 hours, beginning to end.

That's too fast - any kind of rational technical discussion needs more
time than that, time to think, to experiment, and to consider alternatives
(and probably more than 2 people involved.)

Normally, a minimum of several days (at least) - sometimes several weeks,
before any conclusion is drawn from the discussions should be allowed.
5 hours is simply absurd.

Now to the actual issue:

To me, the whole thing is woefully underspecified.   From make(1) - about -V:

	Print make's idea of the value of variable,

What is this "the value"?   If it is a variable, it varies, right?  Otherwise
it would be a constant, and printing the value of a constant is typically
boring.

So given we have something that varies, exactly when in its life cycle
do we get to see this "the value" ?

Eg: I see in <bsd.own.mk> the following fragment 
...

	.for _t in CC CPP CXX FC OBJC
	ACTIVE_${_t}=   ${AVAILABLE_COMPILER:@.c.@ ${
		!defined(UNSUPPORTED_COMPILER.${.c.}) &&
		 defined(TOOL_${_t}.${.c.}) :? ${.c.} : }@:[1]}
		 SUPPORTED_${_t}=${AVAILABLE_COMPILER:Nfalse:@.c.@
		 ${ !defined(UNSUPPORTED_COMPILER.${.c.}) &&
		 defined(TOOL_${_t}-.${.c.}) :? ${.c.} : }@}
	.endfor

(where I wrapped the (one long) middle line for the purposes of this e-mail,
it is probably impossible to reconstruct the original, accurately, from this,
but that's OK, as the contents of that line are irrelevant to my question).

If given that (and that bsd.own.mk is included), I do

	make -V _t
		   (or make -v _t or make -dV -V _t or make -V \_t ...)

what do I expect to see?

Does anything even make sense?   And if not for this variable, why for
any others?  And how do I know which ones I can look at, and which not?
If I ask for .ALLSRC or .TARGET what do I get?  And how do I work out
from the documentation that whatever happens is correct?

Then the man page goes on to say (still about -V):

	Do not build any targets.

which is also perplexing - if we are not building anything, how can we
know that we have the correct value of a variable, particularly one that
is set from the results of running some external command

	VAR!=...

where the value returned may differ depending upon what make has already
produced.

All of this suggests to me that the whole notion of getting "the value of
a variable" is hopelessly broken, and that part of the reason for the
disagreement, is that there is no common ground as to what is really
needed, or why.

In cases where the objective is to extract some data to use (ie: not
debugging the Makefiles) that is, have make do something and obtain a result,
the mechanism looks simple to me: that's what make targets are for - you build
some target, and the result of that is that something happens - if that
"something" happens to be just some text string being written to stdout, rather
than new/modified files appearing then that is fine.  Doing it this way means
that the value produced can be exactly the value that is meaningful, as it
is designed by whoever wants/needs the value, rather than the coders of make,
who cannot possibly have any idea what is useful or needed for the purpose in
mind.

For debugging, "the value" of a variable seems like a particularly useless
feature, and is only tolerated as it is all that currently exists.
What is really wanted there is a way to trace the successive values of the
variable, and where they come from, so every time it is changed, the
developer can see it happening.   So, an option that tagged a variable
to be traced when set would be useful (the output needs to include the new
value assigned, and the line # and filename of the Makefile (or fragment)
that is making that change happen, or the external source -- command line, 
environment, MAKEFLAGS, ....)  Request that, in conjunction with making
some appropriate target (for cases where all that is wanted is to see what
happens during make initialisation, all that is needed is a dummy target that
depends upon nothing and makes nothing) and it should be possible to easily
observe what is happening to the variable, and why, and hence, find any error
in the Makefile much more easily.

Of course all this is complicated by make's somewhat strange concepts
of when the actual value of a variable is produced (hence the VAR= and
VAR:= difference, and that it is possible to have a variable set to an
"unexpanded" value which mysteriously simply gets expanded when used,
rather than when set.)


You can stop reading here, beyond is just a rant...  (but what is below might
explain why I will not be implementing any of what I just suggested...)


Once upon a time, make was a nice (fairly simple) tool, whose "one thing"
(as in "do one thing, and do it well") was working out what had been modified,
what needed to be updated because of that, and then making that update happen.

That's it.

To do that, it needs a specification of what depends upon what, and the
method used to make X out of Y and Z.   That is/was the Makefile.

Unfortunately, early make had a bunch of the methods compiled into the
binary - getting that out, and into external files was one good, and much
needed change.   Along with that comes an "include" mechanism, to allow
re-use of fragments of Makefiles (obviously more useful when there is
less built in, which in practice, most Makefiles want to do the same way.)
So that's another good change.   Pity that the various versions of make
failed to agree on a common syntax for such a simple operation...

And then, with today's environment, we can sensibly build multiple targets
in parallel, and as it is make that schedules the building (it knows what
results are needed for what other targets, that's its job) so the -j
option, and everything that is needed to support it is another good change.

And lastly, the ridiculous $@ $* $< ... names that make used (limited
addr space on pdp-11's so keep things small, and simple to parse...)
definitely needed to be given "proper" names - no-one (by which I mean, I)
could ever remember which of those idiot symbols meant what!

Aside from that, everything (and I think I mean *everything*, though I may
have forgotten some other reasonable change) that has been done to make over
the years has been a mistake.

That all started with "make depend" - which started out reasonably, when
"depend" was just a target that made the dependency list, which was then
incorporated into the makefile, but has now become a monstrosity, all in
the name of making it easier for the Makefile builder.   But that one is
just a minor flaw.

Much worse has been turning make into a programming language.   It was
never designed for that, is not suited for it,   Hence we get nonsense like
the .for loop I included above, which has its whole purpose of avoiding
the need to explicitly write out

ACTIVE_CC= ...
ACTIVE_CPP= ...
ACTIVE_CXX+ ...

and because of which, I expect the one "..." that is included is more
complex than it needs to be (beats me, I cannot figure it out) because
it has to handle all those different cases.

Now I am absolutely a big fan of programmer laziness - do something once,
to work out what is required, do it again, to learn how best to achieve
the result, and then automate it, so we never have to ever do it (or even
think about it) again.

But the automation needs to be done the right way, not just as an extra
hack shoved into something that exists because that is the easy way at
the time - that's the bad form of laziness.   Being lazy is actually hard
work, and arranging to automate whatever it is can sometimes take much much
longer to do properly than simply doing it - though the long term gains if
we end up with a good result are significant.   But that's how we got all
the best of the tools we use today (including make) in the first place.

For example, before make existed, programs were built from shell scripts,
that simply compiled everything, every time.   Clearly wasteful.  At that
point, the "quick hack" would have been to make "cc" simply skip compiling
a file, if the .o already existed (for -c, or the .s for -S, or the a,out)
and was newer than the source.  That would not have been all that hard (esp
as cpp could still be run to find all the dependencies).  But where would we
be now if that had been the solution implemented, instead of designing and
building the more general purpose tool that is make ?

I am no fan of the auto* tools, and would never suggest any of them as the
right solution to anything, but one thing that they do which is absolutely
the right way, is to build a makefile from a specification, and some
programming to work out what should go in it.   The auto* tools don't try to
work out what is out of date, or what order is the right order to build things,
that is make's task.  And when done this way, make does not have to try to
work out what is the compiler to use, or what options it takes, ...
This is a good division of labour, and is the correct model.  The details of
how they do it, and the specification used might all be brain dead in this
particular case, but the model is a good one.

The system building "config" program is another example of doing it the
right way, though that one is much too specialised for building kernels,
and only kernels.   Still, it constructs the Makefile to be used from
a specification of what is needed, and background specifications of how
it is all to be put together.

The correct model would be to create a (good) tool to make Makefiles,
based upon a program specific designation of what is to be built, and
out of what, system dependent configuration information (which can be
selected, to allow cross compiling - it does not need to be "this" system's
specification that is used) and a specification format to express all of
this, and almost all the crap that is in current make systems (for whatever
"extended" version of make you're looking at - and there are many),
and then leave make to do the one thing it was designed for and does well,
which is actually scheduling the various tasks that need to be performed
(and skipping those that do not) into the correct order to build the
desired target.

kre



Home | Main Index | Thread Index | Old Index