18

When using CMake for cross compiling, one generally specifies a toolchain file via the CMAKE_TOOLCHAIN_FILE option. In GNU terminology, one can specify the host architecture toolset using this file. However, one can generally not expect to be able to execute anything built with this toolchain. So often enough, some build tools need to be compiled for the build architecture.

Consider the following setup. I have two source files genfoo.c and bar.c. During build, genfoo.c needs to be compiled and run. Its output needs to be written to foo.h. Then I can compile bar.c, which #include "foo.h". Since CMake defaults to using the host architecture toolchain, the instructions for bar.c are easy. But how do I tell it to use the build architecture toolchain for compiling genfoo.c? Simply saying add_executable(genfoo genfoo.c) will result in using the wrong compiler.

6
  • As you have stated, in GNU terminology you would use the configure command to specify the host architecture. And you would have to run configure twice if you want to build for two different host environments. So I see two possibilities with CMake: use a build script that creates and executes the two environments needed (see here) or use ExternalProject_Add() and split your CMakeLists.txt into dedicated subprojects. Commented Mar 23, 2016 at 13:27
  • No, with autotools you simply use CC_FOR_BUILD. See ax_cc_for_build. Commented Mar 23, 2016 at 13:39
  • Ok, didn't know that one. If I understand correctly we are talking about the use case described in CMake Cross Compiling - Using executables in the build created during the build? Commented Mar 23, 2016 at 13:52
  • @Florian I believe that you should make an answer with that link as it answers a huge part of my question. I don't like the approach taken, but it certainly solves the problem at hand. I was originally blind to recognizing this as a solution as it puts the burdon (of building twice) on the user. Commented Mar 24, 2016 at 7:01
  • 1
    Perhaps also consider the approach described here where an executable needs to be built within the CMake run so it can be used to generate sources and even other CMakeLists.txt files. I have it working to generate files on OS X for an iOS build, so it works in practice for a situation like yours. Commented Apr 28, 2016 at 12:09

2 Answers 2

16

CMake can only handle one compiler at a time. So - if you don't go the long way to set up the other compiler as a new language - you will end up with two configuration cycles.

I see the following approaches to automate this process:

  1. Taking the example "CMake Cross Compiling - Using executables in the build created during the build?" from the CMake pages as a starting point I'll get:

    CMakeLists.txt

    cmake_minimum_required(VERSION 3.0) project(FooBarTest) # When crosscompiling import the executable targets if (CMAKE_CROSSCOMPILING) set(IMPORT_PATH "IMPORTFILE-NOTFOUND" CACHE FILEPATH "Point it to the export file path from a native build") file(TO_CMAKE_PATH "${IMPORT_PATH}" IMPORT_PATH_CMAKE) include(${IMPORT_PATH_CMAKE}/genfooTargets.cmake) # Then use the target name as COMMAND, CMake >= 2.6 knows how to handle this add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/foo.h COMMAND genfoo ) add_executable(bar bar.cpp ${CMAKE_CURRENT_BINARY_DIR}/foo.h) target_include_directories(bar PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) endif() # Only build the generator if not crosscompiling if (NOT CMAKE_CROSSCOMPILING) add_executable(genfoo genfoo.cpp) export(TARGETS genfoo FILE "${CMAKE_CURRENT_BINARY_DIR}/genfooTargets.cmake") endif() 

    Then using a script like:

    build.sh

    #!/bin/bash if [ ! -d hostBuild ]; then cmake -E make_directory hostBuild cmake -E chdir hostBuild cmake .. fi cmake --build hostBuild if [ ! -d crossBuild ]; then cmake -E make_directory crossBuild cmake -E chdir crossBuild cmake .. -DIMPORT_PATH=${PWD}/hostBuild -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake fi cmake --build crossBuild 

    I'll get the desired results by calling ./build.sh.

  2. Splitting the CMakeLists.txt and maybe even replace the export()/include() with something where I know the output path of my build tools e.g. by using CMAKE_RUNTIME_OUTPUT_DIRECTORY would simplify things:

    CMakeLists.txt

    cmake_minimum_required(VERSION 3.0) project(FooBarTest) # Then use the target name as COMMAND. CMake >= 2.6 knows how to handle this add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/foo.h COMMAND genfoo ) add_executable(bar bar.cpp ${CMAKE_CURRENT_BINARY_DIR}/foo.h) target_include_directories(bar PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) 

    buildTools/CMakeLists.txt

    cmake_minimum_required(VERSION 3.0) project(BuildTools) add_executable(genfoo genfoo.cpp) 

    build.sh

    #!/bin/bash if [ ! -d crossBuild ]; then cmake -E make_directory crossBuild cmake -E chdir crossBuild cmake .. -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake fi if [ ! -d hostBuild ]; then cmake -E make_directory hostBuild cmake -E chdir hostBuild cmake ../buildTools -DCMAKE_RUNTIME_OUTPUT_DIRECTORY:PATH=${PWD}/crossBuild fi cmake --build hostBuild cmake --build crossBuild 

