(this is similar to GNU make: Execute target but take dependency from file but slightly different).
When I try to force to build a target, make automatically thinks that this target is out of date and forces a run of all targets which depend on it.
In my case, the target is a recursive make call which does a lot of work and might just return with "nothing to be done":
.PHONY: intermediate.dat
intermediate.dat:
$(MAKE) expensive_chain_which_finally_creates_intermediate.dat
step1.dat: intermediate.dat
sleep 10
step2.dat: step1.dat
sleep 15
step3.dat: step2.dat
sleep 10
all: step3.dat
sleep 5
In this case, "make all" runs for 40 seconds although intermediate.dat might not have changed (recursive make returned "nothing to be done"). However, if the recursive make updated intermediate.dat, the target shall be out of date.
Is there really no way to do this?
Regards
divB
Make intermediate.dat
depend on a phony target instead of being phony itself.
.PHONY : always-remake
intermediate.dat : always-remake
IIRC, the last time I solved the problem, the .PHONY didn't work as intended and I used:
always-remake :
@true
instead. However, I can't recall why, so try the .PHONY first.
The problem with making intermediate.dat
itself phony is that make
never checks the existence/date of a phony file, which is behaviour that you want. You only need to trigger the rebuild rule, which is done by a prerequisite that is out of date; a phony prerequisite is always out of date, so it does the job.
Another trick is to use an order-only prerequisite, plus a recursive call to make
. This can be useful when you want to implement some additional logic around when the intermediate.dat
file needs to be created, or when you want to decouple a target from the usual exists && newer than
check that make
does.
For example:
Let's say that the file contains some date-sensitive material that subsequent tasks depend on, and that it expires after 12 hours. We only want to recreate the file when any of these is true:
- The file is older than 12 hours
- The file does not exist
To implement the make target & recipes, we'll create a .PHONY
check-intermediate.dat
target, which runs the time check, conditionally removes the file if it's expired, and then re-runs make
for that file.
Note that the check-intermediate.dat
target will be defined via the order-only prerequisite syntax (e.g. intermediate.dat: | check-intermediate.dat
) which causes it to run always before the target (e.g. intermediate.dat
). The time check is inexpensive, and we always want it to run and manage the file for us, but not invalidate the original make
check on intermediate.dat
if it already exists like a normal prerequisite .PHONY
target would.
.PHONY: check-intermediate.dat
check-intermediate.dat:
if [ -e intermediate.dat ]; then find intermediate.dat -mmin +720 -exec bash -c 'rm -f "{}"; $(MAKE) intermediate.dat' \; ; fi
intermediate.dat: | check-intermediate.dat
echo run-expensive-tasks-here-to-create-intermediate.dat
step1.dat: intermediate.dat
sleep 10
step2.dat: step1.dat
sleep 15
step3.dat: step2.dat
sleep 10
all: step3.dat
sleep 5
Now, anything that depends on the intermediate.dat
target will first run the check, remove the file if it's expired, and re-run make intermediate.dat
before running the rest of the dependent targets. If the intermediate.dat
file already exists, it will run the check, and if the check passes, it will continue without running the time expensive recreation task.
The problem with depending on a .PHONY
target is that it will always run the tasks which depend on it, because of the way GNU Make handles .PHONY
targets. This can cause the make
run to always perform the time expensive operations even when it does not need to:
A phony target should not be a prerequisite of a real target file; if it is, its recipe will be run every time make goes to update that file. As long as a phony target is never a prerequisite of a real target, the phony target recipe will be executed only when the phony target is a specified goal (see Arguments to Specify the Goals).