Создание пакета для Arch Linux
Есть замечательный инструмент для форматирования кода на скале ― scalafmt. Гибкий конфиг, интеграция с IDE, возможность добавить форматирование кода в пайплайны CI… Что ещё нужно пользователю? Нормальному пользователю, наверное, ничего. А вот мне захотелось программировать на скале в vim и форматировать текущий файл по нажатию шортката.
Интеграция форматирования кода в vim делается примерно одинаково для всех языков: плагин дёргает скрипт форматировщика и записывает результат в текущий буфер. Но тут-то и возникает нюанс. scalafmt написана на скале и собирается под JVM ― виртуальной машиной, самым известным недостатком которой является долгий запуск и прогрев. Как можно догадаться, попытки запускать scalafmt из консоли вызывают только раздражение пользователя:
./scalafmt --help  2,74s user 0,18s system 169% cpu 1,719 totalБольше двух секунд на отображение помощи! Само собой, функция, которая выводит на экран подсказки, отработала мгновенно. Всё это время занял запуск виртуальной машины.
Но как же это работает в IDE?
Intellij IDEA написана на джаве и исполняется в уже запущенной JVM. Поэтому при форматировании кода в IDE не надо запускать её по новой ― просто вызывается нужная функция. Аналогичным образом работает интеграция с sbt.
Зачем вообще это использовать, если оно медленно?
Не всем программам обязательно уметь быстро запускаться. Например, для серверных приложений скорость штатной работы важнее времени перезапуска. А со скоростью у прогретой JVM всё в порядке.
Заставляем Scala запускаться быстро
Есть две возможности обеспечить быстрый запуск для программ на скале:
Scala Native ― это компилятор скалы, генерирующий представление для LLVM, которое, в свою очередь, компилируется в бинарник. Проект пока ещё сыроват, но я возлагаю огромные надежды на его будущее. Благодаря ему у скалы есть все шансы перестать быть «языком для бэкенда» и занять ниши низкоуровневых языков, например, в IoT. К сожалению, сейчас далеко не все библиотеки поддерживают компиляцию с помощью Scala Native, хотя это постепенно становится моветоном. На момент написания заметки часть зависимостей scalafmt не поддерживали нативную компиляцию, поэтому пока пришлось отложить этот вариант.
И тут на помощь приходит GraalVM ― относительно молодая виртуальная машина, которая может исполнять программы, написанные на разных языках, и, самое главное, умеет собирать бинарники. Native Image в Граале представляет из себя один исполняемый файл, содержащий в себе минималистичную виртуальную машину Substrate VM и саму скомпилированную программу. Осталось только установить GraalVM и правильным образом собрать scalafmt из исходников:
git clone https://github.com/scalameta/scalafmt
cd scalafmt
git checkout v2.0.0-RC4 # последняя версия на данный момент
sbt cli/assembly
native-image -jar scalafmt-cli/target/scala-2.12/scalafmt.jarТеперь у нас есть CLI-утилита с нормальной скоростью запуска:
./scalafmt --help  0,01s user 0,01s system 96% cpu 0,016 totalДелаем КРАСИВО
Собрал сам ― поделись с другими. У меня на ноутбуке стоит Arch, поэтому пакет будем делать для него. На самом деле, я достаточно долго откладывал этот процесс, предвкушая типичные программистские сложности. Но оказалось, что это максимально лёгкая и доступная процедура, если хочется просто запаковать один бинарник.
Для начала надо создать пустую директорию и добавить туда файл PKGBUILD со следующим содержимым (комментариев, начинающихся с [help] в итоговом файле быть не должно!):
# Maintainer: Mikhail Chugunkov <poslegm@gmail.com> 
pkgname=scalafmt-native # [help] название пакета, по которому пользователи будут его находить в репозитории и устанавливать на свои машины  
pkgver=2.0.0.RC4 # [help] версия ПО, которое поставляется в этом пакете 
pkgrel=1 # [help] версия самого пакета; её нужно увеличивать, например, при исправлении ошибки в этом файле
pkgdesc='Code formatter for Scala built with GraalVM (for fast startup)' # [help] лаконичное описание пакета
arch=('x86_64') # [help] архитектуры, на которые этот пакет может быть установлен
url='https://scalameta.org/scalafmt/' # [help] ссылка на сайт ПО, которое поставляется в этом пакете
license=('Apache')
source=("https://chugunkov.dev/files/scalafmt-native-2.0.0.RC4.tar.gz") # [help] ссылка на tar.gz архив с содержимым пакета, который скачается и распакуется при установке
sha256sums=('deefaa75b5363872f1f8da5d2a881db3a8cb05df6692989b450963b79a7b6efd') # [help] хеш-сумма архива
# [help] действия, выполняющиеся при установке
package() {
  cd "$pkgname-$pkgver"
  mkdir -p $pkgdir/usr/bin
  install -D -m755 scalafmt "${pkgdir}/usr/bin/scalafmt"
}Когда пользователь выполнит в терминале команду yaourt -S scalafmt-native, пакетный менеджер создаст окружение fakeroot, загрузит и распакует то, что указано в source, сверит хеш-суммы и выполнит действия из package(). Сейчас там просто нужный файл копируется в /usr/bin и делается исполняемым.
В принципе, можно прямо тут описать сборку свежей версии программы из исходников, но я решил так не делать, чтобы не заставлять пользователя выкачивать 600мб GraalVM и ждать, пока пройдёт компиляция.
Вместо этого я создал на своей VPS директорию scalafmt-native-2.0.0.RC4, положил туда заранее скомпилированный бинарник, заархивировал и пробросил наружу через nginx, а получившуюся ссылку скопировал в source. Когда выйдет новая версия scalafmt, я повторю эту процедуру и обновлю ссылку вместе с версией пакета.
Теперь осталось зарегистрироваться на AUR, указав там свой публичный ключ ssh, и выполнить несколько команд:
makepkg --printsrcinfo > .SRCINFO
git init
git add PKGBUILD .SRCINFO # остальные файлы можно кинуть в .gitignore
git commit -a
git remote add aur ssh://aur@aur.archlinux.org/scalafmt-native.git
git fetch aur
git push --set-upstream aur masterЧерез несколько секунд пакет становится доступным для установки! На сайте scalafmt теперь красуется инструкция по установке для арча. А чтобы держать пакет в актуальном состоянии, я подписался на релизы через гитхаб: при выходе новой версии утилиты мне придёт уведомление на почту.
