Auto delete hook in GNU Make - makefile

By default make authomatically deletes targets when they are not needed anymore.
For example:
do_it: write_bar write_baz
echo done > $#
.INTERMEDIATE: write_foo write_bar write_baz
write_foo:
echo foo > $#
write_bar: write_foo
cat $< > $#
echo bar >> $#
write_baz: write_foo
echo buz > $#
Here write_foo will be deleted after execution of both write_bar and write_baz.
I want not to simply remove file, but to do some actions just before write_foo deleted.
Can I change command for auto deletion or assign any hook for this action?
Update: autodeletion is applicable only for intermediate rules.

make (at least GNU Make) does not automatically delete successfully built targets (it does delete target if make was killed).
.DEFAULT does not do what you expect it to do. See https://www.gnu.org/software/make/manual/html_node/Special-Targets.html
Invoking make on command line executes first target. Since your first target is write_foo, only write_foo will be executed.
If you invoke make do_it, all targets will be executed (and 3 files will appear in current directory).
Added after question update:
According to https://www.gnu.org/software/make/manual/html_node/Special-Targets.html, including intermediate target in prerequisites for .PRECIOUS special target prevents deleting it.

Related

How to build make dependency when the target name is not the file name

I am trying to write a very simple Makefile which I can't make it to work as I expect.
target_a : file.txt
echo "this is target_a"
touch 0_$#
target_b : 0_target_a
echo "executing target_b"
touch 0_$#
Whenever I run make taget_b it gives out an error:
make: *** No rule to make target '0_target_a', needed by 'target_b'. Stop.
I can just change touch 0_$# to touch $#. But I really want a solution for touch 0_$# (a free choice of filename).
From GNU-man page ftp://ftp.gnu.org/old-gnu/Manuals/make-3.79.1/html_chapter/make_2.html
A target is usually the name of a file that is generated by a program; examples of targets are executable or object files. A target can also be the name of an action to carry out
I want to know how to build the Make dependency when the target name is an:
Action
I am afraid you cannot directly do just that and you'd have to help yourself with an intermediate target that makes the connection between target and its output clear (and hence gives make a chance to decide when it does or does not need to be remade:
0_target_a: file.txt
echo "this is target_a"
touch $#
target_b: 0_target_a
echo "executing target_b"
touch 0_$#
I.e. defining rule for target 0_target_a and updating touch accordingly will give you the behavior you wanted as make now understand the rule the connection between target and file 0_target_a and know when it does not need to be remade as a dependency of target_b. Now if you still want to also have a standalone target_a that would generate file 0_target_a, you can define it as follows:
target_a: 0_target_a
Since we know this target is not really creating a file itself, we can spare make a little effort looking for its result (target_a) and also prevent clashes should such file be created by declaring it as phony.
As a matter of fact you may want to give your target_b the same treatment, as otherwise (again make does not have enough information to understand the relation between target_b and 0_target_b) make target_b is always remade even though the file has already been generated.
The whole make file would look like this:
.PHONY: target_a target_b
target_a: 0_target_a
target_b: 0_target_b
0_target_a: file.txt
echo "this is target_a"
touch $#
0_target_b: 0_target_a
echo "executing target_b"
touch $#
If that is a reoccurring theme throughout the file, you could also express the relation on second and third line by defining a static pattern rule:
target_a target_b: %: 0_%
This defines a rule that a any target (first '%' without anything else) has a prerequisite of 0_ prefix followed by that target name (0_%, 0_ plus stem which in this case is a target name in its entirety as matched by previous %). and makes this rule applicable to targets target_a and target_a. This rule has no recipe and hence only describe target/prerequisite relation between the two.
In other words it means the same thing as the full example lines 2 and 3 combined.
The dependency of target_b should be a valid target itself, or a file which already exists
target_a : file.txt
echo "this is target_a"
touch 0_$#
target_b : target_a
echo "executing target_b"
touch 0_$#
if you want to "alias" creation of 0_target_a file to the target_a "action" you can add intermediate rule:
0_target_a : file.txt
echo "creating 0_target_a"
touch 0_$#
target_a : 0_target_a
target_b : target_a
echo "executing target_b"
touch 0_$#

Path dependency in Makefile

Here is two targets in my Makefile.
.SECONDARY:
exp-%.ans:
echo $* > eval/$#
%.scores: %.ans
cat eval/$< > eval/$#
When I write make -n exp-40.scores output would be:
echo 40 > eval/exp-40.ans
cat eval/exp-40.ans > eval/exp-40.scores
which is good except one thing. It does not aware of the dependency is already hold. If I create eval/exp-40.scores (first time) then I expect that make will say it is already in there if I run the same command. So I try to change my Makefile like this:
.SECONDARY:
exp-%.ans:
echo $* > eval/$#
%.scores: eval/%.ans
cat eval/$< > $#
When I write make -n exp-40.scores again and output would be:
echo eval/40 > eval/eval/exp-40.ans
cat eval/eval/exp-40.ans > exp-40.scores
which is completely wrong because my parameter should be 40 not eval/40.
How can I achieve the best of the these two worlds? Namely, I want to create *.scores in eval/ folder. I also want make to check whether file is already exist or not. Then make should proceed according to that file existence.
Thanks.
One of the core rules of make is that you need to build the target your rule told make that you'd build. A rule like foo : bar tells make that if it runs that recipe, the recipe will create or update a file named foo. If the recipe creates or updates a file named biz/foo instead, then your makefile is wrong and things will not work.
Make always puts the target it expects the recipe to create into the $# automatic variable. Your recipe should create or update $# and exactly $#. Not something else.
So in your case, you need to write:
eval/exp-%.ans:
echo $* > $#
eval/%.scores: eval/%.ans
cat $< > $#
If you want to be able to run make exp-40.scores and have it actually create eval/exp-40.scores, then you can add a rule like this:
%.scores: eval/%.scores ; #:
(you have to provide some kind of recipe for pattern rules, they cannot have an empty recipe; the one above does nothing).

GNU Make to list modifed source files since last invoke of make?

I know that
Make figures out automatically which files it needs to update, based on which source files have changed. It also automatically determines the proper order for updating files, in case one non-source file depends on another non-source file.
As a result, if you change a few source files and then run Make, it does not need to recompile all of your program. It updates only those non-source files that depend directly or indirectly on the source files that you changed.
Now I want to know whether I can ask Make to list out these modified sources?
You'll need a dummy file which uses all of your sources as prerequisites:
mod_list: foo.c bar.cc baz.cpp
#echo modified sources: $?
#touch $#
You can keep the list of sources as a separate variable:
WATCHED_SOURCES = foo.c bar.cc baz.cpp
mod_list: $(WATCHED_SOURCES)
#echo modified sources: $?
#touch $#
Or use a wildcard to look at all sources present:
WATCHED_SOURCES = $(wildcard *.c *.cc *.cpp *.whatever)
mod_list: $(WATCHED_SOURCES)
#echo modified sources: $?
#touch $#
One easy way is to use the dry run option to make, which is either -n or --dry-run or a couple of other choices, depending in part on exactly which implementation you are using. This tells you what make would do if executed which will, inter alia, show you what source files it would re-compile.
It's all in the man pages.

GNU make: Execute target but take dependency from file

