makefile conditional - makefile

TOUCHFILE=.touch
NM = meow
.PHONY: extract
extract: $(TOUCHFILE)
$(TOUCHFILE): $(ARCHIVE) Makefile
ifeq ($(wildcard TOUCHFILE),)
rm -rf $NM
touch $(TOUCHFILE)
else
#echo "nice going";
Shouldnt the above work? first time when I dont have the .touch, it removes the dir and creates .touch. next time I run with extract, it should echo nice going correct? I see everytime make removes the meow directory and does rest of stuff.

Note that a Make if-then-else must be terminated with endif, and the directives must not be preceded by TABs, since they are not commands. (Also note that your wildcard expression is wrong -- it searches for "TOUCHFILE", not ".touch"):
$(TOUCHFILE): $(ARCHIVE) Makefile
ifeq ($(wildcard $(TOUCHFILE)),)
rm -rf $NM
touch $(TOUCHFILE)
else
#echo "nice going";
endif

Related

How to check if a file with a certain extension exists using a makefile

I want to check whether exists files with a certain extension in a makefile, however this piece of code does not work:
ejecutar: $(OUTPUT) clean
ifeq (,$(wildcard *.dat))
./$(OUTPUT) < $(OUTPUT).dat >$(OUTPUT).txt
else
./$(OUTPUT) < $(OUTPUT).dat >$(OUTPUT).txt
The error is said to be in the ifeq line.
The stuff in the recipe should be shell script, not Makefile syntax. Anything that Make interprets gets expanded as the Makefile is being read, where you typically want your recipe to examine things as they are when that specific recipe is executed. (This is a common beginner FAQ.)
Checking whether a wildcard matches any files in shell script is surprisingly unobvious, too.
ejecutar: $(OUTPUT) clean
set -- *.dat \
; if [ -e "$$1" ]; then \
./$< < $<.dat >$<.txt; \
else \
./$< < $<.dat >$<.txt; \
fi
It's also weird that your then and else cases are identical, but I'm not judging.

gnu make - recipe to keep installed version of file aligned with a master version of file

