Checking if file exists before removing it in makefile - makefile

I have a makefile within a C++ project (compiler: C++11). How can you check to see if a particular file exists before removing it with a makefile command?
Here is the code:
bin: charstack.h error.h
g++ -Wall -std=c++11 main.cpp charstack.cpp error.cpp -o bin
run:
./bin.exe
clean:
rm bin.exe
# This statement removes auto generated backups on my system.
cl:
rm charstack.h~ charstack.cpp~ main.cpp~ makefile~ error.h~ error.cpp~
How would I have the makefile check to see whether the auto generated .~ backup files exist before attempting to remove them when the user passes
make cl
in the command line? The goal here is to avoid outputting these errors to the terminal upon running "make cl":
rm: cannot remove `charstack.h~': No such file or directory
rm: cannot remove `charstack.cpp~': No such file or directory
rm: cannot remove `main.cpp~': No such file or directory
rm: cannot remove `error.h~': No such file or directory
rm: cannot remove `error.cpp~': No such file or directory
make: *** [cl] Error 1

Honestly, that's an XY problem, it is not due neither to the fact that the project is a C++ one nor that it uses the spec C++11.
Because of that, the title of the question is a bit misleading, as well as its tags.
Anyway, you can use the option -f. From the man page of rm:
ignore nonexistent files and arguments, never prompt
So, it's enough to use the following line:
rm -f charstack.h~ charstack.cpp~ main.cpp~ makefile~ error.h~ error.cpp~
Actually, it doesn't check if those files exist, but also it doesn't complain if they don't exist.

Even though this question has been answered, I attach a different solution that works for both files AND directories (because rm -rf DIRNAME is not silent anymore)
Here is a rule for removing a directory in variable ${OUTDIR} only if the directory exists. The example is easily adjusted for files:
clean:
if [ -d "${OUTDIR}" ]; then \
rm -r ${OUTDIR}; \
fi \
Note that the key observation is that you have to write the usual bash if-then-esle as if it where on a single line (i.e. using \ before a newline, and with a ; after each command). The example can be easily adapted to different (non-bash) shells.

Related

Shell Commands Using ( on Makefile

I'm preparing some latex files and decided to make some makefile to help me to compile and clean de latex files. So I created the following makefile
aula=listaProb
all: compile clean
compile:
pdflatex $(aula).tex
clean:
rm -rf !(makefile|$(aula).tex|$(aula).pdf) -v
But when I execute "make" I get the following mistake
rm -rf !(makefile|listaProb.tex|listaProb.pdf) -v
/bin/sh: 1: Syntax error: "(" unexpected
makefile:8: recipe for target 'clean' failed
make: *** [clean] Error 2
But the command
rm -rf !(makefile|listaProb.tex|listaProb.pdf) -v
works fine on the terminal.
What is wrong? I can't find any mistake :/..
Ps. I use this way to remove the files because I want to delete all but the specified files. It needs the command
shopt -s extglob
before use it. If anyone knows how to do it without use extglob, it would be nice.
Thanks
The problem is recipe commands are passed to /bin/sh which cannot process that syntax. You can change your Makefile to say:
clean:
bash -O extglob -c "rm -rf !(makefile|$(aula).tex|$(aula).pdf) -v"
To force this command to be run in bash with extglob on.
Or define SHELL variable for your make e.g. by running:
make SHELL="/bin/bash -O extglob" clean
Or adding:
SHELL := /bin/bash -O extglob
To your make file. The former option only affects shell invocation of that one command, the latter will apply to all your recipes (commands).

Why does this makefile recipe always run?

My Makefile downloads a number of third-party files if they are not locally available.
CLOSURE_VERSION=20161024
CLOSURE_BASE_URL="http://dl.google.com/closure-compiler"
build/bin/closure-compiler.jar: build/src/hashes/closure-compiler-${CLOSURE_VERSION}.tar.gz.sha256
download-if-sha-matches <$< >$#.tar.gz \
${CLOSURE_BASE_URL}/compiler-${CLOSURE_VERSION}.tar.gz
tar -zxf $#.tar.gz closure-compiler-v${CLOSURE_VERSION}.jar
mv closure-compiler-v${CLOSURE_VERSION}.jar $#
rm $#.tar.gz
Here, build/src/hashes/closure-compiler-${CLOSURE_VERSION}.tar.gz.sha256 is the saved hash of the version of the file which we already know is correct.
download-if-sha-matches <hash >outfile url downloads the url and compares its hash to stdin, failing if they don't match.
This recipe works except that it always runs, even if build/bin/closure-compiler.jar already exists. Naturally, its timestamp is later than that of $< so I would expect this to not execute the recipe the second time I run make.
What have I gotten wrong?
Looks like tar -x preserves the timestamps of the contained files.
Add this to the recipe.
touch $#

Makefile remove files

For a schoolproject I try around with makefiles. First I create the files with
install: main.c
gcc -asve-temps main.c
#if test ! -d bin/; then mkdir bin; else : fi
mv a.out $(shell pwd)/bin/
chmod 555 ./bin/a.out
Now I want to clear the project:
clear:
#if test -d *.[osia]; then rm *.[osia] else : ; fi
#if test -d a.out then rm a.out; else: ; fi
Running make install works fine. Running make clear produces the error code:
/bin/sh: 1: test: main.i: unexpected operator
and does not remove the requested files. I want to delete all the *.o *.s *.i and *.a files by running the make clear target using the pattern given above avoiding the error cannot remove ... : no such file or directory
test expects a single argument; when you pass it a glob, it's getting a bunch of them. Something like find will work in this case:
find . -maxdepth 1 -name '*.[osia]' -delete
Or, why check if the file exists at all?
rm -f *.[osia]
Couple of other notes: if you don't have an else clause in your if statement, don't include it. Read up on the test command; you certainly don't want to be using -d if you're looking for files. And, you can use the variable $PWD in place of running a subshell to get it.

Make fails to process a multi-line shell function

After showing here, that Make is quick to remove new-lines from a shell-function argumnet.
I tried this makefile:
# The original lines of the command were too long
# and in-fact, that's why i split them into 2 lines.
# For simplicity, I replaced the lines with single-character words.
define cmd
rm
dir
endef
x := $(shell $(cmd))
all:
#:
Running, I get:
make: rm
dir: Command not found
Does it mean, that Make passes the command "rm\ndir" (note the NL charachter in-between!)?
Because, that's certainly not what we see in the above link.
Yes it does. If you put an extra space after rm you should see the output rm: cannot remove ‘\ndir’: No such file or directory. If however, as I have previously answered here, you use
define cmd
rm dir
endef
or
define cmd
rm \
dir
endef
It will execute rm dir.

Understanding make-command in makefile?

I'm trying to modify a makefile to cross-compile binaries. The command in question is below:
# GNU Make solution makefile autogenerated by Premake
# Type "make help" for usage help
ifndef config
config=debug
endif
export config
PROJECTS := json openjaus openjaus-core openjaus-environment openjaus-mobility openjaus-manipulator openjaus-ugv Base Managed PingTest LargeMessageTest PdDemo GposDemo GposClientDemo StillImageSensorDemo StillImageClientDemo
.PHONY: all clean help $(PROJECTS)
all: $(PROJECTS)
json:
#echo "==== Building json ($(config)) ===="
#${MAKE} --no-print-directory -C .build -f json.make
As can be seen the makefile has several targets. They all have the same structure as the 'json' target. The command in question in question is
#${MAKE} --no-print-directory -C .build -f json.make
The '${MAKE}' variable = make (I have verified this with echo)
What does the -C do?
What does the .build do?
I'm good with -f json.make
Also, when I run make the json.make file gets created compiles file and deletes it self, so I do not have access to that file.
The error I receive when I modify the command in question is
==== Building json (debug) ====
make[1]: Nothing to be done for `/home/botbear/openwrt/trunk/staging_dir/toolchain- arm_v6k_gcc-linaro_uClibc-0.9.32_eabi/bin/arm-openwrt-linux-c++'.
The command after modifications looks like:
#${MAKE} /home/botbear/openwrt/trunk/staging_dir/toolchain-arm_v6k_gcc-linaro_uClibc-0.9.32_eabi/bin/arm-openwrt-linux-c++ --no-print-directory -C .build -f json.make
Any help is appreciated!
you can use man make to understand the parameters for make:
-C dir, --directory=dir
Change to directory dir before reading the makefiles or doing any-
thing else. If multiple -C options are specified, each is inter-
preted relative to the previous one: -C / -C etc is equivalent to
-C /etc. This is typically used with recursive invocations of
make.
-f file, --file=file, --makefile=FILE
Use file as a makefile.
so -C .build changes to the directory .build.
and i don't understand the part of your question about modifying the command.
Try to find where json.make lives. It seems that it's that makefile which creates & deletes the directory you were talking about.
From the command line it seems that make changes directory to .build and executes the json.make. Not sure how json.make ends up there. Is .build the directory which is created and then deleted?

Resources