@pkmn/engine 0.1.0-dev.15f64810 → 0.1.0-dev.18478e7d

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +30 -17
  3. package/build/pkg/addon/browser.d.ts +3 -0
  4. package/build/pkg/addon/browser.js +47 -0
  5. package/build/pkg/addon/browser.js.map +1 -0
  6. package/build/pkg/addon/node.d.ts +3 -0
  7. package/build/pkg/addon/node.js +110 -0
  8. package/build/pkg/addon/node.js.map +1 -0
  9. package/build/pkg/addon/wasm.d.ts +2 -0
  10. package/build/pkg/addon/wasm.js +53 -0
  11. package/build/pkg/addon/wasm.js.map +1 -0
  12. package/build/pkg/addon.d.ts +26 -4
  13. package/build/pkg/addon.js +34 -65
  14. package/build/pkg/addon.js.map +1 -1
  15. package/build/pkg/data/layout.json +15 -2
  16. package/build/pkg/data/protocol.json +23 -9
  17. package/build/pkg/data.d.ts +23 -1
  18. package/build/pkg/data.js.map +1 -1
  19. package/build/pkg/gen1.d.ts +15 -18
  20. package/build/pkg/gen1.js +28 -19
  21. package/build/pkg/gen1.js.map +1 -1
  22. package/build/pkg/index.d.ts +40 -90
  23. package/build/pkg/index.js +23 -70
  24. package/build/pkg/index.js.map +1 -1
  25. package/build/pkg/protocol.d.ts +29 -1
  26. package/build/pkg/protocol.js +105 -103
  27. package/build/pkg/protocol.js.map +1 -1
  28. package/build/tools/debug.js +83 -73
  29. package/build/tools/debug.js.map +1 -1
  30. package/build/tools/display/debug.jsx +66 -0
  31. package/build/tools/display/debug.jsx.map +1 -0
  32. package/build/tools/display/index.js +152 -0
  33. package/build/tools/display/index.js.map +1 -0
  34. package/build/tools/display/ui.jsx +360 -0
  35. package/build/tools/display/ui.jsx.map +1 -0
  36. package/build/tools/display/util.js +157 -0
  37. package/build/tools/display/util.js.map +1 -0
  38. package/build.zig +346 -128
  39. package/package.json +59 -138
  40. package/src/bin/install-pkmn-engine +25 -7
  41. package/src/bin/pkmn-debug +1 -1
  42. package/src/lib/bindings/c.zig +201 -0
  43. package/src/lib/bindings/node.zig +117 -0
  44. package/src/lib/bindings/wasm.zig +51 -0
  45. package/src/lib/common/array.zig +70 -0
  46. package/src/lib/common/data.zig +8 -8
  47. package/src/lib/common/debug.zig +45 -29
  48. package/src/lib/common/js.zig +3 -7
  49. package/src/lib/common/optional.zig +26 -22
  50. package/src/lib/common/options.zig +4 -0
  51. package/src/lib/common/protocol.zig +196 -160
  52. package/src/lib/common/rational.zig +338 -254
  53. package/src/lib/common/rng.zig +5 -7
  54. package/src/lib/common/util.zig +53 -1
  55. package/src/lib/gen1/calc.zig +651 -205
  56. package/src/lib/gen1/chance.zig +443 -257
  57. package/src/lib/gen1/data/moves.zig +2 -4
  58. package/src/lib/gen1/data/species.zig +1 -3
  59. package/src/lib/gen1/data/types.zig +3 -4
  60. package/src/lib/gen1/data.zig +40 -43
  61. package/src/lib/gen1/helpers.zig +39 -574
  62. package/src/lib/gen1/mechanics.zig +328 -229
  63. package/src/lib/gen2/calc.zig +35 -28
  64. package/src/lib/gen2/chance.zig +117 -518
  65. package/src/lib/gen2/data/items.zig +1 -3
  66. package/src/lib/gen2/data/moves.zig +2 -4
  67. package/src/lib/gen2/data/species.zig +1 -3
  68. package/src/lib/gen2/data/types.zig +7 -5
  69. package/src/lib/gen2/data.zig +33 -35
  70. package/src/lib/gen2/generated.zig +18 -23
  71. package/src/lib/gen2/helpers.zig +32 -33
  72. package/src/lib/gen2/mechanics.zig +105 -132
  73. package/src/lib/napi.h +2 -0
  74. package/src/lib/node.zig +4 -121
  75. package/src/lib/pkmn.zig +103 -22
  76. package/src/lib/wasm.zig +9 -9
  77. package/src/tools/display/dom.ts +108 -0
  78. package/src/tools/display/ui.css +354 -0
  79. package/build/test/display.js +0 -400
  80. package/build/test/display.js.map +0 -1
  81. package/src/test/display/pkmn.html.tmpl +0 -368
