A Bluesky Playdate client
at main 7.4 kB view raw
1const std = @import("std"); 2const builtin = @import("builtin"); 3 4const name = "bluesky"; 5pub fn build(b: *std.Build) !void { 6 const pdx_file_name = name ++ ".pdx"; 7 const optimize = b.standardOptimizeOption(.{}); 8 9 const writer = b.addWriteFiles(); 10 const source_dir = writer.getDirectory(); 11 writer.step.name = "write source directory"; 12 13 const FORCE_COMPILE_M1_MAC = false; 14 const supported_targets = [_]std.Build.ResolvedTarget{ 15 host_or_cross_target( 16 b, 17 .{ 18 .abi = .msvc, 19 .os_tag = .windows, 20 .cpu_arch = .x86_64, 21 }, 22 false, 23 ), 24 host_or_cross_target( 25 b, 26 .{ 27 .abi = .none, 28 .os_tag = .macos, 29 .cpu_arch = .aarch64, 30 }, 31 FORCE_COMPILE_M1_MAC, 32 ), 33 host_or_cross_target( 34 b, 35 .{ 36 .abi = .gnu, 37 .os_tag = .linux, 38 .cpu_arch = .x86_64, 39 }, 40 false, 41 ), 42 }; 43 for (supported_targets) |target| { 44 try compile_simulator_binary(b, optimize, target, writer); 45 } 46 47 const playdate_target = b.resolveTargetQuery(try std.Target.Query.parse(.{ 48 .arch_os_abi = "thumb-freestanding-eabihf", 49 .cpu_features = "cortex_m7+vfp4d16sp", 50 })); 51 const elf = b.addExecutable(.{ 52 .name = "pdex.elf", 53 .root_source_file = b.path("src/main.zig"), 54 .target = playdate_target, 55 .optimize = optimize, 56 .pic = true, 57 .single_threaded = true, 58 }); 59 elf.link_emit_relocs = true; 60 elf.entry = .{ .symbol_name = "eventHandler" }; 61 62 elf.setLinkerScript(b.path("link_map.ld")); 63 if (optimize == .ReleaseFast) { 64 elf.root_module.omit_frame_pointer = true; 65 } 66 _ = writer.addCopyFile(elf.getEmittedBin(), "pdex.elf"); 67 _ = writer.addCopyFile(b.path("pdxinfo"), "pdxinfo"); 68 69 try addCopyDirectory(writer, "assets", "./assets"); 70 71 const playdate_sdk_path = try std.process.getEnvVarOwned(b.allocator, "PLAYDATE_SDK_PATH"); 72 const pdc_path = b.pathJoin(&.{ playdate_sdk_path, "bin", if (builtin.os.tag == .windows) "pdc.exe" else "pdc" }); 73 const pd_simulator_path = switch (builtin.os.tag) { 74 .linux => b.pathJoin(&.{ playdate_sdk_path, "bin", "PlaydateSimulator" }), 75 .macos => "open", // `open` focuses the window, while running the simulator directry doesn't. 76 .windows => b.pathJoin(&.{ playdate_sdk_path, "bin", "PlaydateSimulator.exe" }), 77 else => @panic("Unsupported OS"), 78 }; 79 80 const pdc = b.addSystemCommand(&.{pdc_path}); 81 pdc.addDirectoryArg(source_dir); 82 pdc.setName("pdc"); 83 const pdx = pdc.addOutputFileArg(pdx_file_name); 84 85 b.installDirectory(.{ 86 .source_dir = pdx, 87 .install_dir = .prefix, 88 .install_subdir = pdx_file_name, 89 }); 90 b.installDirectory(.{ 91 .source_dir = source_dir, 92 .install_dir = .prefix, 93 .install_subdir = "pdx_source_dir", 94 }); 95 96 const run_cmd = b.addSystemCommand(&.{pd_simulator_path}); 97 run_cmd.addDirectoryArg(pdx); 98 run_cmd.setName("PlaydateSimulator"); 99 const run_step = b.step("run", "Run the app"); 100 run_step.dependOn(&run_cmd.step); 101 run_step.dependOn(b.getInstallStep()); 102 103 const clean_step = b.step("clean", "Clean all artifacts"); 104 clean_step.dependOn(&b.addRemoveDirTree(b.path("zig-out")).step); 105 if (builtin.os.tag != .windows) { 106 //Removing zig-cache from the Zig build script does not work on Windows: https://github.com/ziglang/zig/issues/9216 107 clean_step.dependOn(&b.addRemoveDirTree(b.path("zig-cache")).step); 108 clean_step.dependOn(&b.addRemoveDirTree(b.path(".zig-cache")).step); 109 } 110 111 // Add test step 112 const test_step = b.step("test", "Run unit tests"); 113 114 // Create test executables for each test file 115 const test_files = [_][]const u8{ 116 "src/test_memory.zig", 117 "src/test_json_parser.zig", 118 "src/test_network.zig", 119 }; 120 121 for (test_files) |test_file| { 122 const test_exe = b.addTest(.{ 123 .root_source_file = b.path(test_file), 124 .optimize = optimize, 125 }); 126 127 const run_test = b.addRunArtifact(test_exe); 128 test_step.dependOn(&run_test.step); 129 } 130} 131 132//The purpose of this function is a result of: 133// 1) This script supports cross-compiling PDX's that work on Mac, Windows or Linux without having 134// to compile on those OS's. 135// 136// 2) Inside of a PDX, there can only be 1 pdex executable per OS regardless of the CPU architecture. 137// This has unexpected consequences where, say, a given PDX file can only work on M1 Macs, 138// but not Intel ones. Or, vice versa. 139// 140// So, in the build() function above, I hardcoded ".cpu_arch = .aarch64", which is for M1 Macs. 141// What this means is that if you compiling your game on, say, Windows, it will generate a .pdx 142// that will only work on M1 Macs, but not Intel Macs. 143// BUT, cruicially, if you compiling your game on an Intel Mac, the resulting PDX will work 144// on Intel Macs, but not M1 Macs. Without this function, the game would fail 145// to run on the machine your compiling the code on (Intel Mac), which I'd like to avoid. 146fn host_or_cross_target( 147 b: *std.Build, 148 cross_target: std.Target.Query, 149 force_use_cross_target: bool, 150) std.Build.ResolvedTarget { 151 const result = 152 if (!force_use_cross_target and b.graph.host.result.os.tag == cross_target.os_tag.?) 153 b.graph.host 154 else 155 b.resolveTargetQuery(cross_target); 156 return result; 157} 158 159fn compile_simulator_binary( 160 b: *std.Build, 161 optimize: std.builtin.OptimizeMode, 162 target: std.Build.ResolvedTarget, 163 writer: *std.Build.Step.WriteFile, 164) !void { 165 const os_tag = target.result.os.tag; 166 const lib = b.addSharedLibrary(.{ 167 .name = "pdex", 168 .root_source_file = b.path("src/main.zig"), 169 .optimize = optimize, 170 .target = target, 171 }); 172 const pdex_extension = switch (os_tag) { 173 .windows => "dll", 174 .macos => "dylib", 175 .linux => "so", 176 else => @panic("Unsupported OS"), 177 }; 178 const pdex_filename = try std.fmt.allocPrint(b.allocator, "pdex.{s}", .{pdex_extension}); 179 _ = writer.addCopyFile(lib.getEmittedBin(), pdex_filename); 180 181 if (os_tag == .windows) { 182 _ = writer.addCopyFile(lib.getEmittedPdb(), "pdex.pdb"); 183 } 184} 185 186fn addCopyDirectory( 187 wf: *std.Build.Step.WriteFile, 188 src_path: []const u8, 189 dest_path: []const u8, 190) !void { 191 const b = wf.step.owner; 192 var dir = try b.build_root.handle.openDir( 193 src_path, 194 .{ .iterate = true }, 195 ); 196 defer dir.close(); 197 var it = dir.iterate(); 198 while (try it.next()) |entry| { 199 const new_src_path = b.pathJoin(&.{ src_path, entry.name }); 200 const new_dest_path = b.pathJoin(&.{ dest_path, entry.name }); 201 const new_src = b.path(new_src_path); 202 switch (entry.kind) { 203 .file => { 204 _ = wf.addCopyFile(new_src, new_dest_path); 205 }, 206 .directory => { 207 try addCopyDirectory( 208 wf, 209 new_src_path, 210 new_dest_path, 211 ); 212 }, 213 //TODO: possible support for sym links? 214 else => {}, 215 } 216 } 217}