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

DLang, Vibe.d и кросс-компиляция для RPi4

D *Разработка на Raspberry Pi *
Из песочницы

Добрый вечер!

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

Недавно я написал сервер на DLang с использованием библиотеки Vibe.d. Для него я пишу кросс-платформенный клиент. Основной моей системой является Arch, и мне этого более чем достаточно, но для тестирования некоторых платформозависимых вещей я перезагружаюсь в Windows 10.

Что-то в Windows отпугнуло меня от того, чтобы собирать сервер для нее, хотя это и возможно. Поэтому мне в голову пришло очень логичное решение - запустить сервер на моем Raspberry Pi 4. Сейчас я использую его для удаленного доступа к принтеру и сканеру.

Попытка 1. Компиляция на микрокомпьютере

Первое, что пришло мне в голову - скинуть исходники на целевое устройство, скачать туда компилятор, систему сборки (в моем случае DUB) и собрать все там. Я предполагал, что это будет немного дольше, но я всегда мог оставить этот процесс выполняться на ночь, но не тут-то было...

Исходники я быстро скинул при помощи scp, после чего подключился к микрокомпьютеру по ssh и начал разбираться с компилятором.

На целевом устройстве стоит основанный на Debian Raspbian 10 Buster. Для поиска пакетов придется использовать apt... По сравнению с pacman он гораздо менее удобный, выдает кучу лишнего мусора (ИМХО).

Эталонным компилятором для D является dmd, но поиск по пакетам не выдал ничего полезного. Как оказалось : первое - dmd нет в стандартном репозитории Debian и Ubuntu, второе - он не поддерживает arm.

Чтож... Ничего страшного, ведь для D есть еще минимум 2 полноценных компилятора. Один из них, GDC, является частью GNU-Compiler-Collection и, вероятно, найдется везде, где есть Linux. Второй же, ldc, использует LLVM для генерации кода, поэтому его вы можете найти практически везде (Можно даже скачать его на Android через Termux).

Именно эти два компилятора оказались доступны для загрузки на микрокомпьютер :

sudo apt install ldc2 gdc

Оставался только DUB - de facto система сборки и менджер пакетов для D. Он есть в стандартном репозитории, но он не работает... Проекты не инициализируются, при загрузке зависимостей не может установить соединение с сервером. Сплошное расстройство!

В одной из статей про сборку D для arm было указано про проблемы с DUB. В качестве решения там предлагалось собрать его самостоятельно из исходников. Почему нет? Спускаемя к исходникам : https://github.com/dlang/dub

Для сборки в репозитории есть скрипт build.d (Код на D может быть запущен как скрипт при помощи специального интерпретатора - rdmd для dmd, ldmd для ldc, gdmd для gdc). gdmd не установился вместе с gdc, так что будем использовать ldmd. Собираем :

ldmd -run build.d

Собрать-то собрали, но лучше от этого не стало. dub работает, но очень медленно. Загрузить все зависимости для Vibe.d ему так и не удалось...

Попытка 2. Кросс-компиляция через GDC

Первая статья, которая была про компиляцию D кода под arm предлагала использовать кросс версию gdc. Почему нет?

Идем на официальный сайт и скачиваем последнюю доступную версию : https://gdcproject.org/old/downloads . Первоначально меня смутила дата сборки компилятора, но что же поделать.

После загрузки и распаковки нужно было проверить работоспособность компилятора. Для этого я написал обычный для всех Hello world :

import std.stdio;
int main(string [] args) {
  writeln("Hello, World!");
  return 0;
}

Теперь нужно это откомпилить и проверить на целевой платформе. С тулчейном это делается достаточно просто (только вместо arm-linux-gnueabihf-gdc нужно указывать полный путь) :

arm-linux-gnueabihf-gdc app.d

Получается исполняемый файл. Скидываем его на Raspberry, выдаем разрешение на исполнение и... Работает!

Оставалось только собрать vibe.d. Для работы с HTTPS он использует криптографическую библиотеку : OpenSSl или Botan. В той же статье был представлен интересный метод, позволяющий избежать их ручной компиляции : смонтировать всю файловую систему микрокомпьютера и указать компилятору путь до библиотек, хранящихся на нем.

Я сделал все по инструкции и запустил сборку. К сожалению - неудача. С момента выхода скачанной мной версии GDC в языке появились новые конструкции, которые компилятор 2016 не поддерживает... Найти более новую версию GDC я не смог, так что необходимо было искать другие варианты.

Статья, которую я использовал для этого пункта.

Попытка 3. LDC и мутки с библиотеками

Остался только один компилятор, способный откомпилить нужную мне программу для arm - ldc. Он установлен у меня на основном компьютере при помощи pacman. Какие-то дополнительные действия не нужны.

Для компиляции под другую платформу нужно лишь указать специальный флаг и дополнительные библиотеки. Первоначально необходимо было собрать стандартную библиотеку D для arm. Делается это одной простой командой :