So here's a Makefile to install foo.conf, based on a master copy called foo.conf.master. It installs it to the current directory rather than /etc, just for testing purposes:
all: foo.conf.copied
foo.conf.copied: foo.conf.master foo.conf
cp foo.conf.master foo.conf
touch $#
# Recipe to tell make that it is okay for foo.conf not to exist beforehand.
foo.conf:
So then create foo.conf.master:
$ touch foo.conf.master
$
and you're ready to test:
$ make
cp foo.conf.master foo.conf
touch foo.conf.copied
$
The point is that if I (with my "trusted" sysadmin hat on) modify foo.conf.master then make (possibly called by cron) will roll out the update:
$ touch foo.conf.master
$ make
cp foo.conf.master foo.conf
touch foo.conf.copied
$
But equally important: if I (with my "rogue" sysadmin hat on) modify the installed version then make will back out the update:
$ touch foo.conf
$ make
cp foo.conf.master foo.conf
touch foo.conf.copied
$
Woohoo.
Okay, so now the problem: obviously foo.conf isn't the only file I want do this for, so I need to change my static rules to pattern rules. Okay, that's easy: substitute foo.conf for % in targets and dependencies, substitute foo.conf for $* in the commands, and make a minor modification to the last recipe (which would otherwise become only '%:') so that it doesn't look like I'm trying to cancel a builtin pattern rule.
So clean up and create this Makefile:
all: foo.conf.copied
%.copied: %.master %
cp $*.master $*
touch $#
# Recipe to tell make that it is okay for foo.conf not to exist beforehand.
# Nop tells make that I'm not *cancelling* a pattern rule here
# (see http://stackoverflow.com/questions/34315150/make-implicit-rules-dont-work-without-command).
%: ;
But this doesn't work:
$ make
make: *** No rule to make target `foo.conf.copied', needed by `all'. Stop.
$
The error message is misleading; it is really foo.conf that it doesn't know how to make, which can be demonstrated by adding the following at the bottom of the Makefile:
foo.conf:
touch $#
But then that's a static rule again, which I don't want.
There are a couple more requirements I would also like to satisfy, which the above example doesn't demonstrate. These are:
foo.conf should be installable anywhere in the filesystem (e.g. /etc/foo/server/foo.conf)
foo.conf.master should be in a central directory, or subdirectly thereof, for all master versions, preferably without the '.master' extension (e.g. ~/poor-mans-puppet/master-files/etc/foo/foo.conf)
foo.conf.copied should be in a central directory, not in the same directory as foo.conf (e.g. ~/poor-mans-puppet/timestamp-files/etc/foo/foo.conf)
After much googling, hair pulling, I'm asking here! Any ideas please? (PS: if copying Makefiles from here, remember to change indentation back to tabs.)
Mad Scientist below suggested an elegant static rule, but I really need it to be a pattern rule. The reason is that I need to hook extra dependencies in using rules:
all: <new-dependency>
rather than hooking them in using variables:
STUFF_ALL_SHOULD_DEPEND_ON += <new-dependency>
The reason for this requirement is for consistency with how other (non-%.copied) targets are handled in my very large Makefile.
However, based on Mad Scientist's idea, I tried the following, which didn't work, but perhaps helps somebody to help me:
all: foo.conf.copied
%.copied: %.master %
$(eval FILES_FOR_WHICH_AN_EMPTY_RECIPE_ARE_NEEDED += $$*)
cp $*.master $*
touch $#
define GENERATE_STATIC_EMPTY_RULE
$(1):
endef
$(foreach X,$(FILES_FOR_WHICH_AN_EMPTY_RECIPE_ARE_NEEDED),$(eval $(call GENERATE_STATIC_EMPTY_RULE,$(X))))
Can you explain why you're using this extra ".copied" file? Why don't you just use:
%: %.master ; cp $< $#
?
Anyway, you're running afoul of make's special rules related to match-anything rules (pattern rules like % that can build everything). If you change your pattern so it's not match-anything, like %.conf: ; then it will work. However you probably don't want to assume that all files end in .conf.
Alternatively you can use static pattern rules, like this:
FILES_TO_COPY = foo.conf bar.conf biz.baz
all: $(FILES_TO_COPY:%=%.copied)
$(FILES_TO_COPY:%=%.copied): %.copied : %.master %
cp $*.master $*
touch $#
and you don't need the extra pattern rule.
In the end, I dynamically generated static rules. The following pseudo-code hopefully makes the actual Makefile easier to understand:
if flag not set # flag won't be set on first call
prepare static rules
set flag # prevent running this clause again
recurse! # make invokes make
else
include static rules
do the normal thing
endif
Here's the real Makefile:
ifeq ($(MAKELEVEL),0)
all:
for X in $(patsubst %.copied,%,$^); do \
echo "$$X.copied: $$X.master $$X"; \
echo " cp $$X.master $$X"; \
echo " touch \$$#"; \
echo "$$X: ;"; \
done > Makefile.include
$(MAKE)
# The real dependencies on all are defined below, but while creating
# Makefile.include, we don't want make to digress and start making
# the dependencies; this pattern rule will stop it from doing that.
%.copied: ;
else
include Makefile.include
endif
all: foo.conf.copied
The outer make can be silenced by use of .SILENT and the --no-print-directory option (not shown above for clarity).
Here's the output:
$ touch foo.conf.master
$ make
cp foo.conf.master foo.conf
touch foo.conf.copied
$ touch foo.conf
$ make
cp foo.conf.master foo.conf
touch foo.conf.copied
$

GNU Make - Set a flag if multiple files exist

