I need to concatenate a String in makefile to get this output:
username\$databasename
for automating the build process for a django project.
I've figured out that the $ sign has to be $$ in makefile so this is what i have now:
MYVAR = username$$databasename
My problem is the backslash in the string! As I know, the \\ like usually the backslash is escaped does not work here because it is meant for the linebreak function of Makefiles.
You'll probably want to try myvar = username\\"$$"databasename.
With
rule :
#echo $(myvar)
This outputs username\$databasename.
If you think the double quotes are ugly, single quotes work just as fine: myvar = username\\'$$'databasename.
If databasename is stored in an environment variable you want to access then use:
myvar = username\\$$"$$databasename"
for example, myvar = username\\$$"$$USER" would output username\$guiltydolphin for me.
In a Makefile I sometimes want to include scripts or fragments of python
code in a say Latex document.
I define BACKSLASH as Makefile macro and then use it in echo statements.
BACKSLASH = '\\'
reactance_sim: reactance.py
echo "{"$(BACKSLASH)tiny > reactance_sim
echo $(BACKSLASH)"begin{verbatim}" >> reactance_sim
cat reactance.py >> reactance_sim
echo $(BACKSLASH)"end{verbatim}" >> reactance_sim
echo "}" >> reactance_sim
cp reactance_sim reactance_simulation.tex
# vi reactance_simulation.tex
The above Makefile code will take the reactance.py file and
if required create a reactance_sim file which will have
backslashes in it (in this case Latex commands like \tiny and \begin)
Related
I have this code in a Makefile:
$(TCLNAME).batch.tcl: $(TCLNAME).tcl
echo source $::env(TOOLS_DIR)/my.tcl > $#
What I want to be printed in $(TCLNAME) is:
source $::env(TOOLS_DIR)/my.tcl
But I get an error because $::env(TOOLS_DIR) is being interpreted as a Make variable and it is expecting ( after the $.
How do I make it to print that line as is and not interpret it as Make variable ?
I tried to use escape character such as \$::env(TOOLS_DIR) but that also did not work.
Escape the $ with another $, and the parentheses with backslashes:
$(TCLNAME).batch.tcl: $(TCLNAME).tcl
echo source $$::env\(TOOLS_DIR\)/my.tcl > $#
A more universal escape method, as suggested by MadScientist:
Replace each $ with $$. The $ is the only symbol that Make has special treatment for.
Enclose the whole string in single quotes. Make doesn't care about them, they are for the shell.
If you need to print single quotes, replace each ' with '\''.
The end result:
echo 'source $$::env(TOOLS_DIR)/my.tcl' > $#
This way, only $ needs to be doubled (to prevent Make from interpreting it as a variable). Everything else is handled by the single quotes (which have no special meaning for Make, and are there for the shell).
new_contents = "\$$(cooly)"
all:
mkdir -p subdir
echo $(new_contents) | sed -e 's/^ //' > subdir/makefile
#echo "---MAKEFILE CONTENTS---"
#cd subdir && cat makefile
#echo "---END MAKEFILE CONTENTS---"
#cd subdir && $(MAKE)
# Note that variables and exports. They are set/affected globally.
cooly = "The subdirectory can see me!"
export cooly
# This would nullify the line above: unexport cooly
clean:
rm -rf subdir
What I want is a "$(cooly)" string, not the variable value.
I tried several combinations:
new_contents = "$(cooly)", gives the variable value, The subdirectory can see me!
new_contents = "$$", gives $
new_contents = "\$(cooly)", gives Syntax error: Unterminated quoted string error
Why new_contents = "$$(cooly)" doesn't give "$(cooly)" but result in nothing?
"$$" -> "$", so why isn't "$$(cooly)" ---> "$(cooly)"?
You have to understand both how make expansion works, and how shell expansion works, in order to write more complicated recipes in make. That's because, make recipes are passed to the shell after make is done expanding them.
Make passes recipe lines to the shell virtually verbatim: there is only one character that's special (not counting backslash/newlines at the end) and that's $. If make sees a $ it will try to expand it as a variable reference. To avoid that, you have to escape it as $$ to hide it from make.
So let's look at your makefile:
cooly = "The subdirectory can see me!"
echo $(new_contents) ...
If new_contents is "$(cooly)", make sees the $(cooly) as a variable reference and expands it before it even invokes the shell. So first make expands $(new_contents) to "$(cooly)", then it expands that to ""The subdirectory can see me!"" (because the quotes are in both variables, and quotes are not special to make: they're just like any other character like a or b). The result will be:
echo ""The subdirectory can see me!""
The shell will toss the quotes since they're no-ops and echo that value (into the pipe).
If new_contents is "\$(cooly)", that backslash doesn't mean anything to make. Just like quotes, backslashes (unless they are at the end of a line) are not special to make. So make expands just as before, but this time the command it passes to the shell is this:
echo "\"The subdirectory can see me!""
backslashes are not special to make, but they are special to the shell. Here you've escaped the second quote so the shell doesn't treat it as a quote character, which means you have an odd number of quotes in your command, which is why you get an error from the shell about non-terminated quotes.
If new_contents is "$$(cooly)", make doesn't expand the variable, it is passed along to the shell like this:
echo "$(cooly)"
However, $ is also special to the shell. Putting it in double quotes doesn't prevent the shell from trying to expand it. This tells the shell to run the command cooly and substitute the output. Almost certainly there is no command named cooly and so you'll get an error message to stderr (maybe you didn't notice it) and the shell will replace it with nothing because it didn't print anything to stdout.
If new_contents is "\$$(cooly)" then make will not expand, and run this shell command:
echo "\$(cooly)"
The shell sees the backslash and doesn't expand the $ but instead uses it literally, and you get the result you want.
Here are some hints:
First, do not include quotes in your make variables (unless the variable contains an entire shell command and you need quotes inside it). Make doesn't care about quotes and having them embedded in variables makes it very difficult to reason about what the shell will see.
Include the quotes only in the recipe.
Second, remember that since make doesn't care about quotes, it doesn't have the same behavior as the shell WRT single vs. double quotes. You can use single quotes around make variables to reduce the need to escape things from the shell, without hiding them from make.
So, I would write this:
new_contents = $$(cooly)
cooly = The subdirectory can see me!
all:
mkdir -p subdir
echo '$(new_contents)' | sed -e 's/^ //' > subdir/makefile
...
BTW, it's never a good idea to add # values to your makefile until it's completely done and working. Seeing the output make prints (which is what it's sending to the shell) is a great help in figuring out whether your recipes are right, and whether the problem is with your make constructs or shell constructs.
I have a shell variable in a for loop and I would like to indicate the end of the name of this variable:
$(DEBUG)for extblock in $(EXT_BLOCKS_LIST); \
do cat syn/ext_$$extblock_syn.tcl >> syn/$(SYN_TCL_SCRIPT); \
done;
My variable is only $$extblock but make takes $$extblock_syn as variable.
I don't achieve to put () correctly. Is there another character to indicate the end of a variable name?
Try this:
do cat syn/ext_$${extblock}_syn.tcl >> syn/$(SYN_TCL_SCRIPT);\
Braces can be used as delimiters. Also see the bash manual on shell parameter expansion.
Does anyone know how to use a here-document redirection on a recipe?
test:
sh <<EOF
echo I Need This
echo To Work
ls
EOF
I can't find any solution trying the usual backslash method (which basically ends with a command in a single line).
Rationale:
I have a set of multi-line recipes that I want to proxy through another command (e.g., sh, docker).
onelinerecipe := echo l1
define twolinerecipe :=
echo l1
echo l2
endef
define threelinerecipe :=
echo l1
echo l2
echo l3
endef
# sh as proxy command and proof of concept
proxy := sh
test1:
$(proxy) <<EOF
$(onelinerecipe)
EOF
test2:
$(proxy) <<EOF
$(twolinerecipe)
EOF
test3:
$(proxy) <<EOF
$(threelinerecipe)
EOF
The solution I would love to avoid: transform multiline macros into single lines.
define threelinerecipe :=
echo l1;
echo l2;
echo l3
endef
test3:
$(proxy) <<< "$(strip $(threelinerecipe))"
This works (I use gmake 4.0 and bash as make's shell) but it requires changing my recipes and I have a lot.
Strip removes the newlines, from the macro, then everything is written in a single line.
My end goal is: proxy := docker run ...
Using the line .ONESHELL: somewhere in your Makefile will send all recipe lines to a single shell invocation, you should find your original Makefile works as expected.
When make sees a multi-line block in a recipe
(i.e., a block of lines all ending in \, apart from the last),
it passes that block un-modifed to the shell.
This generally works in bash,
apart from here docs.
One way around this is to strip any trailing \s,
then pass the resulting string to bash's eval.
You do this in make by playing with ${.SHELLFLAGS} and ${SHELL}.
You can use both of these in target-specific form if you only want it to kick in for a few targets.
.PHONY: heredoc
heredoc: .SHELLFLAGS = -c eval
heredoc: SHELL = bash -c 'eval "$${#//\\\\/}"'
heredoc:
#echo First
#cat <<-there \
here line1 \
here anotherline \
there
#echo Last
giving
$ make
First
here line1
here anotherline
Last
Careful with that quoting, Eugene.
Note the cheat here:
I am removing all backslashes,
not just the ones at the ends of the line.
YMMV.
With GNU make, you can combine multi-line variables with the export directive to use a multi-line command without having to turn on .ONESHELL globally:
define script
cat <<'EOF'
here document in multi-line shell snippet
called from the "$#" target
EOF
endef
export script
run:; # eval "$$script"
will give
here document in multi-line shell snippet
called from the "run" target
You can also combine it with the value function to prevent its value from being expanded by make:
define _script
cat <<EOF
SHELL var expanded by the shell to $SHELL, pid is $$
EOF
endef
export script = $(value _script)
run:; # eval "$$script"
will give
SHELL var expanded by the shell to /bin/sh, pid is 12712
Not a here doc but this might be a useful workaround.
And it doesn’t require any GNU Make’isms.
Put the lines in a subshell with parens, prepend each line with echo.
You’ll need trailing sloshes and semi-colon and slosh where appropriate.
test:
( \
echo echo I Need This ;\
echo echo To Work ;\
echo ls \
) \
| sh
I want to perform a string substitution in my Makefile. I can easily do this with a string literal like so:
foo:
echo $(subst /,-,"hello/world")
Which yields the expected:
hello-world
But when I switch to using a variable, I can't seem to get the substitution to stick:
foo:
x="hello/world" ; \
echo $(subst /,-,$$x)
Instead of replacing the slash with a dash, I still get the original string printed back. Can someone explain what is going on here? Does the variable need to be explicitly converted to a string literal or something?
UPDATE:
The fix based on MadScientist's answer--this will allow me to reference the modified string as a variable.
foo:
x="hello/world" ; \
y=`echo $$x | tr / -` ; \
echo $$y
But instead of echo $$y this could be something more useful.
You can't combine make functions with shell variables... all make functions are expanded first, then the resulting script is passed to the shell to be run. When the shell gets the script there are no more make functions in it (and if there were, the shell wouldn't know what to do with them!)
Your subst is running on the literal string $x, which has no / so nothing to replace and results in $x, which the shell expands to the string hello/world.
If you have to work on a shell variable value you must use shell commands such as sed or tr, not make's subst function:
foo:
x="hello/world" ; \
echo $$x | tr / -
You could define x as a make variable:
Makefile:
x = foo bar baz
t:
#echo $(subst bar,qux,$(x))
Output:
make
foo qux baz
Version:
make --version
GNU Make 3.81