Как стать автором
Обновить

Комментарии 7

Кросс компиляция это выход.
В студенческую бытность имел коммуникатор asus p525 (217МГц и 32Мб оперативки), на него был способ запустить gentoo.
Вот там брался настольный комп, на нём настраивался кросс компилятор, и мобилка выполняла только этап конфигурации, затем отдавала компиляцию большому брату.
Так же знаю людей кто таким же образом настраивал билд сервер, для не отличающейся архитектуры.

Если честно, то так себе решение. Обычно, когда собираешь под embedded Linux, то тебе нужна специфическая периферия, которую не сможет предоставить ваша виртуалка. Так что только cross-compiling. Правда у меня собрана 5.9, которая распространяется в виде готового дебиан пакета. Более новые версии Qt не собирал

Посмотрите про Cross build environment в гентушной вики. Там всё это уже давно автоматизировано и работает намного быстрее, так как сборка не задействует эмуляцию в виртуальной машине. Хотя я не пробовал собирать так тяжёлые приложения типа Qt, только лёгкие вроде сетевых утилит (на компьютере x86-64 собирались приложения под USB-модем с процессором типа ARM). Наверняка можно нарваться на то, что некоторые ебилды (скрипты сборки для Gentoo) не работают в режиме кросс-компиляции и нужно их править. Но зависимости должны доставляться автоматически, а не вручную, как у автора статьи.

Как мне известно, статья появилась из обсуждения в одном чате следующей проблемы: у человека было устройство на ARM-процессоре с Linux'ом на борту, но тулчейн/исходники ядра/инструкцию по сборке rootfs от вендора получить не удалось. На устройстве был доступ к gcc и старой версии Qt, а для нужд компании требовалось собрать более новую версию Qt. В таком случае варианты со сборкой прямо на железке или с эмуляцией ARM вместо сборки кросс-тулчейна не так уж и плохи.

Кросс-тулчейном действительно собирать в разы проще, когда этот кросс-тулчейн есть или когда его вообще возможно собрать. Но иногда у тебя на руках только железка с SD-картой с ядром и rootfs'ом на борту, где, о чудо, иногда ещё есть и компилятор. Без исходников кросс-тулчейн нужной версии получить сложнее, чем собирать код прямо на железке.

Другой вопрос, что когда ты встречаешься с жЫрным софтом, сборка на дохлой ARM-железке оказывается слишком медленной. В таком случае у тебя несколько вариантов:

  • собирать таки кросс-тулчейн, подбирая libc и хэдэры ядра в соответствии rootfs'ом железки
  • брать rootfs с железки и запускать сборку внутри неё на хостовой системе


Автор выбрал второй путь. Проблема лишь в том, что он воспользовался полной виртуализацией (=эмулировал оборудование, I/O, работу драйверов файловой системы, графику и т.д.) тогда, когда было достаточно преобразовывать ARM-вызовы syscall'ов в x86. Для этого у qemu есть набор инструментов под все популярные архитектуры. Например, можно взять любой статический armv7-бинарь, собранный под линукс (например, busybox) и запустить его через qemu-arm busybox. А можно пойти дальше и заchroot'иться в систему, засунув туда qemu-arm-static (static потому что у него не будет доступа до хостовых библиотек вне chroot), тогда /bin/bash, собранный под ARM, будет запускаться на х86, используя qemu-arm-static и библиотеки из rootfs.

В отличие от полной виртуализации, при chroot в ARM-окружение у тебя есть доступ до всех ядер процессора, до всей доступной RAM, до всего дискового пространства хостовой системы, так ещё и очень сильно сокращается overhead от эмуляции процессора и I/O.

Я попробовал сделать Proof-of-Concept, у меня получились очень неплохие результаты:
pi@lerochka:~/qt-everywhere-src-5.15.0 $ time ./configure -skip qt3d -no-warnings-are-errors -release -recheck-all -prefix /Qt/5.15.0 -opensource -confirm-license -nomake examples -nomake tests -c++std c++17 -I /usr/include/xcb/ -xcb-xlib -xcb -feature-thread -feature-xkbcommon -qt-libpng -qt-libjpeg -qt-zlib -I /usr/include/xcb/ --recheck-all -skip wayland -skip qtwebengine -skip qtwayland
...
real    17m22.799s
user    16m59.336s
sys     0m23.783s


