diff --git a/src/ch09.md b/src/ch09.md index 929d606..2394bdd 100644 --- a/src/ch09.md +++ b/src/ch09.md @@ -564,9 +564,126 @@ pub const User = struct { Обычно `@TypeOf` используется в паре с `@typeInfo`, которая возвращает `std.builtin.Type`. Это весьма интересное маркированное объединение, которое полностью описывает тип. Функция `std.json.stringify` (к примеру) -рекурсивно использует такие описания для того, чтобы выяснить, -как именно нужно сериализвать значения. +рекурсивно использует такие описания для того, чтобы выяснить, как именно +нужно сериализвать значения. ## Система сборки +Если вы читали всю эту книгу и при этом ожидали каких-то рецептов для +работы с более сложными проектами (множественные зависимости, поддержка +разных целевых архитектур и т.п), то увы, вас ждёт разочарование. Zig на +самом деле имеет очень мощную систему сборки, настолько мощную, что она +начала использоваться для проектов, написаннных не на Zig. К сожалению, +вся эта мощь означает, что для более простых нужд система сборки Zig не +самая простая в использовании. + +Тем не менее, небольшой обзор мы всё же сделаем. Чтобы запускать наши +примеры, мы использовали `zig run file.zig`. Однажды мы также +использовали `zig test file.zig` для запуска теста. Команды `run` и +`test` хороши для всяких простых упражнений, но для чего-то более +серьёзного вам понадобится команда `build`. Для этой команды нужно, чтобы +в корневом каталоге проекта был файл `build.zig` со специальной +одноимённой точкой входа: + +```zig +// build.zig + +const std = @import("std"); + +pub fn build(b: *std.Build) !void { + _ = b; +} +``` + +Каждая сборка имеет по умолчанию этап "install", который можно +выполнить при помощи `zig build install`, но поскольку наш файл +`build.zig` практически пуст, на выходе ничего особо значимого и не +будет. Как минимум, мы должны сообщить, где у нас файл с функцией `main` +(тут предполагается, что это `program.zig`): + +```zig +const std = @import("std"); + +pub fn build(b: *std.Build) !void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + // setup executable + const exe = b.addExecutable(.{ + .name = "program", + .target = target, + .optimize = optimize, + .root_source_file = .{ .path = "program.zig" }, + }); + b.installArtifact(exe); +} +``` + +Теперь, если вы выполните `zig build install`, то получите +скомпилированный исполнимый файл `./zig-out/bin/program`. Целевую +платформу и вид оптимизации при этом можно указать в аргументах командной +строки. Например, если мы хотим оптимизацию по размеру и делаем +исполнимый файл для ОС Windows на архитектуре x86_64, то делаем так: + +``` +zig build install -Doptimize=ReleaseSmall -Dtarget=x86_64-windows-gnu +``` + +Помимо этапа "install", процесс сборки может включать в себя ещё два, +"run" и "test". Если это библиотека, то только один, "test". Чтобы +включить этап "run", в простейшем случае, то есть без передачи программе +аргументов, нужно добавить 4 строчки: + +```zig +// add after: b.installArtifact(exe); + +const run_cmd = b.addRunArtifact(exe); +run_cmd.step.dependOn(b.getInstallStep()); + +const run_step = b.step("run", "Start learning!"); +run_step.dependOn(&run_cmd.step); +``` + +Это создаёт две зависимости, путём двух вызовов `dependOn`. Первый из них +привязывает команду "run" к встроенному этапу "install". Второй +привязывает этап "run" к созданной команде "run". Вы, наверное, спросите, +зачем нужны как команда "run", так и этап "run". Надо полагать, такое +разделение существует для возможности более сложных конфигураций: этапы, +которые зависят от более чем одной команды или команды, которые +исппользуются на разных этапах. Если вы выполните `zig build --help` и +посмотрите на начало вывода, то увидите наш новый этап "run". Теперь вы +можете запускать программу при помощи `zig build run`. + +Для добавления этапа "test" нужно продублировать практически весь код для +этапов "install" и "run", только вместо `b.addExecutable` надо +использовать `b.addTest`: + +```zig +const tests = b.addTest(.{ + .target = target, + .optimize = optimize, + .root_source_file = .{ .path = "program.zig" }, +}); + +const test_cmd = b.addRunArtifact(tests); +test_cmd.step.dependOn(b.getInstallStep()); +const test_step = b.step("test", "Run the tests"); +test_step.dependOn(&test_cmd.step); +``` + +Тут мы дали этапу имя "test". При запуске `zig build --help` +теперь мы должны увидеть новый доступный этап. Если в нашей +`program.zig` никаких тестов нет, сложно сказать, что тут +получится, поэтому добавим в `program.zig` заглушку: + +```zig +test "dummy build test" { + try std.testing.expectEqual(false, true); +} +``` + +Теперь, если вы выполните `zig build test`, то увидите, +что тест не прошёл, как и должно было быть, поскольку `true != false`. + + ## Сторонние зависимости diff --git a/src/zig-build/build.zig b/src/zig-build/build.zig new file mode 100644 index 0000000..541df84 --- /dev/null +++ b/src/zig-build/build.zig @@ -0,0 +1,34 @@ + +const std = @import("std"); + +pub fn build(b: *std.Build) !void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + // setup executable + const exe = b.addExecutable(.{ + .name = "program", + .target = target, + .optimize = optimize, + .root_source_file = .{ .path = "program.zig" }, + }); + b.installArtifact(exe); + + // run step + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(b.getInstallStep()); + const run_step = b.step("run", "test run"); + run_step.dependOn(&run_cmd.step); + + // test step + const tests = b.addTest(.{ + .target = target, + .optimize = optimize, + .root_source_file = .{ .path = "program.zig" }, + }); + + const test_cmd = b.addRunArtifact(tests); + test_cmd.step.dependOn(b.getInstallStep()); + const test_step = b.step("test", "Run the tests"); + test_step.dependOn(&test_cmd.step); +} diff --git a/src/zig-build/program.zig b/src/zig-build/program.zig new file mode 100644 index 0000000..0dbc26a --- /dev/null +++ b/src/zig-build/program.zig @@ -0,0 +1,11 @@ + +const std = @import("std"); + +pub fn main() void { + std.debug.print("test\n", .{}); +} + +test "dummy build test" { + //try std.testing.expectEqual(false, true); // failed + try std.testing.expectEqual(false, false); // passed +}