I managed to reproduce the issue and then modified the A assignment to...
A = $(shell date -Ins)
to give a more precise timestamp. That then shows...
2025-06-11T13:31:01,093108847+01:00
sleep 2s;
2025-06-11T13:31:01,094414902+01:00
sleep 2s;
2025-06-11T13:31:01,096214217+01:00
Wed 11 Jun 13:31:01 BST 2025
So there are slight differences but not the expected 2s.
Changing A further to...
A = $(shell echo 'Evaluating A' >&2 && date -Ins)
highlights the real issue: the output becomes...
Evaluating A
Evaluating A
Evaluating A
2025-06-11T13:33:17,699419455+01:00
sleep 2s;
2025-06-11T13:33:17,700834383+01:00
sleep 2s;
2025-06-11T13:33:17,702354865+01:00
Wed 11 Jun 13:33:17 BST 2025
So it appears that when make is about to run the commands associated with a recipe it evaluates all of the recursively expanded variables one after the other but before any of the commands are run.
(Note: I've written this up as a 'tentative' answer. Doubtless one of the more knowledgeable make users will provide a more complete answer at some point -- including the reasoning behind this feature.)
Edit 1: Having given this a bit more thought the behaviour actually makes perfect sense. The makefile in the question is using A as if it were a shell command. But it isn't -- it's a make variable and, as such, make is free to evaluate it prior to any use in a set of shell commands associated with a recipe.
Early valueI meant that the B's value is before A's value.allat once, before passing the result to the shell. So all your$(A)become the same date/time string before the shell sees the expanded recipe. With a higher resolution you can see the small delays during this expansion process performed by make but certainly not the 2 seconds delays because they are introduced after that.echo Wed Jun 11 12:44:46 CEST 2025(first line),sleep 2s(second line),echo Wed Jun 11 12:44:46 CEST 2025(third line)... And only after that expansion, the result is passed to the shell.