References

Sign up to request clarification or add additional context in comments.

Comments

6

It is possible to do that completely within CMake.

The trick is to run a separate CMake configuring stage within its own space, silently dismissing every crosscompiling setting and using the host's default toolchain, then import the generated outputs into it's parent, crosscompiling build.

First part:

set(host_tools_list wxrc generate_foo) if(CMAKE_CROSSCOMPILING) # Pawn off the creation of the host utilities into its own dedicated space file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/host_tools) file(TO_NATIVE_PATH ${CMAKE_COMMAND} native_cmake_command) file(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR} native_cmake_current_source_dir) execute_process( COMMAND "${native_cmake_command}" "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" "${native_cmake_current_source_dir}" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/host_tools ) add_custom_target(host_tools COMMAND ${CMAKE_COMMAND} --build . --target host_tools --config $<CONFIG> WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/host_tools ) include(${CMAKE_CURRENT_BINARY_DIR}/host_tools/host_tools.cmake) foreach(tgt IN ITEMS ${host_tools_list}) add_dependencies(host${tgt} host_tools) endforeach() else() # Add an empty target, host tools are built inplace add_custom_target(host_tools DEPENDS ${host_tools_list} ) endif() 

... then you add the usual add_executable and whatever ...

At the end:

if(NOT CMAKE_CROSSCOMPILING) foreach(tgt IN ITEMS ${host_tools_list}) add_executable(host${tgt} ALIAS ${tgt}) endforeach() export(TARGETS ${host_tools_list} NAMESPACE host FILE host_tools.cmake) endif() 

When it crosscompiles, it pawns off the creation of the host-run tools into its own dedicated space, and imports the targets as "hostwxrc" and "hostgenerate_foo", with a dependency on generating the host_tools themselves .

When it doesn't crosscompile, it builds wxrc and generate_foo as-is, and aliases them to hostwxrc and hostgenerate_foo.

After this, when you use $<TARGET_FILE:wxrc>, you refer to the wxrc built for the target platform, and $<TARGET_FILE:hostwxrc> refers to the wxrc built for the host platform, regardless whether they are the same or not.

3 Comments

It looks like with this approach you may need to clear CXX/CC environment vars, before calling host cmake with exec_process. i.e. set(ENV{CXX} "") and set(ENV{CC} "")
@kert Yes, this was one issue I ran into. You can use COMMAND "${CMAKE_COMMAND}" -E env --unset=ASM --unset=CC --unset=CXX "${CMAKE_COMMAND}" "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" "${CMAKE_CURRENT_SOURCE_DIR}" to unset the environment vars (I also had ASM set).
The other issue I had was that I wanted to use hostwxrc from the parent file, but the target was exported only to the one CMakeFiles.txt that was including it. You can use set_target_properties(hostwxrc PROPERTIES IMPORTED_GLOBAL true) to make it visible to parent CMakeFiles.txt as well.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.