0
write:
    @echo `date`

file_1: write file_2
    @echo "file_1 begin"
    $(shell sleep 3)
    @echo "file_1 end"

file_3: write file_4
    @echo "file_3 begin"
    $(shell sleep 3)
    @echo "file_3 end"

all: file_1 file_3

.PHONY: write

When I run make, it outputs:

Sat Oct 9 15:22:45 CST 2021
file_1 begin
file_3 begin
file_1 end
file_3 end

target write run only once.

My ultimate goal is to calculate the execution time of file_1 and file_3 by writing the start time of target write. In the process of testing whether it will be covered, the above problems were found. If there is a better way to calculate the time, let me know.

zcfh
  • 101
  • 1
  • 9
  • 1
    It works as intended. I don't think a single target can be executed more than once. For measuring time, consider using the `time` command. – HolyBlackCat Oct 09 '21 at 07:38
  • 1
    Makefile targets are not analogous to functions, and expressing a dependency on a target is not analogous to calling a function. `make` determines which targets need to be built, in what order, so that the requested goal targets are up to date with respect to their dependencies. No target is built more than once in a given run. – John Bollinger Oct 09 '21 at 14:51
  • Also, it is unwise to rely on the ordering of any target's dependency list to be significant. Any behavior pattern you discern connected to such orderings arises from `make` implementation details, not the specifications for the `make` utility. – John Bollinger Oct 09 '21 at 14:55

1 Answers1

3

First, it's virtually always an error to use the $(shell ...) function inside a recipe. A recipe is already running in the shell, so it's not needed, and it has unexpected behaviors. Why not just use @sleep 3 here instead?

There is no way to make a single target get built more than one time in a single invocation of make. That would be very bad: consider if you have multiple programs all depending on one .o file for example: would you want to recompile that .o file many times?

Anyway, this won't really do what you want:

file_1: write file_2

This doesn't measure the execution time of file_1 because the start date is written, then file_2 is built, then file_1 is built. So you're including the execution time of file_2 as well.

And, of course, if you ever enable parallel builds all bets are off.

If you want to measure something, put the start/stop operations inside the recipe, not using prerequisites. You can hide it in a variable to make it less onerous to type.

MadScientist
  • 92,819
  • 9
  • 109
  • 136
  • Thank you for your answer. Regarding the use of $(shell...), it has been corrected. Because it was previously unknown that `\``cannot be used in `'`. `Put the start/stop operation in the recipe without using prerequisites. `Does the execution time in the recipe ignore the prerequisites? – zcfh Oct 11 '21 at 04:07
  • I didn't quite understand the issue requiring `shell` but whatever, it's not important. The recipe is invoked as a unit, after all the prerequisites have completed. So if you put a start time as the first command in the recipe it will show the time when that recipe starts, without including any of its prerequisites. You can easily experiment with this. – MadScientist Oct 11 '21 at 14:28