In my Makefile, I check if a file exists, and execute different code depending on the result of that check.
Previously, there was only one relevant file, OUTLIB_CACHE.
OUTLIB_CACHE := filename
OUTLIB_CACHE_EXITS := $(shell if [ -f $(OUTLIB_CACHE) ]; then echo "1"; fi;)
ifneq ($(OUTLIB_CACHE_EXISTS),1)
# do something
endif
Now I am introducing a second relevant file, storing both of the file names in a list:
OUTLIB_CACHE_LIST := filename1 filename2
I want to set the OUTLIB_CACHE_EXISTS flag to true if and only if all the files in OUTLIB_CACHE_LIST exist. What would be the best way to do this?
You could substitute each element of OUTLIB_CACHE_LIST with a command, then execute the resulting commands in a shell:
OUTLIB_CACHE_MISSING := $(shell $(patsubst %,test -e "%" || echo missing;,$(OUTLIB_CACHE_LIST)))
If all the members exist, then the output of the shell command will be empty, else it will contain one word for each missing file. You can test for emptiness:
ifneq ($(OUTLIB_CACHE_MISSING),)
# do something
endif
If you want to know which files are missing, you can't just replace ! with %, because patsubst only replaces the first % it finds. Instead, you could use foreach:
OUTLIB_CACHE_MISSING := $(shell $(foreach f,$(OUTLIB_CACHE_LIST),test -e "$f" || echo "$f";))
Putting this all together in a testable example:
OUTLIB_CACHE_LIST := /etc /bin
#OUTLIB_CACHE_LIST += /nonexistent
OUTLIB_CACHE_MISSING := $(shell $(foreach f,$(OUTLIB_CACHE_LIST),test -e "$f" || echo "$f";))
.PHONY: all
all:
ifneq ($(OUTLIB_CACHE_MISSING),)
false "$(OUTLIB_CACHE_MISSING)"
else
true "No missing files"
endif
Uncomment the second line to select the first branch of the if.
N.B. You wanted to know whether the files exist, so I've used test -e rather than -f or -r. You'll know which test is actually appropriate for your case...
There is no need to use shell functions to test for the existence of multiple files in make. I use the following construct to make make output decent error messages for a very convoluted makefile. I presume you can adopt it to your needs if need be. The original idea (probably) comes from https://stackoverflow.com/a/20566812/1905491.
$(foreach p,$(ALL_INCLUDES),$(if $(wildcard $(p)),,$(info $(p) does not exist!) $(eval err:=yes)))
$(if $(err),$(error Aborting),)
Simply define OUTLIB_CACHE_EXISTS as follows
OUTLIB_CACHE_EXISTS := $(shell if ls $(OUTLIB_CACHE_LIST) >/dev/null 2>&1; then echo "1"; fi)
This should work
ifeq ($(OUTLIB_CACHE_LIST),$(wildcard $(OUTLIB_CACHE_LIST)))
# do something
endif

Target's directory in prerequisites without second expansion

