Make deletes my target. Why? - shell

From the docs:
The second difference is that if make does create B in order to
update something else, it deletes B later on after it is no longer
needed. Therefore, an intermediate file which did not exist before
make also does not exist after make. make reports the deletion to
you by printing a rm -f command showing which file it is deleting.
Now, a makefile, like this:
$(shell rm -rf x D)
$(shell mkdir D)
$(shell touch D/x)
VPATH = D
all: x ;
x:: phony
#echo '$#'
.INTERMEDIATE: D/x
.PHONY: phony
Running:
$ make
D/x
rm D/x
$ ls D/x
ls: cannot access D/x: No such file or directory
Now, given the above quote, that Make removes only an "intermediate file which did not exist before", we have here a clear case, where:
The target did exist before running Make.
Make did not create the target.
Still, Make feels that it is appropriate to delete this file. A thing that it was not asked to do. So why does it?
Compare it to the following simple makefile:
$(shell rm -rf x)
$(shell touch x)
x ::
#echo '$#'
.INTERMEDIATE: x
Running:
$ make
x
$ ls x
x
Simple as that! Make did not remove a file that:
It did not create!
Existed, before running Make
Because, the all point of intermediate files removal is, to quote the documentation above:
Therefore, an intermediate file which did not exist before make also does not exist after make.
Nothing else!
So, how come in the first example, does Make go-around and deletes pre-existing files?

Description of Execution
In your first example whilst D/x did exist before executing make because you have specified phony as a prerequisite make must remake D/x as the target phony does not exist. So make created the intermediate file D/x and according to your quote it
deletes D/x when it is no longer needed.
after having remade all. The path of execution make takes is
all does not exist, so we must check x
make finds D/x but phony does not exist
make remakes phony and now we must remake D/x as it depends on phony which has been updated
make remakes the intermediate file D/x (which we must now delete when it is no longer needed)
make can now create all, and finally
make deletes the intermediate file D/x
Relation to Documentation
The key part of the documentation you have quoted is the first sentence.
The second difference is that if make does create B in order to update something else, it deletes B later on after it is no longer needed.
This is exactly what happens in your example. make creates D/x and so it must delete D/x later on when it is not needed.
Explanation of Documentation Interpretation
I think you need to be a little careful of your interpretation of the documentation. By stating 'Therefore' they are not saying make will not delete a file that already existed. They are simply saying as a consequence of make updating an intermediate file which did not previously exist it will delete it when it is finished. They are still leaving the possibility that an intermediate file existed before make was executed that it had to update and subsequently deletes.
I think I should stress this a bit more. Given the quote
Therefore, an intermediate file which did not exist before make also does not exist after make.
this does not mean make wont delete files that existed beforehand.

Related

Makefile rule only works if file exists before make is invoked

Consider the following (MCVE of a) Makefile:
my_target: prepare test.bin
prepare:
echo >test.dat
%.bin: %.dat
cp $? $#
If you run make in a clean directory, it fails:
echo >test.dat
make: *** No rule to make target 'test.bin', needed by 'my_target'. Stop.
Run it again and it succeeds:
echo >test.dat
cp test.dat test.bin
What seems to happen is that the rule to make *.bin from *.dat only recognises that it knows how to make test.bin if test.dat exists before anything is executed, even though according to the output it has already created test.dat before it tries to create test.bin.
This is inconvenient for me as I have to prepare a few files first (import them from a different, earlier part of the build).
Is there a solution? Perhaps some way to allow the rules to be (re)evaluated in the light of the files which are now present?
There are a number of issues with your makefile. However based on your comments I'm inclined to assume that the MCVE here is just a little too "M" and it's been reduced so much that it has a number of basic problems. So I won't discuss them, unless you want me to.
The issue here is that you're creating important files without indicating to make that that's what you're doing. Make keeps internally a cache of the contents of directories that it's worked with, for performance reasons, and that cache is only updated when make invokes a rule that it understands will modify it.
Here your target is prepare but the recipe actually creates a completely different file, test.dat. So, make doesn't modify its internal cache of the directory contents and when it checks the cache to see if the file test.dat exists, it doesn't.
You need to be sure that your makefile is written such that it doesn't trick make: if a recipe creates a file foo then the target name should be foo, not bar.
This happens for wildcard targets, like %.bin. They get evaluated at the first pass. You could add an explicit target of test.bin. Or, follow the advice of tkausl and have test.dat depend on prepare (a phony target). In this case, you don't need the double dependency anymore:
my_target: test.bin
you have to write
test.dat: prepare
or (when when you want to stay with wildcards)
%.dat: prepare
#:
Usually, you might want to create and use .stamp files instead of a prepare target.

