Custom Toolchain - how to prevent library filename extension stripping

I am trying to use CMake with the MSVC compiler/linker via a custom toolchain.
Everything works good until a project tries to specify custom link libraries, like this:

CMakeLists.txt:

target_link_libraries(Simulator PRIVATE
    Rpcrt4.lib
    Ws2_32.lib
)

Resulting Link command:

    c:\dev\mytools\bin\amd64\link.exe  @CMakeFiles\Simulator.dir\objects1.rsp /out:Simulator.exe 
    /implib:Simulator.lib /pdb:c:\dev\myproj\cmake_out\Simulator\Simulator.pdb /version:0.0 
    /LIBPATH:c:\dev\mytools\lib1\amd64\ /LIBPATH:c:\dev\mytools\lib2\x64\  Rpcrt4 Ws2_32 

Result:

Microsoft (R) Incremental Linker Version 14.13.26132.0
Copyright (C) Microsoft Corporation.  All rights reserved.

CMakeFiles/Simulator.dir/src/simulator.c.obj
LINK : fatal error LNK1181: cannot open input file 'Rpcrt4.obj'

What seems to be happening is the libraries at the end (Rpcrt4 & Ws2_32) have the extension stripped. This works okay with linkers that use LINK_LIBRARY_FLAG (e.g. gcc), but the Microsoft linker uses file extensions. Libraries that are coming from target_link_libraries(Simulator … come to the link line with .lib extension and work fine.

The toolchain file specifies extensions:

set(CMAKE_EXECUTABLE_SUFFIX .exe)
set(CMAKE_IMPORT_LIBRARY_SUFFIX .lib)
set(CMAKE_LINK_LIBRARY_SUFFIX .lib)
set(CMAKE_SHARED_LIBRARY_SUFFIX .dll)
set(CMAKE_STATIC_LIBRARY_SUFFIX .lib)

and

set(CMAKE_EXECUTABLE_SUFFIX_${lang}     .exe)
set(CMAKE_IMPORT_LIBRARY_SUFFIX_${lang} .lib)
set(CMAKE_LINK_LIBRARY_SUFFIX_${lang}   .lib)
set(CMAKE_SHARED_LIBRARY_SUFFIX_${lang} .dll)
set(CMAKE_STATIC_LIBRARY_SUFFIX_${lang} .lib)
set(CMAKE_${lang}_LINK_LIBRARY_FLAG "")

I have tried looking in the 3.23.1 source, but haven’t found a solution. (I am using CMake 3.23.1).

Also, the result is the same whether the target_link_libraries specifies extensions (ws2_32.lib) or without (ws2_32).

How can I get CMake to provide the .LIB extension on library files when they are passed to the linker?

Thanks,
Brad

One thing you could try is to make it an IMPORTED target with the IMPORTED_LIBNAME name to Rpcrt4. That might work?

Barring that, doing find_library and giving the full path might be suitable.

I’m not sure where the suffix stripping happens though…

Cc: @brad.king

Seems like the problem is the library suffix variable is getting overwritten.

As shown in the dump below, the toolchain file is included (twice) when the project() command is executed, but the results are lost between the toolchain file and dump 2.

Should the toolchain be setting variables in the CACHE with FORCE?

The examples in cmake-toolchains show the toolchain setting values in regular variables, not the CACHE.

My whole toolchain (revised from what I posted before):
dump.cmake just prints out the watched vars

message( "In my Magical Toolchain File")
include(dump.cmake)

set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR amd64)
set(CMAKE_STAGING_PREFIX "$ENV{ROOT}/cmake_out")
set(tools $ENV{MYTOOLS})

# replace Windows paths from ENV with CMake's forward slash
string(REPLACE "\\" "/" tools "${tools}")
string(REPLACE "\\" "/" CMAKE_STAGING_PREFIX "${CMAKE_STAGING_PREFIX}")

set(tools_target_arch amd64)

set(CMAKE_CROSSCOMPILING 1)

set(CMAKE_C_COMPILER "${tools}/lib/native/bin/x86_${tools_target_arch}/cl.exe")
set(CMAKE_CXX_COMPILER "${tools}/lib/native/bin/x86_${tools_target_arch}/cl.exe")
set(CMAKE_LINKER "${tools}/lib/native/bin/x86_${tools_target_arch}/link.exe" )
set(CMAKE_AR ${CMAKE_LINKER} )

set(CMAKE_C_COMPILER_ID_RUN TRUE)
set(CMAKE_C_COMPILER_FORCED TRUE)
set(CMAKE_C_COMPILER_ID Custom)
set(CMAKE_CXX_COMPILER_ID_RUN TRUE)
set(CMAKE_CXX_COMPILER_FORCED TRUE)
set(CMAKE_CXX_COMPILER_ID Custom)

set(CMAKE_SIZEOF_VOID_P 8)

set(CMAKE_STATIC_LINKER_FLAGS       "/lib" )
set(CMAKE_EXECUTABLE_SUFFIX         ".exe")
set(CMAKE_IMPORT_LIBRARY_SUFFIX     ".lib")
set(CMAKE_LIBRARY_PATH_FLAG         "/LIBPATH:")
set(CMAKE_LIBRARY_PATH_TERMINATOR   "")
set(CMAKE_LINK_LIBRARY_FILE_FLAG    "")
set(CMAKE_LINK_LIBRARY_FLAG         "")
set(CMAKE_LINK_LIBRARY_SUFFIX       ".lib")
set(CMAKE_LINK_OBJECT_FILE_FLAG     "")
set(CMAKE_SHARED_LIBRARY_SUFFIX     ".xxx") # not supported, force garbage
set(CMAKE_STATIC_LIBRARY_SUFFIX     ".lib")

dump_vars("toolchain out")

CMakeLists.txt:

include(dump.cmake)

cmake_minimum_required(VERSION 3.16.3)

dump_vars("1")  
project(hello)
dump_vars("2")

if( CMAKE_SIZEOF_VOID_P EQUAL 8 )
    message("Pointer 8")
elseif( CMAKE_SIZEOF_VOID_P EQUAL 4 )
    message("Pointer 4")
else()
    message(FATAL_ERROR "invalid CMAKE_SIZEOF_VOID_P: ${CMAKE_SIZEOF_VOID_P}")
endif()

add_executable(hello hello.c)
add_library(hellolib STATIC hellolib.c)
target_link_libraries(hello PRIVATE hellolib)

Output:

>cmake -B cmake_out -S . --toolchain .\Toolchain.cmake -G "NMake Makefiles" 
c:\dev\HspTpm\temp>cmake -B cmake_out -S . --toolchain .\Toolchain.cmake -G "NMake Makefiles"
-- dump_vars @ 1----------------------------
-- CMAKE_LINK_LIBRARY_FLAG=
-- CMAKE_LINK_LIBRARY_SUFFIX=
-- CMAKE_C_LINK_LIBRARY_FLAG=
-- CMAKE_CXX_LINK_LIBRARY_FLAG=
-- CMAKE_STATIC_LIBRARY_SUFFIX=
-- ----------------------------
In my Magical Toolchain File
-- dump_vars @ toolchain out----------------------------
-- CMAKE_LINK_LIBRARY_FLAG=
-- CMAKE_LINK_LIBRARY_SUFFIX=.lib
-- CMAKE_C_LINK_LIBRARY_FLAG=
-- CMAKE_CXX_LINK_LIBRARY_FLAG=
-- CMAKE_STATIC_LIBRARY_SUFFIX=.lib
-- ----------------------------
In my Magical Toolchain File
-- dump_vars @ toolchain out----------------------------
-- CMAKE_LINK_LIBRARY_FLAG=
-- CMAKE_LINK_LIBRARY_SUFFIX=.lib
-- CMAKE_C_LINK_LIBRARY_FLAG=
-- CMAKE_CXX_LINK_LIBRARY_FLAG=
-- CMAKE_STATIC_LIBRARY_SUFFIX=.lib
-- ----------------------------
-- dump_vars @ 2----------------------------
-- CMAKE_LINK_LIBRARY_FLAG=-l
-- CMAKE_LINK_LIBRARY_SUFFIX=
-- CMAKE_C_LINK_LIBRARY_FLAG=
-- CMAKE_CXX_LINK_LIBRARY_FLAG=
-- CMAKE_STATIC_LIBRARY_SUFFIX=.a
-- ----------------------------
Pointer 8
-- Configuring done
-- Generating done
-- Build files have been written to: C:/dev/temp/cmake_out

Update - It seems the overwrite of the setting is coming from setting CMAKE_SYSTEM_NAME to Generic. in other words, CMakeGenericSystem.cmake.

I was able to create my own MySystem.cmake file and load it with set(CMAKE_SYSTEM_NAME MySystem) from my toolchain file by setting CMAKE_MODULE_PATH.

That allowed me to change the suffixes and flags to whatever I want, but I was surprised that a “Generic” system wasn’t already an empty template where I could specify things from the toolchain.

This has left me a bit puzzled about which settings should be in a custom toolchain file and which should be in a custom system module, and whether there are any other modules that I need to customize to get a truly “Empty” default where my project files can specify all the build details and command lines.

Ideally, I’d like to keep CMake’s notion of relationships between source, objects, libraries, linkers, compilers, and archivers, while loading all the details and assumptions about command syntax, flags, options, and so forth from files CMakeLists and Module files in my project tree.

Is there a list somewhere that separates which build variables belong in which file (toolchain vs. system) and whether there are any other files I need to customize for a really “new” System?

Thanks in advance for any advice!

As originally designed, toolchain files are meant to be specific to the host machine and set things like paths to compilers. All information that is common to the target platform should go a Platform/ module under CMAKE_MODULE_PATH.

1 Like

thanks for the confirmation, that really helps clarify.