DEV Community

Alexey Bolshakov
Alexey Bolshakov

Posted on

FMix: пакетный менеджер для Forth

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

В Elixir есть Mix. В Rust есть Cargo. В Ruby есть Bundler и RubyGems. В Haskell есть Cabal и Stack. В JavaScript есть npm, pnpm и Yarn. В Go есть modules.

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

Но у старых языков программирования часто другая история.

Когда многие из них создавались, такой подход ещё не был стандартом. Код распространялся иначе, проекты были устроены иначе, а экосистемы развивались без привычных сегодня package managers и build tools.

И это до сих пор чувствуется.

Почему я задумался об этом

Для одного из своих проектов я использую Forth.

Forth — очень интересный язык. Минималистичный, прямой, необычный. В нём есть что-то очень привлекательное: ты находишься близко к вычислениям, близко к стеку, близко к самому железу вычислительной машины (которую иногда я делаю сам).

Но когда я начал писать на Forth что-то более-менее похожее на проект, мне быстро стало не хватать привычных вещей:

  • создать новый проект;
  • описать зависимости;
  • скачать зависимости;
  • запускать тесты;
  • иметь понятную структуру проекта;
  • подключать библиотеки из Git;
  • не держать всё вручную в голове.

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

Я нашёл библиотеку f.4th, связанную с theforth.net. Это полезный инструмент, но у него другой подход: библиотеки и версии хранятся на своём сервере.

Мне же хотелось уметь подтягивать зависимости прямо из Git-репозиториев. Например, указать GitHub URL, ветку или тег, и получить библиотеку в проект.

Так появилась идея сделать fmix.

Что такое FMix

fmix — это небольшой build tool и package tool для Forth, вдохновлённый Elixir Mix.

Репозиторий: github.com/VitaSound/fmix

Он не пытается быть большим фреймворком. Его задача проще: дать Forth-проекту минимальный набор привычных инструментов.

Сейчас fmix умеет:

fmix new example
fmix packages.get
fmix test
fmix version
Enter fullscreen mode Exit fullscreen mode

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

Создание проекта

Новый проект создаётся так:

fmix new example
cd example
Enter fullscreen mode Exit fullscreen mode

После этого появляется базовая структура проекта:

example/
  example.4th
  package.4th
  README.md
  tests/
  forth-packages/
Enter fullscreen mode Exit fullscreen mode

package.4th — это файл описания пакета. В нём указывается имя, версия, лицензия, основной файл и зависимости.

Пример:

forth-package
    key-value name example
    key-value version 0.1.0
    key-value license COPL
    key-value description example
    key-value main example.4th

    key-list dependencies f git https://github.com/VitaSound/f tag 0.2.4
    key-list dependencies ttester git https://github.com/VitaSound/ttester tag 1.1.0
end-forth-package
Enter fullscreen mode Exit fullscreen mode

Мне нравится, что сам файл тоже написан в стиле Forth. Это не JSON, не YAML и не TOML, а маленький декларативный Forth-формат.

Зависимости из Git

Главная причина, по которой я начал делать fmix, — это зависимости из Git.

В package.4th можно указать зависимость так:

key-list dependencies ftest git https://github.com/VitaSound/ftest.git branch main
Enter fullscreen mode Exit fullscreen mode

Или так, если нужна конкретная версия через tag:

key-list dependencies ftest git https://github.com/VitaSound/ftest.git tag 0.1.0
Enter fullscreen mode Exit fullscreen mode

После этого достаточно выполнить:

fmix packages.get
Enter fullscreen mode Exit fullscreen mode

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

./forth-packages/
Enter fullscreen mode Exit fullscreen mode

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

forth-packages/
  f/
    0.2.4/
  ttester/
    1.1.0/
Enter fullscreen mode Exit fullscreen mode

Подключать зависимость можно обычным require:

require ./forth-packages/f/0.2.4/f.4th
Enter fullscreen mode Exit fullscreen mode

Поддержка theforth.net

Хотя мне хотелось Git-зависимостей, я не хотел полностью отказываться от существующей экосистемы.

Поэтому fmix также умеет работать с пакетами из theforth.net через f.4th.

Например:

key-list dependencies f 0.2.4
Enter fullscreen mode Exit fullscreen mode

Таким образом, в одном проекте можно использовать и Git-зависимости, и пакеты из theforth.net.

Тесты

Ещё одна базовая вещь, которой мне не хватало, — запуск тестов.

В fmix есть команда:

fmix test
Enter fullscreen mode Exit fullscreen mode

Она запускает тесты из директории tests/.

Можно запустить и конкретный файл:

fmix test tests/some_test.4th
Enter fullscreen mode Exit fullscreen mode

Для тестов используется ttester. Если ttester есть в зависимостях проекта, fmix берёт его из ./forth-packages. Если нет — использует версию из установленного FMIX_HOME.

Для меня это важно: тесты должны запускаться одной командой. Даже если проект маленький.

