Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 91 additions & 26 deletions scripts/check-examples.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
# # uses checkouts of your own (example-app/ and
# # example-plugin/ in that directory), as they are on
# # disk, copied so they are left untouched
# EXAMPLES_BUILDS="" scripts/check-examples.sh
# # builds none of their apps: the build API fixture alone
#
# CI: the examples job of .github/workflows/ci.yml.
set -euo pipefail
Expand Down Expand Up @@ -48,29 +50,32 @@ pack "$root" | gzip > "$work/fizzy.tar.gz"
bash scripts/pack-sdk.sh "$work/sdk" >/dev/null
sdk_tarball="$(ls "$work"/sdk/fizzy-sdk-v*.tar.gz)"

for repo in example-app example-plugin; do
if [[ -n "${EXAMPLES_DIR:-}" ]]; then
mkdir -p "$work/$repo"
pack "$EXAMPLES_DIR/$repo" | tar -x -C "$work/$repo"
else
git clone --quiet --depth 1 "https://github.com/fizzyedit/$repo" "$work/$repo"
fi
done

(cd "$work/example-plugin" && zig fetch --save=fizzy "$sdk_tarball" >/dev/null)
# example-app bundles example-plugin (`hello`), pinned by URL: the copy here, on this tree's SDK.
pack "$work/example-plugin" | gzip > "$work/example-plugin.tar.gz"
(
cd "$work/example-app"
zig fetch --save=hello "$work/example-plugin.tar.gz" >/dev/null
zig fetch --save=fizzy "$work/fizzy.tar.gz" >/dev/null
# Its replay app depends on the plugin SDK alone: this tree's, packed as a release would be.
zig fetch --save=fizzy_sdk "$sdk_tarball" >/dev/null
# So do the plugins it carries, each pinning a released SDK of its own.
for plugin in plugins/*/; do
(cd "$plugin" && zig fetch --save=fizzy "$sdk_tarball" >/dev/null)
builds="${EXAMPLES_BUILDS-plugin minimal studio endless studio-zon replay dvui dvui-sdl3}"
if [[ -n $builds ]]; then
for repo in example-app example-plugin; do
if [[ -n "${EXAMPLES_DIR:-}" ]]; then
mkdir -p "$work/$repo"
pack "$EXAMPLES_DIR/$repo" | tar -x -C "$work/$repo"
else
git clone --quiet --depth 1 "https://github.com/fizzyedit/$repo" "$work/$repo"
fi
done
)

(cd "$work/example-plugin" && zig fetch --save=fizzy "$sdk_tarball" >/dev/null)
# example-app bundles example-plugin (`hello`), pinned by URL: the copy here, on this tree's SDK.
pack "$work/example-plugin" | gzip > "$work/example-plugin.tar.gz"
(
cd "$work/example-app"
zig fetch --save=hello "$work/example-plugin.tar.gz" >/dev/null
zig fetch --save=fizzy "$work/fizzy.tar.gz" >/dev/null
# Its replay app depends on the plugin SDK alone: this tree's, packed as a release would be.
zig fetch --save=fizzy_sdk "$sdk_tarball" >/dev/null
# So do the plugins it carries, each pinning a released SDK of its own.
for plugin in plugins/*/; do
(cd "$plugin" && zig fetch --save=fizzy "$sdk_tarball" >/dev/null)
done
)
fi

# The plugin build installs the plugin: into a profile of its own, not this machine's fizzy.
export FIZZY_PROFILE="$work/profile"
Expand Down Expand Up @@ -107,8 +112,6 @@ build_one() {
}
export -f build_args build_one

builds="plugin minimal studio endless studio-zon replay dvui dvui-sdl3"

# Each build is one compile of an app that mostly keeps one core busy, so they run side by side,
# EXAMPLES_JOBS at once (3: CI's runner has 4 cores and 16 GB). First each configuration is
# configured alone (`-l` lists its steps), which fetches the lazy dependencies it asks for, so
Expand All @@ -119,8 +122,10 @@ for b in $builds; do
for a in "${args[@]:1}"; do [[ $a == -* ]] && opts+=("$a"); done
(cd "$work/${args[0]}" && zig build -l ${opts[@]+"${opts[@]}"} > /dev/null)
done
echo "== example-plugin and example-app, against this tree"
printf '%s\n' $builds | xargs -P "${EXAMPLES_JOBS:-3}" -n 1 bash -c 'build_one "$1"' _
if [[ -n $builds ]]; then
echo "== example-plugin and example-app, against this tree"
printf '%s\n' $builds | xargs -P "${EXAMPLES_JOBS:-3}" -n 1 bash -c 'build_one "$1"' _
fi

# An app's list is exact: one naming none of fizzy's, workbench included, fails while build.zig
# configures, which compiles nothing. The build must fail and say which plugin is missing.
Expand All @@ -138,3 +143,63 @@ if ! grep -q 'fizzy\.plugins\.workbench' "$work/log-app-only.txt"; then
exit 1
fi
echo "ok app-only (failed as expected)"

# The build API an app and a plugin package call (`fizzy.framework`, `staticModule`, `addApp`,
# `addWebSteps`, `addPackageSteps`; design/build-api.md), on a fixture app of its own: two plugin
# packages, one for both kinds of executable and one `.native` only, which fails to compile if it
# reaches the wasm.
fixtures="scripts/fixtures/build-api"
for id in fixture_both fixture_native; do
mkdir -p "$work/$id"
cp "$fixtures"/plugin/{build.zig,build.zig.zon,plugin.zig,lib.zig,lib.c} "$work/$id/"
sed "s/@ID@/$id/g" "$fixtures/plugin/plugin.zig.zon.tmpl" > "$work/$id/plugin.zig.zon"
(cd "$work/$id" && zig fetch --save=fizzy "$sdk_tarball" >/dev/null)
done
mkdir -p "$work/fixture-app"
cp "$fixtures"/app/{build.zig,build.zig.zon} "$work/fixture-app/"
(cd "$work/fixture-app" && zig fetch --save=fizzy "$work/fizzy.tar.gz" >/dev/null \
&& zig build -l > /dev/null)

echo "== build API fixture app"
fixture_fail=0
# step <label> <ok|fail|any> <expected text in the output> <zig build arguments…>
step() {
local label=$1 want=$2 text=$3 got=0
shift 3
(cd "$work/fixture-app" && zig build "$@") > "$work/log-fixture.txt" 2>&1 || got=$?
if { [[ $want == ok && $got -eq 0 ]] || [[ $want == fail && $got -ne 0 ]] || [[ $want == any ]]; } \
&& { [[ -z $text ]] || grep -q -- "$text" "$work/log-fixture.txt"; }; then
echo "ok $label"
else
echo "FAIL $label (wanted $want, exit $got, output containing '$text')"
tail -n 30 "$work/log-fixture.txt" | cut -c1-300 | sed "s/^/ /"
fixture_fail=1
fi
}
step "native executable, both plugins linked" ok "" --prefix "$work/out/fixture"
# The step list holds what the API adds: `web`, and `package`, `desktop` and `packageall`.
step "web, package and packageall steps added" ok "packageall" -l
for name in web package desktop; do
grep -q "^ $name " "$work/log-fixture.txt" || { echo "FAIL step '$name' missing"; fixture_fail=1; }
done
step "web build leaves the native-only plugin out" ok "" web --prefix "$work/out/fixture-web"
[[ -f "$work/out/fixture-web/web/index.html" ]] \
|| { echo "FAIL addWebSteps installed no web/index.html"; fixture_fail=1; }
# `package` runs vpk, which needs dotnet; `--verbose` prints its command line, `vpk-args` on it.
# Whether vpk then packs the fixture is not the question, so the status is not read; the files of
# `package-dirs` are staged into the pack input before vpk runs, and that is read from disk.
if command -v dotnet > /dev/null; then
step "vpk-args reach vpk pack" any "--fixture-vpk-arg" package --verbose --prefix "$work/out/fixture-pkg"
staged=$(ls "$work"/out/fixture-pkg/*/.pack-input/fixture-helper.txt 2>/dev/null || true)
if [[ -n $staged && $(cat "$staged") == "beside the executable" ]]; then
echo "ok package-dirs staged into the pack input"
else
echo "FAIL package-dirs file missing from the pack input"
fixture_fail=1
fi
else
echo "skip vpk-args reach vpk pack, package-dirs staged (no dotnet)"
fi
step "a plugin listed twice is an error" fail "listed twice" -Dfixture-fail=twice
step "an app's own web step is an error" fail "already has a \`web\` step" -Dfixture-fail=web-taken
[[ $fixture_fail -eq 0 ]]
50 changes: 50 additions & 0 deletions scripts/fixtures/build-api/app/build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//! An application over fizzy's build API, for `scripts/check-examples.sh`: two plugin packages
//! built with `fizzy.framework` (one for both kinds of executable, one native-only), the web and
//! package steps, and the failures the API promises.
//!
//! `-Dfixture-fail=<case>` builds a list the API refuses: `twice` lists a plugin twice,
//! `web-taken` has a `web` step already.
const std = @import("std");
const fizzy = @import("fizzy");
const both = @import("fixture_both");
const native = @import("fixture_native");

pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const fail = b.option([]const u8, "fixture-fail", "A case the API must refuse") orelse "";

// Files that go beside the executable in the pack input, and an argument for `vpk pack`.
const helpers = b.addWriteFiles();
_ = helpers.add("fixture-helper.txt", "beside the executable\n");

const fizzy_dep = b.dependency("fizzy", .{
.target = target,
.optimize = optimize,
.@"defer-app" = true,
.@"app-name" = @as([]const u8, "fixtureapp"),
.@"package-dirs" = &[_]std.Build.LazyPath{helpers.getDirectory()},
.@"vpk-args" = &[_][]const u8{ "--fixture-vpk-arg", "1" },
});
const both_dep = b.dependency("fixture_both", .{});
const native_dep = b.dependency("fixture_native", .{});

const list = [_]fizzy.BundledPlugin{
fizzy.plugins.workbench,
fizzy.plugins.text,
.{ .name = "fixture_both", .targets = &.{
.{ .target = .native, .module = both.staticModule(both_dep.builder, fizzy.framework(fizzy_dep, .native)) },
.{ .target = .web, .module = both.staticModule(both_dep.builder, fizzy.framework(fizzy_dep, .web)) },
} },
.{ .name = "fixture_native", .targets = &.{
.{ .target = .native, .module = native.staticModule(native_dep.builder, fizzy.framework(fizzy_dep, .native)) },
} },
};
const twice = list ++ [_]fizzy.BundledPlugin{list[2]};
const app = try fizzy.addApp(fizzy_dep, if (std.mem.eql(u8, fail, "twice")) &twice else &list);
b.installArtifact(app.exe);

if (std.mem.eql(u8, fail, "web-taken")) _ = b.step("web", "taken before the API's");
try fizzy.addWebSteps(b, fizzy_dep);
try fizzy.addPackageSteps(b, fizzy_dep);
}
16 changes: 16 additions & 0 deletions scripts/fixtures/build-api/app/build.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.{
.name = .fixture_app,
.version = "0.0.1",
.fingerprint = 0x915c21d842947814,
.minimum_zig_version = "0.17.0",
.paths = .{ "build.zig", "build.zig.zon" },
.dependencies = .{
// Replaced by `zig fetch --save=fizzy` with this tree.
.fizzy = .{
.url = "git+https://github.com/fizzyedit/fizzy#d99a7509032aea580d2db7a2eae5100bdb9b6afd",
.hash = "fizzy-0.2.0-c46aVltCpgC5UG61y5Nm4TH_52KKUl5xQHTS7SayqK3W",
},
.fixture_both = .{ .path = "../fixture_both" },
.fixture_native = .{ .path = "../fixture_native" },
},
}
24 changes: 24 additions & 0 deletions scripts/fixtures/build-api/plugin/build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//! A plugin package the way `fizzy.plugin.staticModule` documents it: the module is built against
//! the framework the application's executables link, with a dependency of its own added.
const std = @import("std");
const fizzy = @import("fizzy");

// anytype: the Framework of fizzy's build, a build-time duck type (pinned SDKs have their own type)
pub fn staticModule(b: *std.Build, framework: anytype) *std.Build.Module {
const m = fizzy.plugin.staticModule(b, framework, .{});
const lib = b.createModule(.{
.root_source_file = b.path("lib.zig"),
.target = framework.target,
.optimize = framework.optimize,
});
lib.addCSourceFile(.{ .file = b.path("lib.c") });
m.addImport("fixture_lib", lib);
return m;
}

pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const plugin = fizzy.plugin.create(b, .{ .target = target, .optimize = optimize });
fizzy.plugin.install(b, plugin.lib, .{});
}
14 changes: 14 additions & 0 deletions scripts/fixtures/build-api/plugin/build.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.{
.name = .fixture_plugin,
.version = "0.0.1",
.minimum_zig_version = "0.17.0",
.fingerprint = 0x9b70afac3d2d8bc6,
.paths = .{ "build.zig", "build.zig.zon", "plugin.zig", "plugin.zig.zon", "lib.zig", "lib.c" },
.dependencies = .{
// Replaced by `zig fetch --save=fizzy` with this tree's packed SDK.
.fizzy = .{
.url = "https://github.com/fizzyedit/fizzy/releases/download/sdk-v0.2.30/fizzy-sdk-v0.2.30.tar.gz",
.hash = "fizzy-0.2.30-SGBwY1QnJwDznUMVnSLATxXgH2e8_Vz7eMwLj9UQ0sVU",
},
},
}
4 changes: 4 additions & 0 deletions scripts/fixtures/build-api/plugin/lib.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Weak: both fixture packages link this file into one executable.
__attribute__((weak)) int fixture_answer(void) {
return 42;
}
3 changes: 3 additions & 0 deletions scripts/fixtures/build-api/plugin/lib.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
//! The fixture plugin's dependency of its own: Zig declaring a function that `lib.c` defines, so
//! a lost import or a lost C source fails the plugin's compile or link.
pub extern fn fixture_answer() c_int;
49 changes: 49 additions & 0 deletions scripts/fixtures/build-api/plugin/plugin.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//! The build-API fixture plugin: one surface, built by `staticModule` for each kind of
//! executable. `scripts/check-examples.sh` links it into `app/`.
const std = @import("std");
const builtin = @import("builtin");
const sdk = @import("fizzy_sdk");
const dvui = @import("dvui");
const fixture_lib = @import("fixture_lib");

pub const plugin_options = @import("fizzy_plugin_options");
pub const plugin_id = plugin_options.id;

// The native-only fixture is the same file under another id: a web build that links it
// fails here, which is how the check sees a `.native` entry kept out of the wasm.
comptime {
if (std.mem.eql(u8, plugin_options.id, "fixture_native") and
builtin.cpu.arch == .wasm32)
{
@compileError("a native-only plugin was linked into the web build");
}
}

// The host writes plugin lifecycle state on the main UI thread.
var plugin: sdk.Plugin = .{
.state = undefined,
.vtable = &vtable,
.id = plugin_id,
.display_name = plugin_options.name,
};

const vtable: sdk.Plugin.VTable = .{};

pub fn register(host: *sdk.Host) !void {
// Calls into the C source of the package's own dependency: it must have reached the link.
if (fixture_lib.fixture_answer() != 42) return error.FixtureDependency;
try host.registerPlugin(&plugin);
try host.registerSurface(.{
.id = plugin_id ++ ".surface",
.owner = &plugin,
.icon = .{ .tvg = dvui.entypo.emoji_happy },
.title = plugin_options.name,
.keywords = sdk.keywords.ide.sidebar,
.draw = draw,
});
}

fn draw(_: ?*anyopaque) !dvui.App.Result {
dvui.label(@src(), "{s}", .{plugin_id}, .{});
return .ok;
}
7 changes: 7 additions & 0 deletions scripts/fixtures/build-api/plugin/plugin.zig.zon.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.{
.id = "@ID@",
.name = "@ID@",
.version = "0.0.1",
.min_sdk_version = "0.2.30",
.description = "A build-API fixture.",
}
Loading