cannot define custom toolchain because exe/lib flags are hardcoded for CMAKE_SYSTEM_NAME=Generic
Hello, I am writing a custom toolchain i386-pc-win32-darwin-llvm.cmake
that I invoke with
cmake -D CMAKE_TOOLCHAIN_FILE=i386-pc-win32-darwin-llvm.cmake .
It is intended to:
- cross-compile to Windows compatible binaries
- using Clang (
clang++ -target i386-pc-win32
andlld -flavor link
) -brew install llvm
- running from MacOSX terminal
Problem:
- When I define these in my toolchain file, they all get overwritten when PROJECT() is called:
CMAKE_LIBRARY_PATH_FLAG
,CMAKE_LINK_LIBRARY_FLAG
,CMAKE_LINK_LIBRARY_SUFFIX
,CMAKE_STATIC_LIBRARY_PREFIX
,CMAKE_STATIC_LIBRARY_SUFFIX
,CMAKE_EXECUTABLE_SUFFIX
,CMAKE_C_OUTPUT_EXTENSION
,CMAKE_CXX_OUTPUT_EXTENSION
--> they all get overwritten with default values bycmake/Modules/CMakeGenericSystem.cmake
(looks like GCC style...)
Example of working compilation (showing clang++/lld lines shown in toolchain below):
Using clang/clang++ and lld
:
===================
compile some 64bit C++
/usr/local/opt/llvm/bin/clang++ -target x86_64-pc-windows-msvc -isystem "/Volumes/[C] Windows 10/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include" -isystem "/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/10/Include/10.0.10150.0/ucrt" -fmsc-version=1900 -fms-extensions -fms-compatibility -fdelayed-template-parsing -std=c++11 -c main.cpp -o mainCPP-x64.o
/usr/local/opt/llvm/bin/lld -flavor link /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Microsoft Visual Studio 14.0/VC/lib/amd64" /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/10/Lib/10.0.10150.0/ucrt/x64" /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/8.1/Lib/winv6.3/um/x64" libucrt.lib libcmt.lib /subsystem:console /out:mainCPP-x64.exe mainCPP-x64.o
===================
compile some 64bit C
/usr/local/opt/llvm/bin/clang -target x86_64-pc-windows-msvc -isystem "/Volumes/[C] Windows 10/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include" -isystem "/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/10/Include/10.0.10150.0/ucrt" -fmsc-version=1900 -fms-extensions -fms-compatibility -fdelayed-template-parsing -c main.c -o mainC-x64.o
/usr/local/opt/llvm/bin/lld -flavor link /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Microsoft Visual Studio 14.0/VC/lib/amd64" /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/10/Lib/10.0.10150.0/ucrt/x64" /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/8.1/Lib/winv6.3/um/x64" libucrt.lib libcmt.lib /subsystem:console /out:mainC-x64.exe mainC-x64.o
===================
compile some 32bit C++
/usr/local/opt/llvm/bin/clang++ -target i386-pc-win32 -isystem "/Volumes/[C] Windows 10/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include" -isystem "/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/10/Include/10.0.10150.0/ucrt" -fmsc-version=1900 -fms-extensions -fms-compatibility -fdelayed-template-parsing -std=c++11 -c main.cpp -o mainCPP-x86.o
/usr/local/opt/llvm/bin/lld -flavor link /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Microsoft Visual Studio 14.0/VC/lib" /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/10/Lib/10.0.10150.0/ucrt/x86" /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/8.1/Lib/winv6.3/um/x86" libucrt.lib libcmt.lib /subsystem:console /out:mainCPP-x86.exe mainCPP-x86.o
===================
compile some 32bit C
/usr/local/opt/llvm/bin/clang -target i386-pc-win32 -isystem "/Volumes/[C] Windows 10/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include" -isystem "/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/10/Include/10.0.10150.0/ucrt" -fmsc-version=1900 -fms-extensions -fms-compatibility -fdelayed-template-parsing -c main.c -o mainC-x86.o
/usr/local/opt/llvm/bin/lld -flavor link /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Microsoft Visual Studio 14.0/VC/lib" /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/10/Lib/10.0.10150.0/ucrt/x86" /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/8.1/Lib/winv6.3/um/x86" libucrt.lib libcmt.lib /subsystem:console /out:mainC-x86.exe mainC-x86.o
Example of compilation that works, using clang-cl and lld-link
(for comparison):
===================
compile some 64bit C++
/usr/local/opt/llvm/bin/clang-cl -m64 /arch:AVX2 /imsvc "/Volumes/[C] Windows 10/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include" /imsvc "/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/10/Include/10.0.10150.0/ucrt" -fmsc-version=1900 -fms-extensions -fms-compatibility -fdelayed-template-parsing /c /Tpmain.cpp /omainCPP-x64.o
/usr/local/opt/llvm/bin/lld-link /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Microsoft Visual Studio 14.0/VC/lib/amd64" /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/10/Lib/10.0.10150.0/ucrt/x64" /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/8.1/Lib/winv6.3/um/x64" libucrt.lib libcmt.lib /subsystem:console /out:mainCPP-x64.exe mainCPP-x64.o
===================
compile some 64bit C
/usr/local/opt/llvm/bin/clang-cl -m64 /arch:AVX2 /imsvc "/Volumes/[C] Windows 10/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include" /imsvc "/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/10/Include/10.0.10150.0/ucrt" -fmsc-version=1900 -fms-extensions -fms-compatibility -fdelayed-template-parsing /c /Tcmain.c /omainC-x64.o
/usr/local/opt/llvm/bin/lld-link /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Microsoft Visual Studio 14.0/VC/lib/amd64" /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/10/Lib/10.0.10150.0/ucrt/x64" /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/8.1/Lib/winv6.3/um/x64" libucrt.lib libcmt.lib /subsystem:console /out:mainC-x64.exe mainC-x64.o
===================
compile some 32bit C++
/usr/local/opt/llvm/bin/clang-cl -m32 /arch:SSE2 /imsvc "/Volumes/[C] Windows 10/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include" /imsvc "/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/10/Include/10.0.10150.0/ucrt" -fmsc-version=1900 -fms-extensions -fms-compatibility -fdelayed-template-parsing /c /Tpmain.cpp /omainCPP-x86.o
/usr/local/opt/llvm/bin/lld-link /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Microsoft Visual Studio 14.0/VC/lib" /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/10/Lib/10.0.10150.0/ucrt/x86" /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/8.1/Lib/winv6.3/um/x86" libucrt.lib libcmt.lib /subsystem:console /out:mainCPP-x86.exe mainCPP-x86.o
===================
compile some 32bit C
/usr/local/opt/llvm/bin/clang-cl -m32 /arch:SSE2 /imsvc "/Volumes/[C] Windows 10/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include" /imsvc "/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/10/Include/10.0.10150.0/ucrt" -fmsc-version=1900 -fms-extensions -fms-compatibility -fdelayed-template-parsing /c /Tcmain.c /omainC-x86.o
/usr/local/opt/llvm/bin/lld-link /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Microsoft Visual Studio 14.0/VC/lib" /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/10/Lib/10.0.10150.0/ucrt/x86" /libpath:"/Volumes/[C] Windows 10/Program Files (x86)/Windows Kits/8.1/Lib/winv6.3/um/x86" libucrt.lib libcmt.lib /subsystem:console /out:mainC-x86.exe mainC-x86.o
And here is the toolchain file i386-pc-win32-darwin-llvm.cmake
I am working on:
message ("Loaded: ${CMAKE_TOOLCHAIN_FILE}")
# I cannot use Windows here, it doesn't allow me to change `CMAKE_CXX_COMPILE_OBJECT` or `CMAKE_CXX_LINK_EXECUTABLE` (missing /Tc or /Tp flags for compile)
# and REALLY, I want a blank slate, so we want Generic:
SET(CMAKE_SYSTEM_NAME Generic CACHE STRING "Target system.")
set( _MSC_VER 1900 ) # 1800=VC2013, 1900=VC2015, 1910=VC2017.. etc.
if (NOT DEFINED ARCH)
set( ARCH "x86" CACHE STRING "" FORCE )
endif()
# MSVC Includes and Libs:
# currently I am mounting MSVC from Parallels - TODO: allow configuring the path to MSVC.
set( PROGRAMFILES "/Volumes/[C] Windows 10/Program Files (x86)" )
IF(NOT EXISTS "${PROGRAMFILES}")
message(FATAL_ERROR "\n\nERROR: INCLUDE/LIB DIRECTORY DOESNT EXIST:\n ${PROGRAMFILES}\nLocation doesn't exist, please mount it, or install MSVC version ${_MSC_VER} v2015\n\n" )
endif()
set( UniversalCRT_IncludePath "${PROGRAMFILES}/Windows Kits/10/Include/10.0.10150.0/ucrt" )
set( UniversalCRT_Lib "${PROGRAMFILES}/Windows Kits/10/Lib/10.0.10150.0/ucrt/${ARCH}" )
set( MSVC_INCLUDE "${PROGRAMFILES}/Microsoft Visual Studio 14.0/VC/include" )
if (ARCH STREQUAL "x64" )
set( MSVC_LIB "${PROGRAMFILES}/Microsoft Visual Studio 14.0/VC/lib/amd64" )
set( triple "x86_64-pc-windows-msvc" )
else()
set( MSVC_LIB "${PROGRAMFILES}/Microsoft Visual Studio 14.0/VC/lib" )
set( triple "i386-pc-win32" )
endif()
set( WINSDK_LIB "${PROGRAMFILES}/Windows Kits/8.1/Lib/winv6.3/um/${ARCH}" )
# compiler clang (same interface as gcc, with -target i386-pc-win32 outputs MSVC .obj files
#set( SYSFLAGS "-target ${triple} -isystem \"${MSVC_INCLUDE}\" -isystem \"${UniversalCRT_IncludePath}\" -fmsc-version=${_MSC_VER} -fms-extensions -fms-compatibility -fdelayed-template-parsing" )
# compiler clang-cl (clang-cl has same interface as cl.exe... outputs MSVC .obj files)
set( SYSFLAGS "/imsvc \"${MSVC_INCLUDE}\" /imsvc \"${UniversalCRT_IncludePath}\" -fmsc-version=${_MSC_VER} -fms-extensions -fms-compatibility -fdelayed-template-parsing" )
set( CMAKE_INCLUDE_SYSTEM_FLAG_C "${SYSFLAGS}" CACHE STRING "" FORCE)
set( CMAKE_INCLUDE_SYSTEM_FLAG_CXX "${SYSFLAGS}" CACHE STRING "" FORCE)
set( CMAKE_CXX_LINK_FLAGS "-flavor link /libpath:\"${MSVC_LIB}\" /libpath:\"${UniversalCRT_Lib}\" /libpath:\"${WINSDK_LIB}\"" CACHE STRING "" FORCE)
# compiler clang
# somehow choosing clang here switches around all the flags to gcc style. HOW?
#set( CMAKE_C_COMPILER "/usr/local/opt/llvm/bin/clang" CACHE STRING "" FORCE)
#set( CMAKE_CXX_COMPILER "/usr/local/opt/llvm/bin/clang++" CACHE STRING "" FORCE)
#set( CMAKE_C_COMPILE_OBJECT "<CMAKE_C_COMPILER> ${CMAKE_INCLUDE_SYSTEM_FLAG_C} <DEFINES> <FLAGS> -o <OBJECT> -c <SOURCE>" CACHE STRING "" FORCE)
#set( CMAKE_CXX_COMPILE_OBJECT "<CMAKE_CXX_COMPILER> ${CMAKE_INCLUDE_SYSTEM_FLAG_CXX} <DEFINES> <FLAGS> -o <OBJECT> -c <SOURCE>" CACHE STRING "" FORCE)
# compiler clang-cl
# somehow choosing clang-cl here switches around all the flags to cl.exe style. HOW?
set( CMAKE_C_COMPILER "/usr/local/opt/llvm/bin/clang-cl" CACHE STRING "" FORCE)
set( CMAKE_CXX_COMPILER "/usr/local/opt/llvm/bin/clang-cl" CACHE STRING "" FORCE)
# CMAKE_SYSTEM_NAME=Windows will ignore CMAKE_??_COMPILE_OBJECT, WTF skips the /Tp and /Tc, so we get error about source path (/Users/blah/file.cxx results in /U flag error)
set( CMAKE_C_COMPILE_OBJECT "<CMAKE_C_COMPILER> ${CMAKE_INCLUDE_SYSTEM_FLAG_C} <DEFINES> <FLAGS> /o<OBJECT> /c /Tc<SOURCE>" CACHE STRING "" FORCE)
set( CMAKE_CXX_COMPILE_OBJECT "<CMAKE_CXX_COMPILER> ${CMAKE_INCLUDE_SYSTEM_FLAG_CXX} <DEFINES> <FLAGS> /o<OBJECT> /c /Tp<SOURCE>" CACHE STRING "" FORCE)
# linker lld
set( CMAKE_LINKER "/usr/local/opt/llvm/bin/lld" CACHE STRING "" FORCE)
set( CMAKE_C_LINK_EXECUTABLE "<CMAKE_LINKER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> /subsystem:console libcmt.lib <OBJECTS> /out:<TARGET> <LINK_LIBRARIES>; echo <LINK_LIBRARIES>" CACHE STRING "" FORCE)
# https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html
#set(CMAKE_CXX_LINK_EXECUTABLE "echo CXXFLAGS: ${CMAKE_CXX_FLAGS} LINK_FLAGS: <LINK_FLAGS> LINK_LIBRARIES: <LINK_LIBRARIES> OBJECTS: <OBJECTS>" CACHE STRING "" FORCE)
set( CMAKE_CXX_LINK_EXECUTABLE "echo '';echo LINK_FLAGS: <LINK_FLAGS>;echo LINK_LIBRARIES: <LINK_LIBRARIES>;echo OBJECTS: <OBJECTS>; echo ''; <CMAKE_LINKER> <CMAKE_CXX_LINK_FLAGS> <FLAGS> <LINK_FLAGS> /subsystem:console libcmt.lib <OBJECTS> /out:<TARGET> <LINK_LIBRARIES>")
# needed when using CMAKE_SYSTEM_NAME Windows, so weird... ar... in windows.. hmm.
# set( CMAKE_AR "/usr/local/opt/llvm/bin/llvm-ar" CACHE INTERNAL STRING)
set(CMAKE_LIBRARY_ARCHITECTURE x86 CACHE STRING "" FORCE)
# you can set lib paths with , and libs with cmt (which will expand to libcmt.lib)
# PROBLEM: makes no difference, every one gets overridden by cmake/Modules/CMakeGenericSystem.cmake during PROJECT() in the CMakeLists
SET(CMAKE_LIBRARY_PATH_FLAG "/libpath:" CACHE STRING "" FORCE)
SET(CMAKE_LINK_LIBRARY_FLAG "" CACHE STRING "" FORCE)
SET(CMAKE_LINK_LIBRARY_SUFFIX ".lib" CACHE STRING "" FORCE)
set(CMAKE_STATIC_LIBRARY_PREFIX "")
SET(CMAKE_STATIC_LIBRARY_SUFFIX ".lib" CACHE STRING "" FORCE)
set(CMAKE_EXECUTABLE_SUFFIX ".exe")
set(CMAKE_C_OUTPUT_EXTENSION ".obj" CACHE STRING "C compiler object extension.")
set(CMAKE_CXX_OUTPUT_EXTENSION ".obj" CACHE STRING "C++ compiler object extension.")
# makes no difference, not sure _INIT is even a thing...
SET(CMAKE_LIBRARY_PATH_FLAG_INIT "/libpath:" CACHE STRING "" FORCE)
SET(CMAKE_LINK_LIBRARY_FLAG_INIT "" CACHE STRING "" FORCE)
SET(CMAKE_LINK_LIBRARY_SUFFIX_INIT ".lib" CACHE STRING "" FORCE)
set (CMAKE_STATIC_LIBRARY_PREFIX_INIT "")
SET(CMAKE_STATIC_LIBRARY_SUFFIX_INIT ".lib" CACHE STRING "" FORCE)
set(CMAKE_C_OUTPUT_EXTENSION_INIT ".obj" CACHE STRING "C compiler object extension.")
set(CMAKE_CXX_OUTPUT_EXTENSION_INIT ".obj" CACHE STRING "C++ compiler object extension.")
set (CMAKE_EXECUTABLE_SUFFIX_INIT ".exe")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
CMakeLists:
# compile for windows:
# mkdir -p build && cd build
# cmake -D CMAKE_TOOLCHAIN_FILE=../i386-pc-win32-darwin-llvm.cmake ..
# make
CMAKE_MINIMUM_REQUIRED(VERSION 3.10.0 FATAL_ERROR)
SET(CMAKE_CXX_STANDARD 11)
SET(CMAKE_CXX_STANDARD_REQUIRED ON)
SET(CMAKE_CXX_EXTENSIONS OFF)
PROJECT(example)
INCLUDE_DIRECTORIES(.)
ADD_EXECUTABLE(example ../main.cpp)
set_property( TARGET example APPEND_STRING PROPERTY LINK_LIBRARIES "libcmt" )
# just testing that these come through
set_property( TARGET example APPEND_STRING PROPERTY LINK_FLAGS "-Tsomeflag1" )
# this one doesn't seem to come through, WHY?
set_property( TARGET example APPEND_STRING PROPERTY CXXFLAGS "-Tsomeflag2" )
Run:
Loaded: ../i386-pc-win32-darwin-llvm.cmake
Loaded: /Users/...path.../i386-pc-win32-darwin-llvm.cmake
-- The C compiler identification is Clang 6.0.0
-- The CXX compiler identification is Clang 6.0.0
-- Check for working C compiler: /usr/local/opt/llvm/bin/clang-cl
Loaded:
-- Check for working C compiler: /usr/local/opt/llvm/bin/clang-cl -- works
-- Detecting C compiler ABI info
Loaded:
-- Detecting C compiler ABI info - done
-- Detecting C compile features
Loaded:
Loaded:
Loaded:
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/local/opt/llvm/bin/clang-cl
Loaded:
-- Check for working CXX compiler: /usr/local/opt/llvm/bin/clang-cl -- works
-- Detecting CXX compiler ABI info
Loaded:
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
Loaded:
Loaded:
Loaded:
Loaded:
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/...path.../build-w
Scanning dependencies of target example
[ 50%] Building CXX object CMakeFiles/example.dir/Users/...path.../main.cpp.obj
[100%] Linking CXX executable example
LINK_FLAGS: -Tsomeflag1
LINK_LIBRARIES: -llibcmt
OBJECTS: CMakeFiles/example.dir/Users/...path.../main.cpp.obj
/usr/local/opt/llvm/bin/lld: warning: ignoring unknown argument: -Tsomeflag1
/usr/local/opt/llvm/bin/lld: warning: ignoring unknown argument: -llibcmt
Tracking it down, looks like CMAKE_STATIC_LIBRARY_PREFIX
and friends are set correctly until PROJECT()
is called, then they default to gcc
defaults -l
-L
etc... See the -llibcmt
? It should read libcmt.lib
without any switch. I set this up, but it gets overridden to GCC defaults. Similarly, I've output, and none of the other flags, prefixes, exe suffix, are preserved either - same problem. Looks like maybe it's CMakeGenericSystem.cmake
is setting these to defaults, and I'm not able to override them in my toolchain - which doesn't seem right to me that I can't. How do I define those for my toolchain?
The documentation for Custom Toolchains / Cross Compiling, is a little light, and I've gotten into territory that is perhaps more advanced than your documentation. Could you advise?