74

I want to setup a custom toolchain with CMake. I've set the compiler but I don't know how to set the linker. This error is reported because CMake try to use the compiler to link:

The C compiler "xgcc.exe" is not able to compile a simple test program.

Here there is a snippet of my toolchain file

# specify the cross compiler
INCLUDE(CMakeForceCompiler)
SET(CMAKE_C_COMPILER   xgcc.exe)
SET(CMAKE_CXX_COMPILER xgcc.exe)
#CMAKE_FORCE_C_COMPILER(xgcc.exe GNU)
#CMAKE_FORCE_CXX_COMPILER(xgcc.exe GNU)

I've tried to force the compiler but the linker problem will not be solved.

usr1234567
  • 21,601
  • 16
  • 108
  • 128
Breezeight
  • 1,855
  • 2
  • 22
  • 27

9 Answers9

46

The link command line is set in Modules/CMake{C,CXX,Fortran}Information.cmake and defaults to using the compiler, not CMAKE_LINKER (see source code). This can be changed by replacing the rule that builds the link command line, which lives in variables CMAKE_CXX_LINK_EXECUTABLE (and friends). NB that variable does not indicate the path to the linker executable; it says how to link an executable!

One approach is to set that rule to use the linker, e.g.

cmake -DCMAKE_LINKER=/path/to/linker -DCMAKE_CXX_LINK_EXECUTABLE="<CMAKE_LINKER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>"

See also this post from CMake mailing list and this one - this also makes a natural place to prepend a linker modifier to another linker.

Kevin
  • 16,549
  • 8
  • 60
  • 74
mabraham
  • 2,806
  • 3
  • 28
  • 25
  • 1
    How to define `OBJECTS` in `-DCMAKE_CXX_LINK_EXECUTABLE=" -o "` ? – Ezio Mar 24 '17 at 06:07
  • 3
    You don't specify anything in this rule. It's a rule for cmake to follow to build an executable, so it inserts the objects that got built from eg sources you added to targets in your CMakeLists.txt files. – mabraham Mar 24 '17 at 06:44
  • Or ,how can I change the value of strings like CMAKE_CXX_LINK_FLAGS , LINK_FLAGS ? – Ezio Mar 26 '17 at 14:51
  • LINK_FLAGS is handled by cmake. The other is a cache variable you can set in the usual way, eg cmake -DCMAKE_CXX_LINKER_FLAGS=whatever – mabraham Mar 26 '17 at 15:26
  • oh no,it does not work when I modify CMAKE_CXX_LINKER_FLAGS. – Ezio Mar 26 '17 at 16:00
  • Actually it's cmake -DCMAKE_CXX_LINK_FLAGS. But this is not the right approach for appending to linker flags (use the appropriate property of the targets you want to modify), nor the place for debugging the lots of things you could be doing wrong, even if using this rule is the right approach and my original advice was accurate :-) – mabraham Mar 26 '17 at 16:12
  • After setting `CMAKE_CXX_LINK_EXECUTABLE`, I got `bad -rpath option` error. – Robin Hsu Sep 21 '18 at 09:48
  • I tried -DCMAKE_LINKER=/path/to/linker -DCMAKE_CXX_LINK_EXECUTABLE=" -o " and it did not have any effect. CMake version 3.15-rc2. – Alexander Samoylov Oct 08 '19 at 11:49
  • 1
    @mabraham Overriding CMAKE_CXX_LINK_EXECUTABLE confuses CMake and it doesn't provide the object file on the initial testing of linking a sample C application. I'm getting "The C compiler ... is not able to compile a simple test program. clang++: error: no input files". Maybe this is what Ezio meant with his 1st comment. CMake version: 3.12.4. See also https://cmake.org/pipermail/cmake-developers/2014-June/022364.html – Zuzu Corneliu Mar 19 '20 at 09:22
  • The CMAKE_CXX_LINK_EXECUTABLE rule is probably being used in the standard CMake check that the compiler work. If you've overridden it in a way that doesn't work, then you would get the symptoms you describe. – mabraham Mar 20 '20 at 08:22
  • Here's the CMake [source code](https://github.com/Kitware/CMake/blob/5beaee81ef04241758e3d9b879413ee9d2b570b7/Modules/CMakeCXXInformation.cmake#L287-L290) of setting the default value of `CMAKE_CXX_LINK_EXECUTABLE`. I don't know why it does not use `CMAKE_LINKER` though. – cyfdecyf Mar 05 '22 at 08:23
37

As Mabraham points out, CMake calls the compiler to do the linking. So, by far the simplest solution to this is to LET IT, and instead tell the compiler to run a different linker when called.

Which, as noted in this other answer — but now it's even a documented option in gcc --help=common — is as easy as:

cmake -DCMAKE_CXX_FLAGS="-fuse-ld=lld"

g++ or clang++ will get passed the -fuse-ld=lld1 flag on every call, and when they do any linking they'll use the specified command instead of the built-in default. Easy-peasy, and CMake need not concern itself with such things at all.

(BTW, the option is parsed (-f) (use-ld) (=) (lld), there's no "fuse" option to gcc.)

Notes

  1. When using Clang, lld can be replaced with whatever other linker command you want to use, like ld.exe, ld.gold, mingw32/bin/ld.exe, etc.

    GCC isn't as flexible, its -fuse-ld only accepts a limited set of possible arguments. (They're listed in the gcc --help=common output, as of GCC 12.2.1 the list is: bfd, gold, lld, or mold.) It will invoke the first matching ld.foo executable it finds on the PATH. (Thanks to bviktor for pointing out GCC's limitations for alternate linker selection.)

FeRD
  • 1,699
  • 15
  • 24
  • 3
    Of all the "solutions" presented here, this is the only command-line solution that actually worked for me (cmake 3.19.2) – sdenham Dec 29 '20 at 15:36
  • 1
    Wait, this isn't the best idea, right? After all this flag won't be used unless the compiler driver is used to link some binary. @sdenham there are so many CMake projects out there all with subtle differences, whether this works or not depends very much on your project. Should work on most barebones/vanilla projects, though. – 0xC0000022L Jan 21 '21 at 15:45
  • 2
    @0xC0000022L I agree that it is not an ideal solution, but for anyone finding this question, as I did, because the CMake project they are trying to use *does* rely on the compiler driver to invoke the linker, this may be the best of all solutions proposed here. – sdenham Jan 21 '21 at 22:30
  • @0xC0000022L I mean, I guess if a CMake project is calling the linker _itself_ or something, then this won't work, sure. But if that's your concern (or am I misunderstanding?), I don't really see how there could be ANY general solution for that, since it would depend how they've coded the project. But for any project that just uses CMake's default compiler-driven linking, like sdenham says, this should influence it with minimal friction. – FeRD Jan 22 '21 at 23:39
  • 1
    @FeRD Nope, my point is that this is the wrong variable to set. It _will_ work, don't get me wrong. But every single compilation unit being compiled will cause a warning of an unused command line switch. Because that switch is really only ever relevant during the linking step. So while this works it can hardly be called a best practice. In a GNUmakefile you wouldn't use `CXXFLAGS` to convey linker flags, typically. Regardless, I upvoted for the general utility of this answer, despite this flaw. – 0xC0000022L Jan 22 '21 at 23:43
  • @0xC0000022L Ah, gotcha, now I understand. Yeah unfortunately there's no way to specify switches _only_ for the linker call (AFAICT), and the warnings are ultimately harmless. Though I also agree, not _ideal_. (And I suppose the warning itself does make "**minimal** friction" somewhat of an exaggeration.) – FeRD Jan 22 '21 at 23:46
  • 2
    @FeRD Ah, but there is. It's just that you have to do it for every single type of artifact you're linking. [`CMAKE_EXE_LINKER_FLAGS`](https://cmake.org/cmake/help/latest/variable/CMAKE_EXE_LINKER_FLAGS.html) and [`CMAKE_SHARED_LINKER_FLAGS`](https://cmake.org/cmake/help/latest/variable/CMAKE_SHARED_LINKER_FLAGS.html) are two of them – 0xC0000022L Jan 23 '21 at 00:07
  • 1
    The problem with this solution is that it assumes your linker executable is `ld.lld` while in fact all LLVM packages have versioned executables, e.g. `ld.lld-11`. Which 1) requires sudo rights to fix 2) makes it impossible to target different LLD versions. – bviktor Feb 04 '21 at 19:37
  • 1
    @bviktor My Fedora-packaged install of LLVM 11 provides the RPM `lld-11.0.0-1.fc33`, which includes the linker binary `/usr/bin/lld`. Regardless, what's wrong with using `-fuse-ld=lld-11` if that's the name of the executable? As I said in the answer, you can specify any linker executable you like. – FeRD Feb 18 '21 at 05:35
  • The problem with that is that GCC errors out. `g++: error: unrecognized command line option ‘-fuse-ld=lld-11’; did you mean ‘-fuse-ld=lld’?`. It doesn't matter if the Fedora package creates the symlink, the point is, you can only use ONE version at a time. Every time you need to target a different version, you have to replace that symlink as root. – bviktor Feb 18 '21 at 09:44
  • @bviktor Ah, yes, I see. It seems that in GCC [unlike Clang](https://stackoverflow.com/questions/40470931/how-to-specify-alternate-linker-command-when-linking-with-cc#comment116013301_59326463), `-fuse-ld` _doesn't_ take an arbitrary path, it can only accept [one of three arguments](https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html#Link-Options): `bfd`, `gold`, or `lld`. It will respect `$PATH` in choosing which binary to run. So, multiple versions installed in separate, versioned dirs (i.e. Homebrew-style) will work. Multiple versions all installed in `/usr/bin/` with suffixed names won't. – FeRD Feb 24 '21 at 03:12
  • (On Fedora, `/usr/bin/lld` _isn't_ a symlink, it's the actual `lld` binary. `/usr/bin/ld.lld` is a symlink to it. Fedora doesn't generally support installing multiple versions of the same tool, mostly because of issues like this.) So, if Ubuntu (I'm guessing) is packaging suffixed binaries like `/usr/bin/ld.lld-11`, then they're making that up themselves and agreed — it won't work. Linuxbrew for example installs multiple LLVM versions as e.g. `/home/linuxbrew/.linuxbrew/Cellar/llvm@9/9.0.1_3/bin/ld.lld`, and then you can use the `PATH` along with `-fuse-ld=lld` to select a version. – FeRD Feb 24 '21 at 03:19
9

I had success with doing

add_link_options("-fuse-ld=lld")

It is a variation on the previous answers here. The difference is the CMake command I use to set the flag.

Adding it to CMAKE_CXX_FLAGS has the disadvantage of then also having to add -Wno-unused-command-line-argument as the flags get also added to compilation commands, not only to linking ones.

The disadvantage of CMAKE_SHARED_LINKER_FLAGS is that you have to add it multiple times, to _SHARED_, _EXE_, and maybe I forgot something.

user7610
  • 25,267
  • 15
  • 124
  • 150
  • This is the right solution. No long, error-prone command lines. `cmake -DCMAKE_CXX_FLAGS="-fuse-ld=lld"` as suggested above would be a good solution if it didn't cause an error while compiling (not linking) with `-Werror`, since the flag is unused during compilation. – swineone Aug 12 '21 at 18:40
  • There are also both `CMAKE_MODULE_LINKER_FLAGS` and `CMAKE_STATIC_LINKER_FLAGS`, for completeness' sake. And then of course potentially separate `CMAKE_SHARED_LINKER_FLAGS_DEBUG`, `CMAKE_SHARED_LINKER_FLAGS_RELEASE`, etc... though I don't know what possible advantage there would be to modifying the linker for only _some_ configurations. – FeRD Aug 30 '21 at 11:19
  • 1
    This answer depends on the right compiler. Setting the linker has a different syntax for GCC and Clang. – usr1234567 Mar 12 '22 at 21:09
  • @usr1234567 In general, you are correct. Thankfully, there is some convergence between GCC and Clang in the area of specifying the linker https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93645. Here's an example of a reasonable approach to maintain separate flags for different compilers using variable interpolation, which is sometimes necessary to do https://github.com/apache/qpid-proton/blob/e47b0805abe5bd25a4f979142d620f498b296407/CMakeLists.txt#L71-L78. – user7610 Aug 15 '22 at 09:57
9

CMake only gives you direct control over the compiler for each language. To call the linker, it goes through the configured compiler. This means that there is no universal way to set the linker in CMake, you must configure your compiler to use the linker you intend.

Such flags need to be set before CMake's compiler detection routines run because it will try to compile a test binary. The best way to do this is by creating a toolchain file. The best way to set these flags in the toolchain file is like so:

# e.g. to use lld with Clang
set(CMAKE_EXE_LINKER_FLAGS_INIT "-fuse-ld=lld")
set(CMAKE_MODULE_LINKER_FLAGS_INIT "-fuse-ld=lld")
set(CMAKE_SHARED_LINKER_FLAGS_INIT "-fuse-ld=lld")

These three variables control the (default) set of linker flags for executables, loadable modules, and shared libraries, respectively. There is no need to handle CMAKE_STATIC_LINKER_FLAGS_INIT (for static libraries) here because the archiver is invoked, rather than the linker.

You can then set the toolchain file when you first run CMake by setting -DCMAKE_TOOLCHAIN_FILE=/path/to/toolchain.cmake at the command line. As of CMake 3.21, you will be able to pass --toolchain /path/to/toolchain.cmake instead (which is entirely equivalent, but a little less typing).

Alex Reinking
  • 16,724
  • 5
  • 52
  • 86
6

Set the variable ${CMAKE_LINKER} either in CMakeCache.txt or after ccmake . under advanced options.

Gunther Piez
  • 29,760
  • 6
  • 71
  • 103
  • 1
    Can I set it in CMakeLists.txt? I 've just looked for CMAKE_LINKER in the cmake man page but I cannot find it. Are You sure about the name of the variable? – Breezeight Dec 08 '09 at 16:56
  • 2
    Yes. Invoke `ccmake .` in your build directory and press 't'. Scroll down until you hit CMAKE_LINKER. – Gunther Piez Dec 08 '09 at 22:53
  • Sure but when I compile the project for the first time I start with cmake -DCMAKE_TOOLCHAIN_FILE=MyToolchain.cmake . And this generate an error. – Breezeight Dec 09 '09 at 09:34
  • AFAIK there is a "CACHE" option for the SET command, you could try to use ist for setting CMAKE_LINKER in CMakeLists.txt. Or try `cmake -DCMAKE_LINKER=yourlinker -DCMAKE_TOOLCHAIN_FILE=MyToolchain.cmake` – Gunther Piez Dec 09 '09 at 10:19
  • Ok! Now the CMAKE_LINKER variable is ok but cmake still want to link with xgcc. :( – Breezeight Dec 09 '09 at 12:14
  • This behaviour of using the compiler and not the linker seems intended (but I'm not sure if it's a feature!) - see my answer – mabraham Aug 12 '14 at 21:28
  • 2
    I used to do exactly this, and now it doesn't work. As if cmake doesn't look at this variable at all: setting `CMAKE_LINKER` to complete nonsense doesn't change the error (or the command-lines) in the slightest. CMake 3.14.5. – ulidtko Sep 19 '19 at 17:31
  • 1
    @mabraham one example of when it can be a feature is when the compile flag requires linking with a run-time library (e.g. fsanitize) – pooya13 Feb 01 '20 at 10:05
6

I have to use CMAKE_CXX_LINK_EXECUTABLE, CMAKE_C_LINK_EXECUTABLE variable:

SET(CMAKE_C_LINK_EXECUTABLE "c:\\MoSync\\bin\\pipe-tool.exe")
Breezeight
  • 1,855
  • 2
  • 22
  • 27
  • 28
    This doesn't work in most cases - that variable establishes the rule for the whole link command, and generally flags and arguments will be required for linking. – mabraham Aug 12 '14 at 21:27
3

Here is a CMake function which sets linker based on some predefined arbitrary rules (Clang -> lld-version or lld, GCC -> gold).

The important parts:

  1. Search for lld-version which matches the Clang compiler version (ex. lld-13 if Clang 13.x.x is used), falls back to lld if not found
    add_link_options("-fuse-ld=lld-${CLANG_VERSION_MAJOR}")
  1. Use all system threads when linker is set to gold:
    add_link_options("-fuse-ld=gold;LINKER:--threads,--thread-count=${HOST_PROC_COUNT}")

The example is a bit too long because of comments, logs and custom logic, but it is self-contained and could be useful staring point for beginners.

function(select_best_linker) #lld for Clang and GNU gold for GCC
    if (UNIX AND NOT APPLE)
        include(ProcessorCount)
        ProcessorCount(HOST_PROC_COUNT)

        if(${CMAKE_CXX_COMPILER_ID} MATCHES Clang)

            # By default LLD uses all system threads.
            # This could be tweaked for versions 11+ (--threads=1), but cannot be disabled for older versions
            # add_link_options("-fuse-ld=lld-${CLANG_VERSION_MAJOR};LINKER:--threads=${HOST_PROC_COUNT}") #LLD>=11
            # add_link_options("-fuse-ld=lld;LINKER:--threads")#LLD <= 10 this is the default state

            string(REPLACE "." ";" VERSION_LIST ${CMAKE_CXX_COMPILER_VERSION})
            list(GET VERSION_LIST 0 CLANG_VERSION_MAJOR) #extract major compiler version

            find_program(LLD_PROGRAM_MATCH_VER lld-${CLANG_VERSION_MAJOR}) #search for lld-13 when clang 13.x.x is used
            find_program(LLD_PROGRAM lld) #else find default lld

            if (LLD_PROGRAM_MATCH_VER) #lld matching compiler version
                message(STATUS "Set linker to LLD (multi-threaded): ${LLD_PROGRAM_MATCH_VER}")
                add_link_options("-fuse-ld=lld-${CLANG_VERSION_MAJOR}")
            elseif(LLD_PROGRAM) #default lld
                message(STATUS "Set linker to LLD (multi-threaded): ${LLD_PROGRAM}")
                add_link_options("-fuse-ld=lld")
            endif(LLD_PROGRAM_MATCH_VER)

        elseif(${CMAKE_CXX_COMPILER_ID} MATCHES GNU)

            find_program(GNU_GOLD_PROGRAM gold)
            if (GNU_GOLD_PROGRAM)
                message(STATUS "Set linker to GNU gold: ${GNU_GOLD_PROGRAM}, using threads: ${HOST_PROC_COUNT}")
                add_link_options("-fuse-ld=gold;LINKER:--threads,--thread-count=${HOST_PROC_COUNT}")
            endif(GNU_GOLD_PROGRAM)

        endif(${CMAKE_CXX_COMPILER_ID} MATCHES Clang)
    endif(UNIX AND NOT APPLE)
endfunction(select_best_linker)

Tested on:

  • Ubuntu 20.04
  • CMake 3.16.3
  • GCC 9.4.0
  • Clang-12
  • Clang-13
  • GNU gold (GNU Binutils 2.37) 1.16
  • LLD 10.0.0 (compatible with GNU linkers)
  • Ubuntu LLD 13.0.1 (compatible with GNU linkers)
MartinBG
  • 1,500
  • 13
  • 22
2

For completeness, another full-proof option is to just link /usr/bin/ld to ld.gold by running

sudo ln -sf /usr/bin/x86_64-linux-gnu-ld.gold /usr/bin/ld

as suggested here

Vik
  • 690
  • 7
  • 22
1

There is another way to do it, gcc has a "-fuse-ld" option, you can set LINKER_FLAGS in CMakeLists.txt like these:

set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fuse-ld=lld")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=lld")

then the custom specified linker should be invoked.

buffy
  • 71
  • 2
  • 8