Using variables with export in Makefile - makefile

I would like to add some variables and echo commands in a Makefile. The section is:
.PHONY: run_harness
run_harness: link_dirs
#mkdir -p $(LOG_DIR)
#set -o pipefail && $(PYTHON3_CMD) code/main.py $(RUN_ARGS) --action="run_harness" 2>&1 | tee $(LOG_DIR)/stdout.txt
#set -o pipefail && $(PYTHON3_CMD) scripts/print_harness_result.py $(RUN_ARGS) 2>&1 | tee -a $(LOG_DIR)/stdout.txt
and I want to add
.PHONY: run_harness
run_harness: link_dirs
#mkdir -p $(LOG_DIR)
########
#export WRK="test"
#export startTime=$(date +%s)
#echo $(WRK), $(startTime)
########
#set -o pipefail && $(PYTHON3_CMD) code/main.py $(RUN_ARGS) --action="run_harness" 2>&1 | tee $(LOG_DIR)/stdout.txt
#set -o pipefail && $(PYTHON3_CMD) scripts/print_harness_result.py $(RUN_ARGS) 2>&1 | tee -a $(LOG_DIR)/stdout.txt
But those export and echo don't work because I get , in the output which mean both $(WRK) and $(startTime) are empty. How can I fix that?
UPDATE:
The following change
#mkdir -p $(LOG_DIR)
export WRK="3d-unet"; \
export startTime=$(date +%s); \
echo $(WRK), $(startTime) ;
doesn't work due to this error

Each line in a recipe is executed in a separate shell. You need to put all shell commands on one line, separated by semicolons (and possibly use line continuation with backslash newline for readability).
And you must reference shell variables with $$name (not with make variable $(name)). Note that make replaces a $$ with a single $ for the shell.
foo: bar
export x=hello y=world; \
echo "$$x" "$$y"

Related

Makefile: cannot create a variable with random