Такой вариант в 5 раз быстрее (17 минут против 91 у автора), чем сборка в полностью виртуализированном окружении. И это на Intel® Pentium® CPU G4560 @ 3.50GHz в 1 потоке.
Я думаю, можно вызвать ./configure так, чтобы он юзал все потоки, будет намного быстрее.
Конечно, это всё ещё не нативная сборка Qt под x86 на x86: там «конфигурация» проходит за 2 минуты на этом же процессоре.

Под спойлером небольшая инструкция о том, как воспроизвести chroot в ARM-окружение (проверялось на Arch Linux, /home/xxx/rootfs замените на какую-нибудь удобную для вас директорию):

Инструкция по chroot'у в rootfs от Raspberry Pi OS
1. Скачиваем образ Raspberry Pi OS и разархивировываем:
downloads.raspberrypi.org/raspios_lite_armhf_latest

2. Вытаскиваем rootfs из образа диска:
sudo su
modprobe loop
losetup -fP --show 2020-05-27-raspios-buster-lite-armhf.img
mount /dev/loop0p2 /mnt
cd /mnt
cp -r * /home/xxx/rootfs


3. Скачиваем на свою систему binfmt-support и qemu-arm-static (для Debian, у арча в AUR'е есть пакет qemu-user-static):
sudo apt-get install qemu-user-static qemu-user-static


4. Копируем qemu-arm-static в rootfs:
cp "$(which qemu-arm-static)" /home/xxx/rootfs/usr/bin


5. Монтируем dev, sys, proc, run, tmp в rootfs (не забудьте потом это всё отмонтировать после работы):
mount --bind /dev /home/xxx/rootfs/dev
mount --bind /sys /home/xxx/rootfs/sys
mount --bind /proc /home/xxx/rootfs/proc
mount -t tmpfs tmp /home/xxx/rootfs/tmp
mount -t tmpfs run /home/xxx/rootfs/run


6. Делаем chroot в систему:
chroot /home/xxx/rootfs/
export PATH=/bin:/sbin:/usr/bin:/usr/sbin
echo "nameserver 8.8.8.8" > /etc/resolv.conf


7. Профит, мы как будто в обычном chroot'е, но только в ARM'овом :)

P.S. Для сборки я брал Qt 5.15 отсюда, поставил все зависимости из статьи, собирал под пользователем pi (нужно вызвать su — pi, чтобы не собирать под root'ом), + я не очень понял почему это так работает, но я поправил ещё строчку в qtbase/configure:
- "$outpath/bin/qmake" "$relpathMangled" -- "$@"
+ "$outpath/bin/qmake" "$relpathMangled/qt.pro" -- "$@"

Да, при такой постановке задачи описываемые мучения имеют рациональный смысл. Спасибо за пояснения.
> но тулчейн/исходники ядра/инструкцию по сборке rootfs от вендора получить не удалось.

Я как бы в таком случае много раз собирал Qt как то так (так, сказать, по-быстрому):

1. Берется и «сливается» rootfs из реального у-ва посредством rsync (или иным образом).
2. Берется любой готовый тулчейн (GCC) для ARM примерно той-же версии что и использовался на у-ве (просто ставим из репозитория на хост или качаем отдельно).
3. На хосте запускаем 'qemu-static-arm' (или как оно называется), чрутимся на скачанную rootfs и доустанавливаем 'devel' пакеты в rootfs (те, которые нужны для сборки, если, конечно, у железки есть свой репозиторий). Если репозитория нет — то придется собирать все недостающие пакеты кросс-компилятором и «устанавливать» в rootfs.
4. Собственно, всё, кросс-компилим Qt используя наш тулчейн и «нашу» rootfs.

PS: И да, как уже сказали выше — это самый быстрый и простой способ.

Да, подставить в готовый тулчейн с похожим libc/ядром rootfs с железки как sysroot — это решение. Собственно, это подпункт «собирать таки кросс-тулчейн, подбирая libc и хэдэры ядра в соответствии rootfs'ом железки» из моего комментария, но в таком случае ты не можешь быть уверен до конца, что всё будет друг с другом совместимо. Я думаю, что в 99% случаев проблем не будет, но на 1% придётся потратить слишком много ресурсов для нахождения проблемы.

В чате ещё предлагали собрать отдельно кросстулчейн на musl и под Linux 2.6, им же скомпилировать статически-слинкованный Qt, потом собрать приложение и упаковать ресурсы Qt и статически-слинкованное приложение в один пакет. Таким образом, софт вообще не зависит от системного libc и версии ядра и будет работать где угодно.
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории