Search code examples
makefileautotools

What is the difference between '$$directoryName' and '$(directoryName)' in makefile


I have two makefiles

The first :

dir = ../dir1
dis = ../dir2
test:
    $(MAKE) -C $(dir)

The second one :

DIRS = dir1 dir2 dir3

test:
    for dir in $(DIRS); do \
            if $(MAKE)  -C $$dir ; then \
                true; \
            else \
                exit 1; \
            fi; \
    done

Why in the for loop I need $$dir when in a simple recipe I have to write $(dir)

Another question: I have this other makefile, in which I have this other for loop:

all clean dep depend print:
for dir in $(DIRS); do \
    if $(MAKE) $(MAKE_FLAGS) -C $$dir $@; then \
        true; \
    else \
        exit 1; \
    fi; \
done

What is the meaning of $@ in the line

if $(MAKE) $(MAKE_FLAGS) -C $$dir $@; then \

I know this is an Automatic Variable that matches the file name of the target of the rule. Here the target appears to be a command like cancel:

cancell:
    rm -rf *.o

Solution

  • One is a makefile variable the other is a shell variable. $(directoryName) will be resolved by make, as it's reading. For $$directoryName, make converts the $$ to $, and passes that to the shell (so $$directoryName becomes$directoryName). The shell expands it to whatever it has in its environment.

    A bit more detail:

    If, in make, you define (outside of any recipe)

    var := 1
    

    then var is a make variable. If you then call

    all:
        echo $(var)
    

    Then make expands the recipe to echo ../dir1 before passing the command to the shell. If, on the other hand, you do:

    all:
        var=1; echo $$var
    

    Then make passes var=1; echo $var to the shell. The shell sets var to 1, and then prints it. Notice if you tried:

    all:
        var=1;
        echo $$var
    

    Then the recipe will not print anything -- that's because the first line is run in a shell, which sets var to 1, but then exits. The next line runs, which passes echo $var in a new shell, but this shell doesn't know what var was set to in the previous shell.

    In addition, if you run var=1;make, then make will set a makefile variable var to be 1 when it starts. Thus a $(info $(var)) will show 1, in this case.

    For syntax, in make you can do var := 1 (with spaces). In bash you cannot add spaces. In bash, $var refers to the variable var. In make $var refers to $v followed by the literal ar. (In make you have to use either {} or () to refer to multicharacter variables). In bash, you can either use {}, or no braces ($(var) has a different meaning in bash).

    make also has two flavors of variables, one defined with := and one with =.