I need to generate a random string and save in a variable, with a Makefile.
I wrote
install:
LOCALSTORAGE_ACCESS_TOKEN = echo $RANDOM | md5sum | head -c 20; echo;
echo $(LOCALSTORAGE_ACCESS_TOKEN)
When I launch it with make install, I get
LOCALSTORAGE_ACCESS_TOKEN = echo ANDOM | md5sum | head -c 20; echo;
/bin/sh: 1: LOCALSTORAGE_ACCESS_TOKEN: not found
d41d8cd98f00b204e980
echo
So
missing assigned value to the variable LOCALSTORAGE_ACCESS_TOKEN.
getting every time same result for RANDOM
I tried also
LOCALSTORAGE_ACCESS_TOKEN = echo $$RANDOM | md5sum | head -c 20; echo;
echo $(LOCALSTORAGE_ACCESS_TOKEN)
with same result
LOCALSTORAGE_ACCESS_TOKEN = echo $RANDOM | md5sum | head -c 20; echo;
/bin/sh: 1: LOCALSTORAGE_ACCESS_TOKEN: not found
d41d8cd98f00b204e980
echo
Each line of a recipe is a shell script, run by a different shell. Your first line is syntactically incorrect. You are trying to assign a make variable in a recipe, this is not how make works. Try:
install:
LOCALSTORAGE_ACCESS_TOKEN=$$(echo $$RANDOM | md5sum | head -c 20); \
echo $$LOCALSTORAGE_ACCESS_TOKEN
LOCALSTORAGE_ACCESS_TOKEN is now a shell variable, it is assigned using shell syntax in the first line of the recipe but is still defined in the second line because of the line continuation (the trailing \). Note the use of $$ instead of $ to escape the first expansion by make.
If you would like LOCALSTORAGE_ACCESS_TOKEN to be a make variable available in all lines of all recipes then simply assign it as a real make variable and outside any recipe :
LOCALSTORAGE_ACCESS_TOKEN := $(shell echo $$RANDOM | md5sum | head -c 20)
install:
echo $(LOCALSTORAGE_ACCESS_TOKEN)
This should work:
install:
$(eval LOCALSTORAGE_ACCESS_TOKEN := $(shell /bin/bash -c 'echo $$RANDOM' | md5sum | head -c 20; echo))
echo $(LOCALSTORAGE_ACCESS_TOKEN)
As you also noticed, you need $$RANDOM in order to mask the $ sign in the Makefile
You need to run the command in a bash, because $RANDOM may not work in all shells (did not work for me, so I had to use bash)
You need the $(shell ... so that the variable gets assigned the output of the command (instead of the command text itself)
You need $(eval ... because the code is inside a target, and you can not define variables in targets otherwise

gnu makefile: shell function execution order in a recipe

I'm using $(shell ...) gnu make function in a Makefile recipe, and it runs first before the preceding rows. Why?
A very simple example:
.PHONY: all
all:
#echo 1
#echo $(eval a=$(shell echo 2a 1>&2))2b
#echo 3 $(a)
The output is:
2a
1
2b
3
First runs the $(shell ...) line (2a), then the other lines.
How can I manage to run the $(shell ...) function when its row runs in the recipe, in this order?
1
2a
2b
3
Edit:
Without $(shell ...) it works as I expected:
.PHONY: all
all:
#echo 1
$(eval a=a)
#echo 2 $(a)
$(eval a=b)
#echo 3 $(a)
Output:
1
2 a
3 b
Edit 2:
Here is a part of the original Makefile. The pieces at >>> show the essence of my problem: I want to put the output of udisksctl into a make variable instead of file $#.loop (and do the same with $#.mount).
$(HDIMG): $(BOOTBLOCK_MBR_BIN) $(BOOTBLOCK_EXT2_BIN) $(LOADER_BIN) | $(DESTDIR)
dd if=/dev/zero of=$#.ext2 bs=1 seek=$(PSIZEB) count=0 2>/dev/null
$(MKFSEXT2) -F $#.ext2 >/dev/null
dd if=$(word 2,$^) of=$#.ext2 conv=notrunc 2>/dev/null
cp $< $#
dd if=/dev/zero of=$# bs=1 seek=$(HDSIZEB) count=0 2>/dev/null
echo $(PFDISK) | $(TR) | $(FDISK) $# >/dev/null
dd if=$#.ext2 of=$# bs=512 seek=$(PSTART) conv=sparse,notrunc iflag=fullblock 2>/dev/null
>>> udisksctl loop-setup --file $# --offset $(PSTARTB) --size $(PSIZEB) >$#.loop
sed -i -e 's/.* //;s/\.//' $#.loop
cat $#.loop
>>> udisksctl mount --block-device $$(cat $#.loop) >$#.mount
sed -i -e 's/.* //;s/\.//' $#.mount
cat $#.mount
#
mkdir -p $$(cat $#.mount)/boot/
cp $(word 2,$^) $$(cat $#.mount)/boot/
#/sbin/filefrag -b512 -e /
#
udisksctl unmount --block-device $$(cat $#.loop)
udisksctl loop-delete --block-device $$(cat $#.loop)
rm $#.loop
When make runs a recipe, it first expands all variables/functions in it, and then runs the shell commands line by line.
You can work around that by executing every shell command with $(shell ...), or, in your case, by using $(info ...) instead of echo:
.PHONY: all
all:
$(info 1)
$(info $(eval a=$(shell echo 2a 1>&2))2b)
$(info 3 $(a))
Output:
1
2a
2b
3
make: Nothing to be done for 'all'.
The Nothing to be done for 'all'. part is caused by the fact that after expanding the functions, the recipe is effectively empty (has 0 shell commands). Adding a no-op command (e.g. #true) to it removes the message.

How to set a bash variable in a compound xargs statement

I am looking for a way to set a variable in the statements passed to xargs. The value is to be manipulated in one of the commands. Using a file or another utility is an option but I am not sure why setting the bash variable in the sequence is always coming up as empty.
$ ls c*txt
codebase.txt consoleText.txt
$ ls c*txt | xargs -i bash -c "echo processing {}; v1={} && echo ${v1/txt/file}"
codebase.txt consoleText.txt
processing codebase.txt
processing consoleText.txt
The example above distills the question to the basics. I was expecting the behavior to be something like this but inline:
$ fname=codebase.txt; echo ${fname/txt/file}
codebase.file
Thank you.
This line is resolving ${v1/txt/file} to a value before the command is executed:
$ ls c*txt | xargs -i bash -c "echo processing {}; v1={} && echo ${v1/txt/file}"
And that means the bash -c doesn't even see ${v1/txt/file}
In this line the single quotes inhibit the variable substitution so echo processing {}; v1={} && echo ${v1/txt/file} is actually passed to bash -c as a parameter:
$ ls c*txt | xargs -i bash -c 'echo processing {}; v1={} && echo ${v1/txt/file}'
You could accomplish the same thing by escaping the dollar sign:
$ ls c*txt | xargs -i bash -c "echo processing {}; v1={} && echo \${v1/txt/file}"

Makefile - Select names from list using mask

I have Makefile in which I get a list of files from a certain directory. I need to choose only those, in the names of which there are numbers (like 123.txt). What is the best way to do it?
Thanks.
You can use a glob or wildcard, for example:
all.txt: text/*.txt
cat $^ >> $#
Usage:
$ cd -- "$(mktemp --directory)"
$ mkdir text
$ echo foo > text/1.txt
$ echo bar > text/2.txt
$ make
cat text/1.txt text/2.txt >> all.txt
NUMBER_FILENAMES := $(shell echo " $(LIST) " | sed 's/ [^ 0-9]* / /g')

Problem in Makefile

I am executing the following command in Makefile:-
#ls export_mojave/marker_*.tcl > export_mojave.list
#for file in `cat export_mojave_tcl_files.list`; do \
diff $$file bk_marker > $$file.diff ; \
if ! [ -s $$file.diff ]; then\
rm -f $$file.diff ; \
else \
echo $$file >> marker.fill.tcl.diff; \
fi \
done ;
If there exists some file related to the above expression in the mentioned directory,
it will run fine but if there does not exits any files matching to above expression, It is marking an error. Is there anything exists like "catch" in Makefile?
If you need to skip error in makefiles' rule, then prefix command with '-' sign:
-#ls .... > some_file || echo Error: no file;
if [ -e some_file ] ....
Or modify to be more in make-style:
FILES := $(shell ls ...)
ifeq ($(FILES),)
$(error no files)
endif
....
target:
$(foreach file,$(FILES), ...)

Resources