Что было исправлено в последних версиях

fmix пережил уже несколько поколений изменений.

Первые версии я писал сам. Но я не очень сильный Forth-разработчик, поэтому многие вещи давались тяжело. Одну крупную переработку я сделал с помощью Gemini. Последнюю версию я уже доводил с помощью Cursor.

В последних релизах было исправлено довольно много практических проблем.

Появился GitHub Actions CI: workflow собирает GForth 0.7.9 из исходников и запускает:

gforth fmix.4th -e version
gforth fmix.4th -e test
Enter fullscreen mode Exit fullscreen mode

Позже появился release pipeline: при push version tag автоматически создаётся GitHub Release, а текст релиза берётся из файла .github/RELEASE_NOTES_X.Y.Z.md.

Также были исправлены проблемы с путями. Раньше часть логики была завязана на PWD, теперь проект определяется через текущую директорию запуска. Это важно, потому что FMIX_HOME указывает на установленный fmix, а команды должны работать с текущим проектом.

Были исправлены ошибки при установке зависимостей:

  • git clone теперь проверяет результат;
  • неуспешные shell-команды завершают GForth с exit code 1;
  • перед git clone создаются родительские директории;
  • git fetch и checkout выполняются так, чтобы ошибка не маскировалась;
  • для Git-команд используется GIT_TERMINAL_PROMPT=0, чтобы команда не зависала в non-interactive окружении.

Ещё одна важная вещь — валидация входных данных. fmix проверяет имена пакетов, версии, пути, Git URL и Git refs перед тем, как вызывать cp, sed или git. Это не сложная система escaping, а простой whitelist, но он отсекает пробелы, ;, $, backticks, pipes и другие shell metacharacters.

Неожиданная проблема: терминал

Отдельная история была с терминалом. Перед тем, как пользоваться командой fmix в консоли, ее нужно прописать как alias.

Так вот при запуске через простой alias вроде:

alias fmix='gforth "$FMIX_HOME/fmix.4th" -e'
Enter fullscreen mode Exit fullscreen mode

после выполнения команд терминал иногда оставался в плохом состоянии. На prompt появлялись странные символы вроде:

0c0c
[?2004l
Enter fullscreen mode Exit fullscreen mode

Особенно это проявлялось в WSL.

В версии 0.4.4 я перешёл на launcher script bin/fmix. Он восстанавливает TTY после команд, сбрасывает bracketed paste mode и немного очищает накопившийся ввод из /dev/tty. На самом деле все эти вещи я бы точно не разрешил самостоятельно. Я терпеливо помогал нейросети раз за разом пробовать некоторые гипотезы, чтобы исправить эту проблему. В отличие от тех ИИ, что я пробовал раньше, в этот раз я только один раз решил прервать ИИ и создать новый контекст, потому что она как будто зациклилась в бесконечном исправлении.

Однако, получилось и теперь вместо alias рекомендуется использовать:

export FMIX_HOME="$HOME/fmix"
export PATH="$FMIX_HOME/bin:$PATH"
Enter fullscreen mode Exit fullscreen mode

И запускать просто:

fmix
Enter fullscreen mode Exit fullscreen mode

Установка

Сейчас установка выглядит так:

git clone https://github.com/VitaSound/fmix.git ~/fmix
Enter fullscreen mode Exit fullscreen mode

Затем нужно добавить в shell config (например .bashrc):

export FMIX_HOME="$HOME/fmix"
export PATH="$FMIX_HOME/bin:$PATH"
Enter fullscreen mode Exit fullscreen mode

После этого команда начнет работать прямо в консоли:

fmix version
Enter fullscreen mode Exit fullscreen mode

Важно: сейчас fmix рассчитан на Linux, Git, sed, cp и GForth 0.7.9.

Я не рекомендую использовать GForth более ранних версий и версию из Snap. Snap запускает программы в изолированном окружении, из-за чего текущая директория и пути могут не совпадать с тем, что ожидает shell session. Это ломает команды вроде new и packages.get.

Лучше использовать GForth из apt, локальную сборку или tarball с официальной страницы GForth.

Зачем это всё

Мне кажется, что старым языкам очень нужны такие инструменты, чтобы соответсвовать современным реалиям.

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

Новому человеку проще попробовать язык. Опытному пользователю проще начать новый проект. Библиотеки проще переиспользовать. Тесты проще запускать.

Это не меняет сам язык, но меняет опыт работы с ним. Позволяет взять старый инструмент в новые проекты.

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

Если добавить удобные инструменты, язык может ощущаться совсем иначе.

programming-languages-personified-fmix

Что дальше

fmix пока маленький и экспериментальный.

Сейчас он умеет создавать пакеты, подтягивать зависимости из Git и theforth.net, запускать тесты.

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

Но уже сейчас мне стало гораздо удобнее работать над своими Forth-проектами. Но это уже совсем другая история

Ссылки

Top comments (0)