Why "make all" works as expected without adding "all" to .PHONY target?

I know what a .PHONY does.
If in the folder where my Makefile is, I add an empty file called clean and after I run make clean all of the clean target will not be executed since there was not any change in the file, so the target will not run and this is correct.
If I add .PHONY: clean, than the clean is seen as a command and this is also correct.
My question is why this behavior does not happen the same to all target, since I added a all file in the folder.So basically the all target still executes like if it was a .PHONY: all
I have the fallowing makefile code.
all: test1 test2
test1: test1.o
test1.o: test1.c
test2: test2.o
test2.o: test2.c
clean:
rm -rf *.o test1 test2
How do you know that the all rule is "still executing"? That rule has no recipe, so there's no way it can be "executed".
If what you mean is that even though the all file exists in the local directory, make is still building the targets test1 and test2, that's how make works (this doesn't have anything to do with phony vs. non-phony targets). When make decides whether or not build a particular target first it tries to build all the prerequisites of that target, and all the prerequisites of those targets, etc. Only after all that is complete, can make know whether or not to build the first target (all in this case).
make clean here doesn't have any dependencies, so putting a file named clean there is enough for the target to be considered built.
make all on the other hand has dependencies. Even if you put a file named all there, Make has to check whether the all file is newer than test1 and test2. This process triggers builds of test1 and test2, and it happens to have the same effect as if all was a phony target.
The basis is that all: test1 test2 is a recipe for building a file named all, that depends on the files test1 and test2.
If you ran make all, Make would do something like this:
Analyse the Makefile.
Find out that all depends on test1 and test2.
Check the timestamp of all and see if it is "up to date".
It is "up to date" if none of the dependencies are newer than itself.
In other words, Make can skip building a file if it's newer than all it's dependencies.
Build outdated or missing files.
Now, if you would like to prevent Make from considering the targets as files, you could specify them as phony targets. That is best practice for non-file targets like all.
(This answer isn't disagreeing with either of the existing answers, but suggesting another way of thinking about this).
When you have a rule like
dst: src
action
you're saying two things (as you know):
if dst doesn't exist, or is older than src, then do action; and
when action completes, the file dst will exist.
With targets such as all or clean, the second statement is of course not true. Make doesn't hold you to the promise in (2), so when you say make all, it'll compute and generate the required dependencies, and not complain that there's no file all in place afterwards. You're lying to Make, but it doesn't mind (it's cool with that...). That is, this is basically a makefile hack.
Where this goes wrong, of course, is if for some reason there happens to be a file called all or clean. Then Make will take the modification date of the file all into account when calculating the dependencies, and possibly come to a conclusion you didn't expect.
So what .PHONY: all does is legitimise the hack, and tells Make ‘even if a file all exists, pretend that it doesn't’; you're basically cancelling promise (2).
Therefore, as the other answers state, mentioning .PHONY isn't necessary. It simply forestalls an error – easy to make but easy to miss – when a file matching a phony target is accidentally created.

keep intermediate files defined by wildcards in makefile

I've defined a series of data-processing steps with a Makefile but find that the files belonging to the intermediate steps are deleted by Make. In the following example, the files processed_%.txt are always deleted.
#make some simple data
#echo "test data X" > test_x.txt
#echo "test data y" > test_y.txt
x = test_x.txt
y = test_y.txt
#these are deleted
processed_%.txt: ${x} ${y}
cat $< > $#
#these remain in the directory
processed_again_%.txt: processed_%.txt
cat $< > $#
all: processed_again_x.txt processed_again_y.txt
Can anyone explain what is happening and how to disable/control this behavior?
thanks,
zachcp
This is how chains of implicit rules work.
The second difference is that if make does create b in order to update something else, it deletes b later on after it is no longer needed. Therefore, an intermediate file which did not exist before make also does not exist after make. make reports the deletion to you by printing a ‘rm -f’ command showing which file it is deleting.
and you can control this behavior by marking the file as .SECONDARY
You can prevent automatic deletion of an intermediate file by marking it as a secondary file. To do this, list it as a prerequisite of the special target .SECONDARY. When a file is secondary, make will not create the file merely because it does not already exist, but make does not automatically delete the file. Marking a file as secondary also marks it as intermediate.

