Phony targets in makefile - linux

I was trying to execute the example in this link.
all : prog1 prog2 prog3
.PHONY : all
prog1 : prog1.o utils.o
cc -o prog1 prog1.o utils.o
prog2 : prog2.o
cc -o prog2 prog2.o
prog3 : prog3.o sort.o utils.o
cc -o prog3 prog3.o sort.o utils.o
With or without PHONY (if i comment out the .PHONY line), the outputs are the same. What is the real use of PHONY here?
The article mentions this
When one directory contains multiple programs, it is most convenient to describe all of the programs in one makefile
Since the target remade by default will be the first one in the makefile, it is common to make this a phony target named ‘all’ and give it, as prerequisites, all the individual programs
I cant understand what is the real use of PHONY here. Can someone point me a scenario in this example in which the program would behave differently without PHONY?

without .PHONY "all" is just a regular make target which depends on prog1, prog2, prog3. This means that if a file named "all" would happen to exist in the directory that is newer than prog1, prog2 and prog3, they will not be built (try it!) - but this is obviously not what you had in mind.
.PHONY tells GNU make the "all" target is phony - you don't REALLY intended for a file called "all" to be created and it should build the dependencies regardless if a file called "all" exists or not.
Added later:
My example with all and prog1 above was not correct although the general idea is true. Here is a much simple example
all: prog
prog: prog.c
clean:
$(RM) prog prog.o
giladb#xxx:~/tmp$ ls
Makefile prog.c
giladb#xxx:~/tmp$ make
cc prog.c -o prog
giladb#xxx:~/tmp$ make clean
rm -f prog prog.o
giladb#nps06:~/tmp$ make
cc prog.c -o prog
giladb#xxx:~/tmp$ touch clean
giladb#xxx:~/tmp$ make clean
make: `clean' is up to date.

At least for GNU Make in combination with this example the behavior regarding the output is the same, no matter if the target "all" is a phony target or not. Even if there is a file named "all".
BUT as described in the manual behind your link the internal behavior of GNU Make is different. If "all" is not a phony target, GNU Make checks whether a file named "all" exists and is older than its prerequisites. Try "make -d" and you will see the difference.
Another important point is that the recipe of a phony target is always executed. If you take this example:
all: goal
echo "Done"
.PHONY : clean
goal:
echo "Hello World!" > $(#)
clean:
rm -f goal all
... and execute this:
$ make clean
rm -f goal all
$ make
echo "Hello World!" > goal
echo "Done"
Done
$ make
echo "Done"
Done
$ touch all
$ make
make: 'all' is up to date.
... the recipe will not be executed after creating the file "all".
With "all" being a phony target:
all: goal
echo "Done"
.PHONY : all clean
goal:
echo "Hello World!" > $(#)
clean:
rm -f goal all
... the recipe of "all" will be executed always independent of the existence of the file "all".
$ make clean
rm -f goal all
$ make
echo "Hello World!" > goal
echo "Done"
Done
$ make
echo "Done"
Done
$ make
echo "Done"
Done
$ touch all
$ make
echo "Done"
Done
$ make
echo "Done"
Done

Related

Linux make command is deleting a source file

I have inherited a project file that has a Makefile in it that is doing something I have never seen before--It is injecting a rm command. I cannot find any reason for the rm command, so I am missing something very obvious or very esoteric.
Thanks
The results of running make are:
bison --defines --xml --graph=calc.gv -o calc.c calc.y
Bison flags =
cc -c -o calc.o calc.c
Making BASE = calc
cc -o calc calc.o
Done making BASE
rm calc.c <======== WHERE IS THIS COMING FROM?
The Makefile is:
BASE = calc
BISON = bison
XSLTPROC = xsltproc
all: $(BASE)
%.c %.h %.xml %.gv: %.y
$(BISON) $(BISONFLAGS) --defines --xml --graph=$*.gv -o $*.c $<
#echo "Bison flags = " $(BISONFLAGS)
$(BASE): $(BASE).o
#echo "Making BASE = " $(BASE)
$(CC) $(CFLAGS) -o $# $^
#echo "Done making BASE"
run: $(BASE)
#echo "Type arithmetic expressions. Quit with ctrl-d."
./$<
html: $(BASE).html
%.html: %.xml
$(XSLTPROC) $(XSLTPROCFLAGS) -o $# $$($(BISON) --print-datadir)/xslt/xml2xhtml.xsl $<
CLEANFILES = $(BASE) *.o $(BASE).[ch] $(BASE).output $(BASE).xml $(BASE).html $(BASE).gv
clean:
#echo "Running clean" $(CLEANFILES)
rm -f $(CLEANFILES)
See https://www.gnu.org/software/make/manual/make.html#Chained-Rules:
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.

Making a target out-of-date if compiler options differ respect to the previously used ones

Consider the following directory tree for a made-up toy example that illustrates the problem to solve:
- Makefile
- lib.c
+ foo/
|-- config.mk
+ bar/
|-- config.mk
Content of foo/config.mk:
CFLAGS += -DFOO
Content of bar/config.mk:
CFLAGS += -DBAR
Calling make with Makefile for the targets foo and bar results in the files foo/config.mk and bar/config.mk being included (by means of the include directive), respectively, and lib.o being built, i.e.:
# build lib.o with the macro FOO defined
$ make foo
# build lib.o with the macro BAR defined
$ make bar
# build lib.o with both the macros FOO and BAR defined
$ make foo bar
$ make bar foo
The default rule for building lib.o uses the variable COMPILE.c, which is defined (according to the output obtained by calling make with the option --print-data-base) as:
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
The expansion of COMPILE.c depends, among other things, on the value of the variable CFLAGS, which, in turn, depends on whether foo/config.mk or bar/config.mk were included, since these makefiles modify the CFLAGS variable.
What I would like to achieve is to treat the target lib.o as an out-of-date target if the expansion of the variable COMPILE.c that is currently used is not the same as the one used for the previous build of lib.o. For example:
$ make foo
# It shouldn't rebuild anything since lib.o should be up-to-date
$ make foo
# It should rebuild lib.o since it should be out-of-date
$ make bar
# It should rebuild lib.o since it is again out-of-date
$ make foo bar
# It shouldn't rebuild lib.o since it is up-to-date
$ make bar foo
This solution explains how I've implemented this behaviour so far. Any suggestion is welcomed.
I would dump the variable's value in another included makefile and check whether the current value is different from the one from the included makefile. Something like:
ifeq ($(filter foo,$(MAKECMDGOALS)),foo)
include foo/config.mk
endif
ifeq ($(filter bar,$(MAKECMDGOALS)),bar)
include bar/config.mk
endif
-include old_compile.mk
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
ifneq ($(COMPILE.c),$(OLD_COMPILE.c))
FORCE := force
endif
lib.o: lib.c $(FORCE)
$(COMPILE.c) $< -o $#
echo 'OLD_COMPILE.c := $(COMPILE.c)' > old_compile.mk
.PHONY: foo bar all force
foo bar all: lib.o
Demo:
$ make foo
cc -DFOO -c lib.c -o lib.o
echo 'OLD_COMPILE.c := cc -DFOO -c' > old_compile.mk
$ make foo
make: Nothing to be done for 'foo'.
$ make foo bar
cc -DFOO -DBAR -c lib.c -o lib.o
echo 'OLD_COMPILE.c := cc -DFOO -DBAR -c' > old_compile.mk
make: Nothing to be done for 'bar'.
$ make bar foo
make: Nothing to be done for 'bar'.
make: Nothing to be done for 'foo'.
Here's my crude mockup that encodes compiler/linker flags directly in filenames. It's just an idea, an actual implementaation should be a bit more robust.
empty:=
space:= $(empty) $(empty)
comma:= ,
LDFLAGS_NS=$(subst $(space),$(comma),$(LDFLAGS))
CFLAGS_NS=$(subst $(space),$(comma),$(CFLAGS))
EXEDIR=./test-exedir-cflags=$(CFLAGS_NS)-ldflags=$(LDFLAGS_NS)
EXEDIR_PAT=./test-exedir-*
OBJDIR=./test-objdir-cflags=$(CFLAGS_NS)
OBJDIR_PAT=./test-objdir-*
test.exe: $(EXEDIR)/test.exe
rm -f test
ln -s $< $#
$(EXEDIR)/test.exe: test.o
rm -rf $(EXEDIR_PAT)
mkdir $(EXEDIR)
cc $(LDFLAGS) -o $# $<
test.o: $(OBJDIR)/test.o
rm -f test.o
ln -s $< $#
$(OBJDIR)/test.o: test.c
rm -rf $(OBJDIR_PAT)
mkdir $(OBJDIR)
cc -c $(CFLAGS) -o $# $<
Tested with a hello world test.c file and these commands:
make test.exe
make test.exe
make test.exe CFLAGS="-g -Wall"
make test.exe CFLAGS="-g -Wall"
make test.exe
My approach, so far, consists of generating a file, cc-options-dump, with the contents of the expansion of the COMPILE.c variable everytime lib.o is built.
The MD5 hash resulting of the expansion of the current COMPILE.c variable is compared against the one that was used for the previous build, i.e., the one whose contents are stored in the file cc-options-dump, if any (i.e., if the file does exist).
.PHONY: foo bar require-rebuild cc-options-dump
# include the xxx/config.mk files
# sort built-in function to make it independent of the order (i.e., "foo bar" or "bar foo")
$(foreach opt,$(sort $(MAKECMDGOALS)),$(eval include $(opt)/config.mk))
# obtain the MD5 hash of the previously used flags
ifneq (,$(wildcard cc-options-dump))
prev-cc-options-hash := $(shell md5sum cc-options-dump | cut -f1 -d' ')
endif
# obtain the MD5 hash of the current flags
curr-cc-options-hash := $(shell echo "$(COMPILE.c)" | md5sum | cut -f1 -d' ')
# Do these hashes differ?
ifneq ($(prev-cc-options-hash),$(curr-cc-options-hash))
# keep track of the fact that a rebuilt needs to be triggered
does-need-rebuild := require-rebuild
# just for displaying information
$(info Rebuild required)
else
$(info Rebuild not required)
endif
# make both targets foo and bar dependent on the file with the flags
# so that it has to be generated, since it is a phony target as well
foo bar: cc-options-dump
# lib.o has now an additional prerequisite for determining whether it need to be rebuilt
lib.o: $(does-need-rebuild)
# save the used compiler flags for comparing them with the future flags
cc-options-dump: lib.o
#echo '$(COMPILE.c)' >$#
The behaviour of this makefile when calling make for the targets foo and/or bar corresponds, as far as I can see, to the desired one:
$ make foo
Rebuild required
cc -DFOO -c -o lib.o lib.c
$ make foo
Rebuild not required
$ make bar
Rebuild required
cc -DBAR -c -o lib.o lib.c
$ make bar
Rebuild not required
$ make foo bar
Rebuild required
cc -DBAR -DFOO -c -o lib.o lib.c
$ make bar foo
Rebuild not required
The use of the sort built-in function is crucial for the last two cases above to work correctly.
It would be great if someone could come with a more elegant solution.

specifying the filename in a makefile

I'm new to this and trying to create a makefile where I could, for example, run:
make -f mymakefile testfile
and the makefile would find testfile.java (which exists in the directory I'm running from), compile it, and run the code.
Instead, I must be confused with how automatic variables work and after working all afternoon I still get the error:
make: Nothing to be done for `testfile'.
Any help would be appreciated and my code is below:
JC=javac
JVM=java
JFLAGS= -g
RM = rm -f
CFLAGS =
CXX = gcc
NAME = *
.SUFFIXES: .java .class
all: run
NAME:
$(CXX) $(CFLAGS) -o $^ $#
echo $(NAME)
$(NAME).class: $(NAME)
$(JC) $(JFLAGS) $(NAME).java
run: $(NAME).class
$(JVM) $(NAME)
.PHONY: clean
clean:
$(RM) $(NAME).class
I've tried just having it echo 'testfile' to better understand how automatic variables work, but I couldn't get that to work correctly either.
The arguments on the make command line select the targets to build. You can't pass values to variables in the same way you would with a shell script (like you're trying to do with "NAME".)
If you really want to pass a value for a variable, the command would be:
NAME=testfile make -f mymakefile
You can use pattern rules to create rules from arbitrary names. For example:
%.bin : %.cpp
$(CXX) $(CXXFLAGS) -o $# $<
When you call make test.bin, this matches pattern rule %.bin, where % matches test. Then automatic variables $< substitutes the source file and $# substitutes the target file. What actually runs is something like g++ -O3 -o test.bin test.cpp.

