preventing variable expansion in makefile - shell

I want to write this into a file:
-MF"$(#:%.o=%.d)" -MT"$(#)" -o "$#" "$<"
but when I echo this into a file, with "", or '', the variables will expand, how can I prevent the expansion and write it as is?
P.S. echo '-MF"$(#:%.o=%.d)" -MT"$(#)" -o "$#" "$<"' is called in a makefile.
in my makefile, I let's say I have:
all:
echo '-MF"$(#:%.o=%.d)" -MT"$(#)" -o "$#" "$<"' > $file
what I see in the file is
-MF"all" -MT"all" -o "all" ""

Use $$ to put a dollar sign into a Makefile recipe
all:
echo '-MF"$$(#:%.o=%.d)" -MT"$$(#)" -o "$$#" "$$<"' > $file

Related

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
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 =.

How to use eval Makefile variable from recipe to conditionally execute some commands in recipe

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

What is the filename convention of Makefile templates?

I have a template file, and the configuration script transforms it into the final Makefile. What shoud be the extension of the template?
Example template:
EXEC = $var1
PATH = $var2
CC = $var3
STRIP = $var4
MKDIR = $var5
COPY = $var6
.PHONY: all
all: $(EXEC)
$(EXEC): source.c
$(CC) $< -o $#
$(STRIP) $#
$(MKDIR) $(PATH)
$(COPY) $# $(PATH)/
The variables are replaced by the configuration script.

What's the difference between $# and $1 when there is only one parameter?

There are some C code:
apple.c
#include<stdio.h>
int main(void)
{
printf("apple\n");
return 0;
}
Makefile
apple:
gcc -c $#.c
gcc $#.o -o $#
$ make apple
and it works perfectly. But if I modify Makefile as:
apple:
gcc -c $1.c
gcc $1.o -o $1
$ make apple
It does not work. What is the difference between $# and $1 when there is only one parameter?
In a shell script, there'd be no difference. But this is a Makefile, so these references are to make variables. $# is the name of the rule target (apple here), while $1 is a variable named 1—nothing special. Bash does not see these variable references; they're handled by make.
$ cat Makefile
1 = one
target:
#echo '# = $#'
#echo '1 = $1'
$ make
# = target
1 = one

Makefile: How to assign a command's output and exit code to a variable?

I am searching for a way to get the output and the exit code of a command to variables in a makefile.
Basicly I want this: (bash)
output=$(whoami)
returnval=$?
echo "OUT:"
echo $output
echo "RET:"
echo $returnval
to be in a makefile
note: should work in a rule section
Thanks
EDIT: SOLVED
$(eval OUTPUT_RC="$(shell whoami;echo $$?)")
$(eval OUTPUT="$(shell echo $(OUTPUT_RC) | sed -e "s/^\(.*\)\(.\{2\}\)$$/\1/")")
$(eval RC="$(shell echo $(OUTPUT_RC) | sed -e "s/^.*\(.\)$$/\1/")")
echo $(OUTPUT)
echo $(RC)
If you are going to use eval anyway, make it print things you can eval directly.
$(eval $(shell echo OUTPUT_RC=`whoami`; echo $$?))
OUTPUT=$(word 1,$(OUTPUT_RC))
RC=$(word 2,$(OUTPUT_RC))
GNU make offers the shell function, as in:
OUTPUT=$(shell whoami)
OUTPUT_RC=$(shell whoami; echo $$?)

Resources