Как поручить 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 может обрабатывать только один компилятор за раз. Так что - если вы не сделаете долгий путь, чтобы установить другой компилятор в качестве нового языка - у вас будет два цикла настройки.
Я вижу следующие подходы для автоматизации этого процесса:
Возьмем пример "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
,Расщепление
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, созданным для хост- платформы, независимо от того, одинаковы они или нет.