Как поручить CMake использовать компилятор архитектуры сборки

При использовании CMake для кросс-компиляции обычно указывается файл цепочки инструментов через CMAKE_TOOLCHAIN_FILE вариант. В терминологии GNU с помощью этого файла можно указать набор инструментов архитектуры хоста. Однако обычно нельзя ожидать, что он сможет выполнить что-либо, созданное с помощью этого набора инструментов. Так часто, некоторые инструменты сборки должны быть скомпилированы для архитектуры сборки.

Рассмотрим следующую настройку. У меня есть два исходных файла genfoo.c а также bar.c, Во время сборки genfoo.c нужно скомпилировать и запустить. Его вывод должен быть записан в foo.h, Тогда я могу скомпилировать bar.c, который #include "foo.h", Поскольку CMake по умолчанию использует инструментарий архитектуры хоста, инструкции для bar.c легко Но как мне сказать, что для компиляции нужно использовать инструментарий сборки архитектуры genfoo.c? Просто говоря add_executable(genfoo genfoo.c) приведет к использованию неправильного компилятора.

1 ответ

Решение

CMake может обрабатывать только один компилятор за раз. Так что - если вы не сделаете долгий путь, чтобы установить другой компилятор в качестве нового языка - у вас будет два цикла настройки.

Я вижу следующие подходы для автоматизации этого процесса:

  1. Возьмем пример "CMake Cross Compiling - Использование исполняемых файлов в сборке, созданной во время сборки?" со страниц CMake в качестве отправной точки я получу:

    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()
    

    Затем с помощью сценария, как:

    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
    

    Я получу желаемые результаты, позвонив ./build.sh,

  2. Расщепление CMakeLists.txt и, возможно, даже заменить export() / include() с чем-то, где я знаю путь вывода моих инструментов сборки, например, с помощью CMAKE_RUNTIME_OUTPUT_DIRECTORY упростил бы вещи:

    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})
    

    инструмент сборки / 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
    

Рекомендации

Это можно сделать полностью в CMake.

Хитрость заключается в том, чтобы запустить отдельный этап настройки CMake в его собственном пространстве, молча отклоняя все настройки кросс-компиляции и используя набор инструментов хоста по умолчанию, а затем импортировать сгенерированные выходные данные в родительскую кросс-компилируемую сборку.

Первая часть:

set(host_tools_list wxrcgenerate_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()

... затем вы добавляете обычный add_executable и все такое...

В конце:

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()

Когда crosscompiles, он закладывает от создания хозяинного запуска инструментов в своем собственное выделенное пространство, а импорт на цели, как "hostwxrc" и "hostgenerate_foo", с зависимостью от генерации host_tools самого.

Когда он не компилируется, он строит wxrc и generate_foo как есть и присваивает им псевдонимы hostwxrc и hostgenerate_foo.

После этого, когда вы используете $<TARGET_FILE:wxrc>, вы ссылаетесь на wxrc, созданный для целевой платформы, и$<TARGET_FILE:hostwxrc>относится к wxrc, созданным для хост- платформы, независимо от того, одинаковы они или нет.

Другие вопросы по тегам