@@ -14,14 +14,9 @@ pub fn build(b: *std.Build) void {
1414 const mb = MicroBuild .init (b , mz_dep ) orelse return ;
1515 const register_schemas = get_register_schemas (b , mb ) catch @panic ("OOM" );
1616 const write_files = b .addWriteFiles ();
17- const register_schema = write_files .add ("register_schemas.json" , std .json .Stringify .valueAlloc (b .allocator , register_schemas , .{}) catch @panic ("OOM" ));
18- const register_schema_install = b .addInstallFile (register_schema , "data/register_schemas.json" );
19- b .getInstallStep ().dependOn (& register_schema_install .step );
2017
21- const dvui_dep = b .dependency ("dvui" , .{
22- .target = target ,
23- .optimize = optimize ,
24- });
18+ // Generate Zig file with embedded schemas (used by both CLI and GUI)
19+ const register_schema_zig = write_files .add ("register_schemas.zig" , generate_zig_schema_literal (b .allocator , register_schemas ) catch @panic ("OOM" ));
2520
2621 const regz_dep = mz_dep .builder .dependency ("tools/regz" , .{
2722 .target = target ,
@@ -32,13 +27,63 @@ pub fn build(b: *std.Build) void {
3227 .optimize = .ReleaseSafe ,
3328 });
3429
30+ const regz_mod = regz_dep .module ("regz" );
31+
32+ // Shared module for RegisterSchemaUsage (used by both schemas_mod and cli_mod)
33+ const register_schema_usage_mod = b .createModule (.{
34+ .root_source_file = b .path ("src/RegisterSchemaUsage.zig" ),
35+ });
36+
37+ // Create schemas module from generated Zig file
38+ const schemas_mod = b .createModule (.{
39+ .root_source_file = register_schema_zig ,
40+ .imports = &.{
41+ .{ .name = "RegisterSchemaUsage" , .module = register_schema_usage_mod },
42+ },
43+ });
44+
45+ // ─────────────────────────────────────────────────────────────────────────
46+ // CLI executable
47+ // ─────────────────────────────────────────────────────────────────────────
48+ const cli_mod = b .createModule (.{
49+ .root_source_file = b .path ("src/cli.zig" ),
50+ .target = target ,
51+ .optimize = optimize ,
52+ .imports = &.{
53+ .{ .name = "regz" , .module = regz_mod },
54+ .{ .name = "schemas" , .module = schemas_mod },
55+ .{ .name = "RegisterSchemaUsage" , .module = register_schema_usage_mod },
56+ },
57+ });
58+
59+ const cli_exe = b .addExecutable (.{
60+ .name = "sorcerer-cli" ,
61+ .root_module = cli_mod ,
62+ });
63+ b .installArtifact (cli_exe );
64+
65+ const run_cli_cmd = b .addRunArtifact (cli_exe );
66+ run_cli_cmd .step .dependOn (b .getInstallStep ());
67+ if (b .args ) | args | {
68+ run_cli_cmd .addArgs (args );
69+ }
70+ const run_cli_step = b .step ("run-cli" , "Run the CLI tool" );
71+ run_cli_step .dependOn (& run_cli_cmd .step );
72+
73+ // ─────────────────────────────────────────────────────────────────────────
74+ // GUI executable
75+ // ─────────────────────────────────────────────────────────────────────────
76+ const dvui_dep = b .dependency ("dvui" , .{
77+ .target = target ,
78+ .optimize = optimize ,
79+ });
80+
3581 const serial_dep = b .dependency ("serial" , .{
3682 .target = target ,
3783 .optimize = optimize ,
3884 });
3985
4086 const dvui_mod = dvui_dep .module ("dvui_sdl3" );
41- const regz_mod = regz_dep .module ("regz" );
4287 const serial_mod = serial_dep .module ("serial" );
4388
4489 const exe_mod = b .createModule (.{
@@ -58,6 +103,14 @@ pub fn build(b: *std.Build) void {
58103 .name = "serial" ,
59104 .module = serial_mod ,
60105 },
106+ .{
107+ .name = "schemas" ,
108+ .module = schemas_mod ,
109+ },
110+ .{
111+ .name = "RegisterSchemaUsage" ,
112+ .module = register_schema_usage_mod ,
113+ },
61114 },
62115 });
63116
@@ -111,7 +164,7 @@ pub fn build(b: *std.Build) void {
111164 run_cmd .addArgs (args );
112165 }
113166
114- const run_step = b .step ("run" , "Run the app" );
167+ const run_step = b .step ("run" , "Run the GUI app" );
115168 run_step .dependOn (& run_cmd .step );
116169
117170 const exe_unit_tests = b .addTest (.{
@@ -368,9 +421,16 @@ fn get_register_schemas(b: *std.Build, mb: *MicroBuild) ![]const RegisterSchemaU
368421 }
369422
370423 if (t .board ) | board | if (boards .getEntry (lazy_path )) | entry | {
371- try entry .value_ptr .append (b .allocator , .{
372- .name = board .name ,
373- });
424+ // Check if this board name already exists (deduplicate by name)
425+ const board_exists = for (entry .value_ptr .items ) | existing_board | {
426+ if (std .mem .eql (u8 , existing_board .name , board .name )) break true ;
427+ } else false ;
428+
429+ if (! board_exists ) {
430+ try entry .value_ptr .append (b .allocator , .{
431+ .name = board .name ,
432+ });
433+ }
374434 } else {
375435 var board_list : std .ArrayList (RegisterSchemaUsage .Board ) = .{};
376436 try board_list .append (b .allocator , .{
@@ -418,3 +478,114 @@ fn get_port_name(path: []const u8) []const u8 {
418478
419479 unreachable ;
420480}
481+
482+ /// Generate a Zig source file containing the register schemas as compile-time constants.
483+ fn generate_zig_schema_literal (allocator : std.mem.Allocator , schemas : []const RegisterSchemaUsage ) ! []const u8 {
484+ var buf : std .ArrayList (u8 ) = .{};
485+ const writer = buf .writer (allocator );
486+
487+ // Helper to normalize paths (convert backslashes to forward slashes for Windows compatibility)
488+ const normalize_path = struct {
489+ fn call (alloc : std.mem.Allocator , path : []const u8 ) ! []const u8 {
490+ const result = try alloc .alloc (u8 , path .len );
491+ for (path , 0.. ) | c , i | {
492+ result [i ] = if (c == '\\ ' ) '/' else c ;
493+ }
494+ return result ;
495+ }
496+ }.call ;
497+
498+ try writer .writeAll (
499+ \\// Auto-generated file - do not edit manually.
500+ \\// Generated by tools/sorcerer/build.zig
501+ \\
502+ \\const RegisterSchemaUsage = @import("RegisterSchemaUsage");
503+ \\
504+ \\pub const schemas: []const RegisterSchemaUsage = &.{
505+ \\
506+ );
507+
508+ for (schemas ) | schema | {
509+ try writer .writeAll (" .{\n " );
510+
511+ // Format
512+ try writer .print (" .format = .{s},\n " , .{@tagName (schema .format )});
513+
514+ // Chips
515+ try writer .writeAll (" .chips = &.{\n " );
516+ for (schema .chips ) | chip | {
517+ try writer .writeAll (" .{\n " );
518+ try writer .print (" .name = \" {s}\" ,\n " , .{chip .name });
519+ try writer .print (" .target_name = \" {s}\" ,\n " , .{chip .target_name });
520+ // Patch files
521+ if (chip .patch_files .len > 0 ) {
522+ try writer .writeAll (" .patch_files = &.{\n " );
523+ for (chip .patch_files ) | patch_file | {
524+ switch (patch_file ) {
525+ .src_path = > | src | {
526+ const sub_path = try normalize_path (allocator , src .sub_path );
527+ const build_root = try normalize_path (allocator , src .build_root );
528+ try writer .writeAll (" .{ .src_path = .{\n " );
529+ try writer .print (" .sub_path = \" {s}\" ,\n " , .{sub_path });
530+ try writer .print (" .build_root = \" {s}\" ,\n " , .{build_root });
531+ try writer .writeAll (" } },\n " );
532+ },
533+ .dependency = > | dep | {
534+ const sub_path = try normalize_path (allocator , dep .sub_path );
535+ const build_root = try normalize_path (allocator , dep .build_root );
536+ try writer .writeAll (" .{ .dependency = .{\n " );
537+ try writer .print (" .sub_path = \" {s}\" ,\n " , .{sub_path });
538+ try writer .print (" .build_root = \" {s}\" ,\n " , .{build_root });
539+ try writer .print (" .dep_name = \" {s}\" ,\n " , .{dep .dep_name });
540+ try writer .writeAll (" } },\n " );
541+ },
542+ }
543+ }
544+ try writer .writeAll (" },\n " );
545+ }
546+ try writer .writeAll (" },\n " );
547+ }
548+ try writer .writeAll (" },\n " );
549+
550+ // Boards
551+ try writer .writeAll (" .boards = &.{" );
552+ for (schema .boards , 0.. ) | board , i | {
553+ if (i > 0 ) try writer .writeAll (", " );
554+ try writer .print (".{{ .name = \" {s}\" }}" , .{board .name });
555+ }
556+ try writer .writeAll ("},\n " );
557+
558+ // Location
559+ try writer .writeAll (" .location = " );
560+ switch (schema .location ) {
561+ .src_path = > | src | {
562+ const sub_path = try normalize_path (allocator , src .sub_path );
563+ const build_root = try normalize_path (allocator , src .build_root );
564+ try writer .writeAll (".{ .src_path = .{\n " );
565+ try writer .print (" .port_name = \" {s}\" ,\n " , .{src .port_name });
566+ try writer .print (" .sub_path = \" {s}\" ,\n " , .{sub_path });
567+ try writer .print (" .build_root = \" {s}\" ,\n " , .{build_root });
568+ try writer .writeAll (" } },\n " );
569+ },
570+ .dependency = > | dep | {
571+ const sub_path = try normalize_path (allocator , dep .sub_path );
572+ const build_root = try normalize_path (allocator , dep .build_root );
573+ try writer .writeAll (".{ .dependency = .{\n " );
574+ try writer .print (" .sub_path = \" {s}\" ,\n " , .{sub_path });
575+ try writer .print (" .build_root = \" {s}\" ,\n " , .{build_root });
576+ try writer .print (" .dep_name = \" {s}\" ,\n " , .{dep .dep_name });
577+ try writer .print (" .port_name = \" {s}\" ,\n " , .{dep .port_name });
578+ try writer .writeAll (" } },\n " );
579+ },
580+ }
581+
582+ try writer .writeAll (" },\n " );
583+ }
584+
585+ try writer .writeAll (
586+ \\};
587+ \\
588+ );
589+
590+ return buf .toOwnedSlice (allocator );
591+ }
0 commit comments