ldc-build-runtime --ninja --dFlags="-w;-mtriple=arm-linux-gnueabihf"

Появится папка ldc-build-runtime.tmp/lib в которой храниться уже откомпилированная стандартная библиотека для arm. Для того чтобы скормить ее компилятору указываем следующий флаг : -L=-Lldc-build-runtime.tmp/lib
Пробуем собрать Hello World - ошибка линковки (не может найти точку входа). "Приехали", - подумал я. Собрал для начала объектный файл и решил попробовать слинковать его уже на raspberry - ld послал меня куда подальше.

Подумав и погадав, я решил скачать кросс-компилятор для C, взять линковщик оттуда и собрать все на основной машине. В Ubuntu и Debian arm-linux-gnueabihf есть в стандартном репозитории, а в Arch его можно было или собрать из AUR, или скачать уже собранный.

Раз уже делать, так делать - собираем из AUR. Процесс долгий, но интересный. Компиляция происходит в 3 этапа, постепенно собирая binutils, glibc и тп.

Спустя пару часов компилятор все же собрался. Я установил путь до него и собрал тестовую программу. Заработало!

Пробуем таким же образом слинковать другие библиотеки для проекта с Vibe.d... Не получилось. Линковщик стал таскать абсолютно все библиотеки из файловой системы raspberry и потерял половину ссылок. Undefined reference на malloc я еще не видел. До этого момента.

Не будем расстраиваться. Раз без включения библиотек от Raspberry все работает, просто локально соберем OpenSSL и ZLib (так же нужен), а потом прилинкуем их.

ZLib собрался довольно просто и быстро. Прилинковать его при помощи -L=-Lzlib/lib тоже не составило труда. Но вот OpenSSL отказался собираться от слова совсем. То ему компилятор не нравиться, то ему просто я не нравлюсь.

Из решений я нашел только одно - собрать OpenSSL на малине, скачать оттуда собранную и прилинковать на основной машине. На микрокомпьютере OpenSSL собрать было нетрудно. Я скачал его и добавил флаг -L=-Lopenssl/lib.

При компиляции я увидел всего 2 ошибки : неопределенная ссылка на SSL_get_peer_certificate и на ERR_put_error. Это меня доконало, я решил оставить это дело и лечь спать. Если бы я знал, как я был близок к победе на этом моменте.

Попытка 4. Toolchain и скрипты

На следующий день я решил попробовать все сначала, учесть все предыдущие ошибки и сделать что-то наподобие toolchainа для сборки D кода под ARM.

Начать я решил с кросс-компилятора для C. Загуглив что-то наподобие "arm-linux-gnueabihf gcc" я нашел вот этот вот сайт.

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

Для проверки работоспособности компилятора я решил запустить Hello World, но уже на C :

#include <stdio.h>
int main(int argc, char ** argv) {
  printf("Hello, World!\n");
  return 0;
}

На этот раз я решил автоматизировать все, чтобы можно было просто запускать скрипты, делающие за меня всю грязную работу, поэтому появился build.sh :

#!/bin/bash
gcc_arm/bin/arm-linux-gnueabihf-gcc main.c -o app.o

