Unexport all variables for a particular Makefile recipe - makefile

I was wondering if it was possible to call make from within a makefile recipe, but have it so the new invokation does not inherit the current makefile's exported variables.
For example:
include $(TRG).vars
OLD_TRG=`cat .last_trg`
all:
if [ $(TRG) -ne $(OLD_TRG) ]; then \
read -p "Clean required -- press enter"; \
make clean TRG=$(OLD_TRG); \
fi
echo $(TRG) > .last_trg
clean:
[ $(SOMEVAR) ] && rm -rf $(SOMEVAR)
If, for the above, SOMEVAR was defined in $(TRG).vars, but not in $(OLD_TRG).vars, then the make clean invocation would inherit SOMEVAR from the parent makefile, and might clean the wrong directory. I would want the make clean to not inherit any of the environment from the current makefile so it could clean independently.

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.

Makefile cutting out variables in for loop

I'm writing my first complex Makefile for a highly-modularized project.
I have various sub-directories, each one has its own Makefile which supports at least the all and the clean targets.
These sub-Makefiles work just fine, however I have a problem with the main Makefile, that should call all the sub-Makefiles automatically from the list contained in the variable COMPONENTS.
I tried with the following Makefile:
OUTFILE = diskimage.bin
export NASM = nasm
COMPONENTS = bootloader
.PHONY = all clean FORCE $(OUTFILE) $(COMPONENTS)
all: $(OUTFILE)
$(OUTFILE): $(COMPONENTS)
./make_image
$(COMPONENTS): FORCE
for component in $(COMPONENTS); do \
make -C $component; \
done
FORCE:
clean:
for component in $(COMPONENTS); do \
make -C $component clean; \
done
This results in the following error message:
for component in bootloader; do \
make -C omponent; \
done
make: *** omponent: No such file or directory. Stop.
make: *** [bootloader] Error 2
as if the $component expression was only parsed as $c. I don't understand why that happens and how to fix it.
Just double the dollar sign:
$(COMPONENTS): FORCE
for component in $(COMPONENTS); do \
make -C $$component; \
done
The trouble is that with your makefile, Make expands $component before executing the rule. And since $c has no value (there is no such variable), it expands to nothing, leaving "omponent", which it passes to she shell, which complains that there's no such directory. (If you had written $(component), Make would have expanded it to nothing, since Make knows of no such variable, and then the shell would have complained that you were not specifying a directory at all.)
With the double dollar sign, Make expands $$component to $component, which it then passes to the shell, which interprets it as the loop variable, and everything proceeds as planned.
You really should have played around with a simple loop in a command, before attempting to do actual work with one.
Several issues.
.PHONY should be written as a dependency, not a macro definition
Don't write shell loops, use make syntax instead
When you call make recursively, you must do it via the ${MAKE} macro invocation
Leading to
OUTFILE = diskimage.bin
export NASM = nasm
COMPONENTS = bootloader
.PHONY: all
all: ${OUTFILE}
.PHONY: ${OUTFILE}
${OUTFILE}: ${COMPONENTS}
./make_image
.PHONY: ${COMPONENTS}
${COMPONENTS}:
${MAKE} -C $#
The advantage of this formulation is that it is parallel make friendly.
Always a test of a good Makefile.
Here make -j5 all will cause make to keep 5 commands running at once,
across all invocations of make.
Nice if you have 4 CPUs.
What about clean?
(Personally I hate clean targets—it's a sign of dodgy dependencies,
and of unhygienic mixing of source and target folders.)
Just add -clean (say) to each of the component names,
and repeat the pattern above.
CLEANS := $(addsuxffix -clean,${COMPONENTS})
.PHONY: clean
clean: ${CLEANS} ; #echo Clean succesful
.PHONY: ${CLEANS}
${CLEANS}: %-clean:
${MAKE} -C $* clean
These two sections can tidied up and combined into one if you feel so inclined.
Tip
Always run make with --warn (or --warn-undefined-variables to give it its full name) to catch inadvertent expansion of $c in things like $component.

Make: .DELETE_ON_ERROR for directory targets

