Building metallib with CMake - cmake

I have some .metal files and I want to compile it to .metallib. Whole build system is using CMake and I am going to use it to compile .metal too.
To build .metallib I must use the next two commands:
xcrun -sdk macosx metal -c MyLibrary.metal -o MyLibrary.air
xcrun -sdk macosx metallib MyLibrary.air -o MyLibrary.metallib
Is there any convenient way to do it with CMake where I can set compiler and CMake will do everything automatically? Or I should use add_custom_command and manually call xcrun?

TL;DR Both approaches, using custom commands/targets and adding a new language, are valid. The choice seems to depend on trade-offs between reusability, distribution range, target users, etc. Personally, I'd initially opt for the former.
Having said that, if you opt for the add_custom_command/add_custom_target method, it can get you pretty far. Just make sure that you provide actual targets and link them with the OUTPUT of custom commands, in order to be able to set up proper dependencies and avoiding unnecessary re-execution of targets when dependencies are still up to date. For example,
set(FOO_FILE "foo.txt")
add_custom_command(OUTPUT ${FOO_FILE}
COMMAND ${CMAKE_COMMAND} -E touch ${FOO_FILE}
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
COMMENT "Creating ${FOO_FILE}"
VERBATIM)
add_custom_target(foo DEPENDS ${FOO_FILE})
The approach of adding a new language is a bit more involved, as it's targeted to be more generic and provide language support as a CMake feature (i.e. not tied to a particular project). For this, you are required to define various aspects of the language, such as the compiler and the rules to use to process the source files. Apart, from these, feature detection and testing usually fall here too. For more detailed documentation have a look here.
However, investigating an actual language as is supported by CMake could be helpful. I'd suggest having a look under CMake's Modules directory for CUDA (this will lead you to have a look into the Compiler and Platform subdirs too).
NOTE: Digging through the mailing list, I bumped into this thread, that mentions that the second approach might not work so well with non Makefile-based generators. It is not clear from the thread (or the existing documentation) and I cannot try it since I'm not using CMake via such a tool (e.g. MSVC or Xcode).

# metal kernel
set(SRC_METAL_KERNEL
"src/rgb2bgr.metal"
"src/yuv2rgb.metal"
)
set_source_files_properties(${SRC_METAL_KERNEL} PROPERTIES LANGUAGE METAL)
# set metal version
if(TARGETOS STREQUAL "mac")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=macos-metal2.0")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=macos-metal2.0")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=ios-metal2.0")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=ios-metal2.0")
endif()
add_library(MetalLibary STATIC ${SRC_METAL_KERNEL})
set(ADDITIONAL_LINKER_FLAGS "-framework Metal -framework MetalPerformanceShaders")
target_link_libraries(MetalLibary ${ADDITIONAL_LINKER_FLAGS})

Related

Use non-default linker (lld, mold, gold) with CMake [duplicate]

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.
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.
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
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 one of three possible arguments: lld, bfd, or gold. 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.)
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).
Set the variable ${CMAKE_LINKER} either in CMakeCache.txt or after ccmake . under advanced options.
I have to use CMAKE_CXX_LINK_EXECUTABLE, CMAKE_C_LINK_EXECUTABLE variable:
SET(CMAKE_C_LINK_EXECUTABLE "c:\\MoSync\\bin\\pipe-tool.exe")
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.
Here is a CMake function which sets linker based on some predefined arbitrary rules (Clang -> lld-version or lld, GCC -> gold).
The important parts:
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}")
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)
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
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.

How can you add warning flags using cmake cross platform?

