4

I need to support custom executable linking command in my CMake script, namely Synopsys VCS. VCS is a wrapper over GCC, but it uses special syntax for passing LD options:

vcs -LDFLAGS "<LINK_FLAGS>" <LINK_LIBRARIES> <OBJECTS>

The rule to link an executable lives in CMAKE_CXX_LINK_EXECUTABLE variable, so I've tried to play with it:

set(CMAKE_CXX_LINK_EXECUTABLE "echo CXXFLAGS: <CMAKE_CXX_LINK_FLAGS>  LINK_FLAGS: <LINKER_FLAGS> LINK_LIBRARIES: <LINK_LIBRARIES> OBJECTS: <OBJECTS> ")

When I build the project I got:

CXXFLAGS: 
LINK_FLAGS:
LINK_LIBRARIES: -rdynamic ../slib/libslib.a ../dlib/libdlib.so -Wl,-rpath,/home/ripopov/proj_cmake/build/dlib 
OBJECTS: CMakeFiles/sim.dir/sc_main.cpp.o

So all linker flags together with libraries are in LINK_LIBRARIES

How can I extract linker flags from LINK_LIBRARIES?

Kevin
  • 16,549
  • 8
  • 60
  • 74
random
  • 3,868
  • 3
  • 22
  • 39
  • 1
    `LINK_FLAGS` contains what you can e.g. define with target property [`LINK_FLAGS`](https://cmake.org/cmake/help/latest/prop_tgt/LINK_FLAGS.html). The additional things you see in `LINK_LIBRARIES` come from `CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS` and `CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG`. So I see three options: either parse for linker `-Wl,` options in an intermediate script, don't use [Expansion Rules](https://cmake.org/Wiki/CMake_Useful_Variables#Expansion_Rules) but CMake variables/generator expressions or define [your own linker language](http://stackoverflow.com/questions/34165365). – Florian May 22 '16 at 20:17
  • Thank you @Florian ! Intermediate script is an option, but I'm trying to avoid it. If I echo CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG I got " -Wl,-rpath," , so rpath itself is not there. So far I was not able to get rpath in some variable. – random May 23 '16 at 21:18
  • Basically CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG is a flag used to pass runtime path to linker, but not runtime path itself. – random May 23 '16 at 21:30
  • Then you could give the last option a try by defining your own linker language: `set(CMAKE_VCS_LINK_EXECUTABLE "vcs -LDFLAGS \"\" ")`. And changing the [`LINKER_LANGUAGE`](https://cmake.org/cmake/help/latest/prop_tgt/LINKER_LANGUAGE.html) of your executable target named `main` for example: `set_target_properties(main PROPERTIES LINKER_LANGUAGE VCS)` – Florian May 24 '16 at 19:55
  • Thank you @Florian , unfortunately `` evaluates into empty string. I've tried `set(CMAKE_VCS_LINK_FLAGS ${CMAKE_CXX_LINK_FLAGS})` but it has no effect. Whatever I've tried , I was not able to extract `-rdynamic -Wl,-rpath,/home/ripopov/proj_cmake/build/dlib` into some cmake variable. Looks like extra intermediate script is an only option. – random May 24 '16 at 20:33
  • @Floarian, I downloaded CMake source code and did a quick search for some useful variables. It looks like, however, that libraries and flags are not separable from each other. It looks like that function that generates `LINK_LIBRARIES` is `cmLocalGenerator::OutputLinkLibraries` . And unfortunately it puts all flags and libs into single std::string. – random May 24 '16 at 21:16

1 Answers1

6

Turning my comments into an answer

The Problem

Let's take a look at the Linux/GNU/GCC/CXX specific link line defined in CMAKE_CXX_LINK_EXECUTABLE:

<CMAKE_CXX_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> 
                     <OBJECTS> -o <TARGET> <LINK_LIBRARIES>

Since you problem is with the LINK_LIBRARIES expansion rule, let's take a closer look (taking your example) at how its content is generated in cmLocalGenerator::OutputLinkLibraries():

LINK_LIBRARIES

But you don't want those linker flags mixed into LINK_LIBRARIES because your Synopsys VCS GCC wrapper wants them a with -LDFLAGS prefix/group.

And you're right "that libraries and flags are not separable from each other. It looks like that function that generates LINK_LIBRARIES is cmLocalGenerator::OutputLinkLibraries. And unfortunately it puts all flags and libs into single std::string."

Possible Solutions

I see five options (or some combination of them):

  1. Prefix/Replace the normal gcc/ld flags with their VCS match
  2. Parse for linker -Wl, options in an intermediate script
  3. Don't use Expansion Rules but CMake variables/generator expressions
  4. Define your own linker language
  5. Prefer static libraries over dynamically linked libraries

So - in an variant of option 3. - you need to know why the linker options you see in LINK_LIBRARIES are there and how you can suppress and then regenerate them elsewhere.

Here is an example that I have successfully tested:

cmake_minimum_required(VERSION 2.8)

project(LinkerVCS CXX)

file(WRITE foo.h   "void foo();\n")
file(WRITE foo.cpp "void foo() {};\n")
file(WRITE bar.h   "void bar();\n")
file(WRITE bar.cpp "void bar() {};\n")

file(
    WRITE main.cpp 
    "#include \"foo.h\"\n"
    "#include \"bar.h\"\n"
    "int main() { foo(); bar(); return 0; }\n"
)

set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_OBJECTS 0)
set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_LIBRARIES 0)

# remove the '-rdynamic'
set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")
# define my own search path for shared libraries
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG}${CMAKE_BINARY_DIR}/lib")

set(CMAKE_CXX_LINK_EXECUTABLE "echo CXXFLAGS: ${CMAKE_CXX_FLAGS} LINK_FLAGS: <LINK_FLAGS> LINK_LIBRARIES: <LINK_LIBRARIES> OBJECTS: <OBJECTS>")

add_library(slib STATIC foo.cpp)
add_library(dlib SHARED bar.cpp)

add_executable(main main.cpp)
target_link_libraries(main slib dlib)
# remove the CMake generated '-Wl,-rpath'
set_target_properties(main PROPERTIES SKIP_BUILD_RPATH 1)

This will give

CXXFLAGS: 
LINK_FLAGS: -Wl,-rpath,[... my binary path ...]/lib
LINK_LIBRARIES: libslib.a lib/libdlib.so 
OBJECTS: CMakeFiles/main.dir/main.cpp.o

The main question would be if you really need the -rdynamic and -Wl,-rpath options.

The first can be skipped, see Linux-GNU.cmake:

# We pass this for historical reasons.  Projects may have
# executables that use dlopen but do not set ENABLE_EXPORTS.
set(CMAKE_SHARED_LIBRARY_LINK_${lang}_FLAGS "-rdynamic")

The later can also be skipped if you have the .so shared libraries in the system paths, see e.g. Wikipedia rpath:

Specifically it encodes a path to shared libraries into the header of an executable (or another shared library). This RPATH header value (so named in the Executable and Linkable Format header standards) may either override or supplement the system default dynamic linking search paths.

References

Florian
  • 39,996
  • 9
  • 133
  • 149
  • Thank you Florian. I've decided to go with intermediate script, because in my case it just solves all the problems. Your latest example does not solves original problem, but offers a workaround: i.e. put all libraries in some predefined directory. Unfortunately this does not work in every case: for example if you import some per-built targets using find_package() or import(). – random May 30 '16 at 21:50
  • @random You're welcome. I've updated my answer to give more details on if and when you actually need the `-rdynamic` and `-Wl,-rpath` options. I think the first one can be safely removed, the later is a question of when and how you actually run the executable. I admit I don't have any experience with Synopsys VCS, so can go a little into detail what you actually do with the linker output? Is it only used during development or does it also build release binaries/executables? – Florian May 31 '16 at 20:58
  • In my usage scenario it is convenient to have RPATH to be built-in into executable so I don't have to deal with LD_LIBRARY_PATH all the time. I use it only for debug. There is no "release binary" in my case, "release" is a piece of silicon with transistors. – random May 31 '16 at 21:40
  • @random Just two thoughts: 1. You could switch to static libraries for development (that's what I've done; it's also easier to distribute/"install"). 2. You could check if `-LDFLAGS "..."` could be given multiple times on `VCS` command line and if the quotes are mandatory, because then you don't need to parse the command line rather just change `CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG` itself to `-LDFLAGS -Wl,-rpath,`. – Florian Jun 02 '16 at 06:46
  • Thank you Florian. 1) is not always an option in my case 2) Yes, it makes sense. I've experimented with `CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG` and come to conclusion that it should be applied at very top CMakeLists.txt. – random Jun 03 '16 at 18:10