Rebuilding object files with Makefile - makefile

Is there any way to cause a certain target to be rebuilt based on the value of a variable?
I have a Makefile with a command-line setting called DEBUG. I want certain object files to be rebuilt (to contain debug information) when I call "make DEBUG=yes" after haveing executed "make DEBUG=no".

Write the value of that variable into a file if the file doesn't exist or if it has a different value. Make all your object files depend on that file, so that whenever the file changes all object files get recompiled. E.g.:
BUILD := debug
SHELL := /bin/bash
$(shell [[ `cat .build 2>/dev/null` == ${BUILD} ]] || echo ${BUILD} > .build)
all : prog
prog : .build
touch $#
clean :
rm prog .build
.PHONY: all clean
Example run:
$ make
touch prog
$ make
make: Nothing to be done for `all'.
$ make BUILD=release
touch prog
$ make BUILD=release
make: Nothing to be done for `all'.
A better way is to build debug and release builds into different directories like MSVS does, but that requires a bit more advanced makefiles.

Related

make simple file copying yields "Nothing to be done"

I'm trying to simply copy files that are modified using make. Here is the entire Makefile:
FILES = www/foo.html www/bar.html www/zap.php
all: $(FILES)
$(FILES): src/$#
cp src/$# $#
clean:
rm $(FILES)
After modifying a file src/www/bar.html, make does not copy the file:
$ make
make: Nothing to be done for 'all'.
$ make www/bar.html
make: 'www/bar.html' is up to date.
Why does make not see the prerequisite has been modified and that the file needs to be copied?
If I run make clean, make it works (copies all files).
src/$# is not well-defined. You want
$(FILES): %: src/%
which declares a pattern rule, and restricts its scope to the files in $(FILES). (You might want or even need to remove this restriction.)

Force rebuild from within makefile

Is there a way to force a complete rebuild (eg -B) from within a makefile?
I have added a pre-build step that increments a build number, stored in a text file, that is used by my project. The build number is only incremented if newbuild=1 is passed as an argument to 'make'.
pre-build:
# This option increments build number
ifdef newbuild
increment_build_number
endif
If this code is called, I would like to force a complete rebuild so that nothing gets out of sync, and I don't have to type -B whenever newbuild=1 is used. Is there a way to do this?
Thanks!
You could:
declare the text file with the build number as a prerequisite of the other targets,
use make conditionals to also declare it phony if newbuild is defined.
Demo where build.txt is the text file with the build number and target is one of the targets to rebuild when newbuild is defined:
# Makefile
BUILD := build.txt
TARGETS := target
ifdef newbuild
.PHONY: $(BUILD)
endif
$(TARGETS): $(BUILD)
touch $#
$(BUILD):
#[ -f "$#" ] && build=$$(cat "$#") || build=0; \
printf 'build '; \
printf '%d\n' "$$((build+1))" | tee "$#"; \
$ make
build 1
touch target
$ make
make: 'target' is up to date.
$ make newbuild=1
build 2
touch target

How to determine if Make target is a PHONY?

I have a make target that depends on a variable, which contains both PHONY and real targets.
This target needs to depend only on the real targets in the variable.
How can I test a variable to determine if it is a PHONY or not, so I can filter them out?
(I can test for a file's existence inside the recipe, but I don't want my target to be triggered by execution of any of the PHONY targets.)
Thanks!
There is a way to do it, but I would strongly recommend against it. First of, phony targets can be also file targets. And there is no way to tell a phony file target from a non-phony file target.
It looks like the question implies that the phony targets the author wants to ignore are all non-file targets. In this case see the example below.
.PHONY: phony_target .FORCE
.FORCE:
ALL_TARGETS = phony_target file_target undetermined_target
-include detect_phony.inc
all: final_target
# All done
final_target: $(REAL_TARGETS)
# create $# triggered by $?
#touch $#
ifeq (,$(MAKE_RESTARTS))
# Generate the list of real file targets in make include file
detect_phony.inc: .FORCE
#echo 'REAL_TARGETS = ' `ls $(ALL_TARGETS) 2>/dev/null` > $# |:
endif
file_target:
touch $#
undetermined_target phony_target:
# process $#
clean:
rm -f file_target final_target
Here are the test results:
$make clean
rm -f file_target final_target
$ make
# create final_target triggered by
# All done
$ touch file_target
$ make
# create final_target triggered by file_target
# All done
$ make
# All done
As you can see it only triggers the final target when the file target is updated.
Before you criticize - Here are the flaws of this implementation:
make is always called twice, updating the generated detect_phony.inc include file at every run
if detect_phony.inc gets corrupted somehow, make execution will be locked by syntax errors, until you manually delete it.
it can't handle phony file targets as I mentioned before
if another generated include is added in this makefile that requires another restart before detect_phony.inc this functionality will break.
So it this method is hacky and has several gotchas. I would not use it in production environment. I would insist on changing the top level Makefile first.

