Cmake cross compiling: finding tools - c++

I am using a standalone toolchain made from the android ndk13b. It works fine, but to find all the tools (linker, archiver etc.) I have a quite verbose section in my toolchain file. Is there a way to make it more condensed?
SET(COMPILER_PATH "<path_to_my_llvm_directory>")
SET(CMAKE_TOOLCHAIN_PREFIX aarch64-linux-android-) #In theory should allow to find minor tools like ar and objdump, see http://stackoverflow.com/a/7032021/2436175
find_program(CMAKE_C_COMPILER clang.cmd PATH ${COMPILER_PATH})
find_program(CMAKE_CXX_COMPILER clang++.cmd PATH ${COMPILER_PATH})
find_program(CMAKE_AR ${CMAKE_TOOLCHAIN_PREFIX}ar.exe PATHS ${COMPILER_PATH})
find_program(CMAKE_RANLIB ${CMAKE_TOOLCHAIN_PREFIX}ranlib.exe PATHS ${COMPILER_PATH})
find_program(CMAKE_LINKER ${CMAKE_TOOLCHAIN_PREFIX}ld.exe PATHS ${COMPILER_PATH})
find_program(CMAKE_NM ${CMAKE_TOOLCHAIN_PREFIX}nm.exe PATHS ${COMPILER_PATH})
find_program(CMAKE_OBJCOPY ${CMAKE_TOOLCHAIN_PREFIX}objcopy.exe PATHS ${COMPILER_PATH})
find_program(CMAKE_OBJDUMP ${CMAKE_TOOLCHAIN_PREFIX}objdump.exe PATHS ${COMPILER_PATH})
find_program(CMAKE_STRIP ${CMAKE_TOOLCHAIN_PREFIX}strip.exe PATHS ${COMPILER_PATH})
What didn't work:
Not explicitly using find_program -> It finds some other tools from some other mingw toolchain I have in my path
Setting CMAKE_FIND_ROOT_PATH to ${COMPILER_PATH}. It won't even find the compiler at that point. I can workaround that by setting the compiler instead with SET(CMAKE_C_COMPILER ${COMPILER_PATH}/clang.cmd) (same for clang++), but it still doesn't find the other tools
Trying various flags with find_program, especially ONLY_CMAKE_FIND_ROOT_PATH
Note that I found find_program to be the only workaround to find the tools, because for example the following won't work:
SET(CMAKE_AR ${COMPILER_PATH}/${CMAKE_TOOLCHAIN_PREFIX}ar.exe
(The archive operation will fail and I can see from cmake-gui that the variable is not set).

The good new is that Android NDK support got a lot easier with the latest CMake 3.7 release. See Kitware Increases Android Support in CMake 3.7 and Cross Compiling for Android.
Edit: I have successfully run a test with CMake 3.7 (e.g. installed ADK to root on my Windows PC):
toolchain.cmake
set(CMAKE_SYSTEM_NAME Android)
set(CMAKE_SYSROOT "C:/android-ndk-r13b/platforms/android-24/arch-arm64")
And used e.g. the Ninja makefile generator:
> cmake -DCMAKE_TOOLCHAIN_FILE=../toolchain.cmake -G "Ninja" ..
-- Android: Targeting API '24' with architecture 'arm64', ABI 'arm64-v8a', and processor 'aarch64'
-- Android: Selected GCC toolchain 'aarch64-linux-android-4.9'
-- The C compiler identification is GNU 4.9.0
-- The CXX compiler identification is GNU 4.9.0
Simplified Toolchains in General
I've made some good experiences with minimal toolchain files and generally - if you want to specify tool paths specifically - using cached variables in the toolchain file.
See this minimal example from CMake's documentation, which would translate in your case into something like:
set(CMAKE_SYSTEM_NAME Android)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_C_COMPILER <path_to_my_llvm_directory>/clang.cmd)
set(CMAKE_C_COMPILER_TARGET aarch64-linux-android)
set(CMAKE_CXX_COMPILER <path_to_my_llvm_directory>/clang++.cmd)
set(CMAKE_CXX_COMPILER_TARGET aarch64-linux-android)
Note that specifying CMAKE_SYSTEM_NAME is essential to enable crosscompiling.
Why specifying CMAKE_AR didn't work
Regarding your CMAKE_AR problem please note that CMake itself does use find_program() to find ar.exe. Since find_program() does cache its results, you have to prefill CMAKE_AR also as cached variable (see 0013038: cannot set CMAKE_AR when cross-compiling Fortran-only project).

