Is there a way to make the command make clean require user confirmation? I mistakenly executed it and now I have to wait 6 hours for the build to complete again.
The Makefiles were created by cmake.
Desired workflow:
> make clean
> [make] Are you sure you want to remove all the built files? [Y/N]
> N
> [make] Target 'make clean' not executed.
> make clean
> [make] Are you sure you want to remove all the built files? [Y/N]
> Y
> [make] Target 'make clean' executed.
I'm not familiar with cmake, but for gnu make, one possible hack would be:
clean: check_clean
check_clean:
#echo -n "Are you sure? [y/N] " && read ans && [ $${ans:-N} = y ]
.PHONY: clean check_clean
If check_clean fails (user does not type in y), then make will exit with an error before performing the clean.
Makefile:
clean:
#read -p "Are you sure? [y/N] " ans && ans=$${ans:-N} ; \
if [ $${ans} = y ] || [ $${ans} = Y ]; then \
printf $(_SUCCESS) "YES" ; \
else \
printf $(_DANGER) "NO" ; \
fi
#echo 'Next steps...'
_SUCCESS := "\033[32m[%s]\033[0m %s\n" # Green text for "printf"
_DANGER := "\033[31m[%s]\033[0m %s\n" # Red text for "printf"
Related
I'm building a Linux from scratch on my old Android phone in a chrooted environment (which can be part of the problem), on a mounted ext4 partition on the sd card. I managed to cross compile install most of the stuff I need to build locally there. I installed the latest release of everything.
And now time to make something in place without cross compilation.
I decided to make and install inetutils-1.9.4.
The configure runs just fine, but when I try to make, it just fails without saying a thing:
# make
make all-recursive
make[1]: Entering directory '/tmp/inetutils_build'
Making all in lib
make[1]: *** [Makefile:1491: all-recursive] Error 1
make[1]: Leaving directory '/tmp/inetutils_build'
make: *** [Makefile:1428: all] Error 2
So I looked at the generated makefile and it contains this rule:
$(am__recursive_targets):
#fail=; \
if $(am__make_keepgoing); then \
failcom='fail=yes'; \
else \
failcom='exit 1'; \
fi; \
dot_seen=no; \
target=`echo $# | sed s/-recursive//`; \
case "$#" in \
distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
*) list='$(SUBDIRS)' ;; \
esac; \
for subdir in $$list; do \
echo "Making $$target in $$subdir"; \
if test "$$subdir" = "."; then \
dot_seen=yes; \
local_target="$$target-am"; \
else \
local_target="$$target"; \
fi; \
($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
|| eval $$failcom; \
done; \
if test "$$dot_seen" = "no"; then \
$(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
fi; test -z "$$fail"
After inserting some echos I have found that the line with the $(am__cd)... fails, and the ored expression runs which exits the script with failure. So I attempted to find out which command fails there, and inserted some echos within the parenthesis, like this:
(echo "1" && $(am__cd) $$subdir && echo "2" && $(MAKE) $(AM_MAKEFLAGS) $$local_target && echo "3") \
|| eval $$failcom; \
Now it prints:
make all-recursive
make[1]: Entering directory '/tmp/inetutils_build'
Making all in lib
1
make[1]: *** [Makefile:1491: all-recursive] Error 1
make[1]: Leaving directory '/tmp/inetutils_build'
make: *** [Makefile:1428: all] Error 2
However I wanted to make sure the echo doesn't fail, so I added another one.
(echo "1" && echo "1b" && $(am__cd) $$subdir && echo "2" && $(MAKE) $(AM_MAKEFLAGS) $$local_target && echo "3") \
|| eval $$failcom; \
When I tried it. It again prints just 1.
I didn't give it up just yet.
I wanted to make sure echo really fails within the script.
So I tried adding new statements into the script, before the problematic command:
(echo "A" && echo "B"); \
(echo "X" || echo "Y"); \
echo "N" && echo "M"; \
(echo "C" && echo "D"); \
It prints:
A
X
Y
N
M
C
D
Then it prints
1
1b
2
and starts building!
I'm totally puzzled here. Why does the first two parenthesized expressions fail, but not the last one?
In fact even writing (true) || echo "ooops" fails at first, but not when I paste it below the ored X Y parenthesis. Apparently the ored parenthesis "fixes" it, whatever state it got stuck on.
What is going on?
It should be noted I cannot reproduce this at the shell prompt but only the statements inserted into the makefile does this.
EDIT:
By removing the # from the beginning it echoes back the command but nothing else, no errors:
fail=; \
if (target_option=k; case ${target_option-} in ?) ;; *) echo "am__make_running_with_option: internal error: invalid" "target option '${target_option-}' specified" >&2; exit 1;; esac; has_opt=no; sane_makeflags=$MAKEFLAGS; if { if test -z '1'; then false; elif test -n 'arm-unknown-linux-gnueabi'; then true; elif test -n '4.2' && test -n '/tmp/inetutils_build'; then true; else false; fi; }; then sane_makeflags=$MFLAGS; else case $MAKEFLAGS in *\\[\ \ ]*) bs=\\; sane_makeflags=`printf '%s\n' "$MAKEFLAGS" | sed "s/$bs$bs[$bs $bs ]*//g"`;; esac; fi; skip_next=no; strip_trailopt () { flg=`printf '%s\n' "$flg" | sed "s/$1.*$//"`; }; for flg in $sane_makeflags; do test $skip_next = yes && { skip_next=no; continue; }; case $flg in *=*|--*) continue;; -*I) strip_trailopt 'I'; skip_next=yes;; -*I?*) strip_trailopt 'I';; -*O) strip_trailopt 'O'; skip_next=yes;; -*O?*) strip_trailopt 'O';; -*l) strip_trailopt 'l'; skip_next=yes;; -*l?*) strip_trailopt 'l';; -[dEDm]) skip_next=yes;; -[JT]) skip_next=yes;; esac; case $flg in *$target_option*) has_opt=yes; break;; esac; done; test $has_opt = yes); then \
failcom='fail=yes'; \
else \
failcom='exit 1'; \
fi; \
dot_seen=no; \
target=`echo all-recursive | sed s/-recursive//`; \
case "all-recursive" in \
distclean-* | maintainer-clean-*) list='lib libinetutils libtelnet libicmp libls src telnet telnetd ftp ftpd talk talkd whois ping ifconfig doc man tests' ;; \
*) list='lib libinetutils libtelnet libicmp libls src telnet telnetd ftp ftpd talk talkd whois ping ifconfig doc man tests' ;; \
esac; \
for subdir in $list; do \
echo "Making $target in $subdir"; \
if test "$subdir" = "."; then \
dot_seen=yes; \
local_target="$target-am"; \
else \
local_target="$target"; \
fi; \
(CDPATH="${ZSH_VERSION+.}:" && cd $subdir && make $local_target) \
|| eval $failcom; \
done; \
if test "$dot_seen" = "no"; then \
make "$target-am" || exit 1; \
fi; test -z "$fail"
EDIT 2:
Thanks for the tips, I added set -x to the beginning of the rule. These are the commands:
+ fail=
+ target_option=k
+ case ${target_option-} in
+ has_opt=no
+ sane_makeflags=w
+ test -z 1
+ test -n arm-unknown-linux-gnueabi
+ true
+ sane_makeflags=-w
+ skip_next=no
+ for flg in $sane_makeflags
+ test no = yes
+ case $flg in
+ case $flg in
+ test no = yes
+ failcom='exit 1'
+ dot_seen=no
++ echo all-recursive
++ sed s/-recursive//
+ target=all
+ case "all-recursive" in
+ list='lib libinetutils libtelnet libicmp libls src telnet telnetd ftp ftpd talk talkd whois ping ifconfig doc man tests'
+ for subdir in $list
+ echo 'Making all in lib'
Making all in lib
+ test lib = .
+ local_target=all
+ CDPATH=:
+ eval exit 1
++ exit 1
Did you put SHELL := /bin/bash at the top of your makefile? Otherwise, make(1) tends to invoke /bin/sh as the shell, which has very different properties.
Goal is to apply patch ONLY if patch is not present. If patch is present don't do anything.
I used below makefile rule.
C_FILE_PATCH_SIG=###MAGIC_CODE;
C_FILE_CODE=~/code/file.c
C_PATCH_FILE=~/test.patch
.tmp/patch_c:
cp ${C_PATCH_FILE} ${SDK}
ifneq ($(PATCH_DONE), 1)
$(MAKE) applypatch || $(MAKE) helppatch
endif
#echo DONE > .tmp/patch_c
applypatch:
#echo "Patching ${C_FILE_CODE}"
if grep -Fq '${C_FILE_PATCH_SIG}' ${C_FILE_CODE} ; \
then \
echo 1 > .tmp/PATCH_PRESENT_file; \
else \
echo 0 > .tmp/PATCH_PRESENT_file;\
fi
cat .tmp/PATCH_PRESENT_file
# $(eval PATCH_PRESENT := `cat .tmp/PATCH_PRESENT_file`)
$(eval PATCH_PRESENT := $(shell cat .tmp/PATCH_PRESENT_file))
#echo "WWWWWW PATCH_PRESENT=[$(PATCH_PRESENT)] WWWWWWW"
ifeq ($(PATCH_PRESENT), 0)
#echo "Applying the patch $(PATCH_PRESENT)"
cd ~/code && git apply -v ${C_PATCH_FILE}
else
#echo "NOT Applying the patch $(PATCH_PRESENT)"
endif
helppatch:
#echo -e "\n\n\n"
#echo -e "++++++++++ Apply below patch manually then run 'make build PATCH_DONE=1' ++++++++++\n\n"
#echo -e "+++++++++++++++++++++++++++++++++++++"
#cat ${C_PATCH_FILE}
#echo -e "+++++++++++++++++++++++++++++++++++++"
#echo -e "\n\n\n"
false
But it always evaluates to the else part of ifeq.
Where am I doing wrong?
If I use the patch command of git withing the shell multiline I loose the error code returned by the git patch.
Thanks in advance...
Your ifeq will be evaluated when the makefile is first read (as opposed to when the recipe is run). The eval, on the other hand, will not be executed until the recipe is run (afterwards). Thus, PATCH_PRESENT is not equal to 0 at parse time, and make will expand the else portion of the clause. By the time the eval is run, the if statement is already evaluated and gone from memory.
BTW, a cleaner way to do this is to do everything in bash:
applypatch:
#echo "Patching ${C_FILE_CODE}"
#if grep -Fq '${C_FILE_PATCH_SIG}' ${C_FILE_CODE}; then \
echo "NOT Applying the patch"; \
else \
echo "Applying the patch"; \
cd ~/code && git apply -v ${C_PATCH_FILE}; \
fi
%/all:
if [ -f $(#D)/src/Makefile ]; then \
$(MAKE) -C $(#D); \
fi
If the inner make fails, the outer make continues, presumably because the implicit exit status of the 'if' command is 0. Is there a way around this?
This cannot be a real example. The shell will exit with the result of the last command executed, which if the if-statement succeeds will be the exit code of make, which is what you want. So obviously in your real code, you must be doing some other command between the make and the end. You can keep a copy of the result in a variable and use that as the exit:
if [ -f $(#D)/src/Makefile ]; then \
$(MAKE) -C $(#D); \
r=$$?; \
...do other stuff...; \
exit $$r; \
fi
Somehow I couldn't reproduce your problem but I suppose the following should work for you :
%/all:
if [ -f $(#D)/src/Makefile ]; then \
$(MAKE) -C $(#D) || (echo "make failure: $$?"; exit 1) \
fi
I'm trying to do this ..... and this is how my Makefile look like
.PHONY: run
SHELL := /bin/tcsh
run:
md5sum -c md; \
if ($$?==0) then \
echo "PASS" \
else \
echo "FAIL" \
endif
But i got this error.
if: Badly formed number.
make: *** [run] Error 1
Is what I'm doing correct? Or is there a better way of doing that in a Makefile?
Basically, you should simply not, ever, use csh (or tcsh) for writing makefile rules. Write the rule using POSIX shell:
.PHONY: run
run:
md5sum -c md; \
if [ $$? -eq 0 ]; then \
echo "PASS"; \
else \
echo "FAIL"; \
fi
If I have the following bash command:
for i in ./ x ; do ls $i ; done && echo OK
"ls ./" is executed, and then "ls x", which fails (x is missing) and OK is not printed.
If
for i in x ./ ; do ls $i ; done && echo OK
then even though "ls x" fails, because the last statement in the for loop succeeded, then OK is printed. This is a problem when using shell for loops in makefiles:
x:
for i in $(LIST) ; do \
cmd $$i ;\
done
How can I make make fail if any of the individual executions of cmd fails?
Use the break command to terminate the loop when a command fails
x:
for i in $(LIST) ; do \
cmd $$i || break ;\
done
That won't make the makefile abort, though. You could instead use exit with a non-zero code:
x:
for i in $(LIST) ; do \
cmd $$i || exit 1 ;\
done
After executing the command, check for return value of that command using $?, as its make file you need to use double $. If its non zero, then exit with failure.
x:
set -e
for i in $(LIST); do \
cmd $$i; \
[[ $$? != 0 ]] && exit -1; \
echo 'done'; \
done