Makefile wildcard with variable inside - makefile

I am trying to get all the files inside each folder with Makefile:
Test = $(wildcard */)
install: all
#for dir in $(TEST); do \
echo $$dir; \
echo $(wildcard $$dir); \
done
First echo outputs correctly: folder1/ folder2/ folder3/ but when used with wildcard in second echo I am getting empty output (each folder contains many files).
Any suggestions why this doesn't work and how to archive this?

$(wildcard) is make function, which is evaluated once while makefile is parsed.
dir is a shell variable, which exists only when receipt is evaluated (using shell).
Because make is unaware about shell variables, pattern in $(wildcard $$dir) is interpreted by make literally: $$dir.
If you want to output list of files in a directory pointed by dir variable, you should use shell utilities. E.g. ls $$dir.

Related

Makefile: variables don't get expanded correctly

I am having a problem with this Makefile structure.
make commit.install should create foo.txt and then put it into some folder. However, make doesn't call the target that creates foo.text. This means I have to call make foo.txt to create the file itself and then call make commit.install to copy this over. I think there is a problem with make not expanding variable dependencies.
./common.mk
src.install: ${INSTALLSRC}
mkdir -p result
for f in ${INSTALLSRC}; do \
cp -a $$f result/.; \
done
commit.install: src.install
echo "finished"
clean:
rm -rf result
./inner/nested/GNUmakefile
include ../common.mk
include Makefile
./inner/nested/Makefile
SRC=foo.txt
foo.txt:
echo "hello world" > foo.txt
./inner/common.mk
include ../../common.mk
INSTALLSRC=${SRC}
Repository
The problem is that you define INSTALLSRC after the rule that has it as a prerequisite. So when Make evaluates the prerequisite list, the variable is empty, so Make sees no need to build foo.txt. This would have been pretty obvious if you had simplified this collection of makefiles.
The fix is simple; swap the two lines in inner/common.mk:
INSTALLSRC=${SRC}
include ../../common.mk
and GNUMakefile:
include Makefile
include ../common.mk

what does #D mean in shell script