I see a number of articles that suggest you check for compiler and add flags as appropriate, eg.
if (CMAKE_COMPILER_IS_GNUCC)
...
endif()
if (MSVC)
...
endif()
This is a deeply undesirable situation though.
It relies on you having, for every project, to add specific support for each compiler that you support, one at a time.
Other things, like C++11 features and debug flags are automatically generated by cmake for each of the compilers it supports.
Is there no equivalent solution for adding the equivalent of -Wall / /W3 to the compile simply via a cmake setting?
It relies on you having, for every project, to add specific support for each >compiler that you support, one at a time.
At now you can only have something like compiler.cmake, where you configure suitable flags for each compiler, and share compiler.cmake among projects.
Is there no equivalent solution for adding the equivalent of -Wall / /W3 to the >compile simply via a cmake setting?
No, now there is only disscussion about similar feature and it's possible implementation, see
https://cmake.org/pipermail/cmake-developers/2016-March/028107.html
For anyone else who finds this...
There is a reasonably robust implementation of this which can be found here, as a 3rd party addition:
https://github.com/ruslo/sugar/wiki/Cross-platform-warning-suppression
You use it like this:
## Project
cmake_minimum_required(VERSION 3.1)
project(npp)
# Dependencies
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/npp)
... whatever ...
# Clone entire sugar repo to source folder and import
include(${CMAKE_CURRENT_SOURCE_DIR}/sugar/cmake/Sugar)
include(sugar_generate_warning_flags)
# Generate flags, included excluded flags, etc.
# see: https://github.com/ruslo/leathers/wiki/List
sugar_generate_warning_flags(
flags
properties
ENABLE ALL
DISABLE c++98-compat padded
TREAT_AS_ERROR ALL
CLEAR_GLOBAL)
# Library / executable if any
file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/npp/*.cpp)
add_library(npp STATIC ${SOURCES})
# Set flags
set_target_properties(npp PROPERTIES ${properties} COMPILE_OPTIONS "${flags}")
# Local tests
enable_testing()
add_executable(tests "${CMAKE_CURRENT_SOURCE_DIR}/tests/tests.cpp")
# Set flags
set_target_properties(tests PROPERTIES ${properties} COMPILE_OPTIONS "${flags}")
target_link_libraries(tests npp)
add_test(tests tests)
Obviously this is far from ideal, as it's quite irritating to have to clone a set of modules, but it's practical for the moment.

How to apply different compiler options for different compilers in cmake?

I'm currently working on using cmake to build some projects, with the main platforms being Visual C++, MinGW GCC and Linux GCC. When building with GCC, I need to specify the -Wno-invalid-offsetof compiler option.
My current fix is as follows...
if ( "${CMAKE_GENERATOR}" MATCHES "^Visual Studio"
OR "${CMAKE_GENERATOR}" MATCHES "^NMake"
)
set (CPPLIB_COMPILER_OPTS "")
else ()
set (CPPLIB_COMPILER_OPTS "-Wno-invalid-offsetof")
endif ()
...
set_target_properties(sh_core PROPERTIES COMPILE_FLAGS "${CPPLIB_COMPILER_OPTS}")
# repeated for all targets
This works, but assuming that all generators other than the visual studio ones will build with gcc is obviously unsafe. For a start, there are IIRC generators for Borland compilers. More importantly, using make doesn't always mean using gcc.
Other compilers I'm likely to use are llvm-gcc and clang. Fortunately, I think even clang supports gcc-compatible options. But this logic is only good for as long as the relevant code is never released.
Cmake appears to check for available compilers and generate a makefile specifically for that compiler (raising the question - why not at least have the option of building the project directly, without the need for a middle-man like make?).
That being the case, I was hoping to be able to test directly for gcc in my CMakeLists.txt files. So far, though, I can't find an appropriate variable to test or any other obvious solution.
Is this possible?
To create a portable build system, it is best to not test for platforms, but to test for features.
Instead of testing "if Windows then do this", test for "if the -Wno-invalid-offsetof flag works then use it". You can do that with the CheckCCompilerFlag module, for example:
include(CheckCCompilerFlag)
check_c_compiler_flag(-Wno-invalid-offsetof HAS_NO_INVALID_OFFSETOF)
if (HAS_NO_INVALID_OFFSETOF)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-invalid-offsetof")
endif()
For C++ there is a similar CheckCXXCompilerFlag with a check_cxx_compiler_flag(flag var) command.

How to set warning level in CMake?

How to set the warning level for a project (not the whole solution) using CMake? Should work on Visual Studio and GCC.
I found various options but most seem either not to work or are not consistent with the documentation.
In modern CMake, the following works well:
if(MSVC)
target_compile_options(${TARGET_NAME} PRIVATE /W4 /WX)
else()
target_compile_options(${TARGET_NAME} PRIVATE -Wall -Wextra -Wpedantic -Werror)
endif()
My colleague suggested an alternative version:
target_compile_options(${TARGET_NAME} PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:/W4 /WX>
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Wpedantic -Werror>
)
Replace ${TARGET_NAME} with the actual target name. -Werror is optional, it turns all warnings into errors.
Or use add_compile_options(...) if you want to apply it to all targets as suggested by #aldo in the comments.
Also, be sure to understand the difference between PRIVATE and PUBLIC (public options will be inherited by targets that depend on the given target).
As #davidfong notes in the comments, since CMake v3.24, there is the CMAKE_COMPILE_WARNING_AS_ERROR variable that switches on treating compile warings as errors. In case it is set inside CMakeLists.txt, the user can still turn it off with the --compile-no-warning-as-error cmake flag. In case you want to add warning-as-error manually, add /WX in Windows and -Werror elsewhere to target_compile_options.
UPDATE: This answer predates the Modern CMake era. Every sane CMake user should refrain from fiddling with CMAKE_CXX_FLAGS directly and call the target_compile_options command instead. Check the mrts' answer which presents the recommended best practice.
You can do something similar to this:
if(MSVC)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
# Update if necessary
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic")
endif()
Some CMake modules I've written include experimental cross-platfrom warning suppression:
sugar_generate_warning_flags(
target_compile_options
target_properties
ENABLE conversion
TREAT_AS_ERRORS ALL
)
set_target_properties(
foo
PROPERTIES
${target_properties}
COMPILE_OPTIONS
"${target_compile_options}"
)
Result for Xcode:
Set CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION Xcode attribute
(aka build settings -> warnings -> suspicious implicit conversions -> YES)
Add compiler flag: -Werror
Makefile gcc and clang:
Add compiler flags: -Wconversion, -Werror
Visual studio:
Add compiler flags: /WX, /w14244
Links
List of available warnings
Usage and more options
As per Cmake 3.24.2 documentation:
if (MSVC)
# warning level 4 and all warnings as errors
add_compile_options(/W4 /WX)
else()
# lots of warnings and all warnings as errors
add_compile_options(-Wall -Wextra -pedantic -Werror)
endif()
GCC and Clang share these flags, so this should cover all 3.
Here is the best solution I found so far (including a compiler check):
if(CMAKE_BUILD_TOOL MATCHES "(msdev|devenv|nmake)")
add_definitions(/W2)
endif()
The GCC equivalent is -Wall (untested).
if(MSVC)
string(REGEX REPLACE "/W[1-3]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
endif()
If you use target_compile_options - cmake will try to use double /W* flag, which will give warning by compiler.
How to set the warning level for a project (not the whole solution) using CMake?
(I assume this to mean a CMake target, and not a CMake project.)
I found various options but most seem either not to work or are not consistent with the documentation.
Kitware's APIs may be trying to deter you from making your build system brittle and error-prone. The special-casing encouraged by other answers to this question violate at least two important principles of modern CMake build systems...
Firstly, prefer not to specify toolchain-specific details in CMakeLists.txt files. It makes the build system brittle. For example, if a new warning appears in a future version of the toolchain, the compiler will emit an error and your user may need to hack your project in order to build the target.
Instead, write toolchain-agnostic CMakeLists.txt files and preserve the user's ability to customise as they see fit. Ideally, your project should build everywhere with vanilla toolchain configuration - even if that doesn't enable your preferred warnings by default.
Secondly, if you intend to link binaries together, flags should be consistent. This reduces the risk of incompatibility which could result in an ill-formed program. However, warning flags are unlikely to affect code generation, so it may be safe to vary these between the targets you link together.
So... if you wish to specify flags per toolchain and if you absolutely must have different flags for different targets, use custom variables:
# CMakeLists.txt
project(my_project)
add_executable(my_target source_file.cpp)
target_compile_options(my_target PRIVATE "${MY_PROJECT_ELEVATED_WARNING_FLAGS}")
There are many ways to set these variables, such as CMakeCache.txt, a toolchain file, and via CMAKE_PROJECT_INCLUDE_BEFORE. But the simplest way is on the command line during configuration, for GCC
cmake -DMY_PROJECT_ELEVATED_WARNING_FLAGS:STRING="-Wall;-Wextra;-Wpedantic;-Werror" <path-to-project>
for MSVC
cmake -DMY_PROJECT_ELEVATED_WARNING_FLAGS:STRING="/W4;/WX" <path-to-project>

CMake: use a custom linker

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.
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.
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
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 one of three possible arguments: lld, bfd, or gold. 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.)
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).
Set the variable ${CMAKE_LINKER} either in CMakeCache.txt or after ccmake . under advanced options.
I have to use CMAKE_CXX_LINK_EXECUTABLE, CMAKE_C_LINK_EXECUTABLE variable:
SET(CMAKE_C_LINK_EXECUTABLE "c:\\MoSync\\bin\\pipe-tool.exe")
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.
Here is a CMake function which sets linker based on some predefined arbitrary rules (Clang -> lld-version or lld, GCC -> gold).
The important parts:
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}")
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)
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
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.