RE: ?= $(shell ...) re-runs shell in sub-makes when $(origin VAR) is environment (regression in 4.4)

Gunter Woytowitz <[email protected]> Wed, 20 May 2026 15:09:03 +0000
Newsgroups gmane.comp.gnu.make.bugs
Message-ID <SN7PR03MB7183E486AF5BD38E70B9503DBB012@SN7PR03MB7183.namprd03.prod.outlook.com>
I have reverted to make 4.3 as my temporary workaround, just wanted to repo=
rt the issue

Once the new assignment operation ?:=3D is available I will update make to =
the latest and convert my makefiles.
will this change be available as an update in the ubuntu resolute repos ?


-----Original Message-----
From: Paul Smith <[email protected]>
Sent: Wednesday, May 20, 2026 8:54 AM
To: Gunter Woytowitz <[email protected]>; [email protected]
Subject: Re: ?=3D $(shell ...) re-runs shell in sub-makes when $(origin VAR=
) is environment (regression in 4.4)

On Mon, 2026-05-18 at 02:06 +0000, Gunter Woytowitz wrote:
> In GNU Make 4.4 (confirmed on 4.4.1), "export VAR ?=3D $(shell COMMAND)"
> re-evaluates $(shell) in every recursive sub-make, even though the
> variable was already evaluated and exported by the top make and the
> sub-make's $(origin VAR) correctly reports "environment".

You have definitely found a problem, but your diagnosis isn't accurate
(unfortunately: if it were the problem would be a simple bug, rather than a=
 deep issue that's hard to resolve).

I think you're getting confused by the facilities you are choosing in your =
makefile to show the problem.  If you choose a simpler reproducer you'll ge=
t a clearer view of the issue.

Consider this makefile:

  export FOO ?=3D $(shell echo foo)
  export BAR ?=3D $(shell echo bar)

  $(info origin FOO =3D $(origin FOO))
  $(info FOO =3D $(value FOO))

  $(info origin BAR =3D $(origin BAR))
  $(info BAR =3D $(value BAR))

  all: ; echo $$FOO $$BAR

Note this things: first, we use $(info ...) rather than echo to show inform=
ation directly.  Second, we use $(value ...) to show the actual value of th=
e variable, which is what make will evaluate when it expands the variable.

Finally, we don't bother with recursion which adds confusion to the output;=
 this is not needed since we can simply set the variables in the environmen=
t before invoking make.

So, no matter which version of make you use you get the expected
result:

  $ make
  origin FOO =3D file
  FOO =3D $(shell echo foo)
  origin BAR =3D file
  BAR =3D $(shell echo bar)
  echo $FOO $BAR
  foo bar

However, if you check how many instances of the shell are invoked in versio=
n 4.3 you'll see (this is on GNU/Linux of course):

  $ strace make-4.3 2>&1 | grep clone | wc -l
  3

whereas in 4.4.1 you'll see:

  $ strace make-4.4.1 2>&1 | grep clone | wc -l
  5

Now, two extra clones doesn't seem like much but as you observed, this valu=
e goes up very quickly with the number of exported variables that contain s=
hell calls.  If I add a new line to the makefile:

  export BAZ ?=3D $(shell echo baz)

then the clone() call count in make-4.4.1 goes up to 16!

But we can see that your guess that the problem is that make is still expan=
ding values that are overridden in the environment, is not correct by provi=
ding environment variables:

  $ FOO=3Denv1 BAR=3Denv2 make
  origin FOO =3D environment
  FOO =3D env1
  origin BAR =3D environment
  BAR =3D env2
  echo $FOO $BAR
  env1 env2

and now the clone count:

  $ FOO=3Denv1 BAR=3Denv2 strace make-4.3 2>&1 | grep clone | wc -l
  1

  $ FOO=3Denv1 BAR=3Denv2 strace make-4.4.1 2>&1 | grep clone | wc -l
  1

So, no more work is done here.

The problem you are running into is a result of this change from the NEWS f=
ile:

* WARNING: Backward-incompatibility!
  Previously makefile variables marked as export were not exported to comma=
nds
  started by the $(shell ...) function.  Now, all exported variables are
  exported to $(shell ...).  If this leads to recursion during expansion, t=
hen
  for backward-compatibility the value from the original environment is use=
d.
  To detect this change search for 'shell-export' in the .FEATURES variable=
.

Resolving https://savannah.gnu.org/bugs/?10593


Unfortunately that has the unanticipated side effect that EVERY time $(shel=
l ...) is invoked, EVERY exported variable is expanded.  This gives the beh=
avior you are seeing.

There is a still-open issue we are using to consider how to alleviate this =
but so far no brilliant ideas have surfaced:
https://savannah.gnu.org/bugs/index.php?64746

One thing we did do is add support for a "?:=3D" assignment operator that d=
oes conditional assignment but results in a simple variable assignment, not=
 a recursive variable assignment.  That would fix the issue above (in the n=
ext release of GNU Make) but of course it's not backward-compatible.

> Replacing ?=3D with :=3D (immediate assignment, computed once at top-make
> parse time) avoids the problem at the cost of breaking command-line
> variable override.

There are other ways, of course.  You can do something like:

  default_TOP  :=3D $(shell pwd)
  export TOP   ?=3D $(default_TOP)

That will force the shell to run every time, even when overridden, but it w=
ill only run once (per invocation of make) not multiple times.

Or, you can get very tricksy and use my hack for "deferred simple variable =
expansion":

https://make.mad-scientist.net/deferred-simple-variable-expansion/

Although not discussed in that blog post this works with ?=3D as well.

If you use that trick in my example above and repeat the experiment you'll =
see that make-4.4.1 does the same number of clone() calls as make-4.3, incl=
uding only one when variables are inherited from the environment.

--
Paul D. Smith <[email protected]>            Find some GNU Make tips at:
https://www.gnu.org/                       http://make.mad-scientist.net/
"Please remain calm...I may be mad, but I am a professional." --Mad Scienti=
st



This email may contain confidential and privileged information and is inten=
ded solely for the use of the addressee(s). Unless you are the addressee or=
 are authorized to receive messages for the addressee, you may not use, cop=
y, disseminate, or disclose the information or any attachments to any third=
 party. If you have received this correspondence in error, please notify th=
e sender immediately and delete this email. Your cooperation and understand=
ing are greatly appreciated. Attention Federal Customers: Please note this =
email platform is NOT approved to communicate (send or receive) CUI. For qu=
estions on the approved system to communicate CUI, please contact your desi=
gnated Vcinity Representative.