I'm trying to use readlink -f to get the absolute path to a shared library following all symlinks.
>readlink -f /opt/gcc4.9.3/lib64/libstdc++.so.6 /opt/gcc4.9.3/lib64/libstdc++.so.6.0.20
but when I do this in cmake it doesn't expand the full path
E.g.
set(CPP11_PATH ${CMAKE_CURRENT_BINARY_DIR}/)
execute_process(COMMAND ldd ${CPP11_PATH} COMMAND grep libstdc++ COMMAND awk "{ print $3; }" OUTPUT_VARIABLE LIBSTDCPP_PATH)
message("LIBSTDCPP_PATH=${LIBSTDCPP_PATH}")
execute_process(COMMAND readlink -f ${LIBSTDCPP_PATH} OUTPUT_VARIABLE LIBSTDCPP_ABSPATH)
message("LIBSTDCPP_ABSPATH=${LIBSTDCPP_ABSPATH}")
prints:
LIBSTDCPP_PATH=/opt/gcc4.9.3/lib64/libstdc++.so.6 LIBSTDCPP_ABSPATH=/opt/gcc4.9.3/lib64/libstdc++.so.6
This also happens if I wrap it in a shell script:
execute_process(COMMAND doreadlink.sh ${LIBSTDCPP_PATH} OUTPUT_VARIABLE LIBSTDCPP_ABSPATH2)
message("LIBSTDCPP_ABSPATH2=${LIBSTDCPP_ABSPATH2}")
>LIBSTDCPP_ABSPATH2=/opt/gcc4.9.3/lib64/libstdc++.so.6
It also happens using get_filename_component() with REALPATH which ought to be the canonical cmake way to do this.
get_filename_component(LIBSTDCPP_PATH ${LIBSTDCPP_PATH} REALPATH)
message("LIBSTDCPP_PATH2=${LIBSTDCPP_PATH}")
Can anyone explain?
I have tried both cmake rebuild_cache and removing the CMakeCache.txt and running with --trace to ensure what I think is being run is really being run. I've also confirmed it happens on rhel5 rhel6 & rhel7 and with both cmake 2.8 & 3.4.
There is a workaround that behaves correctly:
execute_process(COMMAND ldd ${CPP11_PATH} COMMAND grep libstdc++ COMMAND awk "{ print $3; COMMAND xargs readlink -f }" OUTPUT_VARIABLE LIBSTDCPP_PATH)
What might cmake be doing under the hood?
I tried running:
strace -f cmake
pwd
some of the output is:
[pid 23195] execve("/usr/lib64/qt-3.3/bin/readlink", ["readlink", "-f", "/lib64/libstdc++.so.6\n"], [/* 53 vars */]) = -1 ENOENT (No such file or directory) [pid 23195] execve("/usr/local/bin/readlink", ["readlink", "-f", "/lib64/libstdc++.so.6\n"], [/* 53 vars */]) = -1 ENOENT (No such file or directory) [pid 23195] execve("/usr/bin/readlink", ["readlink", "-f", "/lib64/libstdc++.so.6\n"], [/* 53 vars */] [pid 23174] "", 1) = 0 [pid 23195] ) = 0 [pid 23174] close(11) = 0 [pid 23174] rt_sigprocmask(SIG_SETMASK, [], [pid 23195] brk(0 [pid 23174] NULL, 8) = 0 [pid 23195] ) = 0x12e5000 [pid 23174] read(9, "", 1024) = 0 [pid 23174] close(9) = 0 [pid 23195] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0 [pid 23174] close(6) = 0 [pid 23195] ) = 0x7ff41654f000 [pid 23174] close(8) = 0 [pid 23195] access("/etc/ld.so.preload", R_OK [pid 23174] select(8, [3 5 7], NULL, NULL, NULL [pid 23195] ) = -1 ENOENT (No such file or directory) [pid 23195] open("tls/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) [pid 23195] open("tls/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) [pid 23195] open("x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) [pid 23195] open("libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) [pid 23195] open("/usr/local/lib/tls/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) [pid 23195] stat("/usr/local/lib/tls/x86_64", 0x7fff36038350) = -1 ENOENT (No such file or directory) [pid 23195] open("/usr/local/lib/tls/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) [pid 23195] stat("/usr/local/lib/tls", 0x7fff36038350) = -1 ENOENT (No such file or directory) [pid 23195] open("/usr/local/lib/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
snip
O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) [pid 23195] stat("/usr/lib/oracle/10.2.0.4/client/lib/tls", 0x7fff36038350) = -1 ENOENT (No such file or directory) [pid 23195] open("/usr/lib/oracle/10.2.0.4/client/lib/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) [pid 23195] stat("/usr/lib/oracle/10.2.0.4/client/lib/x86_64", 0x7fff36038350) = -1 ENOENT (No such file or directory) [pid 23195] open("/usr/lib/oracle/10.2.0.4/client/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) rt_sigprocmask(SIG_BLOCK, [INT TERM CHLD], [], 8) = 0 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 ioctl(0, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, {B38400 opost isig icanon echo ...}) = 0 ioctl(0, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, {B38400 opost isig icanon echo ...}) = 0 write(2, "LIBSTDCPP_ABSPATH=/usr/lib64/lib"..., 45LIBSTDCPP_ABSPATH=/usr/lib64/libstdc++.so.6 ) = 45
So it even looks like its executing the right system command.
Here is a CMakeLists.txt to reproduce the problem:
cmake_minimum_required(VERSION 2.8) project(TEST CXX) set(CPP11_PATH ${CMAKE_CURRENT_BINARY_DIR}/cpp11) execute_process(COMMAND g++ ${CMAKE_CURRENT_SOURCE_DIR}/cpp11.cpp -o${CPP11_PATH}) #execute_process(COMMAND ldd ${CPP11_PATH} COMMAND grep libstdc++ COMMAND awk "{ print $3; }" COMMAND xargs readlink -f OUTPUT_VARIABLE LIBSTDCPP_PATH) execute_process(COMMAND ldd ${CPP11_PATH} COMMAND grep libstdc++ COMMAND awk "{ print $3; }" OUTPUT_VARIABLE LIBSTDCPP_PATH) message("LIBSTDCPP_PATH=${LIBSTDCPP_PATH}") execute_process(COMMAND readlink -f ${LIBSTDCPP_PATH} OUTPUT_VARIABLE LIBSTDCPP_ABSPATH) message("LIBSTDCPP_ABSPATH=${LIBSTDCPP_ABSPATH}")
Many shell utilities which output single line terminates it with newline (\n
). This is done for pretty output in the terminal.
Unlike to backtricks operator (`exec-some-command`) in Linux shell, which automatically strips trailing newline, execute_process
doesn't do this by default.
The simplest way to strip trailing newline within execute_process
output is to use OUTPUT_STRIP_TRAILING_WHITESPACE
option for it:
execute_process(COMMAND <...>
OUTPUT_VARIABLE LIBSTDCPP_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE
)