I\'ve done a lot of research and been unable to find an answer to this... how can I reliably find the target architecture I\'m compiling for, using CMake? Basically, the equ
So I devised a rather creative solution to my problem... it appears that CMake has no functionality to detect the target architecture whatsoever.
Now, we know we can easily do this in C because symbols like __i386__
, __x86_64__
, etc., will be defined depending on your environment. Fortunately CMake has a try_run function which will compile and run an arbitrary C source code file during the configure stage.
We could then write a small program which uses a bunch of ifdefs and writes the architecture name to the console as a string. The only problem is that this only works if the host and target system are the same... it can't work during cross compilation because while you can compile the binary, you can't run it to see its output.
Here's where things get interesting. We can exploit the C preprocessor to get the necessary information by deliberately writing a broken C program... we use the original concept of writing the architecture name to the console based on ifdefs but instead of doing that, we'll simply place an #error preprocessor directive in place of a printf call.
When CMake's try_run function compiles the C file, compilation will always fail, but whatever message we placed in the #error directive will show up in the compiler's error output, which try_run returns to us.
Therefore, all we have to do is parse the architecture name from the compiler's error output using some CMake string commands, and we can retrieve the target architecture... even when cross compiling.
The OS X specific part of the code mostly uses CMAKE_OSX_ARCHITECTURES to determine the target architecture, but in the case it's unspecified it will use the same code as other systems and correctly return x86_64 (for modern systems on which that is the compiler's default) or i386 (for older OS X systems such as Leopard).
I've tested and verified this works on Windows, OS X and Linux using Visual Studio 9 and 10 generators (x86, x86_64, ia64), Xcode, NMake, MSYS Makefiles and Unix Makefiles. The correct result is returned every time.
Notice: This solution can fail if you deliberately do things like pass -m32 or -m64 to your compiler, or other flags that may affect the target architecture (is there a way to pass all environment settings through to try_run?); this is not something I've tested. As long as you're using the default settings for your generator and all targets are being compiled for the same architecture you should be OK.
The full source code for my solution can be found at GitHub: https://github.com/petroules/solar-cmake/blob/master/TargetArch.cmake
For now, you don't need any hacks for determining target architecture: per-target variable OSX_ARCHITECTURES was added to cmake and can be used for your purpose: http://public.kitware.com/Bug/view.php?id=8725
I case your build process involved more than 1 target, I this it is better to let CMake know what ARCH/toolchain it is building against. You can follow the instructions for CMake cross-compilation, that encourages you to create a toolchain CMake file, which lets you pick the toolchain/compiler being used.
I've created one for building my C++ Linux application for the arm processor, and named it toolchain-arm.cmake
.
It includes set(CMAKE_SYSTEM_PROCESSOR arm)
.
I then executed CMake like so:
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE={my toolchain cmake path}/toolchain-arm.cmake {my source path}
within my project's CMakeList.txt I can refer to CMAKE_SYSTEM_PROCESSOR any way I wish.
When building for X86, I don't include the reference to -DCMAKE_TOOLCHAIN_FILE, leaving CMAKE_SYSTEM_PROCESSOR undefined, or at least not defined as arm
.
Here's my toolchain-arm.cmake
SET(CMAKE_SYSTEM_NAME Linux)
SET(CMAKE_SYSTEM_VERSION 1)
set(CMAKE_SYSTEM_PROCESSOR arm)
# specify the cross compiler
SET(ENV{TOOLCHAIN_ROOT} /home/user/toolchain/tools-master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin )
SET(CMAKE_C_COMPILER $ENV{TOOLCHAIN_ROOT}/arm-linux-gnueabihf-gcc)
SET(CMAKE_CXX_COMPILER $ENV{TOOLCHAIN_ROOT}/arm-linux-gnueabihf-gcc)
# search for programs in the build host directories
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# for libraries and headers in the target directories
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
I think the easiest and most reliable solution is to specify the architecture somehow by hand, for the platform you're building on (you can use cmake . -DMY_ARCHITECTURE=x86
or something similar). At least that's what we're doing in our projects, because of the same issues you described above
Android ${ANDROID_ABI}
The ${ANDROID_ABI}
variable is the way to go in Android, where it assumes values like arm64-v8a
, x86_64
and so on.
It is used on the official NDK library example: https://github.com/googlesamples/android-ndk/blob/840858984e1bb8a7fab37c1b7c571efbe7d6eb75/hello-libs/app/src/main/cpp/CMakeLists.txt#L25
I have further commented on that example at: NDK: How to include Prebuilt Shared Library Regardless of Architecture
This is a well tested way of knowing the host architecture:
# Store in CMAKE_DEB_HOST_ARCH var the current build architecture
execute_process(COMMAND
dpkg-architecture
-qDEB_HOST_ARCH
OUTPUT_VARIABLE
CMAKE_DEB_HOST_ARCH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
And use that information later in CMakeLists as you need
if(${CMAKE_DEB_HOST_ARCH} MATCHES "armhf")
...
elseif(${CMAKE_DEB_HOST_ARCH} MATCHES "i386")
...
else()
...
endif()