Cтажировка в Haxe Foundation

Автор оригинала: Aurel Bily
  • Перевод

Представляю вам перевод еще одного доклада с HaxeUp Sessions 2019 Linz, считаю что он хорошо дополняет предыдущий, т.к. продолжает тему изменений в Haxe, произошедших в 2019 году, а также немного рассказывает о его будущем.
Немного об авторе доклада: Аурел Били познакомился с Haxe, участвуя в различных гейм-джемах, и он продолжает в них участвовать (к примеру вот его игра с последнего Ludum Dare 45).


image


В настоящее время Аурел заканчивает обучение в Имперском колледже Лондона, что подразумевает обязательное прохождение стажировок. Первая стажировка, которую он проходил, была в удаленном офисе, дорога до которого занимала много времени. Поэтому он надеялся, что следующую практику удастся пройти удаленно.


image


Так получилось, что в Haxe Foundation долгое время не могли найти сотрудника на должность разработчика компилятора. Аурел решил испытать удачу и отправил письмо с заявкой об удаленной работе. Ему повезло — его приняли на шестимесячную стажировку с возможностью работать из Лондона.


image


При устройстве был оговорен круг задач, которыми будет заниматься Аурел (правда не все в итоге удалось реализовать).


image


Чем же он занимался?


Во-первых, документацией, которая находилась в печальном состоянии: были описаны все изменения в синтаксисе, новые возможности языка и компилятора, дополнены разделы, посвященные строкам, литералам и константам.


image


Вся документация была переведена из LaTeX в Markdown!


image


Во-вторых, форматирование кода стандартной библиотеки было приведено к единому стилю (т.к. над ней на протяжении более 10 лет работали разные люди с разным стилем оформления кода). Таким образом, в репозитории компилятора Haxe Аурел занял седьмое место по количеству добавленных строк кода :)


image


В третьих, Аурел поработал и над стандартной библиотекой и компилятором:
Например, у контейнера Map появился новый метод clear(), который удаляет все хранимые значения. Сделано это в первую очередь для удобства работы с контейнерами, созданными как final переменные (то есть им нельзя присвоить новое значение, но можно модифицировать их):


image


Для объектов типа Date появились методы для работы с датами в формате UTC (универсальное глобальное время). Работа над ними показала, насколько сложно реализовать единое API, одинаково работающее на всех 11 поддерживаемых в Haxe языках / платформах.


image


В старом компиляторе define’ы и мета-тэги были определены на OCaml, теперь же они описываются в формате JSON, что должно упростить их парсинг внешними утилитами (например, для автоматической генерации документации):


image


Также вы могли заметить, что на крупных проектах сервер компиляции начинает использовать много памяти.


image


Для решения этой проблемы Саймон Краевский и Аурел разработали бинарный формат hxb, который используется для сериализации типизированного AST. Теперь сервер компиляции может загрузить модуль в память, работать с ним до тех пор пока он нужен, а затем выгрузить его из памяти в файл в формате hxb и освободить занимаемую память.


image


Спецификация формата hxb доступна в отдельном репозитории, а текущая его реализация в компиляторе (с сериализатором / десериализатором) лежит в отдельной ветке Haxe. Работа над данной фичей пока что не закончена, и возможно она появится в Haxe 4.1.


image


Четвертым и основным направлением работы Аурела в ходе стажировки было создание нового асинхронного системного API — asys.


image


Потребность в его создании обусловлена тем, что существующее API не предоставляет легких способов выполнения системных операций асинхронно. Например, для работы с файлами асинхронно вам придется создать отдельный поток, в котором будут выполняться требуемые операции, и вручную управлять его состоянием. Кроме того, в текущем API не доступны все функциональные возможности для работы с UDP-сокетами, которые есть в стандартных библиотеках в других языках, нет поддержки IPC-сокетов.


image


При создании и реализации нового API возникает множество вопросов:


Как проектировать API? Может быть стоит взять в качестве примера какое-либо существующее? Ведь мы же не хотим создавать все с нуля, т.к. это потребует больше времени, а также может не прийтись по вкусу остальной команде и стать причиной долгих споров.


И, как уже упоминалось, актуальной для Haxe проблемой является реализация единого API для всех поддерживаемых платформ.


image


В качестве образца было выбрано API Node.js. Оно хорошо продумано, поддерживает необходимые системные функции и хорошо подходит для создания серверных приложений.


image


Но в то же время API Node.js — это Javascript API с отсутствием строгой типизации. Например, функции из модуля fs для работы с файловой системой могут принимать в качестве путей как строки, так и объекты типа Buffer и даже URL. А это уже не так хорошо подходит для Haxe.


image


Node.js в свою очередь использует библиотеку libuv, написанную на C. Работать с API libuv из Haxe напрямую было бы уже не так удобно: например, для того, чтобы асинхронно переименовать файл, вам потребовалось бы дополнительно создать объекты типа uv_loop_t (структура для управления циклом событий в libuv) и uv_fs_t (структура для описания запроса к файловой системе):


image