i have a code as
echo -e "\\n" "===== Making: $(#D)\n";\
if [ ! -d $(#D) ]; then \
mkdir $(#D); \
else \
if [ -e $(#D)\PackageBuild.error ]; then \
rm $(#D)\PackageBuild.error;\
fi; \
i am not sure what this #D is doing.
can someone help me out here
Usually $(command) executes command and replaces $(command) with the output of command.
So there must be a file named #D which is executable and located in the search path.
But if this is not a shell script but a make file it means:
$(#D)
The directory part of the file name of the target, with the trailing
slash removed. If the value of $# is dir/foo.o then $(#D) is
dir. This value is . if $# does not contain a slash.
The bash does not have any notion of a syntax like #D. I guess you are in a special context here, I guess again in a Makefile. make is a special program which does not verbatim execute the scripts in the Makefile. Instead it preprocesses the scripts.
$(x) is evaluated prior to calling the scripts and replaced by variables set in the Makefile. So I guess somewhere in the Makefile you have a variable called #D set to a specific value. (And there is a predefined one, as D. Mika found out ;-)
The documentation https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html#Automatic-Variables mention that
‘$(#D)’: The directory part of the file name of the target, with the trailing slash removed [...]
But also
‘$(#F)’: The file-within-directory part of the file name of the target[...]
So if ‘$#’ is dir/foo.o then ‘$(#D)’ is dir and '$(#F)' is foo.o

How do you call functions in the body of a for loop in a Makefile

I have defined a list of files in a Makefile.
I need to copy those files, with their directory structure, to a new location.
I can't use cp --parent because the source files live in ../ (so .. will be a part of the dest path).
I've decided, then, to do one for loop that creates the dirs, then a second for loop that copies the files. The problem, however, is that I can't seem to call $(dir $$file) in the body of the for loop. I always get "./" as the result, rather than the actual dir name. If I do something like echo $(dir foo/bar), that works fine, but echo $(dir $$file) (when $$file is 'foo/bar') always returns './'.
I've tried other options like $(shell basename $$file), but that also doesn't work.
I've tried defining a function and calling it in the body of the for, but that, too, doesn't work.
Here's a quick example Makefile:
FILES := foo/faz \
bar/baz \
gah/gaz
all:
#for f in $(FILES); do \
echo $(dir $$f); \
done
I expect the output of this to be:
foo
bar
gah
but instead, I'm getting:
./
./
./
I'm open to other solutions if my method is not the best. At the end of the day, I need to be able to copy all the $(FILES) that exist in ../ (so ../foo/bar, for example) to a new dir called $(NEWDIR) (so newdir/foo/bar, for example).
This only needs to work under Linux.
f is a shell variable here. The make function dir is called on the string $f (which the shell then interprets as a variable expansion).
If you use a shell loop, you need to use the shell's constructs to extract the directory part:
#set -e; for f in $(FILES); do \
echo $$(dirname $$f); \
done
Don't forget set -e, so that if there's an error during the copy, the make rule will fail.
You can also use a foreach function in make to generate a bunch of cp commands. It makes the makefile harder to debug though.

Getting directory portion of a list of source files in a for loop

I have the following gnu make script:
for hdrfile in $(_PUBLIC_HEADERS) ; do \
echo $(dir $$hdrfile) ; \
done
The _PUBLIC_HEADERS variable has a list of relative paths, like so:
./subdir/myheader1.h
./subdir/myheader2.h
The output I get from the for loop above is:
./
./
I expect to see:
./subdir/
./subdir/
What am I doing wrong? Note that if I change the code to:
echo $(dir ./subdir/myheader1.h)
it works in this case. I think maybe it has something to do with the for loop but I'm not sure.
You are confusing make variables (or functions) with shell variables when executing the for-loop. Note that $(dir ...) is a make construct that gets expanded by make before the command is executed by the shell. However, you want the shell to execute that command inside the loop.
What you could do is replace $(dir) with the corresponding command dirname which gets executed by the shell. So it becomes:
for hdrfile in $(_PUBLIC_HEADERS) ; do \
dirname $$hdrfile ; \
done
This should give the desired result.

Define make variable at rule execution time

In my GNUmakefile, I would like to have a rule that uses a temporary directory. For example:
out.tar: TMP := $(shell mktemp -d)
echo hi $(TMP)/hi.txt
tar -C $(TMP) cf $# .
rm -rf $(TMP)
As written, the above rule creates the temporary directory at the time that the rule is parsed. This means that, even I don't make out.tar all the time, many temporary directories get created. I would like to avoid my /tmp being littered with unused temporary directories.
Is there a way to cause the variable to only be defined when the rule is fired, as opposed to whenever it is defined?
My main thought is to dump the mktemp and tar into a shell script but that seems somewhat unsightly.
In your example, the TMP variable is set (and the temporary directory created) whenever the rules for out.tar are evaluated. In order to create the directory only when out.tar is actually fired, you need to move the directory creation down into the steps:
out.tar :
$(eval TMP := $(shell mktemp -d))
#echo hi $(TMP)/hi.txt
tar -C $(TMP) cf $# .
rm -rf $(TMP)
The eval function evaluates a string as if it had been typed into the makefile manually. In this case, it sets the TMP variable to the result of the shell function call.
edit (in response to comments):
To create a unique variable, you could do the following:
out.tar :
$(eval $#_TMP := $(shell mktemp -d))
#echo hi $($#_TMP)/hi.txt
tar -C $($#_TMP) cf $# .
rm -rf $($#_TMP)
This would prepend the name of the target (out.tar, in this case) to the variable, producing a variable with the name out.tar_TMP. Hopefully, that is enough to prevent conflicts.
A relatively easy way of doing this is to write the entire sequence as a shell script.
out.tar:
set -e ;\
TMP=$$(mktemp -d) ;\
echo hi $$TMP/hi.txt ;\
tar -C $$TMP cf $# . ;\
rm -rf $$TMP ;\
I have consolidated some related tips here: Multi-line bash commands in makefile
Another possibility is to use separate lines to set up Make variables when a rule fires.
For example, here is a makefile with two rules. If a rule fires, it creates a temp dir and sets TMP to the temp dir name.
PHONY = ruleA ruleB display
all: ruleA
ruleA: TMP = $(shell mktemp -d testruleA_XXXX)
ruleA: display
ruleB: TMP = $(shell mktemp -d testruleB_XXXX)
ruleB: display
display:
echo ${TMP}
Running the code produces the expected result:
$ ls
Makefile
$ make ruleB
echo testruleB_Y4Ow
testruleB_Y4Ow
$ ls
Makefile testruleB_Y4Ow
I dislike "Don't" answers, but... don't.
make's variables are global and are supposed to be evaluated during makefile's "parsing" stage, not during execution stage.
In this case, as long as the variable local to a single target, follow #nobar's answer and make it a shell variable.
Target-specific variables, too, are considered harmful by other make implementations: kati, Mozilla pymake. Because of them, a target can be built differently depending on if it's built standalone, or as a dependency of a parent target with a target-specific variable. And you won't know which way it was, because you don't know what is already built.

Resources