Related

CMake+OpenCL+CUDA -> runtime library may be hidden

I'm using CMake 3.16, with a CMakeLists.txt file specifying a minimum CMake version of 3.9. In my file, I have:
find_package(CUDA 8.0 REQUIRED)
find_package(OpenCL REQUIRED)
# etc. etc.
target_link_libraries(my_executable
PRIVATE
cuda # The NVIDIA CUDA driver API
${CUDA_LIBRARIES}
OpenCL::OpenCL
)
Now, in the CMake generation phase, I get the error:
CMake Warning at CMakeLists.txt:44 (add_executable):
Cannot generate a safe runtime search path for target my_executable because
files in some directories may conflict with libraries in implicit
directories:
runtime library [libOpenCL.so.1] in /usr/lib64 may be hidden by files in:
/usr/local/cuda/lib64
Some of these libraries may not be found correctly.
Now, I do want the libOpenCL.so.1 from the CUDA directories; and building does produce an executable with the correct dependency. How can I tell CMake that this masking is ok, and not have it print the warning message?
Note: Working with CUDA in CMake has changed a lot over the 3.x series of releases. So whatever was happening before 3.8 is irrelevant, and also things changed significantly in 3.17 with a few more non-trivial changes afterwards. Answers about pre-3.8 and 3.17-or-later are, well, fine - but not what I need.
I know this doesn't answer your question precisely, but as of CMake 3.17+, the CUDA OpenCL libraries are loaded by the FindCUDAToolkit module. It is used like so:
cmake_minimum_required(VERSION 3.17)
project(my_proj LANGUAGES C CXX CUDA)
find_package(CUDAToolkit 8.0 REQUIRED)
# ...
target_link_libraries(
my_executable
PRIVATE
CUDA::cuda_driver
CUDA::cudart
CUDA::OpenCL
)
I hope this answer will help other readers who are using up-to-date CMake, because whatever answer works on 3.9 will not be quite as nice.
As #AlexReinking suggests in a comment, you can avoid this warning by giving CMake a (strong) hint regarding which OpenCL location you want to use. Before running CMake, set your OpenCL_ROOT environment variable to /usr/local/cuda; with a CMake version higher than 3.12, the find_package() command will use that variable, preferring to locate OpenCL there if possible - and not warn you about the alternative location.

CMake won't select the correct C++ compiler [duplicate]