К счастью, все заработало с первого раза. Теперь я решил приступить к настройке компилятора для D. Как самый успешный, был выбран ldc. Я скачал последний релиз с GitHub (https://github.com/ldc-developers/ldc) и также поместил рядом с C-компилятором.

Теперь было необходимо собрать стандартную библиотеку D для arm. Так появился build_ldc_runtime.sh :

#!/bin/bash
export CURRENT_DIR=$(pwd)
export LDC_PATH=$CURRENT_DIR/"ldc2/bin"
export GCC_PATH=$CURRENT_DIR/"gcc_arm/bin"
export CC=$GCC_PATH/"arm-linux-gnueabihf-gcc"
$LDC_PATH/ldc-build-runtime --ninja --dFlags="-w;-mtriple=arm-linux-gnueabihf"

Сборка прошла успешно, и я перешел к тестированию HelloWorld. Также откомпилил его и запустил на Raspberry, но получил слегка неожиданную ошибку. Исполняемый файл требовал GLIBC не ниже, чем 2.29. Погуглив, как посмотреть версию GLIBC, я понял, что на малине стоит всего-лишь 2.28...

Так как, по моему мнению, clang и llvm, а следовательно и ldc, не пропагандируют GLIBC и вроде как даже могут обходиться без нее, был сделан вывод о том, что проблема в gcc.

Я решил скачать Toolchain с той же версией gcc, что и на целевом устройстве, то есть 8.3.0. К счастью, на том же сайте был найден подходящий компилятор. Я удалил старый, распаковал новый, пересобрал ldc_runtime и все заработало.

Чудесно. Оставалось присобачить сюда ZLib и OpenSSL. Начал я, конечно, с ZLib, так как с ним меньше проблем :

#!/bin/bash
export CURRENT_DIR=$(pwd)
export GCC_PATH=$CURRENT_DIR/"gcc_arm/bin"
export CC=$GCC_PATH/"arm-linux-gnueabihf-gcc"
mkdir tmp_zlib
cd tmp_zlib
git clone https://github.com/madler/zlib.git
cd zlib
./configure --prefix=$CURRENT_DIR/zlib
make
make install
cd ../..
rm -rf tmp_zlib

На выходе получается папка с готовой для линковки библиотекой. Потом я начал собирать OpenSSL. На этот раз сборка прошла успешно. Но вот на этапе линковки, снова вылезли неразрешенные зависимости : SSL_get_peer_certificate и ERR_put_error.

Не имею никакого желания с этим разбираться, я решил отказаться от OpenSSL и использовать Botan (Vibe.d имеет такую возможность). К слову, все собралось и слинковалось с первого раза :

#!/bin/bash
export CURRENT_DIR=$(pwd)
export GCC_PATH=$CURRENT_DIR/"gcc_arm/bin"
export CCX=$GCC_PATH/"arm-linux-gnueabihf-g++"
mkdir tmp_botan
cd tmp_botan
git clone https://github.com/randombit/botan.git
cd botan
python configure.py --cpu=arm --cc-bin=${CCX} --prefix=${CURRENT_DIR}/botan
make && make install
cd ../..
rm -rf tmp_botan

Приложение с Vibe.d И Botan успешно откомпилилось и ДАЖЕ ЗАПУСТИЛОСЬ на raspberry. Просто замечательно. Но есть одно "но" - OpenSSL. Мы так и не разобрались с ним. Проблема оказалась достаточно глупой и легко решаемой.

Поискав SSL_get_peer_certificate и ERR_put_error в репозитории OpenSSL я понял, что в последних alpha версиях они были объявлены deprecated и удалены. Vibe.d официально поддерживает OpenSSL версии 1.1, а я скачал alpha 3.x, где этих функций просто не было.

Необходимо было просто скачать более старую, стабильную версию исходников и собрать их :

#!/bin/bash
export CURRENT_DIR=$(pwd)
mkdir tmp_openssl
cd tmp_openssl
git clone https://github.com/openssl/openssl -b OpenSSL_1_1_1-stable
cd openssl
./Configure linux-generic32 shared \
    --prefix=$CURRENT_DIR/openssl --openssldir=$CURRENT_DIR/tmp_openssl/openssl \
    --cross-compile-prefix=$CURRENT_DIR/gcc_arm/bin/arm-linux-gnueabihf-
make && make install
cd ../..
rm -rf tmp_openssl

И вот наконец, все это заработало. Я написал небольшой скрипт, который сразу запускает компилятор с нужными флагами :

#!/bin/bash
export CURRENT_DIR=/home/test_user/Projects/rpi4_d_toolchain
export LDC_PATH=$CURRENT_DIR/ldc2/bin
export GCC_PATH=$CURRENT_DIR/gcc_arm/bin
export LDC_RUNTIME_PATH=$CURRENT_DIR/ldc-build-runtime.tmp/lib
export OPENSSL_PATH=$CURRENT_DIR/openssl/lib
export ZLIB_PATH=$CURRENT_DIR/zlib/lib
export BOTAN_PATH=$CURRENT_DIR/botan/lib
export CC=$GCC_PATH/"arm-linux-gnueabihf-gcc"
$LDC_PATH/ldc2 -mtriple=arm-linux-gnueabihf -gcc=$CC -L=-L${LDC_RUNTIME_PATH} -L=-L${OPENSSL_PATH} -L=-L${ZLIB_PATH} -L=-L${BOTAN_PATH} $@

Теперь все проекты легко и просто собираются следующей командой :

dub build --compiler=~/ldc_rpi

Прототипом и основным источником информации послужил вот этот GitHub репозиторий.

Заключение

Если вы когда-нибудь захотите воспользоваться кросс-компиляцией, то :

  1. Лучше использовать уже готовый компилятор. Самостоятельно его собирая, вы потратите много времени

  2. Версии компиляторов основной и целевой платформы должны совпадать, иначе что-то может пойти не так

  3. Скрипты автоматизируют и упростят вашу жизнь. Используйте их

  4. Если есть возможность примонтировать файловую систему целевого устройства, то можете попробовать украсть библиотеки оттуда. Если получиться - ваша жизнь станет легче

А если вы когда-нибудь соберетесь собирать что-то для raspberry pi 4, то можно скачать отсюда тот Toolchain, который в итоге у меня получился (тут все компиляторы и библиотеки).

Только в файле ldc_rpi, поменяйте значение CURRENT_DIR на путь до папки с toolchainом.

Надеюсь, вам понравилась моя статья. Приму к сведению любые замечания и пожелания.

Теги: Ddlangraspberrycross-platformкомпиляция
Хабы: D Разработка на Raspberry Pi
Всего голосов 5: ↑5 и ↓0 +5
Комментарии 6
Комментарии Комментарии 6

Похожие публикации

Лучшие публикации за сутки