How can i call make file from other directory

I have directory structure like this
containers/con1
containers/con2
containers/con3
Now every folder like con1, con2 has Makefile in it with targets like build, run
I run it like make run and make build
But i have to go inside that folder.
Is it possible that i have another Makefile in containers/Makefile
and i can run like
Make con1.run Make con2.run
Yes, you can do that. Something like the following should do what you want.
$ cat containers/Makefile
%.run: %
$(MAKE) -C $#
That being said as you can see the command to do what you want is trivial enough to make such a makefile not really necessary (and a simple shell script is as useful here as a makefile).
$ cat run.sh
[ -d "$1" ] || { echo 'No such directory.' >&2; exit 1; }
#make -C "$1"
# OR
#cd "$1" && make
If you wanted to be able to build all the sub-directory projects at once then a makefile could help you with that but even that is a simple enough shell one-liner.
$ for mkfile in */Makefile; do make -C "$(dirname "$mkfile"); done
$ for mkfile in */Makefile; do (cd "$(dirname "$mkfile") && make); done
As far as I understand you want this:
-C dir, --directory=dir
Change to directory dir before reading the makefiles or doing anything else. If multiple -C options are specified, each is interpreted relative to the previous one: -C / -C etc is equivalent to -C /etc. This is typi‐
cally used with recursive invocations of make.
Add -C option like this: make -C con1/
Recursive makes are evil, but if you want that:
# con1.run is a phony target...
.PHONY: con1.run
con1.run:
$(MAKE) -C con1

makefile not generating executeble files

I am fairly new at makefile. I am trying to create a makefile that compiles yu.c and link yu.c to get a executable file called yu.
Below is the code i have. I am using make -f mymakefile to run the file and each time i run it, only gcc -c yuFile.c is done.
Where am I wrong?
CC=gcc
yu.o: yuFile.c
${CC} -c yuFile.c
yu: yuFile.o
${CC} yu -o yuFile.o
run-c: yu
./yu
clean:
rm -f "*.o" core
make with no target specified makes the first target in the file, so in your case it is equivalent to make yu.o.
Typically, you make the first target all so that everything is built:
all: yu
.PHONY: all
(Note that the .PHONY prevents make from doing nothing if there's a file named all already).
Worth pointing out: you've got your arguments reversed in the yu target. It should be
yu: yuFile.o
${CC} yuFile.o -o yu
or more generically
yu: yuFile.o
${CC} $^ -o $#
($^ means all prereqs, and $# means the target, as documented in Automatic Variables).
The first target is the one picked when no argument is specified.
The standard is to have an 'all' target first:
all: yu
Also, your 'yu' target is incorrect.
yo: yufile.o
$(CC) -o $# $^
should work

Resources