GNU Make includes a special target called .DELETE_ON_ERROR. If this is included in your Makefile, Make will delete any target whose build sequence completes with a non-zero return status. This is helpful so that in subsequent invocations Make does not assume that the target has been properly built.
Here's a dummy example.
.DELETE_ON_ERROR:
out.dat: in.dat
touch out.dat
false
Because false gives a non-zero return value, the build is considered failed and Make deletes the out.dat target. This is the advertised and expected behavior. However, this behavior does not seem to be preserved when the target is a directory. Consider another dummy example.
.DELETE_ON_ERROR:
outdir/: in.dat
mkdir outdir/
false
In this case, the build fails again but Make does not remove the outdir directory. Is there any way I can instruct Make to do this?
As noted in the comments, it is hard to use timestamps on directory. Few options:
proxy target (%.dir)
Atomic update using temporary folder.
Using proxy target, Makefile can be modified to incude a '%.done' target, which will embed the cleanup logic.
.PHONY: %.dir
outdir.dir:
$(MAKE) outdir ; if [ $? -ne 0 ] ; then echo CLEANUP $# ; rm -rf dir ; false ; fi
outdir: ... # as before
And use the outdir.dir as a dependency. Not elegant, but will get the work done. May be possible to to convert into a rule (disclaimer: I did not test this approach).
.PHONY %.dir
%.dir:
$(MAKE) $* ; if [ $? -ne 0 ] ; then echo CLEANUP $* ; rmd -rf $* ; false ; fi
Another variation is to change the outdir to add a "done" indicator file (if completed successfully), and use the proxy target to validate
%.dir:
$(MAKE) $* ; if [ ! -f $*.done ] ; then rm -rf $* ; false ; fi
outdir:
... commands, any can fail.
touch $*.done
As last resort (or first option, depending on your situation), consider, 'atomic' build for outdir - creating a temporary folder, and renaming it to outdir on success
outdir:
rm -rf $#.new $#
mkdir $#.new
# Command to create outdir.new here
mv $#.new $#

Make Placeholder Dependence

