On branch main

modified:   src/ch09.md
new file:   src/zig-build/build.zig
new file:   src/zig-build/program.zig
This commit is contained in:
zed
2023-11-20 10:31:51 +03:00
parent 1a01a8a9b9
commit db43b389ef
3 changed files with 164 additions and 2 deletions

View File

@@ -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`.
## Сторонние зависимости

34
src/zig-build/build.zig Normal file
View File

@@ -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);
}

11
src/zig-build/program.zig Normal file
View File

@@ -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
}