package/build.zig CHANGED
@@ -1,5 +1,39 @@
1
- const std = @import("std");
2
1
  const builtin = @import("builtin");
2
+ const std = @import("std");
3
+
4
+ fn ArrayList(comptime T: type) type {
5
+ return std.array_list.Aligned(T, null);
6
+ }
7
+
8
+ pub const Options = struct {
9
+ showdown: ?bool = null,
10
+ log: ?bool = null,
11
+ chance: ?bool = null,
12
+ calc: ?bool = null,
13
+ };
14
+
15
+ pub fn module(
16
+ b: *std.Build,
17
+ options: Options,
18
+ target: std.Build.ResolvedTarget,
19
+ optimize: std.builtin.OptimizeMode,
20
+ ) *std.Build.Module {
21
+ const build_options = b.addOptions();
22
+ build_options.addOption(?bool, "showdown", options.showdown);
23
+ build_options.addOption(?bool, "log", options.log);
24
+ build_options.addOption(?bool, "chance", options.chance);
25
+ build_options.addOption(?bool, "calc", options.calc);
26
+
27
+ const root_source_file = b.path("src/lib/pkmn.zig");
28
+ const imports: []const std.Build.Module.Import =
29
+ &.{.{ .name = "build_options", .module = build_options.createModule() }};
30
+ return b.createModule(.{
31
+ .root_source_file = root_source_file,
32
+ .target = target,
33
+ .optimize = optimize,
34
+ .imports = imports,
35
+ });
36
+ }
3
37
 
