Pein
Pein

Reputation: 1581

Variable dereferncing in makefiles

Given a list of paths I want to separate out the directory part and the filename part of each of the element of the list inside a makefile. Something like following

MYLIST = \
        /home/folder1/folder2/fileName1 \
        /home/folder3/folder4/fileName2 \
        /home/folder5/folder6/fileName3 \
MYLIST:
    @for elems in $(MYLIST); \
    do \
        echo $(dir $$elems); \
        echo $(notdir $$elems); \
    done

But there is a problem with the variable dereferencing. I get the output as

 ./
 home/folder1/folder2/fileName1
 ./
 home/folder3/folder4/fileName2
 ./
 home/folder5/folder6/fileName3

whereas i want it to be

         /home/folder1/folder2/
         fileName1
         /home/folder3/folder4/
         fileName2
         /home/folder5/folder6/
         fileName3

Somehow $(@D) and $(@F) are not giving all the dir and fileName parts just the first one in the list. Can somebody please tell how to get about this problem ?

Upvotes: 1

Views: 1097

Answers (3)

Pein
Pein

Reputation: 1581

I found another way to do that now...

MYLIST = \
    /home/folder1/folder2/fileName1 \
    /home/folder3/folder4/fileName2 \
    /home/folder5/folder6/fileName3

MYLIST:
    @$(foreach ELEMS,$(MYLIST), echo $(dir $(ELEMS)); echo $(notdir $(ELEMS));)

Don't know how i missed this before.

Upvotes: 0

eriktous
eriktous

Reputation: 6649

This occurs because you're mixing two stages of expansion. Before invoking the shell to execute the rule all make variables and functions are expanded. So $$elems becomes $elems and this string is then used as the input for the $(dir ...) and $(notdir ...) functions. This string doesn't contain a /, so dir returns ./, and notdir returns $elems. In the end, the following command is executed in the shell.

@for elems in /home/folder1/folder2/fileName1 /home/folder3/folder4/fileName2 /home/folder5/folder6/fileName3; \
do \
    echo ./; \
    echo $elems; \
done

William Pursell has given a possible workaround by using shell functions. Another possibility would be to perform the expansion before execution of the rule, like such:

MYLIST = \
        /home/folder1/folder2/fileName1 \
        /home/folder3/folder4/fileName2 \
        /home/folder5/folder6/fileName3 \

MYDIRS = $(dir $(MYLIST))
MYFILES = $(notdir $(MYLIST))

MYLIST:
        @for elems in $(MYDIRS) $(MYFILES); \
        do \
            echo $$elems; \
        done


$(@D) and $(@F) are not giving you all dir and fileName parts because they give the file and directory of the target of the current invokation of the rule. There is only one target at each moment. You may be able to use these automatic variables, however, to do what you want, by letting make do the looping, instead of the shell, like this:

MYLIST = \
        /home/folder1/folder2/fileName1 \
        /home/folder3/folder4/fileName2 \
        /home/folder5/folder6/fileName3 \

all: $(MYLIST)
$(MYLIST):
    @echo $(@D)
    @echo $(@F)

Upvotes: 4

William Pursell
William Pursell

Reputation: 212228

I would suggest using the shell instead of make:


MYLIST = \
        /home/folder1/folder2/fileName1 \
        /home/folder3/folder4/fileName2 \
        /home/folder5/folder6/fileName3
MYLIST:
    for elems in $(MYLIST); \
    do \
    echo $$(dirname $$elems); \
    echo $$(basename $$elems); \
    done

Of course, at this point, the echo is redundant, and you could just as well do:

for elems in $(MYLIST); \
    do \
    dirname $$elems; \
    basename $$elems; \
    done

Upvotes: 1

Related Questions