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). Основное преимущество данного подхода – абсолютная универсальность и стабильность. Не важно какой дистрибутив установлен, будут взяты идеально подходящие библиотеки. Нет никаких конфликтов и несоответствия версий дистрибутива или библиотек.
Почему бы не подцепить Raspbian как источник пакетов на самой билд-машине
К сожалению все попытки подключить этот источник и использовать библиотеки не увенчались успехом. После долгих мучений, очень похожих на эти: answers.launchpad.net/ubuntu/+source/build-essential/+question/250970 затея была заброшена.

Почему бы не взять репозиторий ubuntu для arm
Есть небольшие проблемы с подключением. Хотя позже всё же получилось подключить и даже собрать, я получил 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 библиотеку.

Комментарии (12)


  1. BelBES
    09.03.2016 16:20
    +2

    А как там дела с перформансами обстоят на этой железяке "C++ vs. D" ?


    1. bfDeveloper
      09.03.2016 16:32
      +1

      Бенчмарков не ставил, но не вижу причин, почему он должен отличаться от x86. Бэкенд у компиляторов одинаковый, сами языки примерно на одном уровне производительности, так что +- то же самое.


  1. scifix
    11.03.2016 20:57

    а зачем D если есть C?)


    1. sigod
      11.03.2016 21:45
      +1

      А зачем был придуман C++, если есть C?


    1. bfDeveloper
      11.03.2016 21:52

      Холиварный вброс, но всё же отвечу. Затем, что быстрее. Бытрее писать и внезапно быстрее работает.
      Писать быстрее благодаря обилию алгоритмов, шаблонов, нормальной типизации, безопасности (в виде RAII, контрактов, тестов и сборке мусора) и кучи плюшек (http://dlang.org/ctod.html).
      Быстрее работает — немного спорное утверждение. Эквивалентный код чуть медленнее, но на D не пишут как на C. Банальный пример: там, где в C будет массив или копия массива, в D будет ленивый диапазон. По моему опыту замена конкатенации строк на ленивый диапазон даёт прирост скорости 6 раз. Опыт портирования проектов с С или С++ говорит об ускорении результата. Например, в фейсбуке портировали небольшую библиотеку https://code.facebook.com/posts/729709347050548/under-the-hood-building-and-open-sourcing-flint/ D версия работает быстрее.
      Чтобы повторить то, что даёт стандартная библиотека диапазонов в D, понадобится куча кода на C, который никто просто не будет писать. Писать быстрый код на D быстрее. Писать обычный код ещё быстрее.
      Писать супероптимизированный код на D тоже проще, так как ничто не мешает опуститься до уровня C, пользоваться указателями и всеми низкоуровневыми оптимизациями вплоть до встроенного ассемблера. Вот только мощность шаблонов и прочих фич времени выполнения будет помогать даже на низком уровне и всем этим можно пользоваться.
      Ситуаций, в которых D уступает C или C++, очень мало и с каждым днём становится всё меньше.


      1. BelBES
        11.03.2016 23:41

        На D уже есть поддержка CUDA/OpenCL, или только через какие-нибудь биндинги(ну или как это в D называется) C/C++?


        1. bfDeveloper
          12.03.2016 17:12

          Есть динамический биндинг OpenCL. http://code.dlang.org/packages/derelict-cl
          Это просто описанный интрефейс к обычному OpenCL. Как хэдеры в сях. Аналогичная штука есть для CUDA
          http://code.dlang.org/packages/derelict-cuda


  1. scifix
    12.03.2016 07:13

    из статьи просто непонятно чем же лучше D и что в нем такого.helloworld везде можно написать


    1. guai
      15.03.2016 18:28

      «Пишем на D для Raspberry Pi» — разве статья не соответствует заявленному в заголовке? Где там про «лучше»?


      1. scifix
        15.03.2016 18:45

        соответствует, но надо раскрывать тему подробнее, а иначе в чем смысл.Самое главное — не раскрыт смысл слова "Пишем")


        1. bfDeveloper
          15.03.2016 19:35

          Согласен, язык не так популярен на хабре, стоило подробнее описать, зачем вообще писать на D. Восполню пробел парой ссылок на статьи на хабре:
          https://habrahabr.ru/post/246623/
          https://habrahabr.ru/post/224419/
          https://habrahabr.ru/post/197480/
          https://habrahabr.ru/post/154345/


        1. guai
          15.03.2016 20:08

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