How can make get confused without .PHONY targets?

I'm reading the make manual and I ended up on this paragraph:
Here is how we could write a make rule for cleaning our example
editor:
clean:
rm edit $(objects)
In practice, we might want to write the rule in a somewhat more
complicated manner to handle unanticipated situations. We would do
this:
.PHONY : clean clean :
-rm edit $(objects)
This prevents make from getting confused by an actual file called
clean and causes it to continue in spite of errors from rm. (See Phony
Targets, and Errors in Recipes.)
I understand that clean target does not refer to a file on the filesystem but how make can getting confused if I omit the .PHONY declaration on a target that should be declared as .PHONY?
I would see an example that confuses the make command.
P.S.: what does the minus symbol before rm command represent?
Thanks.
Let's test this. Fire your terminal of choice, navigate to your directory of choice and then create a new directory (to hold the test files). I chose so-27204300:
$ mkdir so-27204300
$ cd so-27204300
Now create a simple Makefile without the .PHONY annotation. I'll only show the contents:
clean:
echo "Clean is executing"
Execute the make command, you should get the following output:
$ make
echo "Clean is executing"
Clean is executing
Now, create a clean file (touch clean) and execute make again. You'll get
$ make
make: `clean' is up to date.
because there is a file clean newer than the output of the target clean. Add .PHONY clean to the Makefile so it will read
.PHONY: clean
clean:
echo "Clean is executing"
Now, even with the file clean in your directory, make will execute the target
$ make
echo "Clean is executing"
Clean is executing
Regarding your PS: change this Makefile so that it reads:
.PHONY: clean
clean:
rm test
echo "Done"
As you see, there's no - there but also there is no test file in the current directory. Running make will give you
$ make
rm test
rm: cannot remove ‘test’: No such file or directory
make: *** [clean] Error 1
And no output from echo. If the clean target was a prerequisite to another target then because of this error the other target would not have been built either.
Adding a - so that the Makefile reads:
.PHONY: clean
clean:
-rm test
echo "Done"
solves this problem:
$ make
rm test
rm: cannot remove ‘test’: No such file or directory
make: [clean] Error 1 (ignored)
echo "Done"
Done
For rm you can also use -f to let it stop complaining on errors. But there are other commands which don't have a silencer of errors argument.
If you want to hide the printing of command being executed you can use # instead of -.

Makefile pattern rule either ignores phony rule or spontaneously deletes output file

I'm trying to write a makefile to produce several output files for each of several sources, using pattern rules.
I have the following Makefile (GNU Make 3.8.1):
all : foo.all bar.all
%.all : %.pdf %.svg
#echo Made $*
%.pdf :
touch $#
%.svg :
touch $#
.PHONY: foo.all bar.all
Since *.all do not represent real output files, I tried marking them as .PHONY. However, running make then doesn't work:
$ ls
Makefile
$ make
make: Nothing to be done for `all'.
According to make -d:
No implicit rule found for `all'.
Considering target file `foo.all'.
File `foo.all' does not exist.
Finished prerequisites of target file `foo.all'.
Must remake target `foo.all'.
Successfully remade target file `foo.all'.
Considering target file `bar.all'.
File `bar.all' does not exist.
Finished prerequisites of target file `bar.all'.
Must remake target `bar.all'.
Successfully remade target file `bar.all'.
Finished prerequisites of target file `all'.
Must remake target `all'.
Successfully remade target file `all'.
make: Nothing to be done for `all'.
which seems to be pretending to run the %.all rules, but skipping the bodies.
But with the .PHONY line commented out, Make runs the targets, but then spontaneously decides to delete the output files:
$ make
touch foo.pdf
touch foo.svg
Made foo
touch bar.pdf
touch bar.svg
Made bar
rm foo.pdf foo.svg bar.pdf bar.svg
According to make -d, it says:
Removing intermediate files...
Minimal example
A minimal example giving anomalous behavior:
%.all: %.out
#echo Made $*
%.out:
touch $#
I expect running make somefile.all to cause it to create the file somefile.out, but it gets deleted:
$ make somefile.all
touch somefile.out
Made somefile
rm somefile.out
Keeping make from deleting intermediary files
I recommend against using .PRECIOUS (see below as to why). Using .SECONDARY would preserve the .out files:
TARGETS=foo bar
all: $(TARGETS:=.all)
%.all: %.out
#echo Made $*
%.out:
touch $#
.SECONDARY: $(TARGETS:=.out)
$(TARGETS:=.all) just appends .all to all names in TARGETS. $(TARGETS:=.out) appends .out. We apparently cannot use %.out as a target of .SECONDARY. These just save having to relist all targets individually.
I prefer to not use .PRECIOUS for this because the documentation says
if make is killed or interrupted during the execution of their recipes, the target is not deleted.
This can leave corrupted files in the file system. Here's an example.
all: foo.all bar.all
%.all: %.out
#echo Made $*
%.out:
sh -e -c 'echo "{1, 2, 3" > $#; FAIL!; echo "}" >> $#'
.PRECIOUS: %.out
The FAIL! command simulates a tool that crashes in the middle of its work. Here's a shell session working with the Makefile above:
$ ls
Makefile
$ make
sh -e -c 'echo "{1, 2, 3" > foo.out; FAIL!; echo "}" >> foo.out'
sh: 1: FAIL!: not found
make: *** [foo.out] Error 127
$ cat foo.out
{1, 2, 3
Yikes... my foo.out file is incomplete. Let's try making again:
$ make
Made foo
sh -e -c 'echo "{1, 2, 3" > bar.out; FAIL!; echo "}" >> bar.out'
sh: 1: FAIL!: not found
make: *** [bar.out] Error 127
$ cat *.out
{1, 2, 3
{1, 2, 3
Make is none the wiser about files left around by earlier runs so when you run make again, it will take the corrupted files at face value. foo.out was not remade (despite the "Made foo" message) because it already exists and the Makefile went straight to trying to make bar.
.SECONDARY makes it so that:
The targets which .SECONDARY depends on are treated as intermediate files, except that they are never automatically deleted.
This means they are never automatically deleted just because they are intermediate files. The default make behavior of deleting targets that were being rebuilt if the tool rebuilding them crashed is not affected.
Using .PHONY with pattern rules
It seems though that .PHONY works only for targets that are explicit, not inferred. I've not found documentation confirming this. However, this works:
TARGETS:=foo bar
TARGETS_all:=$(TARGETS:=.all)
.PHONY: all
all: $(TARGETS_all)
.PHONY: $(TARGETS_all)
$(TARGETS_all): %.all: %.out
#echo Made $*
%.out:
touch $#
.SECONDARY: $(TARGETS:=.out)
In this rule $(TARGETS_all): %.all: %.out $(TARGETS_all): gives the list of targets to which the pattern can be applied. It makes foo.all and bar.all explicit targets. Without this, they would be inferred targets.
You can test that it works by creating file called foo.all in your directory and run make over and over. The foo.all file has no effect on make.
Your somefile.out files are considered intermediate by GNU make, which is why they are automatically deleted in your example. You can instruct GNU make to preserve these files by use the of .PRECIOUS special target, like this:
%.all: %.out
#echo Made $*
%.out:
touch $#
.PRECIOUS: %.out

Resources