Пишем на D для Raspberry Pi
Dlang, или просто D — молодой язык программирования с многолетней историей. Не смотря на то, что язык с таким названием появился очень давно, то, что сейчас называется D2 или просто D, появилось недавно и слабо напоминает предшественника. Писать на D очень удобно, а произодительность не уступает C++, поэтому не удивительно, что он добрался до ARM и его мобильных представителей Android и iOS. Кроме того растёт интерес к интернету вещей и просто портативным устройствам.
В статье рассмотрена задача кросскомпиляции кода на dlang для Raspberry Pi. В этом нет ничего сложного, да и подводных камней не замечено. Данная публикация — простой мануал для начала использования D на разных устройствах в целом и Raspberry Pi в частности.
Hello, World
Нам понадобится:
- Компьютер с линуксом (в моём случае Ubuntu 14.04 на виртуалке)
- Raspberry Pi с ssh доступом (использована Raspberry Pi B+)
Не смотря на то, что можно собирать исходники прямо на Raspberry Pi, лучше это делать на отдельном более мощном компьютере. На самой малине слишком мало памяти, а кросскомпиляция под Raspberry Pi — дело несложное. Для начала нам нужен компилятор dlang для Raspberry Pi. Тут два варианта: LDC (LLVM based D Compiler) и GDC (GNU D Compiler). Проще оказалось найти подходящую версию GDC. LDC под ARM есть и его тоже можно использовать, просто так сложилось, что я использую GDC.
Идём на сайт GDC в раздел downloads.
Скачиваем x86_64 сборку для целевой платформы arm-linux-gnueabihf (тут по-внимательнее, сам GDC будет запускаться на x86, а таргет armhf; не путать с GDC для arm, который запускается на устройстве).
wget http://gdcproject.org/downloads/binaries/5.2.0/x86_64-linux-gnu/gdc-5.2.0-arm-linux-gnueabihf+2.066.1.tar.xz
Распаковываем
tar -xf gdc-5.2.0-arm-linux-gnueabihf+2.066.1.tar.xz
для удобства я ещё переименовал папку
mv arm-unknown-linux-gnueabihf gdc-arm
Для постоянного использования я скопировал папку с gdc в /usr/bin и сделал алиас gdc-arm, но для примера к статье будем всё делать локально в папке. И так создаём ссылку-шорткат:
ln -s gdc-arm/bin/arm-linux-gnueabihf-gdc gdc
Вообще говоря, этого уже достаточно чтобы собирать D код под Raspberry PI. Проверим на примере Hello, world. Итак пишем исходник:
import std.stdio;
void main() {
writeln("Hello, World!");
}
И сохраняем в hello.d. Компилируем:
./gdc hello.d -o hello
Теперь копируем на девайс идём на него и проверяем. У меня это выглядело так:
scp hello pi@192.168.1.85/~/dlang/hello
А на девайсе:
pi@raspberrypi:~/dlang $ ./hello
Hello, World!
Усложняем задачу — линковка библиотек
Всё, описанное выше, тривиально и работает из коробки. Теперь соберём что-то посложнее. Например, «Hello, vibe.d», то есть примитивное приложение на web фреймвёрке vibe.d. Отличие от обычного HelloWorld одно — нужно линковать библиотеки. Кросскомпиляция dlang в этом вопросе ничем не отличается от C, C++ и других компилируемых языков. Поэтому можно использовать любой удобный подход к кросскомпиляции на Raspberry Pi.
Библиотеки нужны для конкретной архитектуры и их лучший источник — репозиторий, установленного на Raspberry дистрибутива. Обычно это https://www.raspbian.org/RaspbianRepository. Проще всего использовать библиотеки прямо с самого устройства. Только не собирать на нём (очень плохая идея, очень медленно и вечно в свопе), а просто использовать файлы. Хорошей идеей будет использовать sshfs (подсмотрено здесь: wiki.dlang.org/GDC/Cross_Compiler/Existing_Sysroot). Основное преимущество данного подхода — абсолютная универсальность и стабильность. Не важно какой дистрибутив установлен, будут взяты идеально подходящие библиотеки. Нет никаких конфликтов и несоответствия версий дистрибутива или библиотек.
Есть небольшие проблемы с подключением. Хотя позже всё же получилось подключить и даже собрать, я получил Illegal Instruction при запуске. Архитектура armhf имеет ещё кучу разновидностей и флагов и надо ещё разобраться и найти те репозитории, которые подойдут Raspberry Pi.
Создаём папку, в которую смонтируем всю файловую систему Raspberry Pi и записываем её в переменную окружения:
mkdir rpi
echo $RPIROOT
export RPIROOT=~/test_pi/rpi/
echo $RPIROOT
/home/user/test_pi/rpi/
Монтируем через sshfs:
sshfs -o idmap=user,follow_symlinks pi@192.168.1.85:/ $RPIROOT
Так как нам нужно указывать архитектуру и пути поиска либ, для сокращения записи создадим ещё один шорткат. Создадим файлик gdc-rpi и запишем туда скрипт для запуска GDC с нужными флагами.
#!/bin/bash
~/test_pi/gdc -march=armv6j -mfpu=vfp -mfloat-abi=hard --sysroot=$RPIROOT -B$RPIROOT/usr/lib/arm-linux-gnueabihf "$@"
Тут немного подробнее: -march=armv6j -mfpu=vfp -mfloat-abi=hard — это флаги архитектуры процессора Raspberry Pi. sysroot — рут девайса, -B — место поиска либ, у нас оно на самом же девайсе в usr/lib
Добавляем прав на запуск и проверяем:
user@ubuntu:~/test_pi$ chmod 777 gdc-rpi
user@ubuntu:~/test_pi$ ./gdc-rpi
gdc: fatal error: no input files
compilation terminated.
Всё работает, теперь нам на билд-машине нужен DUB (это билд-система и за одно менеджер зависимостей). Качаем и ставим его любым удобным способом, описанным здесь.
Создаём простой проект vibe.d:
dub init -tvibe.d test_vibe_pi
DUB создаст папку с минимальным проектом внутри. Теперь собираем:
cd test_vibe_pi/
dub build --compiler=../gdc-rpi
На выходе имеем файлик test_vibe_pi. Если нет чего-то вроде libcurl, то идём на девайс и apt-get’ом ставим всё необходимое. У меня уже всё было после прошлых экспериментов.
Копируем его на девайс и проверяем:
cp test_vibe_pi $RPIROOT/home/pi/dlang
На Raspberry Pi:
pi@raspberrypi:~/dlang $ ./test_vibe_pi
Listening for requests on http://[::1]:8080/
Listening for requests on http://127.0.0.1:8080/
Please open http://127.0.0.1:8080/ in your browser.
Всё, минимальный сайтик, отдающий «Hello, World!» на любой запрос на 127.0.0.1:8080/, готов.
Для тех, кто хочет собирать свои проекты прямо на устройстве
Собирать код на столь слабом устройстве — плохая идея, но иногда удобнее выкатывать проект на девайс в виде исходников. GDC для Raspberry есть готовый, можно идти в раздел ARM gdcproject.org/downloads и качать armhf версию. Для сборки скорее всего понадобится DUB и вот его придётся собирать, потому что готовый бинарь с сайта не запускается на Raspberry Pi B+.
Качаем исходники с сайта https://code.dlang.org/download (можно с гитхаба, это уж как хочется). Распаковываем, складываем в папку с удобным называнием
user@ubuntu:~/test_pi$ wget https://github.com/rejectedsoftware/dub/archive/v0.9.24.tar.gz
user@ubuntu:~/test_pi$ tar -xf v0.9.24.tar.gz
user@ubuntu:~/test_pi$ mv dub-0.9.24/ dub
user@ubuntu:~/test_pi$ cd dub
Для сборки под линуксом с использованием GDC есть отдельный скрипт: build-gdc.sh. Он ожидает, что gdc есть в системе, либо задана переменная окружения GDC. Используем переменную. Просто указываем путь до нашего скрипта-шортката и запускаем:
GDC=../gdc-rpi ./build-gdc.sh
Generating version file...
./build-gdc.sh: 15: ./build-gdc.sh: git: not found
Running ../gdc-rpi...
DUB has been built as bin/dub.
You may want to run
sudo ln -s /home/user/test_pi/dub/bin/dub /usr/local/bin
now.
Если получен этот вывод и никаких ошибок линковки, то теперь есть рабочий DUB для Raspberry Pi. Копируем на девайс и проверяем.
user@ubuntu:~/test_pi/dub$ cd ..
user@ubuntu:~/test_pi$ cp dub/bin/dub rpi/home/pi/dlang/dub-test/dub
На устройстве:
pi@raspberrypi:~/dlang/dub-test $ ./dub
Neither a package description file, nor source/app.d was found in
/home/pi/dlang/dub-test
Please run DUB from the root directory of an existing package, or run
"dub init --help" to get information on creating a new package.
Failed to find a package named ''.
DUB запустился и справедливо заметил отсутствие проекта в папке. Проекта у нас действительно нет, а главная цель достигнута — DUB работает на Raspberry Pi.
Вот и всё, мы можем собирать любые проекты на D под Rapberry Pi. Например, можно запустить сервер для умного дома. Есть поддержка MQTT в виде плагина к vibe.d, а так же возможность использовать любую существующую C библиотеку.