I want the rules of a target to be executed but all dependent targets shall regard this target as satisfied.
How can I achieve this?
Example:
$(NETWORK_SHARE)/foo.log:
#echo Warning: server offline, still continue ...
#exit 0
foo.csv: $(NETWORK_SHARE)/foo.log
#echo Long export from a (different) server
#echo sleep 20
#echo foo > $#
If $(NETWORK_SHARE)/foo.log exists: foo.csv shall be rebuilt if $(NETWORK_SHARE)/foo.log is newer than foo.csv; otherwise nothing should happen (default)
If $(NETWORK_SHARE)/foo.log does not exist (e.g., server offline, failure, ...) only a message indicating a problem should be printed but foo.csv shall only be built if foo.csv does not exist.
I played around with .PHONY and returning different return values but for case 2, the expensive "export" happens as soon as I execute something on $(NETWORK_SHARE)/foo.log ...
Regards
divB
Looks like instead of using some old file (that someone can accidentally touch), you can use an order-only prerequisite. Here's a quote from the GNU makefile manual (chapter 4.3)
Occasionally, however, you have a situation where you want to impose a
specific ordering on the rules to be invoked without forcing the
target to be updated if one of those rules is executed. In that case,
you want to define order-only prerequisites. Order-only prerequisites
can be specified by placing a pipe symbol (|) in the prerequisites
list: any prerequisites to the left of the pipe symbol are normal; any
prerequisites to the right are order-only:
targets : normal-prerequisites | order-only-prerequisites
Great, thanks to Thiton's answer in my related question (Force make to find out-of-date condition from file) I can now provide a hack to solve this:
.PHONY: always-remake
NETWORK_SHARE = //server/dfs/common/logs
.PHONY: all
all: foo.csv
# file does not exist ...
ifeq "$(wildcard $(NETWORK_SHARE)/foo.log)" ""
old_file: always-remake
#echo Warning: network is not available ....
foo.csv: old_file
#echo Expensive export
#sleep 10
#echo $# > $#
else
foo.csv: $(NETWORK_SHARE)/foo.log
#echo Doing expensive export since log file changed ...
#sleep 10
#echo $# > $#
endif
"old_file" is a dummy file which must exist and should never be newer than any other file (e.g. 1/1/1971, 00:00)
Regards
divB

What is the purpose of .PHONY in a Makefile?

What does .PHONY mean in a Makefile? I have gone through this, but it is too complicated.
Can somebody explain it to me in simple terms?
By default, Makefile targets are "file targets" - they are used to build files from other files. Make assumes its target is a file, and this makes writing Makefiles relatively easy:
foo: bar
create_one_from_the_other foo bar
However, sometimes you want your Makefile to run commands that do not represent physical files in the file system. Good examples for this are the common targets "clean" and "all". Chances are this isn't the case, but you may potentially have a file named clean in your main directory. In such a case Make will be confused because by default the clean target would be associated with this file and Make will only run it when the file doesn't appear to be up-to-date with regards to its dependencies.
These special targets are called phony and you can explicitly tell Make they're not associated with files, e.g.:
.PHONY: clean
clean:
rm -rf *.o
Now make clean will run as expected even if you do have a file named clean.
In terms of Make, a phony target is simply a target that is always out-of-date, so whenever you ask make <phony_target>, it will run, independent from the state of the file system. Some common make targets that are often phony are: all, install, clean, distclean, TAGS, info, check.
Let's assume you have install target, which is a very common in makefiles. If you do not use .PHONY, and a file named install exists in the same directory as the Makefile, then make install will do nothing. This is because Make interprets the rule to mean "execute such-and-such recipe to create the file named install". Since the file is already there, and its dependencies didn't change, nothing will be done.
However if you make the install target PHONY, it will tell the make tool that the target is fictional, and that make should not expect it to create the actual file. Hence it will not check whether the install file exists, meaning: a) its behavior will not be altered if the file does exist and b) extra stat() will not be called.
Generally all targets in your Makefile which do not produce an output file with the same name as the target name should be PHONY. This typically includes all, install, clean, distclean, and so on.
NOTE: The make tool reads the makefile and checks the modification time-stamps of the files at both the side of ':' symbol in a rule.
Example
In a directory 'test' following files are present:
prerit#vvdn105:~/test$ ls
hello hello.c makefile
In makefile a rule is defined as follows:
hello:hello.c
cc hello.c -o hello
Now assume that file 'hello' is a text file containing some data, which was created after 'hello.c' file. So the modification (or creation) time-stamp of 'hello' will be newer than that of the 'hello.c'. So when we will invoke 'make hello' from command line, it will print as:
make: `hello' is up to date.
Now access the 'hello.c' file and put some white spaces in it, which doesn't affect the code syntax or logic then save and quit. Now the modification time-stamp of hello.c is newer than that of the 'hello'. Now if you invoke 'make hello', it will execute the commands as:
cc hello.c -o hello
And the file 'hello' (text file) will be overwritten with a new binary file 'hello' (result of above compilation command).
If we use .PHONY in makefile as follow:
.PHONY:hello
hello:hello.c
cc hello.c -o hello
and then invoke 'make hello', it will ignore any file present in the pwd 'test' and execute the command every time.
Now suppose, that 'hello' target has no dependencies declared:
hello:
cc hello.c -o hello
and 'hello' file is already present in the pwd 'test', then 'make hello' will always show as:
make: `hello' is up to date.
.PHONY: install
means the word "install" doesn't represent a file name in this
Makefile;
means the Makefile has nothing to do with a file called "install"
in the same directory.
It is a build target that is not a filename.
The special target .PHONY: allows to declare phony targets, so that make will not check them as actual file names: it will work all the time even if such files still exist.
You can put several .PHONY: in your Makefile :
.PHONY: all
all : prog1 prog2
...
.PHONY: clean distclean
clean :
...
distclean :
...
There is another way to declare phony targets : simply put :: without prerequisites :
all :: prog1 prog2
...
clean ::
...
distclean ::
...
The :: has other special meanings, see here, but without prerequisites it always execute the recipes, even if the target already exists, thus acting as a phony target.
The best explanation is the GNU make manual itself: 4.6 Phony Targets section.
.PHONY is one of make's Special Built-in Target Names. There are other targets that you may be interested in, so it's worth skimming through these references.
When it is time to consider a .PHONY target, make will run its recipe
unconditionally, regardless of whether a file with that name exists or
what its last-modification time is.
You may also be interested in make's Standard Targets such as all and clean.
There's also one important tricky treat of ".PHONY" - when a physical target depends on phony target that depends on another physical target:
TARGET1 -> PHONY_FORWARDER1 -> PHONY_FORWARDER2 -> TARGET2
You'd simply expect that if you updated TARGET2, then TARGET1 should be considered stale against TARGET1, so TARGET1 should be rebuild. And it really works this way.
The tricky part is when TARGET2 isn't stale against TARGET1 - in which case you should expect that TARGET1 shouldn't be rebuild.
This surprisingly doesn't work because: the phony target was run anyway (as phony targets normally do), which means that the phony target was considered updated. And because of that TARGET1 is considered stale against the phony target.
Consider:
all: fileall
fileall: file2 filefwd
echo file2 file1 >fileall
file2: file2.src
echo file2.src >file2
file1: file1.src
echo file1.src >file1
echo file1.src >>file1
.PHONY: filefwd
.PHONY: filefwd2
filefwd: filefwd2
filefwd2: file1
#echo "Produced target file1"
prepare:
echo "Some text 1" >> file1.src
echo "Some text 2" >> file2.src
You can play around with this:
first do 'make prepare' to prepare the "source files"
play around with that by touching particular files to see them updated
You can see that fileall depends on file1 indirectly through a phony target - but it always gets rebuilt due to this dependency. If you change the dependency in fileall from filefwd to file, now fileall does not get rebuilt every time, but only when any of dependent targets is stale against it as a file.
I often use them to tell the default target not to fire.
superclean: clean andsomethingelse
blah: superclean
clean:
#echo clean
%:
#echo catcher $#
.PHONY: superclean
Without PHONY, make superclean would fire clean, andsomethingelse, and catcher superclean; but with PHONY, make superclean won't fire the catcher superclean.
We don't have to worry about telling make the clean target is PHONY, because it isn't completely phony. Though it never produces the clean file, it has commands to fire so make will think it's a final target.
However, the superclean target really is phony, so make will try to stack it up with anything else that provides deps for the superclean target — this includes other superclean targets and the % target.
Note that we don't say anything at all about andsomethingelse or blah, so they clearly go to the catcher.
The output looks something like this:
$ make clean
clean
$ make superclean
clean
catcher andsomethingelse
$ make blah
clean
catcher andsomethingelse
catcher blah

Resources