I have a makefile generated by bakefile which is working fine. To run the executable it needs libraries from a different folder. I found the command rpath used to specify the path to these library. But I have to send it to the makefile as an argument when using the command.
I cannot specify it directly from the bakefile.
I can use the LDFLAGS arguments which is fine. And I found here how to use the $ORIGIN variable.
My question is how does this escaping works?
make LDFLAGS="-Wl,-rpath '-Wl,\$\$ORIGIN'"
Is the single quote to prevent make to interpret the variable?
And why the \$ is here twice?
Yeesh. What a mess.
So, the first set of quotes is removed by the shell, before it starts the make command. Since the outer set of quotes is double-quotes, you have to escape the $ otherwise the shell will treat it as a shell variable (compare to a command like echo "my path is $PWD" and how the PWD variable is expanded). The shell uses backslashes to quote things like $.
So, by the time the shell hands the command line to make, it sees the setting LDFLAGS=-Wl,-rpath '-Wl,$$ORIGIN'
Next in your makefile will be a recipe with a command like this:
$(LD) $(LDFLAGS) ...
Make will expand the LDFLAGS variable as above. For make, any name preceded by a $ is considered a make variable and you escape it from expansion by make by doubling the $ (not using backslashes like the shell), and writing $$. Make will remove one $ during expansion.
So, make will reduce LDFLAGS to the string -Wl,-rpath '-Wl,$ORIGIN' and pass that to the shell.
The shell will strip the next level of quoting, which in this case is the single quotes. Variables are not expanded inside single quotes, so the linker actually gets arguments, literally, -Wl,-rpath and -Wl,$ORIGIN, which is what you want.
Related
I found in a makefile the following commands:
$(var):
mkdir -p $(#D)
What is the meaning of this command?
$(VAR) expands to the value of the variable VAR. This is a Make variable (not a shell etc variable). For example, if earlier in your Makefile you define
VAR=ick/poo
then VAR expands to ick/poo, and #D in your recipe expands to the directory part, ick.
As you seem to be confused about the relationship between shell and make, I should perhaps point out that these are two different languages, though in a Makefile, you will encounter both; the recipes - the parts which are indented by a tab - will be passed to a shell for evaluation (though normally the shell will be /bin/sh, not Bash, unless you specifically override the Make variable SHELL to force it).
In the shell, by the way, the superficially similar construct $(cmd) performs a command substitution; that is, the command cmd will be evaluated and its output will be inserted as text. So for example,
echo Running in $(pwd)
will print
Running in /home/you
if executed in the directory /home/you (the command pwd prints out your current working directory). ... Though in a Makefile, the dollar sign will normally be evaluated and consumed by make itself; so to pass a literal dollar sign to the shell, you have to double it.
test:
echo Running in $$(pwd)
As already explained by #tripleee $(var) expands to the variable. Because it is here listed before a colon it means that it is a target in a Makefile.
For $(#D) see 10.5.3 Automatic Variables in the make manual:
The directory part of the file name of the target, with the trailing slash removed. If the value of ‘$#’ is dir/foo.o then ‘$(#D)’ is dir. This value is . if ‘$#’ does not contain a slash.
NOTE: This is NOT a shell script. This is a makefile. Please use "man make" for a description about what "make" does.
I need to pass a string argument to a bash script that may contain a $ character. I don't want to force a \ to be inserted into the string outside of the script.
I tried to do that within the script, but couldn't figure out how to do this.
I had a similar issue at a later point in the script where I read in a string using "read". I could only get it to work by forcing the user to enter \$, which is not going to work for my application.
Any suggestions ?
If you don't want to have to escape the $ with a backslash, then the only other alternative is to surround the argument in single quotes. It's not possible to pass a 'naked' $ into your script because the shell will try to expand it. Using single quotes prevents shell expansion and will preserve the $.
For example:
myscript.sh '$foo'
I am writing a shell script where parameter will be a path to a location. I am using readlink -f command to get the absolute path of the path send by user. Suppose if the path send by the user has spaces like,
/home/stack over flow/location
I am excepting the user to send with quotes like
"/home/stack over flow/location"
I have 2 issues here,
1) Even though if the path is passed with quotes, when I iterate over $#, quotes are suppressed and get path without quotes.
2) I did a work around to check if the parameter contain spaces and I add explicitly like
if [[ $1 = *\ * ]] ; then
temp=\"$1\"
fi
where I added quotes " explicitly, but the problem now I am facing is even though I added variable with spaces now readlink is not working.
When I do
full_path=`readlink -f ${temp}`
Its saying
usage: readlink [-n] [-f] symlink
If I execute it as a normal unix command in shell like
readlink -f "/home/stack over flow/location"
which is working and I am getting full path. Why even I append the spaces readlink is not working in shell script? Please help me out with this.
Well it makes sense that you get the path without quotes in the script parameters: the quotes are meant for the shell processing the call of your script, not for the script itself.
I assume you call the command like this:
./test "/home/stack over flow/location"
where 'test' is the script you implement. The quotes around the path make sure the shell that executes this command treats the path as one single argument, not as three separate strings as it would do without the quotes. But the quotes are not treated as part of the parameter itself. So when the parameter is handed over to your script you get one single parameter holding the whole path, not a parameter holding a modified string based on the path: the string with padded quotes.
You can use that paramter without problems. Just put quotes around it again:
readlink -f "$#"
will protect the blanks contained in the specified path, just as in the original call.
Currently I am setting RPATH using following syntax:
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_RPATH}:$ORIGIN/../lib")
It is working for the binary build using CMake.
The problem is that it is not working for a third party binary I am building using CMake using their auto-configure script. I am using the following command for configure:
add_custom_target(
third_party_bin ALL
COMMAND ./configure
--with-ld-opt=\"-Wl,-rpath,${CMAKE_INSTALL_RPATH}\"
--prefix=${CMAKE_INSTALL_PREFIX}
)
The Makefile generated by third path configure look like:
" -Wl,-rpath,':RIGIN/../lib' -lstdc++"
I think I need to escape ${CMAKE_INSTALL_RPATH} correctly.
I also tried using options like:
add_custom_target(
third_party_bin ALL
COMMAND ./configure
--with-ld-opt=\"-Wl,-rpath,\$\$ORIGIN/../lib\"
--prefix=${CMAKE_INSTALL_PREFIX}
)
and
add_custom_target(
third_party_bin ALL
COMMAND ./configure
--with-ld-opt=\"-Wl,-rpath,\\$\$ORIGIN/../lib\"
--prefix=${CMAKE_INSTALL_PREFIX}
)
but nothing works.
What is the correct way to escape values?
add_custom_target accepts a VERBATIM argument. According to the documentation:
If VERBATIM is given then all arguments to the commands will be
escaped properly for the build tool so that the invoked command
receives each argument unchanged. Note that one level of escapes is
still used by the CMake language processor before add_custom_target
even sees the arguments. Use of VERBATIM is recommended as it enables
correct behavior. When VERBATIM is not given the behavior is platform
specific because there is no protection of tool-specific special
characters.
If I am reading this correctly, you are looking to escape the shell from interpreting the $ variables... If so, use single quotes (') instead of doubles ("). The shell doesn't interpret variables wrapped in single quotes.
My program requires an environment variable as part of one of its parameters:
myprogram --folder=$HOME/.special
However, if I put this into a .desktop file's exec line, it doesn't work:
Exec=myprogram --folder=$HOME/.special
The $HOME seems to resolve to nothing.
By default environment variables do not seem to be resolved by all implementations, however you can instead exec sh, which will resolve the passed environment variable. Note that the desktop spec also requires you to escape the = and $ character with a backslash. So you want:
Exec=sh -c "myprogram --folder\=\$HOME/.special"
For the full list of characters that need escaping, see the specification