В итоге API Node.js и libuv были интегрированы следующим образом (на примере интерпретатора макросов eval и метода rename):


  • взяли АPI метода из Node.js, преобразовали его в Haxe, стараясь при этом стандартизировать типы аргументов и избавиться от избыточных для Haxe аргументов. Например, аргументы-пути (тип FilePath) — это абстракты над строками:

image


  • затем создали OCaml-биндинги для этого метода:

image


  • связали OCaml и C (с помощью CFFI — C Foreign Function Interface):

image


  • и, наконец, написали С-биндинги для вызова C-функций libuv из OCaml:

image


Аналогично было сделано для HashLink и Neko (пока что asys API реализован только для этих трех платформ). Как можно предположить, это потребовало много работы.


Аурел показал несколько небольших приложений, демонстрирующих работу asys API.
Первый пример — демонстрация асинхронного чтения содержимого файла. Пока что в коде явно вызываются методы для инициализации libuv (hl.Uv.init()) и запуска цикла работы приложения (hl.Uv.run()), связано это с тем, что работа над API еще не завершена (но в дальнейшем они будут добавляться автоматически):


image


Результат работы показанного кода:


image


Мы видим, что результаты работы вызванных методов AsyncFileSystem.readFile() выводятся в консоль после trace’а “after call”, который в коде вызывается после попыток прочитать содержимое файлов.


Второй пример — демонстрация асинхронной работы с DNS и IP-адресами.


image


В новом API определить имя хоста станет гораздо проще, а также появятся вспомогательные методы для работы с IP-адресами.


image


Третий пример — это простой TCP эхо-сервер, для создания которого достаточно всего трех строчек кода:


image


Четвертый пример — это демонстрация обмена информацией между процессами:
статический метод makeFrame() в рассматриваемом примере создает отдельные png-изображения:


image


а в методе main мы запускаем процесс ffmpeg, в который будем передавать сгенерированные в makeFrame() кадры:


image


и на выходе получим видео-файл:


image


И пятый пример — UDP video stream. Здесь как и в предыдущем примере запускается процесс ffmpeg, но в этот раз он воспроизводит видео и выводит его данные в стандартный поток вывода. Также создается UDP-сокет, который будет транслировать данные из процесса ffmpeg.


image


И, наконец, разбиваем данные, получаемые из ffmpeg, на меньшие “порции” и транслируем их на указанный порт:


image


И в результате получаем работающий видеопоток:


image


Обобщая вышесказанное, новое asys API включает в себя:


  • методы для работы с файловой системой, включая новые функции, которых не было в стандартной библиотеке (например, для изменения разрешений), а также асинхронные версии всех функций, доступных в старой стандартной библиотеке
  • поддержку асинхронной работы с TCP/UDP/IPC сокетами
  • методы для работы с DNS (пока что 2 метода: lookup и reverse)
  • а также методы для асинхронной работы с процессами.

image


Работа над asys API пока что не завершена, в настоящее время есть некоторые проблемы со сборщиком мусора при работе с библиотекой libuv. Pull Request с соответствующими изменениями еще не вмержен в основную ветку Haxe, в комментариях к нему приветствуются мнения по поводу имен новых методов, их сигнатур, а также документации.
Как уже упоминалось поддержка asys API реализована только для HashLink, Eval и Neko (в виде трех отдельных Pull Request’ов). У Аурела уже сформирован план, как добавить поддержку нового API для C++ и Lua. Реализация для остальных платформ потребует дополнительных исследований.


image


Возможно, что asys API станет доступно в Haxe 4.1 (но только для некоторых платформ).


Также Аурел рассказал о своем стороннем проекте — библиотеке ammer (который все же связан с его работой в Haxe Foundation).


image


Цель ammer — автоматизировать создание биндингов для библиотек на C так, чтобы их можно было использовать и в HashLink и HXCPP (в октябре 2018 года Ларс Дусе назначил вознаграждение за решение этой задачи).


Почему эта задача была актуальной? Дело в том, что хотя процесс создания биндингов для HashLink и HXCPP аналогичен, для каждой платформы придется писать свой связующий код (glue code).


Примерно то же самое Аурел делал, когда интегрировал библиотеку libuv в Haxe — для Eval, Neko и HashLink ему пришлось писать один и тот же код, который отличался только в деталях (вызов функций, отличия в работе FFI и др.):


image


Аналогичную работу требовалось проделать и на стороне Haxe для того, чтобы из него можно было вызывать нативные функции:


image


И идея ammer в том, чтобы взять Haxe-версию API, не загроможденную избыточной информацией, и сделать так, чтобы этот код каким-то образом заработал для всех платформ:


image


Что теперь требуется от пользователя ammer для работы с внешними библиотеками на C:


  • создаете Haxe-спецификацию для библиотеки, которая по существу является экстерном для используемой библиотеки
  • пишете код приложения
  • компилируете проект, указав пути к заголовочным файлам и файлам C-библиотеки
  • ...
  • profit

image


Под капотом ammer выполняет при этом следующую работу:


  • сопоставляет типы в зависимости от целевой платформы
  • автоматически генерирует C-код для вызова нативных функций
  • генерирует makefile, который используется для создания hdll, ndll файлов