Lets assume, i want to call
make somepath/abc.pot
which depends on somepath/somefiles.c
My target I've created so far looks like
%.pot: $(dir $#)*.c
#echo "it works"
ifeq (,$(wildcard $#))
# pot-file does not exist, do something
else
# pot-file already exists, do something else
endif
but does not work as the Automatic Variables
like $# are not available in the prerequisites.
If found, that i can enable second expansion
.SECONDEXPANSION:
%.pot: $$(dir $$#)*.c
#echo "it works"
ifeq (,$(wildcard $#))
# pot-file does not exist, do something
else
# pot-file already exists, do something else
endif
which allows me to use $# in the prerequisites but breaks my ifeq statement which then always results in the first branch. If I change the ifeq to
ifeq (,$$(wildcard $$#))
it's working again but I really don't get why.
Now there a two questions:
A) Is there another way but to enable second expansion to have the path of the target in my prerequisites?
B) Why does the ifeq (,$(wildcard $#)) statement always result in the first branch if second expansion is enabled?
Don't use ifeq in the recipe at all. Just use normal shell functionality. It works better.
.SECONDEXPANSION:
%.pot: $$(dir $$#)*.c
#echo "it works"
if [ ! -f $# ]; then \
: pot-file does not exist, do something; \
else \
: pot-file already exists, do something else; \
fi
That said using wildcard in prerequisite lists is generally a bad idea because the time that they are globbed is not reliable and can cause odd behaviors. See Pitfalls of Using Wildcards for one example of a problem.
If you need to write different recipe contents based on some external factor (like OS) then you need to detect that at make parse time and have two copies of your recipes/makefile that you switch between correctly. You can do that inline but you can't do that per-recipe inline.
Your original attempts (using ifeq in a recipe) do not work. They don't do what you think they do. They may appear to work but they aren't working the way you expect.
Consider this makefile:
all: c
a:
#touch a
c: a
.SECONDEXPANSION:
c d:
ifeq (,$(wildcard a))
#echo "a doesn't exist (make)"
else
#echo 'a does exist (make)'
endif
#if [ ! -f a ]; then \
echo "a doesn't exist (sh)"; \
else \
echo 'a does exist (sh)'; \
fi
ifeq (,$$(wildcard a))
#echo "a doesn't exist (make se)"
else
#echo 'a does exist (make se)'
endif
In an empty directory you would expect make to output (assuming ifeq works the way you want it to):
a does exist (make)
a does exist (sh)
a does exist (make se)
Right? But it doesn't. You get:
a doesn't exist (make)
a does exist (sh)
a does exist (make se)
Ok, you think, that's just things not working without secondary expansion. But the secondary expansion version is working correctly. But it isn't.
If you run make d in an empty directory (note the d target doesn't list a as a prerequisite so it won't create it) you would expect the following output:
a doesn't exist (make)
a doesn' exist (sh)
a doesn' exist (make se)
Right? But what you actually get is:
a doesn't exist (make)
a doesn't exist (sh)
a does exist (make se)
So it appears that the secondary expansion version isn't working either.
A look at the make database explains why not.
Run make -qprR | awk '/c: a/,/^$/; /d:/,/^$/' in an empty directory and you see:
c: a
# Implicit rule search has not been done.
# File does not exist.
# File has been updated.
# Needs to be updated (-q is set).
# variable set hash-table stats:
# Load=0/32=0%, Rehash=0, Collisions=0/2=0%
# commands to execute (from `Makefile', line 12):
#echo "a doesn't exist (make)"
#if [ ! -f a ]; then \
echo "a doesn't exist (sh)"; \
else \
echo 'a does exist (sh)'; \
fi
#echo 'a does exist (make se)'
d:
# Implicit rule search has not been done.
# Modification time never checked.
# File has not been updated.
# commands to execute (from `Makefile', line 12):
#echo "a doesn't exist (make)"
#if [ ! -f a ]; then \
echo "a doesn't exist (sh)"; \
else \
echo 'a does exist (sh)'; \
fi
#echo 'a does exist (make se)'
Which, as you can see, doesn't contain the ifeq lines but just the "correct" branch of the ifeq logic.
And that's the problem, the ifeq conditionals are evaluated at make parse time which is well before any recipes run (and thus before any files can be created, etc.).

Suppress messages in make clean (Makefile silent remove)

I'm wondering how I can avoid some echo in a Makefile :
clean:
rm -fr *.o
this rule will print:
$>make clean
rm -fr *.o
$>
How can I avoid that?
To start with: the actual command must be on the next line (or at least that is the case with GNU Make, it might be different with other Make's - I'm not sure of that)
clean:
rm -rf *.o
(note, you need a TAB before rm -rf *.o as in every rule)
Making it silent can be done by prefixing a #:
so your makefile becomes
clean:
#rm -rf *.o
If there are no *.o files to delete, you might still end up with an error message. To suppress these, add the following
clean:
-#rm -rf *.o 2>/dev/null || true
2>/dev/null pipes any error message to /dev/null - so you won't see any errors
the - in front of the command makes sure that make ignores a non-zero return code
In fact I was looking for something else, adding this line to the Makefile :
.SILENT:clean
while execute every step of the "clean" target silently.
Until someone point some drawback to this, I use this as my favourite solution!
I'm responding to this ancient topic because it comes up high in search and the answers are confusing. To do just what the user wants,all that is needed is:
clean:
#rm -f *.o
The # means that make will not echo that command.
The -f argument to rm tells rm to ignore any errors, like there being no *.o files, and to return success always.
I removed the -r from the OPs example, because it means recursive and here we are just rming .o files, nothing to recurse.
There's no need for the 2>&1 >/dev/null because with the -f there will be no errors printed.
.SILENT: clean
works in place of the #, but it isn't at the same place in the Makefile as the command that it affects, so someone maintaining the project later might be confused. That's why # is preferred. It is better locality of reference.
If you put an # in front of the command, it doesn't echo onto the shell. Try changing rm to #rm. (Reference)
From the manual: .SILENT is essentially obsolete since # is more flexible.
Much worse is that make prints far too much information. Warning/error/private messages are buried in the output. On the other hand -s (.SILENT) suppresses just anything. Especially the "nothing to be done" and "up to date" messages can be a pain. There is no option to suppress them. You have to filter them out actively or use something like colormake. Here is a solution for grep:
make | egrep -hiv 'nothing to be done|up to date'
But the output will have line numbers. The Perl solution is therefore better, because it suppresses line numbers and flushes stdout immediately:
make | perl -ne '$|=1; print unless /nothing to be done|up to date/i'
Make's a flawed tool. "What’s Wrong With GNU make?" explains this better than I can.
There's a great article on using .SILENT that explains how to conditionally activate it.
I have used that information to put this in my Makefile:
# Use `make V=1` to print commands.
$(V).SILENT:
# Example rule, only the #echo needs to be added to existing rules
*.o: %.c
#echo " [CC] $<"
gcc ...
What this does is if you run make normally, normal output is silenced and instead the echo commands work:
$ make
[CC] test.c
[CC] test2.c
But it allows you to debug problems by passing the V=1 parameter, which still shows the [CC] messages as it helps break up the output, but the traditional Makefile output is also visible:
$ make V=1
[CC] test.c
gcc ...
[CC] test2.c
gcc ...

Resources