4
38
  pub fn build(b: *std.Build) !void {
5
39
  const target = b.standardTargetOptions(.{});
@@ -8,6 +42,10 @@ pub fn build(b: *std.Build) !void {
8
42
  const node_headers = b.option([]const u8, "node-headers", "Path to node-headers");
9
43
  const node_import_lib =
10
44
  b.option([]const u8, "node-import-library", "Path to node import library (Windows)");
45
+ const demo = if (try exists(b, "src/tools/demo.zig"))
46
+ b.option(bool, "demo", "Build the demo library") orelse false
47
+ else
48
+ false;
11
49
  const wasm = b.option(bool, "wasm", "Build a WASM library") orelse false;
12
50
  const wasm_stack_size =
13
51
  b.option(u64, "wasm-stack-size", "The size of WASM stack") orelse std.wasm.page_size;
@@ -17,14 +55,17 @@ pub fn build(b: *std.Build) !void {
17
55
  const emit_asm = b.option(bool, "emit-asm", "Output .s (assembly code)") orelse false;
18
56
  const emit_ll = b.option(bool, "emit-ll", "Output .ll (LLVM IR)") orelse false;
19
57
 
20
- const cmd = b.findProgram(&.{"strip"}, &.{}) catch null;
58
+ const cmd = if (@hasDecl(std.Build, "FindProgramOptions"))
59
+ b.findProgram(.{ .names = &.{"strip"} })
60
+ else
61
+ b.findProgram(&.{"strip"}, &.{}) catch null;
21
62
 
22
63
  const json = @embedFile("package.json");
23
64
  var parsed = try std.json.parseFromSlice(std.json.Value, b.allocator, json, .{});
24
65
  defer parsed.deinit();
25
66
  const version = parsed.value.object.get("version").?.string;
26
67
  const description = parsed.value.object.get("description").?.string;
27
- var repository = std.mem.splitSequence(u8, parsed.value.object.get("repository").?.string, ":");
68
+ var repository = std.mem.splitScalar(u8, parsed.value.object.get("repository").?.string, ':');
28
69
  std.debug.assert(std.mem.eql(u8, repository.first(), "github"));
29
70
 
30
71
  const showdown =
@@ -32,6 +73,7 @@ pub fn build(b: *std.Build) !void {
32
73
  const log = b.option(bool, "log", "Enable protocol message logging") orelse false;
33
74
  const chance = b.option(bool, "chance", "Enable update probability tracking") orelse false;
34
75
  const calc = b.option(bool, "calc", "Enable damage calculator support") orelse false;
76
+ const extra = b.option([]const []const u8, "option", "Enable extra option");
35
77
 
36
78
  const options = b.addOptions();
37
79
  options.addOption(?bool, "showdown", showdown);
@@ -39,9 +81,26 @@ pub fn build(b: *std.Build) !void {
39
81
  options.addOption(?bool, "chance", chance);
40
82
  options.addOption(?bool, "calc", calc);
41
83
 
84
+ if (extra) |opts| {
85
+ for (opts) |opt| {
86
+ if (std.mem.indexOfScalar(u8, opt, '=')) |i| {
87
+ options.addOption(?bool, opt[0..i], if (std.mem.eql(u8, opt[i + 1 ..], "true"))
88
+ true
89
+ else if (std.mem.eql(u8, opt[i + 1 ..], "false"))
90
+ false
91
+ else {
92
+ std.log.err("Invalid option: {s}\n", .{opt});
93
+ return error.InvalidOption;
94
+ });
95
+ } else {
96
+ options.addOption(?bool, opt, true);
97
+ }
98
+ }
99
+ }
100
+
42
101
  const name = if (showdown) "pkmn-showdown" else "pkmn";
43
102
 
44
- const module = b.addModule("pkmn", .{
103
+ const pkmn: *std.Build.Module = b.addModule("pkmn", .{
45
104
  .root_source_file = b.path("src/lib/pkmn.zig"),
46
105
  .optimize = optimize,
47
106
  .target = target,
@@ -50,22 +109,34 @@ pub fn build(b: *std.Build) !void {
50
109
 
51
110
  var c = false;
52
111
  if (node_headers) |headers| {
112
+ const translate_c = b.addTranslateC(.{
113
+ .root_source_file = b.path("src/lib/napi.h"),
114
+ .target = target,
115
+ // TODO: workaround for ziglang/zig#35515
116
+ .optimize = .Debug,
117
+ });
118
+ translate_c.addSystemIncludePath(b.path(headers));
53
119
  const addon = b.fmt("{s}.node", .{name});
54
- const lib = b.addSharedLibrary(.{
120
+ const path = b.path("src/lib/node.zig");
121
+ const lib = b.addLibrary(.{
122
+ .linkage = .dynamic,
55
123
  .name = addon,
56
- .root_source_file = b.path("src/lib/node.zig"),
57
- .optimize = optimize,
58
- .target = target,
59
- .strip = strip,
60
- .pic = pic,
124
+ .root_module = b.createModule(.{
125
+ .root_source_file = path,
126
+ .optimize = optimize,
127
+ .target = target,
128
+ .strip = strip,
129
+ .pic = pic,
130
+ }),
61
131
  });
62
132
  lib.root_module.addOptions("build_options", options);
63
- lib.addSystemIncludePath(b.path(headers));
64
- lib.linkLibC();
133
+ lib.root_module.addImport("napi", translate_c.createModule());
134
+ lib.root_module.link_libc = true;
65
135
  if (node_import_lib) |il| {
66
- lib.addObjectFile(b.path(il));
136
+ lib.root_module.addObjectFile(b.path(il));
67
137
  } else if (target.result.os.tag == .windows) {
68
- try std.io.getStdErr().writeAll("Must provide --node-import-library path on Windows\n");
138
+ var err = std.Io.File.stderr().writer(b.graph.io, &.{});
139
+ try err.interface.writeAll("Must provide --node-import-library path on Windows\n");
69
140
  std.process.exit(1);
70
141
  }
71
142
  lib.linker_allow_shlib_undefined = true;
@@ -80,68 +151,49 @@ pub fn build(b: *std.Build) !void {
80
151
  // rename the file ourself in install-pkmn-engine
81
152
  b.installArtifact(lib);
82
153
  } else if (wasm) {
83
- const exe = b.addExecutable(.{
84
- .name = name,
85
- .root_source_file = b.path("src/lib/wasm.zig"),
86
- .optimize = switch (optimize) {
87
- .ReleaseFast, .ReleaseSafe => .ReleaseSmall,
88
- else => optimize,
89
- },
90
- .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
91
- .strip = strip,
92
- .pic = pic,
93
- });
94
- exe.root_module.addOptions("build_options", options);
95
- exe.root_module.export_symbol_names = &[_][]const u8{
96
- "SHOWDOWN",
97
- "LOG",
98
- "CHANCE",
99
- "CALC",
100
- "GEN1_CHOICES_SIZE",
101
- "GEN1_LOGS_SIZE",
102
- };
103
- exe.entry = .disabled;
104
- exe.stack_size = wasm_stack_size;
105
- const opt = b.findProgram(&.{"wasm-opt"}, &.{"./node_modules/.bin"}) catch null;
106
- if (optimize != .Debug and opt != null) {
107
- const out = b.fmt("build/lib/{s}.wasm", .{name});
108
- const sh = b.addSystemCommand(&.{ opt.?, "-O4" });
109
- sh.addArtifactArg(exe);
110
- sh.addArg("-o");
111
- sh.addFileArg(b.path(out));
112
- b.getInstallStep().dependOn(&sh.step);
113
- } else {
114
- b.getInstallStep().dependOn(&b.addInstallArtifact(exe, .{
115
- .dest_dir = .{ .override = std.Build.InstallDir{ .lib = {} } },
116
- }).step);
117
- }
154
+ const path = "src/lib/wasm.zig";
155
+ try buildWasm(b, name, path, optimize, strip, pic, wasm_stack_size, null, options);
156
+ } else if (demo) {
157
+ const path = "src/tools/demo.zig";
158
+ const n = if (showdown) "demo-showdown" else "demo";
159
+ const mod = pkmn;
160
+ try buildWasm(b, n, path, optimize, strip, pic, wasm_stack_size, mod, options);
118
161
  } else if (dynamic) {
119
- const lib = b.addSharedLibrary(.{
162
+ const path = b.path("src/lib/c.zig");
163
+ const lib = b.addLibrary(.{
164
+ .linkage = .dynamic,
120
165
  .name = name,
121
- .root_source_file = b.path("src/lib/c.zig"),
122
- .version = try std.SemanticVersion.parse(version),
123
- .optimize = optimize,
124
- .target = target,
125
- .strip = strip,
126
- .pic = pic,
166
+ .root_module = b.createModule(.{
167
+ .root_source_file = path,
168
+ .optimize = optimize,
169
+ .target = target,
170
+ .strip = strip,
171
+ .pic = pic,
172
+ }),
127
173
  });
128
174
  lib.root_module.addOptions("build_options", options);
129
- lib.addIncludePath(b.path("src/include"));
175
+ lib.root_module.addIncludePath(b.path("src/include"));
130
176
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
131
177
  b.installArtifact(lib);
132
178
  c = true;
133
179
  } else {
134
- const lib = b.addStaticLibrary(.{
180
+ const path = b.path("src/lib/c.zig");
181
+ const lib = b.addLibrary(.{
182
+ .linkage = .static,
135
183
  .name = name,
136
- .root_source_file = b.path("src/lib/c.zig"),
137
- .optimize = optimize,
138
- .target = target,
139
- .strip = strip,
140
- .pic = pic,
184
+ .root_module = b.createModule(.{
185
+ .root_source_file = path,
186
+ .optimize = optimize,
187
+ .target = target,
188
+ .strip = strip,
189
+ .pic = pic,
190
+ }),
141
191
  });
142
192
  lib.root_module.addOptions("build_options", options);
143
- lib.addIncludePath(b.path("src/include"));
144
- lib.bundle_compiler_rt = true;
193
+ lib.root_module.addIncludePath(b.path("src/include"));
194
+ if (target.result.os.tag != .macos) {
195
+ lib.bundle_compiler_rt = true;
196
+ }
145
197
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
146
198
  if (emit_asm) {
147
199
  b.getInstallStep().dependOn(&b.addInstallFileWithDir(
@@ -162,41 +214,52 @@ pub fn build(b: *std.Build) !void {
162
214
  }
163
215
 
164
216
  if (c) {
165
- const header = b.addInstallFileWithDir(
166
- b.path("src/include/pkmn.h"),
167
- .header,
168
- "pkmn.h",
169
- );
217
+ const path = b.path("src/include/pkmn.h");
218
+ const header = b.addInstallFileWithDir(path, .header, "pkmn.h");
170
219
  b.getInstallStep().dependOn(&header.step);
171
220
 
172
- const pc = b.fmt("lib{s}.pc", .{name});
173
- const file = try std.fs.path.relative(
174
- b.allocator,
175
- try std.process.getCwdAlloc(b.allocator),
176
- try b.cache_root.join(b.allocator, &.{pc}),
177
- );
178
- const pkgconfig_file = try std.fs.cwd().createFile(file, .{});
179
-
180
- const dirname = comptime std.fs.path.dirname(@src().file) orelse ".";
181
- const writer = pkgconfig_file.writer();
182
- try writer.print(
183
- \\prefix={0s}/{1s}
221
+ const content = try std.fmt.allocPrint(b.allocator,
222
+ \\prefix=${{pcfiledir}}/../..
184
223
  \\includedir=${{prefix}}/include
185
224
  \\libdir=${{prefix}}/lib
186
225
  \\
187
- \\Name: lib{2s}
188
- \\URL: https://github.com/{3s}
189
- \\Description: {4s}
190
- \\Version: {5s}
226
+ \\Name: lib{0s}
227
+ \\URL: https://github.com/{1s}
228
+ \\Description: {2s}
229
+ \\Version: {3s}
191
230
  \\Cflags: -I${{includedir}}
192
- \\Libs: -L${{libdir}} -l{2s}
193
- , .{ dirname, b.install_path, name, repository.next().?, description, version });
194
- defer pkgconfig_file.close();
231
+ \\Libs: -L${{libdir}} -l{0s}
232
+ , .{ name, repository.next().?, description, version });
195
233
 
196
- b.installFile(file, b.fmt("share/pkgconfig/{s}", .{pc}));
234
+ const pc = b.fmt("lib{s}.pc", .{name});
235
+ if (@hasDecl(std.Build, "FindProgramOptions")) {
236
+ const write_file = b.addWriteFiles();
237
+ const pkgconfig = write_file.add(pc, content);
238
+ b.getInstallStep().dependOn(&b.addInstallFileWithDir(
239
+ pkgconfig,
240
+ .prefix,
241
+ b.fmt("share/pkgconfig/{s}", .{pc}),
242
+ ).step);
243
+ } else {
244
+ const cwd = try std.process.currentPathAlloc(b.graph.io, b.allocator);
245
+ const file = try std.Io.Dir.path.relative(
246
+ b.allocator,
247
+ cwd,
248
+ &b.graph.environ_map,
249
+ cwd,
250
+ try b.cache_root.join(b.allocator, &.{pc}),
251
+ );
252
+ const pkgconfig = try std.Io.Dir.cwd().createFile(b.graph.io, file, .{});
253
+ defer pkgconfig.close(b.graph.io);
254
+
255
+ var writer = pkgconfig.writer(b.graph.io, &.{});
256
+ try writer.interface.writeAll(content);
257
+
258
+ b.installFile(file, b.fmt("share/pkgconfig/{s}", .{pc}));
259
+ }
197
260
  }
198
261
 
199
- const config = .{
262
+ const config: Config = .{
200
263
  .target = target,
201
264
  .optimize = optimize,
202
265
  .pic = pic,
@@ -207,10 +270,14 @@ pub fn build(b: *std.Build) !void {
207
270
  // TODO: tests can be run multiple times due to @imports
208
271
  const tests = TestStep.create(b, options, config);
209
272
 
210
- var exes = std.ArrayList(*std.Build.Step.Compile).init(b.allocator);
273
+ var exes: ArrayList(*std.Build.Step.Compile) = .empty;
211
274
  const tools: ToolConfig = .{
212
- .showdown = showdown,
213
- .module = module,
275
+ .options = .{
276
+ .showdown = showdown,
277
+ .log = log,
278
+ .chance = chance,
279
+ .calc = calc,
280
+ },
214
281
  .general = config,
215
282
  .tool = .{
216
283
  .tests = if (tests.build) tests else null,
@@ -242,13 +309,97 @@ pub fn build(b: *std.Build) !void {
242
309
  if (dump) |t| b.step("dump", "Run protocol dump tool").dependOn(&t.step);
243
310
  if (fuzz) |t| b.step("fuzz", "Run fuzz tester").dependOn(&t.step);
244
311
  if (serde) |t| b.step("serde", "Run serialization/deserialization tool").dependOn(&t.step);
245
- b.step("test", "Run all tests").dependOn(&tests.step);
246
- b.step("tools", "Install tools").dependOn(&ToolsStep.create(b, &exes).step);
312
+ b.step("test", "Run all tests").dependOn(tests.step);
313
+ b.step("tools", "Install tools").dependOn(ToolsStep.create(b, &exes).step);
247
314
  if (transitions) |t| {
248
315
  b.step("transitions", "Visualize transitions algorithm search").dependOn(&t.step);
249
316
  }
250
317
  }
251
318
 
319
+ fn buildWasm(
320
+ b: *std.Build,
321
+ name: []const u8,
322
+ root_src_file: []const u8,
323
+ optimize: std.builtin.OptimizeMode,
324
+ strip: ?bool,
325
+ pic: ?bool,
326
+ wasm_stack_size: u64,
327
+ import: ?*std.Build.Module,
328
+ options: anytype,
329
+ ) !void {
330
+ const mode = switch (optimize) {
331
+ .ReleaseFast, .ReleaseSafe => .ReleaseSmall,
332
+ else => optimize,
333
+ };
334
+ // https://webassembly.org/features/
335
+ var features = std.Target.wasm.featureSet(&.{
336
+ .atomics,
337
+ .bulk_memory,
338
+ // .exception_handling,
339
+ .extended_const,
340
+ // .half_precision,
341
+ // .multimemory,
342
+ .mutable_globals,
343
+ .nontrapping_fptoint,
344
+ .reference_types,
345
+ // .relaxed_simd,
346
+ .sign_ext,
347
+ .simd128,
348
+ // .tail_call,
349
+ });
350
+ if (builtin.zig_version.order(try std.SemanticVersion.parse("0.12.0")) != .lt) {
351
+ features.addFeature(@intFromEnum(std.Target.wasm.Feature.multivalue));
352
+ }
353
+ const freestanding = b.resolveTargetQuery(.{
354
+ .cpu_arch = .wasm32,
355
+ .os_tag = .freestanding,
356
+ .cpu_features_add = features,
357
+ });
358
+ const path = b.path(root_src_file);
359
+ const exe = b.addExecutable(.{
360
+ .name = name,
361
+ .root_module = b.createModule(.{
362
+ .root_source_file = path,
363
+ .optimize = mode,
364
+ .target = freestanding,
365
+ .strip = strip,
366
+ .pic = pic,
367
+ }),
368
+ });
369
+
370
+ var file = try std.Io.Dir.cwd().openFile(b.graph.io, root_src_file, .{});
371
+ defer file.close(b.graph.io);
372
+ var reader = file.reader(b.graph.io, &.{});
373
+ const bytes = try reader.interface.allocRemaining(b.allocator, .unlimited);
374
+ exe.root_module.export_symbol_names = try exports(b, bytes);
375
+ exe.entry = .disabled;
376
+
377
+ if (import) |i| {
378
+ exe.root_module.addImport("pkmn", i);
379
+ }
380
+ exe.stack_size = wasm_stack_size;
381
+ exe.root_module.addOptions("build_options", options);
382
+
383
+ const opt = if (@hasDecl(std.Build, "FindProgramOptions")) blk: {
384
+ if (exists(b, "./node_modules/.bin/wasm-opt") catch false) {
385
+ break :blk "./node_modules/.bin/wasm-opt";
386
+ }
387
+ break :blk b.findProgram(.{ .names = &.{"wasm-opt"} });
388
+ } else b.findProgram(&.{"wasm-opt"}, &.{"./node_modules/.bin"}) catch null;
389
+ if (optimize != .Debug and opt != null) {
390
+ const out = b.fmt("build/lib/{s}.wasm", .{name});
391
+ const sh = b.addSystemCommand(&.{ opt.?, "--enable-bulk-memory", "--enable-simd", "-O4" });
392
+ sh.addArtifactArg(exe);
393
+ sh.addArg("-o");
394
+ sh.addFileArg(b.path(out));
395
+ b.getInstallStep().dependOn(&sh.step);
396
+ } else {
397
+ b.getInstallStep().dependOn(&b.addInstallArtifact(exe, .{
398
+ .dest_dir = .{ .override = std.Build.InstallDir{ .lib = {} } },
399
+ }).step);
400
+ }
401
+ }
402
+
252
403
  fn maybeStrip(
253
404
  b: *std.Build,
254
405
  artifact: *std.Build.Step.Compile,
@@ -276,7 +427,7 @@ const Config = struct {
276
427
  };
277
428
 
278
429
  const TestStep = struct {
279
- step: std.Build.Step,
430
+ step: *std.Build.Step,
280
431
  build: bool,
281
432
 
282
433
  pub fn create(b: *std.Build, options: *std.Build.Step.Options, config: Config) *TestStep {
@@ -285,79 +436,146 @@ const TestStep = struct {
285
436
  b.option([]const u8, "test-filter", "Skip tests that do not match filter");
286
437
 
287
438
  const self = b.allocator.create(TestStep) catch @panic("OOM");
288
- const step = std.Build.Step.init(.{ .id = .custom, .name = "Run all tests", .owner = b });
289
- self.* = .{ .step = step, .build = test_filter == null };
290
439
 
440
+ const path = b.path("src/lib/test.zig");
291
441
  const tests = b.addTest(.{
292
- .root_source_file = b.path("src/lib/test.zig"),
293
- .optimize = config.optimize,
294
- .target = config.target,
295
- .filter = test_filter,
296
- .single_threaded = true,
297
- .strip = config.strip,
298
- .pic = config.pic,
442
+ .root_module = b.createModule(.{
443
+ .root_source_file = path,
444
+ .optimize = config.optimize,
445
+ .target = config.target,
446
+ .single_threaded = true,
447
+ .strip = config.strip,
448
+ .pic = config.pic,
449
+ }),
450
+ .filters = if (test_filter) |filter| &.{filter} else &.{},
299
451
  });
300
452
  tests.root_module.addOptions("build_options", options);
453
+
301
454
  maybeStrip(b, tests, &tests.step, config.strip, config.cmd);
302
455
  if (coverage) |c| {
303
456
  tests.setExecCmd(&.{ "kcov", "--include-pattern=src/lib", c, null });
304
457
  }
305
- self.step.dependOn(&b.addRunArtifact(tests).step);
458
+
459
+ if (@hasDecl(std.Build, "FindProgramOptions")) {
460
+ const run_step = b.addRunArtifact(tests);
461
+ self.* = .{ .step = &run_step.step, .build = test_filter == null };
462
+ } else {
463
+ const custom_step = b.allocator.create(std.Build.Step) catch @panic("OOM");
464
+ custom_step.* = std.Build.Step.init(.{
465
+ .id = .custom,
466
+ .name = "Run all tests",
467
+ .owner = b,
468
+ });
469
+ custom_step.dependOn(&b.addRunArtifact(tests).step);
470
+ self.* = .{ .step = custom_step, .build = test_filter == null };
471
+ }
306
472
 
307
473
  return self;
308
474
  }
309
475
  };
310
476
 
311
477
  const ToolConfig = struct {
312
- showdown: ?bool,
313
- module: *std.Build.Module,
478
+ options: Options,
314
479
  general: Config,
315
480
  tool: struct {
316
481
  tests: ?*TestStep,
317
482
  name: ?[]const u8 = null,
318
- exes: *std.ArrayList(*std.Build.Step.Compile),
483
+ exes: *ArrayList(*std.Build.Step.Compile),
319
484
  },
320
485
  };
321
486
 
322
487
  fn tool(b: *std.Build, path: []const u8, config: ToolConfig) !?*std.Build.Step.Run {
323
- std.fs.cwd().access(path, .{}) catch |err| switch (err) {
324
- error.FileNotFound => return null,
325
- else => |e| return e,
326
- };
488
+ if (!try exists(b, path)) return null;
327
489
  var name = config.tool.name orelse std.fs.path.basename(path);
328
490
  const index = std.mem.lastIndexOfScalar(u8, name, '.');
329
491
  if (index) |i| name = name[0..i];
330
- if (config.showdown orelse false) name = b.fmt("{s}-showdown", .{name});
492
+ if (config.options.showdown orelse false) name = b.fmt("{s}-showdown", .{name});
331
493
 
332
494
  const exe = b.addExecutable(.{
333
495
  .name = name,
334
- .root_source_file = b.path(path),
335
- .target = config.general.target,
336
- .optimize = config.general.optimize,
337
- .single_threaded = true,
338
- .strip = config.general.strip,
339
- .pic = config.general.pic,
496
+ .root_module = b.createModule(.{
497
+ .root_source_file = b.path(path),
498
+ .target = config.general.target,
499
+ .optimize = config.general.optimize,
500
+ .strip = config.general.strip,
501
+ .pic = config.general.pic,
502
+ }),
340
503
  });
341
- exe.root_module.addImport("pkmn", config.module);
504
+
505
+ const import = module(b, config.options, config.general.target, config.general.optimize);
506
+ exe.root_module.addImport("pkmn", import);
507
+ if (config.general.target.result.os.tag == .windows) {
508
+ exe.root_module.linkSystemLibrary("advapi32", .{});
509
+ }
342
510
 
343
511
  if (config.tool.tests) |ts| ts.step.dependOn(&exe.step);
344
- config.tool.exes.append(exe) catch @panic("OOM");
512
+ config.tool.exes.append(b.allocator, exe) catch @panic("OOM");
345
513
 
346
514
  const run = b.addRunArtifact(exe);
347
515
  maybeStrip(b, exe, &run.step, config.general.strip, config.general.cmd);
348
- if (b.args) |args| run.addArgs(args);
516
+ if (@hasDecl(std.Build, "FindProgramOptions")) {
517
+ run.addPassthruArgs();
518
+ } else {
519
+ if (b.args) |args| run.addArgs(args);
520
+ }
349
521
 
350
522
  return run;
351
523
  }
352
524
 
525
+ fn exists(b: *std.Build, path: []const u8) !bool {
526
+ std.Io.Dir.cwd().access(b.graph.io, path, .{}) catch |err| switch (err) {
527
+ error.FileNotFound => return false,
528
+ else => |e| return e,
529
+ };
530
+ return true;
531
+ }
532
+
353
533
  const ToolsStep = struct {
354
- step: std.Build.Step,
534
+ step: *std.Build.Step,
355
535
 
356
- pub fn create(b: *std.Build, exes: *std.ArrayList(*std.Build.Step.Compile)) *ToolsStep {
536
+ pub fn create(b: *std.Build, exes: *ArrayList(*std.Build.Step.Compile)) *ToolsStep {
357
537
  const self = b.allocator.create(ToolsStep) catch @panic("OOM");
358
- const step = std.Build.Step.init(.{ .id = .custom, .name = "Install tools", .owner = b });
359
- self.* = .{ .step = step };
538
+
539
+ if (@hasDecl(std.Build, "FindProgramOptions")) {
540
+ const step = b.allocator.create(std.Build.Step.TopLevel) catch @panic("OOM");
541
+ step.* = .{
542
+ .step = std.Build.Step.init(.{
543
+ .tag = .top_level,
544
+ .name = "Install tools",
545
+ .owner = b,
546
+ }),
547
+ .description = "Install tools",
548
+ };
549
+ self.* = .{ .step = &step.step };
550
+ } else {
551
+ const step = b.allocator.create(std.Build.Step) catch @panic("OOM");
552
+ step.* = std.Build.Step.init(.{
553
+ .id = .custom,
554
+ .name = "Install tools",
555
+ .owner = b,
556
+ });
557
+ self.* = .{ .step = step };
558
+ }
360
559
  for (exes.items) |t| self.step.dependOn(&b.addInstallArtifact(t, .{}).step);
560
+
361
561
  return self;
362
562
  }
363
563
  };
564
+
565
+ pub fn exports(b: *std.Build, bytes: []const u8) ![][]const u8 {
566
+ var symbols: ArrayList([]const u8) = .empty;
567
+
568
+ var it = std.mem.splitSequence(u8, bytes, "export ");
569
+ _ = it.next();
570
+ while (it.next()) |s| {
571
+ if (std.mem.startsWith(u8, s, "const ")) {
572
+ const i = std.mem.indexOf(u8, s[6..], " ").?;
573
+ try symbols.append(b.allocator, s[6 .. 6 + i]);
574
+ } else { // "fn "
575
+ const i = std.mem.indexOf(u8, s[3..], "(").?;
576
+ try symbols.append(b.allocator, s[3 .. 3 + i]);
577
+ }
578
+ }
579
+
580
+ return symbols.items;
581
+ }