help-make
[Top][All Lists]
Advanced

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

Re: Evaluating a variable only once after a recipe has run


From: Kaz Kylheku (gmake)
Subject: Re: Evaluating a variable only once after a recipe has run
Date: Sat, 18 Apr 2020 07:31:38 -0700
User-agent: Roundcube Webmail/0.9.2

On 2020-04-18 06:52, R. Diez wrote:
Instead of trying to uses the execution of a target recipe to try to
calculate this optimized variable (which isn't how make works,
and will likely be fruitless) you can calculate it using a conditional
expression which looks for the presence of that target in $(MAKECMDGOALS).

  THAT_VAR := $(if $(filter that-target,$(MAKECMDGOALS)),$(shell ...))

In fact MAKECMDGOALS can even be tested with ifeq/ifneq to conditionally
include makefile text:

   ifneq($(filter that-target,$(MAKECMDGOALS)),)
   # chunk of makefile syntax here
   THAT_VAR := $(subst ... $(shell ...))
   endif


That does not cut it either, I'm afraid. Say that the makefile has
already run the cross-compiler recipe. The next invocation may not
request it as a goal any more.

Even if it is requested as a goal, it cannot work that way, because
the variable is calculated before any recipes are run.

The whole cross-compiler recipe will have to be handled by the logic
which also sets the variable.

Perhaps something like this:

   ifneq ($(MAKECMDGOALS),cross-toolchain) # prevent runaway recursion
     ifneq ($(filter that-target,$(MAKECMDGOALS)),)
       THAT_VAR := $(shell "make cross-toolchain; /path/to/tool --arg")
     endif
   endif

It's fairly gross that the Makefile rule base will be read twice
though due to the recursion, since the whole point of this exercise
is to optimize something. The optimization is still obtained for
targets which don't need the variable, but those which do are
impacted.

In this situation I would rather farm out the cross toolchain
build into a script, or a smaller Makefile or something. Or use some
conditionals to trim the rule base when cross-toolchain is the
only target.

The variable is not actually needed for
the cross-compiler recipe, but for all targets that depend on it. The
variable's definition uses the cross-compiler, but any other target
later one may use that variable.

I would have to filter for any target that ultimately depends on the
cross-compiler target, and that is not really feasible.

Isn't it? Since we can just add more words to the filter, it doesn't
seem like a difficulty.

   ifneq($(filter a-target b-target c-target,$(MAKECMDGOALS))
   ABCVAR := ...
   endif

Do you think there will be more than (let's pick a number) 17 of these targets,
in the foreseeable lifetime of the project?

If those targets are part of some extensible system like per-directory
include makefiles, that does get ugly. But even that is solvable, because
all the per-directory include makfefiles can be included first. They can
declare their target via some variable like this:

   # local per-directory include file

   TARGETS_NEEDING_VAR += my_local_target

   my_local_target: prereq
      cmd $(THAT_VAR) ...

These all get included, before we do the filter in the top-level Makefile logic:

   ifneq($(filter $(TARGETS_NEEDING_VAR),$(MAKECMDGOALS))
   THAT_VAR := ...
   endif

Given all the lists of targets that get manualy maintained in all kinds of makefiles
it doesn't seem like a big deal. E.g. OBJS := definitions with hundreds
of entries and whatnot.

To catch errors (someone adds a new target and doesn't add it to the filter), we can add an else clause which defines the variable in such a way that bad
syntax will almost certainly result.

    else
    THAT_VAR = ("
    endif

or some better idea to make the dependent recipes fail. Or else
--warn-undefined-variables could be of use, possibly.

In the end, this is just an optimization anyway. Possibly, a negative test
might be possible: test for the exclusive presence of certain targets
which certainly do not need that variable and define it under all other
circumstances.

Maybe the targets can follow some naming pattern which can be exploited
to condense the filter expression.




reply via email to

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