I'm using make to copy files to a DEST directory. I have the following rule
$(THUMBS): $(DEST)/% : %
mkdir -p $(dir $#)
cp $^ $#
The problem is that sometimes the source file may not exist. Rather than generating an error, I would rather copy a placeholder file instead.
I tried adding the placeholder as a dependence with the actual sources as intermediates. That kind of worked, but then if the placeholder is updated make overwrites all of the actual source files with it.
Is there an elegant way to accomplish this?
If the files in $(DEST) are being built externally (that is, not via a make recipe), then you can do this by embedding a little shell script in your recipe:
$(THUMBS):
mkdir -p $(#D)
for file in $(DEST_FILES); do\
if [[ -f $file ]]; then\
cp -f $file $#;\
else\
cp -f $(PLACEHOLDER_FILE) $#;\
fi;\
done
You aren't listing the files in $(DEST) as prerequisites, so make should never try to rebuild them. You will need to set PLACEHOLDER_FILE to the name of the placeholder file that you wish to use for missing files, and set DEST_FILES to the list of files that you expect to see in DEST. The downside is that without prerequisites, make won't know when it doesn't actually need to re-run this rule. You will run it unconditionally every time.
How about this:
$(DEST)/% : %
mkdir -p $(dir $#)
cp $^ $#
$(DEST)/% :
mkdir -p $(dir $#)
touch $#

Getting the name of the makefile from the makefile

How to get the name of the makefile in the makefile?
Thanks.
Note:
I would need that because I would like my makefile to call itself, but the makefile is not called Makefile, so I'd like to write something like this:
target:
($MAKE) -f ($MAKEFILENAME) other_target
location = $(CURDIR)/$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
WHERE_ART_THOU := $(location)
$(warning $(WHERE_ART_THOU))
I also believe this is GNU make-specific, but I'm not too sure.
(Should you have any questions, refer to amazingly written GNU make manual. But remember, that, just like Makefile, this manual should be read completely before putting the concepts into practice).
I couldn't figure out how it is done easily. As far as I understand, you'll have to do some manual job.
Later I will describe how it could be done and show scripts that introduce current_makefile variable. But I would like to stress an important concept at the first place.
You should understand that if we had some kind of variable current_makefile, that expands to the current makefile name, then it will have to change during the process of reading makefiles. That means that it should be used withinin "immediate" expansion context -- i.e. within commands that are executed during reading the makefile. Most commands, however, are executed after makefiles are read. Therefore, some commands will print the correct value smoothly, while in certain places, where "deferred" expansion is used, it will always expand to the root makefile name.
If you would want to use this variable within rule text, for example, you'll have to do tricks, because rule text always has deferred expansion. So, if your have the rule
rule:
echo In makefile $(current_makefile):
echo Making target $#
it will always print the name of the root makefile. Instead, to force immediate expansion, you will have to create another variable with makefile-specific name (i.e. names of such variables should be different in each makefile):
this_makefile_unique_name := $(current_makefile)
rule:
echo In makefile $(current_makefile):
echo Making target $#
or use eval:.
define make_rule
rule:
echo In makefile $(1):
echo Making target $$#
$(eval $(call make_rule,$(current_makefile)))
If you want to use the name of current makefile for debug purpose only, consider special debugging functions, like warning or info:.
$(warning We're in makefile $(current_makefile))
These functions use "immediate" expansion and will print the correct value.
How to define such a $(current_makefile)?
You have to manually maintain stack of makefile inclusions. When you include a makefile, its name is placed to the top of the stack; when you return from included makefile to the outer one, the topmost name is popped out of stack. This is achieved by inserting special calls to the beginning and the end of makefile:
# Beginning of makefile
$(eval $(makefile_names_push))
#... makefile text
$(warning $(current_makefile))
#...
$(eval $(makefile_names_pop))
#End of file
Now define the functions at the beginning of your root makefile.
lastword=$(word $(words $(1)),$(1))
define makefile_names_push
current_makefile := $$(CURDIR)/$$(call lastword,$$(MAKEFILE_LIST))
makefile_stack :=$$(makefile_stack) $$(current_makefile)
endef
define makefile_names_pop
makefile_stack := $$(filter-out $$(current_makefile),$$(makefile_stack))
current_makefile := $$(call lastword,$$(makefile_stack))
endef
If you're sure your make is new enough (version 3.81+), replace lastword call with builtin function:.
#inctead of $$(call lastword,$$(MAKEFILE_LIST))
$$(lastword $$(MAKEFILE_LIST))
Is it useful?
Totally useless. An only use that might be useful here is to make 100 makefiles that are symlinks to one makefile, the rules in these makefiles depending on their names. But it can be achieved within one makefile and foreach-eval technique described in the manual. So my post was a complete waste of time, though I had some fun :-)
This returns the name of the first Makefile called, i.e. the one at the bottom of the call stack:
MAKEFILE_JUSTNAME := $(firstword $(MAKEFILE_LIST))
MAKEFILE_COMPLETE := $(CURDIR)/$(MAKEFILE_JUSTNAME)
When used in non-cross-recursive situations (e.g. for makedepend), it is just the name of the current makefile.
I wanted to do something similar (for echoing the contents of the Makefile) for when I use Make for managing simple repetitive tasks. I came across this page and found it was exactly what I was after and really useful for my limited understanding of make.
My result after reading this page:
# Makefile - 'make' and 'make help' now echo the makefile.
help:
cat $(lastword $(MAKEFILE_LIST))
start:
sudo -u www /path/to/webapp/myhttpd restart
stop:
sudo kill `cat /path/to/webapp/data/httpd.pid`
A quick excursion to Google suggests this site has the answer.
G'day,
If you make a copy of your original makefile, say makefile_test, and then enter the command:
make -np -f makefile_test 2>&1 | tee output
That will evaluate the makefile and your make environment but not execute any of the commands. Looking through the output file for references to makefile_test will show you what is set in make's environment and where that value is being set.
N.B. This can generate a lot of info! And don't add the -d (debug) switch which will generate tons of additional output about make's decision process but minimal additional info about make's env.
HTH
The solutions here addresses 1) POSIX make with 2) Invoked, non included, makefile in 3) A Unix alike platform.
What the OP asked for:
target:
#pid=$$$$; \
while test `ps -ocomm= $$pid` != make; do \
pid=`ps -oppid= $$pid`; \
done; \
MAKEFILENAME=`ps -oargs= $$pid|sed 's/^.* -f *\([^ ]*\).*$$/\1/'`; \
test -z "$$MAKEFILENAME" -a -f Makefile && MAKEFILENAME=Makefile; \
test -z "$$MAKEFILENAME" -a -f makefile && MAKEFILENAME=makefile; \
export MAKEFILENAME; \
$(MAKE) -e -f $$MAKEFILENAME other_target
The targets depends on the makefile, kind of bloated:
TARGET1_MAKEFILENAME = target1_preamble
all: target1 target2...
target1: $(TARGET1_MAKEFILENAME) other_dependencies...
#test $(TARGET1_MAKEFILENAME) == target1_preamble && exit 0; \
built_instructions_for_target1;
target1_preamble:
#pid=$$$$; \
while test `ps -ocomm= $$pid` != make; do \
pid=`ps -oppid= $$pid`; \
done; \
MAKEFILENAME=`ps -oargs= $$pid|sed 's/^.* -f *\([^ ]*\).*$$/\1/'`; \
test -z "$$MAKEFILENAME" -a -f Makefile && MAKEFILENAME=Makefile; \
test -z "$$MAKEFILENAME" -a -f makefile && MAKEFILENAME=makefile; \
export MAKEFILENAME; \
$(MAKE) -e -f $$MAKEFILENAME target1;
Can be a bit simplified if make is invoked only for all targets.
MAKEFILENAME = invoked_makefile_placeholder
all: target1 target2...
target1: $(MAKEFILENAME) other_dependencies...
#test $(MAKEFILENAME) == invoked_makefile_placeholder && exit 0; \
built_instructions_for_target1;
invoked_makefile_placeholder:
#pid=$$$$; \
while test `ps -ocomm= $$pid` != make; do \
pid=`ps -oppid= $$pid`; \
done; \
MAKEFILENAME=`ps -oargs= $$pid|sed 's/^.* -f *\([^ ]*\).*$$/\1/'`; \
test -z "$$MAKEFILENAME" -a -f Makefile && MAKEFILENAME=Makefile; \
test -z "$$MAKEFILENAME" -a -f makefile && MAKEFILENAME=makefile; \
export MAKEFILENAME; \
$(MAKE) -e -f $$MAKEFILENAME
With the previous approach is trivial to implement a solution for included makefiles based in grep and a unique pattern contained in the makefile.
I never answer when I feel the question got a proper solution.

Resources