Execute one make target for multiple rules

I have a list of libraries where each have 2 files (.so, .dll).
How should I create a make rule which would execute the recipe only once if both of the files are missing or if one of them is missing.
LIBS = alib blib
LIBS_SO = $(patsubst %, %.so, $(LIBS))
LIBS_DLL = $(patsubst %, %.dll, $(LIBS))
If I make this target
$(LIBS_SO) $(LIBS_DSS):
cp .....
it copies only once for all of the the possibilities.
If I make this:
all : $(LIBS_SO) $(LIBS_DSS):
$(LIBS_SO) $(LIBS_DSS):
cp .....
I copy in all cases of any missing files.
I want to copy the alib directory if both or one of the files alib.dll / alib.so is missing; the
the same with blib.
You have a consistent typo of LIBS_DSS where you (presumably) meant LIBS_DLL.
Your first 'rule' is a shorthand for:
alib.so:
cp ...
blib.so:
cp ...
alib.dll:
cp ...
blib.dll:
cp ...
So, when asked to build, make builds the first target in the file, which is alib.so. That's why it does it once.
The second version, when fixed to remove the extra colon and the typo, should work:
all: $(LIBS_SO) $(LIBS_DLL)
$(LIBS_SO) $(LIBS_DLL):
cp .....
The default rule is all; to make all, make ensures that each of the files alib.so, blib.so, alib.dll and blib.dll exists and is up to date. It should execute the commands once for each missing target.
You might conceivably run into trouble if you run a parallel make; make -j4 or something similar. It might launch four copy commands almost simultaneously to make each of the targets. But in a non-parallel build, it will ensure alib.so is up to date (and if it isn't, will do the copy). If that copy also copies alib.dll, then it won't recopy when it ensures alib.dll is up to date.
You haven't given us much information, but I think this will do what you want:
all : $(LIBS_SO) $(LIBS_DLL):
%.so %.dll:
cp $* directory ...
If both alib.so and alib.dll are missing, Make will execute this rule only once.
EDIT: Thanks to Jonathan Leffler for catching the typo.

Get error for "make: Nothing to be done for 'target'"

Let me illustrate it with an example.
mkdir test
cd test
touch target
make target
This will result in: make: Nothing to be done for 'target'.
So make tells me there is nothing to do. This is because make did not find a rule to make target, but because the target already exists make tells me there is nothing to do.
Now, I don't want that. I want make to give me an error when it cannot find a rule for target, even though the target already exists.
I have tried the following:
echo '.DEFAULT:
echo make: *** No rule to make target `$#'. Stop.
false'> Makefile
But this does not stop the make when making multiple targets.
The problem is, that make assumes the target name is also a file which will be build by the given commands.
But sometimes this is not true (e.g. think of "clean").
To tell make that some targets don't build this file, you need to make them "phony". Put the following line into your Makefile:
.PHONY: target
If you think about it, you would end up with a chicken-and-egg situation (or infinite regress). Suppose you managed to have a rule that said 'You must have a rule to create target before it is legitimate', then you'd have a rule that says 'target depends on X' for some other file X. That's written:
target: X
command to build target from X
But then you'd be back to the starting point: you'd also want a rule to create X. How can you do that? You might have a rule that depends on nothing and magically creates the file X when it is needed:
X:
command to build X from nothing
Without a rule like that, you have an infinite regress.
So, make is designed to ensure that files exist and are up to date. If a file exists and there are no rules - implicit or explicit - to specify how it is made, then the file is up to date. What you are seeking to do is not possible.
Actually this sounds like a perfectly reasonable (if misguided ;-)) request. You will have to explicitly list every source file with a rule with an empty recipe though.
Here you go:
Makefile: ;
.PHONY: dodgy
dodgy%: dodgy; $(error You must provide a rule for $*)
%: dodgy% ;
The proof of the pudding:
$ rm aa
$ make aa
Makefile:4: *** You must provide a rule for aa. Stop.
$ touch aa
$ make aa
Makefile:4: *** You must provide a rule for aa. Stop.
Note that the line Makefile: ; is necessary. After all, the first thing make tries to do is rebuild the Makefile.
Note also that the catch-all pattern rule is non-terminal. This can be a massive performance hit. As the manual says about match anything rules "They are very useful, but it can take a lot of time for make to think about them."

Resources