Consider a makefile
with a multiline item:
define ITEM
line $$ONE
line $$TWO
endef
item:
echo '$(ITEM)' | ... > $@
The problem is that as ITEM gets complex, I run the risk of getting quote errors and other problems when I echo the item.
I could also run with an export:
export ITEM
item:
print "%sn" '$${ITEM}' | ...
But I am not sure that is going to have the same bugs.
Is there some make function that will inject the contents of a variable directly into the shell, as if it were sent to /dev/stdin
?
# e.g. something like:
$(info $ITEM) | ...
I understand that I can write the value directly to a file, which I’ll be able to process via shell. But being able to write it directly to a pipeline would short circuit that.
- this will have a series of commands, or introduce requirements like
sponge
- it may also cause me to have to shift from simple
envsubst
to heavier things likesed
to allow in-place modification (to avoid installing sponge)
I also understand I can export the value to all subshells:
- this may be problematic if my extension is included in a recursive make project, or a make project with a large amount of export-overhead (e.g.
parameter?=vars
that default to env values introduce a large amount of overhead when.SECONDEXPANSION
is enabled) - in particular, exporting too much can break the performance of
zsh
tab completion for make, which is a soft requirement for mac users
But I have no proof exporting and echo "$$ITEM"
or print("%s/n") "$$ITEM"
will have proper escapes. So I’ll have to test it.
What is not “a way”, but the intended best practice for doing this?
If the answer is: use $(file ..)
, and modify the file in place from there, then so be it; just want to make sure that is optimal.
7
The usual solution for a complex variable expansion is to just write an external shell file. Or a script (perl/python/awk). Or even a special compiled tool to produce something which is needed for actual compilation.
It is not a must that everything would be part of makefile
.
Rule of thumb here is: “if you struggling with variables, dollar signs, and quotation marks – maybe it is time to think of external script?”
2
After reviewing the accepted answer, I am going to adapt the convention from the manual:
For example, the file function can be useful if your build system has a limited command line size and your recipe runs a command that can accept arguments from a file as well. Many commands use the convention that an argument prefixed with an @ specifies a file containing more arguments. Then you might write your recipe in this way:
program: $(OBJECTS)
$(file >[email protected],$^)
$(CMD) $(CMDFLAGS) @[email protected]
@rm [email protected]
And in my case, the intermediate file I’ll have to create to stay simple with envsubst
(rather than sed
) will become the analog for the [email protected]
:
define new-file.sh
#!/bin/bash
echo "$$FOO"
endef
new-file.sh:
$(file >[email protected],$($@))
cat [email protected] | ... > $@
@rm [email protected]
Which will be something I’ll have a good chance of doing the same way next time.
1