В dune, системе сборки OCaml, есть много интересного. Вероятно, она может делать то, что вы хотите, но документация — хотя и подробная — никогда не утруждает себя объяснением того, как выполнять общие задачи так, чтобы это было полезно занятому разработчику.
Это «как» запустить скрипт с помощью dune. В конце вы сможете инициировать выполнение произвольной программы с помощью dune для выполнения полезной задачи для вашей базы кода. Подумайте о "scripts"
в package.json
, или о цели .PHONY
в Makefile
. Например, вы можете запустить CLI для генерации кода, например tailwindcss, или отправить артефакты сборки в S3 с помощью aws
CLI-вызова. Мы будем называть такие вещи «скриптами», как и в проекте node/JavaScript.
Предположения
- Вы знаете, как работать с терминалом и основными командами оболочки POSIX
- У вас есть рабочая установка dune версии 3:
$ dune --version
3.4.1
С нуля
Если у вас уже есть проект dune, найдите файл dune
, в котором вы хотите определить команду, измените директорию на эту папку и перейдите к дальнейшим действиям.
Создание файла dune
Сценарии определяются в файле с именем dune
в корне вашего проекта (или библиотеки, или исполняемого файла). В Dune есть набор команд init
, помогающих создать этот файл, но они создают много лишнего шума, поэтому давайте сделаем минимальную настройку вручную:
$ mkdir run_task && cd run_task
$ touch dune
$ echo "(lang dune 3.3)" > dune-project
На этом этапе вы должны быть в состоянии запустить:
$ dune build
При этом не возникнет ошибки, а появится новая папка _build
, что подтверждает, что мы готовы к работе:
.
├── _build
│ ├── default
│ └── log
├── dune
└── dune-project
Настройка Dune с помощью S-Expressions
Давайте быстро рассмотрим одну из приведенных выше команд:
$ echo "(lang dune 3.3)" > dune-project
Возможно, вам интересно, что означает lang dune 3.3
и почему оно заключено в круглые скобки. Это называется S-выражением, и, подобно JSON
, YAML
или TOML
, является языком, который можно использовать, помимо прочего, для файлов конфигурации. В отличие от последних форматов, S-выражения не часто встречаются за пределами OCaml, но они являются обычным способом сериализации данных в OCaml, подобно тому, как JSON
является обычным способом сериализации данных в JavaScript.
Возможно, вы сочтете это странным выбором для инструмента программирования и языка, который надеется привлечь новых пользователей (я считаю именно так). Но независимо от этого, именно так мы будем «программировать» dune
, чтобы она делала то, что мы хотим.
К счастью, вам не нужно много знать о s-выражениях, чтобы сделать то, что нам нужно. Пока что вы можете думать о них как об именованных вложенных массивах:
(key value value (subkey value))
Вот воображаемый пример:
(package (name my-package)(version 1.0))
(dependencies lib-1 lib-2 lib-3 (testing-only tool-a tool-b)(development-only lib-a))
Хитрость заключается в том, чтобы знать, какие key
являются значимыми для dune
, и как dune
реагирует на различные значения этих ключей, подобно тому, как в проекте npm
вам нужно знать, что ключ package.json
"files"
требует массив шаблонов файлов в качестве значения.
Их может быть трудно читать без добавления белого пространства, поэтому я предлагаю добавить любое белое пространство, чтобы вам было проще. Приведенный ниже пример эквивалентен приведенному выше, но его немного легче читать благодаря стратегическому пробелу:
(package
(name my-package)
(version 1.0))
(dependencies
lib-1
lib-2
lib-3
(testing-only tool-a tool-b)
(development-only lib-a))
Добавление сценария: Правила
Вы определяете сценарий в проекте дюны как правило.
Простое правило
Пока что скопируйте это базовое правило «hello world» в свой файл dune
и сохраните его:
(rule
(alias helloworld)
(deps (universe))
(action (run echo "Hello World!")))
Если у вас уже есть записи в файле dune, добавьте это правило ниже всех остальных, без вложенности.
Теперь вы должны иметь возможность запускать:
$ dune build @helloworld
Hello World!
Потрясающе! Мы прошли 90% пути для вызова простых скриптов.
Понимание простых правил
Псевдонимы
У каждого правила может быть псевдоним, который вы вызываете командой build
, добавляя к нему префикс @
.
Обратите внимание, что в dune
можно назначать псевдонимы многим вещам, например, набору зависимостей. Будьте осторожны, когда ссылаетесь на документацию!
Действие
Действие — это команда, которую dune
выполняет для удовлетворения правила. Основной синтаксис показан выше, но существует внутренний язык, который может быть довольно сложным. И вы всегда можете использовать простой синтаксис для вызова небольшой программы для выполнения любой более сложной задачи сборки! Например, если вам очень нравятся S-выражения, вы можете написать несколько сумасшедших shell-скриптов с помощью библиотеки shexp.
Другие распространенные действия:
Зависимости
В отличие от скриптов в экосистеме npm
, rules
в dune
имеют зависимости, которые отслеживаются dune
, чтобы избежать лишней работы.
Чтобы проиллюстрировать это, удалите эту строку из примера «hello world», приведенного выше:
(rule
(alias helloworld)
- (deps (universe))
(action (run echo "Hello World!")))
И попробуйте запустить dune build @helloworld
дважды. Вы заметите, что она работает так, как вы ожидаете, один раз, а затем перестает работать:
$ dune build @helloworld
Hello World!
$ dune build @helloworld
$ # Huh? No echo?
Это потому, что без определения deps
как universe
, dune
знает, что с момента последнего запуска команды @helloworld
build… ничего не изменилось! И поэтому dune
не делает то, что считает ненужной работой, и не запускает команду, которую мы определили во второй раз.
Это особенно эффективно для таких задач, как генерация кода, где, если ни один из входных файлов не изменился, мы можем не тратить время на создание артефактов сборки.
Вы можете ознакомиться с полной спецификацией определения зависимостей, чтобы поэкспериментировать, но пока знайте, что (deps (universe))
будет действовать аналогично скрипту npm
или цели .PHONY
и выполняться каждый раз, когда его вызывают.
Слон в комнате: Цели
Мы еще не рассмотрели одну важную концепцию: (target ...)
настройки правила. Цели — это любые файлы, которые вы создаете с помощью правила, и они позволяют вам делать более сложные вещи с dune
. Хорошей новостью является то, что если вы не хотите или не нуждаетесь в более продвинутых возможностях dune, вы можете игнорировать эту часть конфигурации.
Например, приведенная выше команда — это всего лишь эфемерный побочный эффект, поэтому в ней нет смысла. Однако обратите внимание, что dune
на самом деле определяет ваши цели для определенных действий, поэтому последствия настройки цели могут проявиться, даже если вы не зададите ее в явном виде. В большинстве случаев все будет «просто работать». Но если вы углубитесь в создание собственных правил и увидите странное поведение или ошибки при упоминании целей, я бы рекомендовал прочитать о них.
Резюме
- Запуск произвольных задач в ваших проектах («скриптов»), называется
dune
«правилом». - Правила записываются в файл
dune
в виде S-выражений. - Правила имеют имена, называемые «псевдонимами».
- Правила имеют зависимости (и не будут выполняться, если зависимости не изменились с момента последнего вызова правила).
- Правила имеют действия, которые определяют команду, выполняемую для выполнения правила.
- Правила можно вызвать, выполнив команду
dune build @
с последующим псевдонимом, напримерdune build @tailwind
.