image


В настоящий момент в ammer поддерживаются:


  • простые функции
  • define’ы из заголовочных файлов (в Haxe-коде к ним можно обращаться как к константам)
  • указатели

Планируется поддержка:


  • коллбеков (их пока что очень не хватает)
  • и структур (очень нужны для работы с C-API)

image


Сейчас ammer работает с C++, HashLink и Eval. И Аурел уверен, что сможет добавить поддержку остальных системных платформ.


image


Для демонстрации возможностей ammer Аурел показал небольшое приложение, в котором выполняется интерпретатор Lua:


image


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


image


Как видно некоторые методы закомментированы, т.к. в них используются коллбеки, поддержка которых еще на реализована, но Аурел надеется, что скоро сможет исправить это.


image


Итак, для чего может применяться ammer:


  • встраивание виртуальной машины Lua
  • создание приложений на SDL
  • возможна автоматизация работы с libuv (как было показано ранее, сейчас для работы с libuv требуется много кода, написанного вручную)
  • и, конечно же, значительно упростится использование множества других полезных библиотек на C (таких как OpenAL, Dear-imgui и др.)

image


И хотя стажировка Ауреля в Haxe Foundation закончилась, он планирует продолжать работу с Haxe, т.к. его обучение в колледже еще не завершено и ему предстоит еще написать выпускную работу. Аурел уже знает чему она будет посвящена — улучшение работы сборщика мусора в HashLink. Что же, это будет интересно!

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

    0
    Во первых, я удивлен что HAXE не имеем механизма interop на всех его платформах. Т.е. пишешь C binding на HAXE и он трнслируется на interop механизмы таргет платформы.

    Во вторых то, что сделал этот стажер (кроме авто-форматинга кода) влили? По тому что я иногда читаю про эти буткампы/стажировки, где стажеры делают прототипы сладких фич. А заглядывая в эти проекты через года видно, что никто и не мержил/доделывал эти фичи. Завершенная фича — это не основной смысл работы погроммиста?
      0
      По первому пункту соглашусь, что interop для нативных таргетов (С++) пока что приходится писать вручную (но, например, для Java-таргета это не так — достаточно скормить компилятору бинарник библиотеки и он сразу «поймет» как работать с ним). Связано это с тем, что команда Haxe Foundation очень небольшая, при этом на full-time работает всего два человека (и то только в этом году нашли второго программиста), и в приоритете были другие задачи. И тот факт, что в Haxe Foundation взяли стажера, для меня хороший знак (он кстати упомянул, что будет и дальше работать с Haxe).
      По второму пункту: Да, в основную ветку на данный момент смержены только изменения, касающиеся документации и форматирования кода, но я верю, что новое асинхронное API будет доделано и появится в следующих релизах (4.1?), тем более, что о нем было официально заявлено (ранее такие фичи в Haxe доводились до реализации).
      Давайте поступим так, как вы предложили — посмотрим через год на прогресс по ним.
        0
        Давайте поступим так, как вы предложили — посмотрим через год на прогресс по ним.

        Я так за HAXe уже три года смотрю. Либо он загнется и не надо будет его поддерживать, либо он станет стандартом в геймдеве и надо срочно впиливать его поддержку.

        Самое забавное, что его используют компании с миллионными оборотами в $, и похоже не особо донатят в фонд т.к. как вы говорите там два землекопа и стажер.
          0
          Если говорить про геймдев, то крупные компании, использующие Haxe, больше интересуются HTML5-играми на нем (Inno Games, FlowPlay, Playtika), куда они, соответственно, и вкладывают средства.
          Нативные игры на Haxe в основном разрабатывают небольшие инди.
          А насчет того, загнется он или нет, вам придется еще долго смотреть — Haxe уже больше десяти лет и интерес к нему хотя и небольшой, но он постепенно растет (говорю как человек, который следит за Haxe лет 7)
            +1
            Либо он загнется и не надо будет его поддерживать, либо он станет стандартом в геймдеве и надо срочно впиливать его поддержку.
            Я бы оценил его шансы не загнуться в обозримом будущем достаточно высоко: инструмент изначально разрабатывался «для себя», и активно используется рядом весьма успешных инди-студий (MotionTwin, Shiro, Proletariat)
            А куда предполагается впиливать его поддержку? Он сам кого хочешь поддержит: есть кейсы успешной интеграции с UnrealEngine, Unity, SDL, Node.js, массы браузерных библиотек типа pixi.js и т.д.; поддержка во многих IDE.

            Самое забавное, что его используют компании с миллионными оборотами в $, и похоже не особо донатят в фонд т.к. как вы говорите там два землекопа и стажер.
            Тут все на удивление прозрачно: на их сайте есть «тарифные планы спонсорства» и список компаний спонсоров (в том числе, уже упомянутых в треде).
            К тому же, несмотря на небольшую команду, работа над компилятором ведется весьма активно (гитхаб, все видно). Далеко не факт, что есть потребность раздувании команды. Ну и вообще, «писать компилятор на OCaml» – довольно нишевая позиция, на которую вряд ли есть куча соискателей.

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое