From 048682bf75c9917359f61a0c0dc80ee8ef9b8adf Mon Sep 17 00:00:00 2001 From: genericjam Date: Wed, 27 May 2026 00:08:10 -0600 Subject: [PATCH] mob.new templates: glob mob ios/*.swift in iOS build.zig MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generated iOS build.zig / build_device.zig hardcoded the mob Swift source list (MobViewModel/MobRootView/MobGpuView). When mob adds a new Swift file, generated apps that don't list it fail with "cannot find '' in scope" (+ cascading onChange errors) — exactly the break that hit master iOS builds. Glob $mob_dir/ios/*.swift at build time (b.build_root.handle.openDir + iterate) so new mob Swift sources compile without a template edit. Mirrors mob_dev's release-path glob. Verified the generated build.zig compiles under zig 0.16 and the glob enumerates the .swift files. Adds a generator test asserting the glob (and that the hardcoded list is gone) for both sim + device templates. Co-Authored-By: Claude Opus 4.7 --- priv/templates/mob.new/ios/build.zig.eex | 16 +++++++++++++--- priv/templates/mob.new/ios/build_device.zig.eex | 16 +++++++++++++--- test/mob_new/project_generator_test.exs | 16 ++++++++++++++++ 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/priv/templates/mob.new/ios/build.zig.eex b/priv/templates/mob.new/ios/build.zig.eex index 4e86549..46e97fd 100644 --- a/priv/templates/mob.new/ios/build.zig.eex +++ b/priv/templates/mob.new/ios/build.zig.eex @@ -105,9 +105,19 @@ pub fn build(b: *std.Build) void { swift_run.addArg("-I"); swift_run.addArg(b.fmt("{s}/ios", .{mob_dir})); swift_run.addArgs(&.{ "-parse-as-library", "-wmo" }); - swift_run.addFileArg(.{ .cwd_relative = b.fmt("{s}/ios/MobViewModel.swift", .{mob_dir}) }); - swift_run.addFileArg(.{ .cwd_relative = b.fmt("{s}/ios/MobRootView.swift", .{mob_dir}) }); - swift_run.addFileArg(.{ .cwd_relative = b.fmt("{s}/ios/MobGpuView.swift", .{mob_dir}) }); + // Glob all mob Swift sources so a newly-added file (e.g. MobGpuView.swift, + // referenced by MobRootView) compiles without editing this template. + { + const glob_io = b.graph.io; + var mob_ios = b.build_root.handle.openDir(glob_io, b.fmt("{s}/ios", .{mob_dir}), .{ .iterate = true }) catch @panic("mob ios dir not found"); + defer mob_ios.close(glob_io); + var swift_entries = mob_ios.iterate(); + while (swift_entries.next(glob_io) catch @panic("mob ios dir iterate failed")) |entry| { + if (entry.kind == .file and std.mem.endsWith(u8, entry.name, ".swift")) { + swift_run.addFileArg(.{ .cwd_relative = b.fmt("{s}/ios/{s}", .{ mob_dir, entry.name }) }); + } + } + } if (project_swift_sources.len > 0) { var swift_it = std.mem.splitScalar(u8, project_swift_sources, ','); while (swift_it.next()) |source| { diff --git a/priv/templates/mob.new/ios/build_device.zig.eex b/priv/templates/mob.new/ios/build_device.zig.eex index 0e13e2c..de2a52d 100644 --- a/priv/templates/mob.new/ios/build_device.zig.eex +++ b/priv/templates/mob.new/ios/build_device.zig.eex @@ -93,9 +93,19 @@ pub fn build(b: *std.Build) void { swift_run.addArg("-I"); swift_run.addArg(b.fmt("{s}/ios", .{mob_dir})); swift_run.addArgs(&.{ "-parse-as-library", "-wmo" }); - swift_run.addFileArg(.{ .cwd_relative = b.fmt("{s}/ios/MobViewModel.swift", .{mob_dir}) }); - swift_run.addFileArg(.{ .cwd_relative = b.fmt("{s}/ios/MobRootView.swift", .{mob_dir}) }); - swift_run.addFileArg(.{ .cwd_relative = b.fmt("{s}/ios/MobGpuView.swift", .{mob_dir}) }); + // Glob all mob Swift sources so a newly-added file (e.g. MobGpuView.swift, + // referenced by MobRootView) compiles without editing this template. + { + const glob_io = b.graph.io; + var mob_ios = b.build_root.handle.openDir(glob_io, b.fmt("{s}/ios", .{mob_dir}), .{ .iterate = true }) catch @panic("mob ios dir not found"); + defer mob_ios.close(glob_io); + var swift_entries = mob_ios.iterate(); + while (swift_entries.next(glob_io) catch @panic("mob ios dir iterate failed")) |entry| { + if (entry.kind == .file and std.mem.endsWith(u8, entry.name, ".swift")) { + swift_run.addFileArg(.{ .cwd_relative = b.fmt("{s}/ios/{s}", .{ mob_dir, entry.name }) }); + } + } + } if (project_swift_sources.len > 0) { var swift_it = std.mem.splitScalar(u8, project_swift_sources, ','); while (swift_it.next()) |source| { diff --git a/test/mob_new/project_generator_test.exs b/test/mob_new/project_generator_test.exs index 128e5e3..d5f9544 100644 --- a/test/mob_new/project_generator_test.exs +++ b/test/mob_new/project_generator_test.exs @@ -1031,6 +1031,22 @@ defmodule MobNew.ProjectGeneratorTest do assert content =~ "std.mem.splitScalar(u8, project_swift_sources, ',')" assert content =~ "swift_run.addFileArg(.{ .cwd_relative = source });" end + + test "#{@label} build.zig globs mob Swift sources (no hardcoded file list)", + %{tmp: tmp} do + {:ok, dir} = ProjectGenerator.generate("test_app", tmp) + content = File.read!(Path.join(dir, @path)) + + # mob Swift sources are globbed from $mob_dir/ios at build time so a + # newly-added file (e.g. MobGpuView.swift, referenced by MobRootView) + # compiles without a template edit — listing files by name is how a new + # mob Swift file broke iOS builds. + assert content =~ ~s|b.build_root.handle.openDir(glob_io, b.fmt("{s}/ios", .{mob_dir})| + assert content =~ ~s|std.mem.endsWith(u8, entry.name, ".swift")| + + refute content =~ ~s|b.fmt("{s}/ios/MobGpuView.swift", .{mob_dir})|, + "#{@label} build.zig must glob ios/*.swift, not list mob Swift files by name" + end end # Regression: the Android jni build.zig must declare `tflite_static` as