Для выполнения основных задач обычно достаточно команд zig build-exe, zig build-lib, zig build-obj и zig test. Тем не менее, в некоторых случаях проекту может понадобиться дополнительный уровень абстракции для более эффективного управления сложностью процесса сборки.
Система сборки Zig может значительно упростить процесс разработки и сборки в следующих ситуациях:
Если команды в терминале становятся слишком громоздкими и сложными для запоминания, использование системы сборки позволит сохранить их в одном месте.
При наличии множества компонентов или этапов сборки Zig поможет организовать процесс, разбив его на более управляемые части.
Если вы хотите сократить время сборки, система сборки Zig поддерживает конкурентность и кэширование, что значительно ускоряет процесс.
Zig позволяет легко задавать параметры конфигурации для вашего проекта, что делает его более гибким и адаптируемым к различным условиям.
Если процесс сборки варьируется в зависимости от целевой системы или других параметров, Zig поможет вам настроить его под конкретные нужды.
При наличии зависимостей от других проектов система сборки Zig упростит управление ими и обеспечит правильную интеграцию.
Если вы стремитесь уменьшить зависимость от таких инструментов, как CMake, Make, Shell, MSVC или Python, Zig поможет сделать ваш проект более универсальным и доступным.
Если вы хотите упростить процесс создания пакетов для использования другими разработчиками, система сборки Zig обеспечит удобные механизмы для этого.
С помощью Zig вы сможете предложить стандартизированный подход к сборке проекта, что облегчит интеграцию с различными IDE и инструментами разработки.
Если хотя бы один из перечисленных пунктов имеет отношение к вашему проекту, применение системы сборки Zig может существенно облегчить процесс разработки.
Начало работы
Простое исполняемое приложение
Этот скрипт сборки создаёт исполняемый файл из Zig-файла, который содержит определение публичной функции main.
Установка артефактов сборки
Система сборки Zig, подобно большинству других систем сборки, моделирует проект как направленный ациклический граф (DAG), где этапы выполняются независимо и параллельно.
По умолчанию основным этапом в графе является этап Установки (Install), который предназначен для копирования артефактов сборки в их конечное местоположение. Этот этап не имеет зависимостей, поэтому при выполнении команды zig build ничего не произойдёт. Скрипт сборки проекта должен дополнительно указать, какие элементы необходимо установить, и именно это делает вызов функции installArtifact, представленный выше.
В этом выводе можно увидеть две сгенерированные директории: .zig-cache и zig-out. Первая из них содержит файлы, которые ускоряют последующие сборки. Эти файлы не предназначены для добавления в систему контроля версий, и данную директорию можно удалить в любое время без каких-либо негативных последствий.
Вторая директория, zig-out, является целевой директорией установки. Она соответствует стандартной структуре файловой системы. Выбор этой директории осуществляется не проектом, а пользователем команды zig build с помощью флага --prefix (сокращенно -p).
Вы, как разработчик проекта, определяете, что будет помещено в эту директорию, но пользователь решает, куда установить её на своём компьютере. Скрипт сборки не должен жёстко задавать пути вывода, так как это может нарушить кэширование, конкурентное выполнение и совместимость, а также вызвать недовольство у конечного пользователя.
Этап запуска приложения
Обычно этап Запуска (Run) добавляют, чтобы упростить процесс запуска основного приложения непосредственно через команду сборки.
Основы
Опции, задаваемые пользователем
Можно использовать b.option, чтобы сделать скрипт сборки настраиваемым для конечных пользователей и других проектов, которые используют данный проект в качестве пакета.
Обратите внимание на эти строки:
Project-Specific Options:
-Dwindows=[bool] Target Microsoft Windows
Эта секция меню помощи создаётся автоматически в результате выполнения build.zig, чтобы пользователи могли ознакомиться с параметрами конфигурации скрипта сборки.
Стандартные параметры конфигурации
Ранее мы применяли булев флаг для обозначения сборки под Windows. Однако, существует более эффективный подход.
Большинство проектов стремятся предоставить возможность изменять целевую платформу и параметры оптимизации. Для стандартизации названий этих параметров Zig предлагает вспомогательные функции standardTargetOptions и standardOptimizeOption.
Стандартные параметры для выбора целевой платформы позволяют пользователю, выполняющему команду zig build, указать, для какой платформы будет осуществляться сборка проекта. По умолчанию поддерживаются все платформы, и если выбор не сделан, проект будет собран для текущей системы. Существуют дополнительные опции для ограничения списка поддерживаемых платформ.
Параметры оптимизации по умолчанию позволяют пользователю выбрать один из следующих режимов: Debug, ReleaseSafe, ReleaseFast и ReleaseSmall. Поскольку ни один из этих режимов по умолчанию не является предпочтительным, пользователю следует самостоятельно решить, какой из них использовать.
Теперь меню --help содержит больше пунктов:
Project-Specific Options:
-Dtarget=[string] The CPU architecture, OS, and ABI to build for
-Dcpu=[string] Target CPU features to add or subtract
-Doptimize=[enum] Prioritize performance, safety, or binary size (-O flag)
Supported Values:
Debug
ReleaseSafe
ReleaseFast
ReleaseSmall
Хотя эти параметры можно задать напрямую с помощью b.option, данный API предоставляет стандартные наименования для часто используемых настроек.
Обратите внимание на вывод в терминале, где мы указали -Dtarget=x86_64-windows -Doptimize=ReleaseSmall. В отличие от первого примера, теперь в целевой директории появляются дополнительные файлы:
zig-out/
└── bin
└── hello.exe
Условная компиляция
Для передачи параметров из скрипта сборки в Zig-код вашего проекта используйте Options.
В этом примере данные, предоставленные через @import("config"), известны на этапе компиляции, что предотвращает срабатывание @compileError. Если бы мы передали -Dversion=0.2.3 или пропустили эту опцию, то компиляция app.zig завершилась бы ошибкой "too old".
Статические библиотеки
Этот скрипт сборки создаёт статическую библиотеку из кода на Zig, а затем и исполняемый файл из другого кода на Zig, который использует эту библиотеку.
В этом случае только статическая библиотека будет установлена:
zig-out/
└── lib
└── libfizzbuzz.a
Однако, если присмотреться, можно заметить, что в скрипте сборки есть опция для установки демо-версии. Если дополнительно указать -Denable-demo, то в целевой директории мы увидим следующее:
zig-out/
├── bin
│ └── demo
└── lib
└── libfizzbuzz.a
Заметьте, что хотя addExecutable вызывается безусловно, система сборки не будет создавать исполняемый файл demo, если только его не запросить с помощью параметра -Denable-demo. Это возможно благодаря тому, что система сборки использует ориентированный ациклический граф (DAG), который управляет зависимостями.
Динамические библиотеки
Здесь мы оставляем все файлы такими же, как в примере со статической библиотекой, за исключением изменений в файле build.zig.
Как и в примере со статической библиотекой, чтобы создать исполняемый файл, который будет ссылаться на неё, можем использовать такой код:
exe.linkLibrary(libfizzbuzz);
Тестирование
Отдельные файлы можно протестировать напрямую с помощью команды zig test foo.zig.
Для более сложных случаев тестирование можно организовать с помощью скрипта сборки. В этом случае юнит-тесты разделяются на два этапа: Компиляции (Compile) и Запуска (Run).
Важно помнить, что если не вызвать addRunArtifact, устанавливающий зависимость между этими двумя этапами, юнит-тесты не будут выполнены.
Этап Компиляции (Compile) настраивается аналогично исполняемым файлам, библиотекам или объектным файлам. Например, его можно связывать с системными библиотеками или задавать параметры целевой платформы.
Этап Запуска (Run) настраивается аналогично другим этапам Запуска (Run). Например, можно пропустить выполнение, если текущая платформа не поддерживает запуск исполняемого файла.
При использовании системы сборки для запуска юнит-тестов сборщик и тестовый раннер взаимодействуют через stdin и stdout. Это позволяет одновременно запускать несколько наборов юнит-тестов и сообщать о сбоях в тестах понятным способом, избегая смешивания их вывода.
Существует два основных способа управления зависимостями:
Использование системы сборки Zig. Этот метод позволяет предоставлять библиотеки через встроенные механизмы сборки Zig. Подробности можно найти в разделах Управление пакетами и Статические библиотеки.
Использование системных библиотек. В этом случае библиотеки предоставляются операционной системой.
Для upstream-проектов использование системы сборки Zig для управления зависимостями снижает уровень сложности и предоставляет полный контроль над конфигурацией. Этот подход обеспечивает воспроизводимость и согласованность результатов сборки на всех операционных системах, а также упрощает процесс кросс-компиляции. Кроме того, он даёт возможность точно выбирать версии всех зависимостей, необходимых для проекта. В большинстве случаев данный метод считается предпочтительным для работы с внешними библиотеками.
Тем не менее, если требуется упаковать программное обеспечение для репозиториев, таких как Debian, Homebrew или Nix, использование системных библиотек становится обязательным. Поэтому в скриптах сборки важно определить режим сборки и соответствующим образом настроить проект.
Пользователи команды zig build могут воспользоваться опцией --search-prefix, чтобы указать дополнительные директории, которые будут рассматриваться как системные для поиска статических и динамических библиотек.
Генерация файлов
Запуск системных инструментов
Эта версия примера с "Hello, World!" ожидает, что в той же папке будет находиться файл word.txt. Мы собираемся использовать системный инструмент для его генерации на основе JSON-файла.
Учтите, что системные зависимости могут усложнить сборку вашего проекта. Например, этот скрипт сборки зависит от программы jq, которая по умолчанию отсутствует в большинстве дистрибутивов Linux и может быть незнакома пользователям Windows.
В следующем разделе мы заменим утилиту jq на встроенный инструмент Zig, который уже присутствует в исходном коде. Этот подход предпочтителен, так как он устраняет необходимость в сторонних зависимостях.
words.json
{
"en": "world",
"it": "mondo",
"ja": "世界"
}
Вывод
zig-out
├── hello
└── word.txt
Обратите внимание, что функция captureStdOut создаёт временный файл с выводом вызова утилиты jq.
Запуск инструментов проекта
Эта версия программы "Hello, World!" ожидает найти файл word.txt в той же директории. Мы планируем сгенерировать его в процессе сборки, используя программу для обработки JSON-файла на Zig.
tools/words.json
{
"en": "world",
"it": "mondo",
"ja": "世界"
}
Вывод
zig-out
├── hello
└── word.txt
Создание ресурсов для @embedFile
В этой версии программы "Hello, World!" планируется использовать @embedFile для встраивания ресурса, который будет создан в процессе сборки. Мы будем генерировать этот ресурс с помощью инструмента, написанного на Zig.
tools/words.json
{
"en": "world",
"it": "mondo",
"ja": "世界"
}
Вывод
zig-out/
└── bin
└── hello
Генерация исходного кода Zig
В этом файле сборки используется программа на Zig, которая создаёт новый Zig-файл. Сгенерированный файл затем подключается к основной программе в качестве зависимости.
Вывод
zig-out/
└── bin
└── hello
Работа с одним или несколькими сгенерированными файлами
Этап WriteFiles предоставляет возможность генерировать один или несколько файлов, которые имеют общую родительскую директорию. Сгенерированная директория располагается внутри локального .zig-cache, и каждый созданный файл доступен как std.Build.LazyPath. Кроме того, сама родительская директория также представлена в виде LazyPath.
Этот API позволяет записывать произвольные строки в сгенерированную директорию, а также копировать в неё файлы.
Вывод
zig-out/
└── project.tar.gz
Мутирование исходных файлов
Хотя это и не является распространённой практикой, иногда проекты сохраняют сгенерированные файлы в системе контроля версий. Это может быть полезно, когда такие файлы обновляются редко и имеют сложные системные зависимости, которые затрудняют процесс обновления, но только в рамках самого процесса обновления.
С этим следует быть осторожным: данный функционал не предназначен для использования в процессе обычной сборки. Он должен применяться как утилита, запускаемая разработчиком с целью обновления исходных файлов, которые затем будут зафиксированы в системе контроля версий. Если использовать эту функцию во время стандартного процесса сборки, это может привести к ошибкам кэширования и конкурентного выполнения.
После выполнения этой команды файл src/protocol.zig будет обновлён.
Полезные примеры
Сборка для нескольких целевых платформ с целью создания релиза
В этом примере мы изменим некоторые стандартные параметры на этапе InstallArtifact, чтобы размещать сборку для каждой целевой платформы в отдельной подпапке. Такой подход позволит более эффективно организовать артефакты, созданные для различных платформ, а также упростит их управление и развёртывание в дальнейшем.