I would like to use the IAR compiler. I noticed CMake has already have a bunch of files about this compiler:
https://github.com/jevinskie/cmake/blob/master/Modules/Compiler/IAR.cmake
From what I read the common solution is to specify manually ALL the toolchain in my CMakeLists.txt:
set(CMAKE_C_COMPILER iccarm)
set(CMAKE_CPP_COMPILER iccarm)
How CMake can link these definitions with `Modules/Compiler/IAR.cmake"?
I thought I would just have to do
include("Modules/Compiler/IAR.cmake")
What is the correct way to specify my IAR compiler?
When I do
cmake .
It still tries to use gcc instead of my IAR compiler. Why?
To select a specific compiler, you have several solutions, as exaplained in CMake wiki:
Method 1: use environment variables
For C and C++, set the CC and CXX environment variables. This method is not guaranteed to work for all generators. (Specifically, if you are trying to set Xcode's GCC_VERSION, this method confuses Xcode.)
For example:
CC=gcc-4.2 CXX=/usr/bin/g++-4.2 cmake -G "Your Generator" path/to/your/source
Method 2: use cmake -D
Set the appropriate CMAKE_FOO_COMPILER variable(s) to a valid compiler name or full path on the command-line using cmake -D.
For example:
cmake -G "Your Generator" -D CMAKE_C_COMPILER=gcc-4.2 -D CMAKE_CXX_COMPILER=g++-4.2 path/to/your/source
Method 3 (avoid): use set()
Set the appropriate CMAKE_FOO_COMPILER variable(s) to a valid compiler name or full path in a list file using set(). This must be done before any language is set (ie: before any project() or enable_language() command).
For example:
set(CMAKE_C_COMPILER "gcc-4.2")
set(CMAKE_CXX_COMPILER "/usr/bin/g++-4.2")
project("YourProjectName")
The wiki doesn't provide reason why 3rd method should be avoided...
I see more and more people who set CMAKE_C_COMPILER and other compiler-related variables in the CMakeLists.txt after the project call and wonder why this approach breaks sometimes.
What happens actually
When CMake executes the project() call, it looks for a default compiler executable and determines the way for use it: default compiler flags, default linker flags, compile features, etc.
And CMake stores path to that default compiler executable in the CMAKE_C_COMPILER variable.
When one sets CMAKE_C_COMPILER variable after the project() call, this only changes the compiler executable: default flags, features all remains set for the default compiler.
AS RESULT: When the project is built, a build system calls the project-specified compiler executable but with parameters suitable for the default compiler.
As one could guess, this approach would work only when one replaces a default compiler with a highly compatible one. E.g. replacement of gcc with clang could work sometimes.
This approach will never work for replacement of cl compiler (used in Visual Studio) with gcc one. Nor this will work when replacing a native compiler with a cross-compiler.
What to do
Never set a compiler in CMakeLists.txt.
If you want, e.g., to use clang instead of defaulted gcc, then either:
Pass -DCMAKE_C_COMPILER=<compiler> to cmake when configure the project. That way CMake will use this compiler instead of default one and on the project() call it will adjust all flags for the specified compiler.
Set CC environment variable (CXX for C++ compiler). CMake checks this variable when selects a default compiler.
(Only in rare cases) Set CMAKE_C_COMPILER variable before the project() call. This approach is similar to the first one, but makes the project less flexible.
If the ways above do not work
If on setting CMAKE_C_COMPILER in the command line CMake errors that a compiler cannot "compile a simple project", then something wrong in your environment.. or you specify a compiler incompatible for chosen generator or platform.
Examples:
Visual Studio generators work with cl compiler but cannot work with gcc.
A MinGW compiler usually requires MinGW Makefiles generator.
Incompatible generator cannot be fixed in CMakeLists.txt. One need to pass the proper -G option to the cmake executable (or select the proper generator in CMake GUI).
Cross-compiling
Cross-compiling usually requires setting CMAKE_SYSTEM_NAME variable, and this setting should normally be done in the toolchain file. That toolchain file is also responsible for set a compiler.
Setting CMAKE_SYSTEM_NAME in the CMakeLists.txt is almost always an error.
You need to create a toolchain file, and use the CmakeForceCompiler module.
Here is an example toolchain file for bare-metal ARM development with IAR:
include(CMakeForceCompiler)
set(CMAKE_SYSTEM_NAME Generic) # Or name of your OS if you have one
set(CMAKE_SYSTEM_PROCESSOR arm) # Or whatever
set(CMAKE_CROSSCOMPILING 1)
set(CMAKE_C_COMPILER iccarm) # Change the arm suffix if appropriate
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) # Required to make the previous line work for a target that requires a custom linker file
The last line is necessary because CMake will try to compile a test program with the compiler to make sure it works and to get some version information from preprocessor defines. Without this line, CMake will use add_executable() for the test program, and you will get the error "The C compiler "XXX" is not able to compile a simple test program." This is because the test program fails to link, as it doesn't have your custom linker file (I'm assuming bare-metal development since this is what IAR is usually used for). This line tells CMake to use add_library() instead, which makes the test succeed without the linker file. Source of this workaround: this CMake mailing list post.
Then, assuming that your toolchain file is named iar-toolchain.cmake, invoke CMake like this:
cmake -DCMAKE_TOOLCHAIN_FILE=iar-toolchain.cmake .
You can call cmake like this:
cmake -DCMAKE_C_COMPILER=iccarm ...
or
cmake -DCMAKE_CXX_COMPILER=...
If you don't want to use your PC's standard compiler, you have to give CMake the path to the compiler. You do this via environment variables, a toolchain file or direct definitions in the CMake command line (see e.g. CMake Error at CMakeLists.txt:30 (project): No CMAKE_C_COMPILER could be found).
Putting the compiler's name/path into your CMakeLists.txt would stop your project from being cross-platform.
CMake does check for the compiler ids by compiling special C/C++ files. So no need to manually include from Module/Compiler or Module/Platform.
This will be automatically done by CMake based on its compiler and platform checks.
References
CMake: In which Order are Files parsed (Cache, Toolchain, …)?
CMake GitLab Commit: Add support files for C, C++ and ASM for the IAR toolchain.
IAR Systems recently published a basic CMake tutorial with examples under their GitHub profile.
I like the the idea of a generic toolchain file which works seamlessly for both Windows and Linux compilers using find_program().
The following snippet will be used for when using C and can be used similarly for CXX:
# IAR C Compiler
find_program(CMAKE_C_COMPILER
NAMES icc${CMAKE_SYSTEM_PROCESSOR}
PATHS ${TOOLKIT}
"$ENV{ProgramFiles}/IAR Systems/*"
"$ENV{ProgramFiles\(x86\)}/IAR Systems/*"
/opt/iarsystems/bx${CMAKE_SYSTEM_PROCESSOR}
PATH_SUFFIXES bin ${CMAKE_SYSTEM_PROCESSOR}/bin
REQUIRED )
For ASM, I initially got puzzled with the NAMES but then I realized that the toolchain file was made that way for working with old Assemblers shipped with XLINK:
find_program(CMAKE_ASM_COMPILER
NAMES iasm${CMAKE_SYSTEM_PROCESSOR} a${CMAKE_SYSTEM_PROCESSOR}
PATHS ${TOOLKIT}
"$ENV{PROGRAMFILES}/IAR Systems/*"
"$ENV{ProgramFiles\(x86\)}/IAR Systems/*"
/opt/iarsystems/bx${CMAKE_SYSTEM_PROCESSOR}
PATH_SUFFIXES bin ${CMAKE_SYSTEM_PROCESSOR}/bin
REQUIRED )
Also, take a look at the full toolchain file. It will work automatically for "Arm" when the tools are installed on their default locations, otherwise it is just about updating the TOOLKIT variable and the compilers for all the supported languages should adjust automatically.
If your wanting to specify a compiler in cmake then just do ...
cmake_minimum_required(VERSION 3.22)
set(CMAKE_C_COMPILER "clang")
set(CMAKE_CXX_COMPILER "clang++")
Options 1 is only used if you want to specify what compiler you want to use as default for everything that you might compile on your computer. And I don't even think it would work on windows.
Option 2 would be used if you only want to use a different temporarily.
Option 3 is used if that's the compiler that should be used for that particular project. Also option 3 would be the most cross compatible.

How to partially disabling cmake C/C++ custom compiler checking

I am trying to do some crosscompilation using cmake. Some are easy with all the examples on Internet, I managed to crosscompile my library on Linux (x86 and ARM), Windows and Android. But now I would like to do it on a custom platform.
The process I need to achieve:
Sourcing my environment (this destroy all previous bash classic environment)
Compile with cmake
Execute what I want
But Cmake is testing for symbols in my custom C/C++ libraries which make my library unable to compile. The errors I have are that cmake some versions of GLIBCXX and CXXABI (no C issues) but not all of them.
Is there a way to make cmake ok with it ?
EDIT:
I tried using:
set(CMAKE_C_COMPILER_WORKS TRUE)
set(CMAKE_CXX_COMPILER_WORKS TRUE)
And with:
include(CMakeForceCompiler)
...
cmake_force_c_compiler(${ENV_PATH}/bin/${CC})
cmake_force_cxx_compiler(${ENV_PATH}/bin/${CXX})
But cmake is still checking for symbols.
Without having your environment nor the error message it's not easy to tell the actual root cause but here are two of the common causes and respective fixes:
If you don't have a complete toolchain file created for your custom environment - so CMake can't link a simple test program - you can try the relatively new (version 3.6) global CMake variable named CMAKE_TRY_COMPILE_TARGET_TYPE.
So just add the following:
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
Then CMake would just try to build a static library.
To have only the most common GCC compiler variables set and have only some basic checks, try:
SET(CMAKE_SYSTEM_NAME Generic)
See CMake Cross Compiling: Setting up the system and toolchain:
If your target is an embedded system without OS set CMAKE_SYSTEM_NAME to "Generic"
References
CMake AMRCC + custom linker
cmake cross-compile with specific linker doesn't pass arguments to armlink
Commandline:
cmake ... \
-DCMAKE_C_COMPILER_FORCED=TRUE \
-DCMAKE_CXX_COMPILER_FORCED=TRUE
or
CMakeLists.txt
...
set(CMAKE_C_COMPILER_FORCED TRUE)
set(CMAKE_CXX_COMPILER_FORCED TRUE)
...
Works for me.

Linking libc++ to CMake project on Linux

I want to use libc++ together with clang on Arch Linux in CMake project. I installed libc++ and added following lines to CMakeLists.txt as said on LLVM site in Linux section of "Using libc++ in your programs":
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++")
set(CMAKE_EXE_LINKER_FLAGS "-lc++abi")
I have tried just "++abi" in linker's flags, but it didn't help. I need some help in figuring out what i should write in my CMakeLists.txt.
Don't forget to set the compiler to clang++:
set(CMAKE_CXX_COMPILER "clang++")
Also, purge the cmake generated files (delete the folder CMakeFiles and CMakeCache.txt).
Depending on your system, it might also help to set
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -lc++abi")
The "proper" way to do this in CMake at the moment, until a specific base feature is added to switch standard libraries that is, is to use a toolchain file.
In that toolchain file you specify the compiler etc similarly to the other answers here.
BUT what's great about toolchains is they can be swapped out quickly either on the commandline (using -DCMAKE_TOOLCHAIN_FILE=path/to/file) OR in VSCode with CMakeTools extension installed, and probably other editors too.
But having to hand code your own toolchain files is yet another obscure chore! No fun!
Luckily, I stumbled upon this github that maintains a suite of them so you don't have to write them from scratch! Should be a lot less likely to get them wrong.
https://github.com/ruslo/polly

How best to deal with libstdc++ dll and libstdc dll when cross-compiling with mingw-w64 using cmake?

I'm cross-compiling a C++ (and some C) project from Linux using Mingw-w64 (really using the MXE project), using cmake as the build system. I have come to realise that I need to either provide the dlls libstdc++ and libstdc with the resulting libraries and exe files, or staticly link to these libraries.
My questions are therefore:
Which option is preferable, static linking or supplying the files? Personally I prefer the idea of static linking, as it is simpler for the end user in my case.
Where can I find the required dlls to supply them if I wanted to (or what steps to create them). I can only find .a versions in my toolchain.
How can I instruct cmake to statically link the libraries, bearing in mind that this is only desired for the cross-compile. As a starter, see the toolchain file provided by MXE below, which is what I'm currently using. I would probably not like to modify this, but rather the CMakeLists.txt files, or via a command line cmake option for my release script.
toolchain file
set(CMAKE_SYSTEM_NAME Windows)
set(MSYS 1)
set(BUILD_SHARED_LIBS OFF)
set(CMAKE_BUILD_TYPE Release)
set(CMAKE_FIND_ROOT_PATH /opt/mxe/usr/x86_64-w64-mingw32)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_C_COMPILER /opt/mxe/usr/bin/x86_64-w64-mingw32-gcc)
set(CMAKE_CXX_COMPILER /opt/mxe/usr/bin/x86_64-w64-mingw32-g++)
set(CMAKE_Fortran_COMPILER /opt/mxe/usr/bin/x86_64-w64-mingw32-gfortran)
set(CMAKE_RC_COMPILER /opt/mxe/usr/bin/x86_64-w64-mingw32-windres)
set(HDF5_C_COMPILER_EXECUTABLE /opt/mxe/usr/bin/x86_64-w64-mingw32-h5cc)
set(HDF5_CXX_COMPILER_EXECUTABLE /opt/mxe/usr/bin/x86_64-w64-mingw32-h5c++)
set(PKG_CONFIG_EXECUTABLE /opt/mxe/usr/bin/x86_64-w64-mingw32-pkg-config)
set(QT_QMAKE_EXECUTABLE /opt/mxe/usr/x86_64-w64-mingw32/qt/bin/qmake)
set(CMAKE_INSTALL_PREFIX /opt/mxe/usr/x86_64-w64-mingw32 CACHE PATH "Installation Prefix")
set(CMAKE_BUILD_TYPE Release CACHE STRING "Debug|Release|RelWithDebInfo|MinSizeRel")
Incidentally, I have tried the following cmake invocation, but this made no difference to the binary sizes, so I assumed it didn't work. Tips on actually testing this are welcome, I do have wine installed.
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} "-static -static-libgcc -static-libstdc++"" \
-DCMAKE_TOOLCHAIN_FILE=/opt/mxe/usr/x86_64-w64-mingw32/share/cmake/mxe-conf.cmake $WORKING_COPY_DIR/release/xfemm_mingw_win64/cfemm