Messing around with linkers & CMake

Tijl Coosemans tijl at FreeBSD.org
Wed Apr 19 18:39:12 UTC 2017


On Wed, 19 Apr 2017 20:06:06 +0200 Adriaan de Groot <groot at kde.org> wrote:
> On Saturday 15 April 2017 13:36:40 Koop Mast wrote:
>> So I was looking into using binutils from ports to build webkitgtk when
>> debug is enabled. With the CMAKE_AR and CMAKE_RANLIB variables I can
>> set the binutils ar and ranlib. However the problem is that for some
>> reason cmake doesn't have a LINKER variable, which results in it using
>> ld from base which doesn't understand thin archives. I tried
>> CMAKE_LINKER a while back and that didn't seem to work.  
> 
> The problem is in several stages:
> 
> 0) I'm going to assume CMake 3.7.1 is being used, and that you have a C-based 
> library that you are building.
> 
> 1) First, the standard definitions can be found in 
> /usr/local/share/cmake/Modules/CMakeCInformation.cmake , and they define so-
> called rule-variables, which are rules that are later expanded by the (Makefile 
> | Ninja | whatever) generator for specific targets. There is a definition in 
> there for the rule that creates a shared library:
> 
>   set(CMAKE_C_CREATE_SHARED_LIBRARY
>       "<CMAKE_C_COMPILER> <CMAKE_SHARED_LIBRARY_C_FLAGS> 
> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS> 
> <SONAME_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>")
> 
> That means that it is really going to use the C compiler (e.g. /usr/bin/cc, if 
> that is what it found), followed by a bunch of flags and thingies.
> 
> There *is* a CMAKE_LINKER variable, but it is not used when creating a C-
> language shared library, because the rule variable doesn't use it. So what you 
> could do, is change the rule for creating a C-language shared library:
> 
>     set(CMAKE_C_CREATE_SHARED_LIBRARY
>         "<CMAKE_LINKER> <CMAKE_SHARED_LIBRARY_C_FLAGS> 
> <LANGUAGE_COMPILE_FLAGS> <
> LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS> <SONAME_FLAG><TARGET_SONAME>   
> -o
>  <TARGET> <OBJECTS> <LINK_LIBRARIES>")
> 
> See how I have changed the command at the start? Do this somewhere really 
> early in the top-level CMakeLists.txt; I'm not sure when the variable is 
> expanded or its value checked. But this does change the rule for *every* C-
> language shared library that is built by the project.
> 
> 2) It's going to take some futzing to massage the command to work with a/the 
> linker directly. For example, SONAME_FLAG expands to -Wl,-soname, -- but I 
> don't think you want to change that variable, you want to DTRT in your command 
> to create C-language shared libraries:
> 
>     set(CMAKE_C_CREATE_SHARED_LIBRARY
>         "<CMAKE_LINKER> <CMAKE_SHARED_LIBRARY_C_FLAGS> 
> <LANGUAGE_COMPILE_FLAGS> <
> LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS> -soname <TARGET_SONAME> -o  
>  <TARGET> <OBJECTS> <LINK_LIBRARIES>")
> 
> Since you're messing around with commands directly, and don't care for 
> portability to other systems or compilers *anyway*, you may want to plumb in 
> values more directly (this example probably does not do what you want):
> 
>     set(CMAKE_C_CREATE_SHARED_LIBRARY
>         "/usr/lbin/verify_krb5_conf --dumpconfig <TARGET_SONAME>")
> 
> 3) You can verify what CMake has decided by subsequently building with 
> VERBOSE=1, but also remember that CMake writes Makefiles (or Ninja, or 
> whatever) that re-invoke CMake to run some commands; those commands are 
> usually read from a text file, and you can look at the text file, too.
> 
> For instance, if I have this CMakeLists.txt:
> 
> project(kwm)  # Kannie Wachten op Meson
> add_executable(kwm main.c)
> add_library(gt SHARED shared.c)
> 
> Then the command that ends up in the Makefile (er .. I may mention Ninja a few 
> times here, but I've only ever tried Make):
> 
>     /usr/local/bin/cmake -E cmake_link_script CMakeFiles/gt.dir/link.txt --
> verbose=1
> 
> So to link my C-language shared library called gt, it calls cmake which reads 
> a file named link.txt (in a subdir obviously named after the shared library 
> being built). The content of that file is:
> 
>     /usr/bin/ld -fPIC   -shared -soname libgt.so -o libgt.so 
> CMakeFiles/gt.dir/shared.c.o 
> 
> (for this example I used the CMAKE_C_CREATE_SHARED_LIBRARY definition from the 
> middle of section (2), not the useless one at the end). Now you can map-
> backwards that line to the variables expanded from the rule variable for 
> creating C-language shared libraries.
> 
> Use that to your advantage while tweaking the rules. What you *could* even do, 
> is post-configure edit the generated link.txt files if you can't get the effect 
> you want from within CMake. That would be "here be dragons" territory, though.
> 
> 4) So coming back to your original question:
> 
>> My question is now does cmake have a variable, or another way, that I
>> can set so it uses ld from binutils instead of ld from base?  
> 
> There's three possible ways:
> 
>  - find a flag to tell the C compiler to use a different linker (e.g. -fuse-ld, 
> if you can use that to set the linker to the one from binutils),


Adding -B${LOCALBASE}/bin to LDFLAGS should do the trick.  It's used in
a few places in the ports tree.


>  - tweak the rule command CMake uses to generate the Makefile rule that builds 
> the shared library, to use the other linker, for instance by replacing the 
> part of the rule <CMAKE_C_COMPILER> with something else,
>  - tweak the generated text file for the command, instead.
> 
> I'm going to take a look at variable scope for rule-generation later tonight, 
> so that you might be able to tweak this for single libraries only, so's to 
> give you more nuanced control.
> 
> So the *actual* answer to your question is "yes".


More information about the kde-freebsd mailing list