@pkmn/engine 0.1.0-dev.5ea92492 → 0.1.0-dev.5fbc7aef

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 (87) hide show
  1. package/LICENSE +1 -3
  2. package/README.md +141 -76
  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 +28 -6
  13. package/build/pkg/addon.js +42 -73
  14. package/build/pkg/addon.js.map +1 -1
  15. package/build/pkg/data/ids.json +12 -12
  16. package/build/pkg/data/layout.json +40 -5
  17. package/build/pkg/data/protocol.json +84 -13
  18. package/build/pkg/data.d.ts +27 -5
  19. package/build/pkg/data.js.map +1 -1
  20. package/build/pkg/gen1.d.ts +18 -20
  21. package/build/pkg/gen1.js +76 -51
  22. package/build/pkg/gen1.js.map +1 -1
  23. package/build/pkg/index.d.ts +57 -103
  24. package/build/pkg/index.js +23 -70
  25. package/build/pkg/index.js.map +1 -1
  26. package/build/pkg/protocol.d.ts +24 -3
  27. package/build/pkg/protocol.js +127 -104
  28. package/build/pkg/protocol.js.map +1 -1
  29. package/build/tools/debug.js +69 -74
  30. package/build/tools/debug.js.map +1 -1
  31. package/build/tools/display/debug.jsx +152 -0
  32. package/build/tools/display/debug.jsx.map +1 -0
  33. package/build/tools/display/index.js +152 -0
  34. package/build/tools/display/index.js.map +1 -0
  35. package/build/tools/display/ui.jsx +359 -0
  36. package/build/tools/display/ui.jsx.map +1 -0
  37. package/build/tools/display/util.js +33 -0
  38. package/build/tools/display/util.js.map +1 -0
  39. package/build.zig +336 -166
  40. package/package.json +71 -120
  41. package/src/bin/install-pkmn-engine +57 -26
  42. package/src/bin/pkmn-debug +1 -1
  43. package/src/lib/bindings/c.zig +200 -0
  44. package/src/lib/bindings/node.zig +117 -0
  45. package/src/lib/bindings/wasm.zig +50 -0
  46. package/src/lib/common/array.zig +70 -0
  47. package/src/lib/common/battle.zig +17 -0
  48. package/src/lib/common/data.zig +19 -31
  49. package/src/lib/common/debug.zig +51 -20
  50. package/src/lib/common/js.zig +114 -0
  51. package/src/lib/common/optional.zig +61 -0
  52. package/src/lib/common/options.zig +18 -3
  53. package/src/lib/common/protocol.zig +1050 -497
  54. package/src/lib/common/rational.zig +440 -0
  55. package/src/lib/common/rng.zig +31 -24
  56. package/src/lib/common/util.zig +44 -0
  57. package/src/lib/gen1/calc.zig +956 -0
  58. package/src/lib/gen1/chance.zig +1170 -0
  59. package/src/lib/gen1/data/moves.zig +203 -193
  60. package/src/lib/gen1/data/species.zig +6 -8
  61. package/src/lib/gen1/data/types.zig +12 -13
  62. package/src/lib/gen1/data.zig +104 -94
  63. package/src/lib/gen1/helpers.zig +138 -51
  64. package/src/lib/gen1/mechanics.zig +2720 -2351
  65. package/src/lib/gen2/calc.zig +150 -0
  66. package/src/lib/gen2/chance.zig +896 -0
  67. package/src/lib/gen2/data/items.zig +15 -23
  68. package/src/lib/gen2/data/moves.zig +665 -573
  69. package/src/lib/gen2/data/species.zig +3 -5
  70. package/src/lib/gen2/data/types.zig +141 -36
  71. package/src/lib/gen2/data.zig +140 -104
  72. package/src/lib/gen2/generated.zig +801 -0
  73. package/src/lib/gen2/helpers.zig +58 -45
  74. package/src/lib/gen2/mechanics.zig +3582 -30
  75. package/src/lib/napi.h +2 -0
  76. package/src/lib/node.zig +14 -0
  77. package/src/lib/pkmn.zig +177 -22
  78. package/src/lib/wasm.zig +13 -0
  79. package/src/tools/display/dom.ts +93 -0
  80. package/src/tools/display/ui.css +354 -0
  81. package/build/test/display.js +0 -382
  82. package/build/test/display.js.map +0 -1
  83. package/src/lib/binding/node.zig +0 -229
  84. package/src/lib/binding/wasm.zig +0 -10
  85. package/src/lib/gen1/README.md +0 -791
  86. package/src/lib/gen2/README.md +0 -309
  87. package/src/test/display/pkmn.html.tmpl +0 -368
package/build.zig CHANGED
@@ -1,17 +1,30 @@
1
- const std = @import("std");
2
1
  const builtin = @import("builtin");
2
+ const std = @import("std");
3
3
 
4
- pub const Options = struct { showdown: ?bool = null, trace: ?bool = null };
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
+ };
5
14
 
6
15
  pub fn module(b: *std.Build, options: Options) *std.Build.Module {
7
16
  const dirname = comptime std.fs.path.dirname(@src().file) orelse ".";
8
17
  const build_options = b.addOptions();
9
18
  build_options.addOption(?bool, "showdown", options.showdown);
10
- build_options.addOption(?bool, "trace", options.trace);
11
- return b.createModule(.{
12
- .source_file = .{ .path = dirname ++ "/src/lib/pkmn.zig" },
13
- .dependencies = &.{.{ .name = "build_options", .module = build_options.createModule() }},
14
- });
19
+ build_options.addOption(?bool, "log", options.log);
20
+ build_options.addOption(?bool, "chance", options.chance);
21
+ build_options.addOption(?bool, "calc", options.calc);
22
+
23
+ const root_source_file: std.Build.LazyPath =
24
+ .{ .cwd_relative = dirname ++ "/src/lib/pkmn.zig" };
25
+ const imports: []const std.Build.Module.Import =
26
+ &.{.{ .name = "build_options", .module = build_options.createModule() }};
27
+ return b.createModule(.{ .root_source_file = root_source_file, .imports = imports });
15
28
  }
16
29
 
17
30
  pub fn build(b: *std.Build) !void {
@@ -21,140 +34,191 @@ pub fn build(b: *std.Build) !void {
21
34
  const node_headers = b.option([]const u8, "node-headers", "Path to node-headers");
22
35
  const node_import_lib =
23
36
  b.option([]const u8, "node-import-library", "Path to node import library (Windows)");
37
+ const demo = if (try exists(b, "src/tools/demo.zig"))
38
+ b.option(bool, "demo", "Build the demo library") orelse false
39
+ else
40
+ false;
24
41
  const wasm = b.option(bool, "wasm", "Build a WASM library") orelse false;
25
42
  const wasm_stack_size =
26
43
  b.option(u64, "wasm-stack-size", "The size of WASM stack") orelse std.wasm.page_size;
27
44
  const dynamic = b.option(bool, "dynamic", "Build a dynamic library") orelse false;
28
- const strip = b.option(bool, "strip", "Strip debugging symbols from binary") orelse false;
29
- const pic = b.option(bool, "pic", "Force position independent code") orelse false;
45
+ const strip = b.option(bool, "strip", "Strip debugging symbols from binary");
46
+ const pic = b.option(bool, "pic", "Force position independent code");
30
47
  const emit_asm = b.option(bool, "emit-asm", "Output .s (assembly code)") orelse false;
48
+ const emit_ll = b.option(bool, "emit-ll", "Output .ll (LLVM IR)") orelse false;
31
49
 
32
50
  const cmd = b.findProgram(&.{"strip"}, &.{}) catch null;
33
51
 
34
- var parser = std.json.Parser.init(b.allocator, false);
35
- defer parser.deinit();
36
- var tree = try parser.parse(@embedFile("package.json"));
37
- defer tree.deinit();
38
- const version = tree.root.Object.get("version").?.String;
39
- const description = tree.root.Object.get("description").?.String;
40
- var repository = std.mem.split(u8, tree.root.Object.get("repository").?.String, ":");
52
+ const json = @embedFile("package.json");
53
+ var parsed = try std.json.parseFromSlice(std.json.Value, b.allocator, json, .{});
54
+ defer parsed.deinit();
55
+ const version = parsed.value.object.get("version").?.string;
56
+ const description = parsed.value.object.get("description").?.string;
57
+ var repository = std.mem.splitSequence(u8, parsed.value.object.get("repository").?.string, ":");
41
58
  std.debug.assert(std.mem.eql(u8, repository.first(), "github"));
42
59
 
43
60
  const showdown =
44
61
  b.option(bool, "showdown", "Enable Pokémon Showdown compatibility mode") orelse false;
45
- const trace = b.option(bool, "trace", "Enable trace logs") orelse false;
62
+ const log = b.option(bool, "log", "Enable protocol message logging") orelse false;
63
+ const chance = b.option(bool, "chance", "Enable update probability tracking") orelse false;
64
+ const calc = b.option(bool, "calc", "Enable damage calculator support") orelse false;
65
+ const extra = b.option([]const []const u8, "option", "Enable extra option");
46
66
 
47
67
  const options = b.addOptions();
48
68
  options.addOption(?bool, "showdown", showdown);
49
- options.addOption(?bool, "trace", trace);
69
+ options.addOption(?bool, "log", log);
70
+ options.addOption(?bool, "chance", chance);
71
+ options.addOption(?bool, "calc", calc);
72
+
73
+ if (extra) |opts| {
74
+ for (opts) |opt| {
75
+ if (std.mem.indexOfScalar(u8, opt, '=')) |i| {
76
+ options.addOption(?bool, opt[0..i], if (std.mem.eql(u8, opt[i + 1 ..], "true"))
77
+ true
78
+ else if (std.mem.eql(u8, opt[i + 1 ..], "false"))
79
+ false
80
+ else {
81
+ std.log.err("Invalid option: {s}\n", .{opt});
82
+ return error.InvalidOption;
83
+ });
84
+ } else {
85
+ options.addOption(?bool, opt, true);
86
+ }
87
+ }
88
+ }
50
89
 
51
90
  const name = if (showdown) "pkmn-showdown" else "pkmn";
52
91
 
92
+ const pkmn: *std.Build.Module = b.addModule("pkmn", .{
93
+ .root_source_file = b.path("src/lib/pkmn.zig"),
94
+ .optimize = optimize,
95
+ .target = target,
96
+ .imports = &.{.{ .name = "build_options", .module = options.createModule() }},
97
+ });
98
+
53
99
  var c = false;
54
100
  if (node_headers) |headers| {
101
+ const translate_c = b.addTranslateC(.{
102
+ .root_source_file = b.path("src/lib/napi.h"),
103
+ .target = target,
104
+ .optimize = optimize,
105
+ });
106
+ translate_c.addSystemIncludePath(b.path(headers));
55
107
  const addon = b.fmt("{s}.node", .{name});
56
- const lib = b.addSharedLibrary(.{
108
+ const path = b.path("src/lib/node.zig");
109
+ const lib = b.addLibrary(.{
110
+ .linkage = .dynamic,
57
111
  .name = addon,
58
- .root_source_file = .{ .path = "src/lib/binding/node.zig" },
59
- .optimize = optimize,
60
- .target = target,
112
+ .root_module = b.createModule(.{
113
+ .root_source_file = path,
114
+ .optimize = optimize,
115
+ .target = target,
116
+ .strip = strip,
117
+ .pic = pic,
118
+ }),
61
119
  });
62
- lib.addOptions("build_options", options);
63
- lib.setMainPkgPath("./");
64
- lib.addSystemIncludePath(headers);
65
- lib.linkLibC();
120
+ lib.root_module.addOptions("build_options", options);
121
+ lib.root_module.addImport("napi", translate_c.createModule());
122
+ lib.root_module.link_libc = true;
66
123
  if (node_import_lib) |il| {
67
- lib.addObjectFile(il);
68
- } else if ((try std.zig.system.NativeTargetInfo.detect(target)).target.os.tag == .windows) {
69
- try std.io.getStdErr().writeAll("Must provide --node-import-library path on Windows\n");
124
+ lib.root_module.addObjectFile(b.path(il));
125
+ } else if (target.result.os.tag == .windows) {
126
+ var err = std.Io.File.stderr().writer(b.graph.io, &.{});
127
+ try err.interface.writeAll("Must provide --node-import-library path on Windows\n");
70
128
  std.process.exit(1);
71
129
  }
72
130
  lib.linker_allow_shlib_undefined = true;
73
131
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
74
- if (pic) lib.force_pic = pic;
75
132
  // Always emit to build/lib because this is where the driver code expects to find it
76
- // TODO(ziglang/zig#2231): using the following used to work (though was hacky):
133
+ // TODO: ziglang/zig#2231 - using the following used to work (though was hacky):
77
134
  //
78
135
  // lib.emit_bin = .{ .emit_to = b.fmt("build/lib/{s}", .{addon}) };
79
136
  // b.getInstallStep().dependOn(&lib.step);
80
137
  //
81
138
  // But ziglang/zig#14647 broke this so we now need to do an install() and then manually
82
139
  // rename the file ourself in install-pkmn-engine
83
- lib.install();
140
+ b.installArtifact(lib);
84
141
  } else if (wasm) {
85
- const lib = b.addSharedLibrary(.{
86
- .name = name,
87
- .root_source_file = .{ .path = "src/lib/binding/wasm.zig" },
88
- .optimize = switch (optimize) {
89
- .ReleaseFast, .ReleaseSafe => .ReleaseSmall,
90
- else => optimize,
91
- },
92
- .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
93
- });
94
- lib.addOptions("build_options", options);
95
- lib.setMainPkgPath("./");
96
- lib.stack_size = wasm_stack_size;
97
- lib.rdynamic = true;
98
- lib.strip = strip;
99
- if (pic) lib.force_pic = pic;
100
- const opt = b.findProgram(&.{"wasm-opt"}, &.{"./node_modules/.bin"}) catch null;
101
- if (optimize != .Debug and opt != null) {
102
- const out = b.fmt("build/lib/{s}.wasm", .{name});
103
- const sh = b.addSystemCommand(&.{ opt.?, "-O4" });
104
- sh.addArtifactArg(lib);
105
- sh.addArg("-o");
106
- sh.addFileSourceArg(.{ .path = out });
107
- b.getInstallStep().dependOn(&sh.step);
108
- }
109
- lib.install();
142
+ const path = "src/lib/wasm.zig";
143
+ try buildWasm(b, name, path, optimize, strip, pic, wasm_stack_size, null, options);
144
+ } else if (demo) {
145
+ const path = "src/tools/demo.zig";
146
+ const n = if (showdown) "demo-showdown" else "demo";
147
+ const mod = pkmn;
148
+ try buildWasm(b, n, path, optimize, strip, pic, wasm_stack_size, mod, options);
110
149
  } else if (dynamic) {
111
- const lib = b.addSharedLibrary(.{
150
+ const path = b.path("src/lib/c.zig");
151
+ const lib = b.addLibrary(.{
152
+ .linkage = .dynamic,
112
153
  .name = name,
113
- .root_source_file = .{ .path = "src/lib/binding/c.zig" },
114
- .version = try std.builtin.Version.parse(version),
115
- .optimize = optimize,
116
- .target = target,
154
+ .root_module = b.createModule(.{
155
+ .root_source_file = path,
156
+ .optimize = optimize,
157
+ .target = target,
158
+ .strip = strip,
159
+ .pic = pic,
160
+ }),
117
161
  });
118
- lib.addOptions("build_options", options);
119
- lib.setMainPkgPath("./");
120
- lib.addIncludePath("src/include");
162
+ lib.root_module.addOptions("build_options", options);
163
+ lib.root_module.addIncludePath(b.path("src/include"));
121
164
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
122
- if (pic) lib.force_pic = pic;
123
- lib.install();
165
+ b.installArtifact(lib);
124
166
  c = true;
125
167
  } else {
126
- const lib = b.addStaticLibrary(.{
168
+ const path = b.path("src/lib/c.zig");
169
+ const lib = b.addLibrary(.{
170
+ .linkage = .static,
127
171
  .name = name,
128
- .root_source_file = .{ .path = "src/lib/binding/c.zig" },
129
- .optimize = optimize,
130
- .target = target,
172
+ .root_module = b.createModule(.{
173
+ .root_source_file = path,
174
+ .optimize = optimize,
175
+ .target = target,
176
+ .strip = strip,
177
+ .pic = pic,
178
+ }),
131
179
  });
132
- lib.addOptions("build_options", options);
133
- lib.setMainPkgPath("./");
134
- lib.addIncludePath("src/include");
180
+ lib.root_module.addOptions("build_options", options);
181
+ lib.root_module.addIncludePath(b.path("src/include"));
135
182
  lib.bundle_compiler_rt = true;
136
183
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
137
- if (pic) lib.force_pic = pic;
138
- if (emit_asm) lib.emit_asm = .emit;
139
- lib.install();
184
+ if (emit_asm) {
185
+ b.getInstallStep().dependOn(&b.addInstallFileWithDir(
186
+ lib.getEmittedAsm(),
187
+ .prefix,
188
+ b.fmt("{s}.s", .{name}),
189
+ ).step);
190
+ }
191
+ if (emit_ll) {
192
+ b.getInstallStep().dependOn(&b.addInstallFileWithDir(
193
+ lib.getEmittedLlvmIr(),
194
+ .prefix,
195
+ b.fmt("{s}.ll", .{name}),
196
+ ).step);
197
+ }
198
+ b.installArtifact(lib);
140
199
  c = true;
141
200
  }
142
201
 
143
202
  if (c) {
144
- const header = b.addInstallFileWithDir(
145
- .{ .path = "src/include/pkmn.h" },
146
- .header,
147
- "pkmn.h",
148
- );
203
+ const path = b.path("src/include/pkmn.h");
204
+ const header = b.addInstallFileWithDir(path, .header, "pkmn.h");
149
205
  b.getInstallStep().dependOn(&header.step);
150
206
 
151
207
  const pc = b.fmt("lib{s}.pc", .{name});
152
- const file = try b.cache_root.join(b.allocator, &.{pc});
153
- const pkgconfig_file = try std.fs.cwd().createFile(file, .{});
208
+ const cwd = try std.process.getCwdAlloc(b.allocator);
209
+ const file = try std.Io.Dir.path.relative(
210
+ b.allocator,
211
+ try std.process.getCwdAlloc(b.allocator),
212
+ &b.graph.environ_map,
213
+ cwd,
214
+ try b.cache_root.join(b.allocator, &.{pc}),
215
+ );
216
+ const pkgconfig_file = try std.Io.Dir.cwd().createFile(b.graph.io, file, .{});
217
+ defer pkgconfig_file.close(b.graph.io);
154
218
 
155
219
  const dirname = comptime std.fs.path.dirname(@src().file) orelse ".";
156
- const writer = pkgconfig_file.writer();
157
- try writer.print(
220
+ var writer = pkgconfig_file.writer(b.graph.io, &.{});
221
+ try writer.interface.print(
158
222
  \\prefix={0s}/{1s}
159
223
  \\includedir=${{prefix}}/include
160
224
  \\libdir=${{prefix}}/lib
@@ -166,26 +230,32 @@ pub fn build(b: *std.Build) !void {
166
230
  \\Cflags: -I${{includedir}}
167
231
  \\Libs: -L${{libdir}} -l{2s}
168
232
  , .{ dirname, b.install_path, name, repository.next().?, description, version });
169
- defer pkgconfig_file.close();
170
233
 
171
234
  b.installFile(file, b.fmt("share/pkgconfig/{s}", .{pc}));
172
235
  }
173
236
 
174
- const config = .{
237
+ const config: Config = .{
175
238
  .target = target,
176
239
  .optimize = optimize,
177
240
  .pic = pic,
178
241
  .strip = strip,
179
242
  .cmd = cmd,
180
243
  };
244
+
245
+ // TODO: tests can be run multiple times due to @imports
181
246
  const tests = TestStep.create(b, options, config);
182
247
 
183
- var exes = std.ArrayList(*std.Build.CompileStep).init(b.allocator);
248
+ var exes: ArrayList(*std.Build.Step.Compile) = .empty;
184
249
  const tools: ToolConfig = .{
250
+ .options = .{
251
+ .showdown = showdown,
252
+ .log = log,
253
+ .chance = chance,
254
+ .calc = calc,
255
+ },
256
+ .module = pkmn,
185
257
  .general = config,
186
258
  .tool = .{
187
- .showdown = showdown,
188
- .trace = trace,
189
259
  .tests = if (tests.build) tests else null,
190
260
  .exes = &exes,
191
261
  },
@@ -194,39 +264,121 @@ pub fn build(b: *std.Build) !void {
194
264
  var benchmark_config = tools;
195
265
  benchmark_config.general.optimize = .ReleaseFast;
196
266
  benchmark_config.general.strip = true;
197
- const benchmark = tool(b, "src/test/benchmark.zig", benchmark_config);
267
+ const benchmark = try tool(b, "src/test/benchmark.zig", benchmark_config);
198
268
 
199
269
  var fuzz_config = tools;
200
270
  fuzz_config.general.strip = false;
201
271
  fuzz_config.tool.name = "fuzz";
202
- const fuzz = tool(b, "src/test/benchmark.zig", fuzz_config);
272
+ const fuzz = try tool(b, "src/test/fuzz.zig", fuzz_config);
273
+
274
+ const analyze = try tool(b, "src/tools/analyze.zig", tools);
275
+ const dump = try tool(b, "src/tools/dump.zig", tools);
276
+ const transitions = try tool(b, "src/tools/transitions.zig", tools);
277
+
278
+ // FIXME: serde randomly fails to build in some release configurations
279
+ var hack = tools;
280
+ if (optimize != .Debug) hack.tool.tests = null;
281
+ const serde = try tool(b, "src/tools/serde.zig", hack);
282
+
283
+ if (analyze) |t| b.step("analyze", "Run LLVM analysis tool").dependOn(&t.step);
284
+ if (benchmark) |t| b.step("benchmark", "Run benchmark code").dependOn(&t.step);
285
+ if (dump) |t| b.step("dump", "Run protocol dump tool").dependOn(&t.step);
286
+ if (fuzz) |t| b.step("fuzz", "Run fuzz tester").dependOn(&t.step);
287
+ if (serde) |t| b.step("serde", "Run serialization/deserialization tool").dependOn(&t.step);
288
+ b.step("test", "Run all tests").dependOn(&tests.step);
289
+ b.step("tools", "Install tools").dependOn(&ToolsStep.create(b, &exes).step);
290
+ if (transitions) |t| {
291
+ b.step("transitions", "Visualize transitions algorithm search").dependOn(&t.step);
292
+ }
293
+ }
203
294
 
204
- const serde = tool(b, "src/tools/serde.zig", tools);
205
- const protocol = tool(b, "src/tools/protocol.zig", tools);
295
+ fn buildWasm(
296
+ b: *std.Build,
297
+ name: []const u8,
298
+ root_src_file: []const u8,
299
+ optimize: std.builtin.OptimizeMode,
300
+ strip: ?bool,
301
+ pic: ?bool,
302
+ wasm_stack_size: u64,
303
+ import: ?*std.Build.Module,
304
+ options: anytype,
305
+ ) !void {
306
+ const mode = switch (optimize) {
307
+ .ReleaseFast, .ReleaseSafe => .ReleaseSmall,
308
+ else => optimize,
309
+ };
310
+ // https://webassembly.org/features/
311
+ var features = std.Target.wasm.featureSet(&.{
312
+ .atomics,
313
+ .bulk_memory,
314
+ // .exception_handling,
315
+ .extended_const,
316
+ // .half_precision,
317
+ // .multimemory,
318
+ .mutable_globals,
319
+ .nontrapping_fptoint,
320
+ .reference_types,
321
+ // .relaxed_simd,
322
+ .sign_ext,
323
+ .simd128,
324
+ // .tail_call,
325
+ });
326
+ if (builtin.zig_version.order(try std.SemanticVersion.parse("0.12.0")) != .lt) {
327
+ features.addFeature(@intFromEnum(std.Target.wasm.Feature.multivalue));
328
+ }
329
+ const freestanding = b.resolveTargetQuery(.{
330
+ .cpu_arch = .wasm32,
331
+ .os_tag = .freestanding,
332
+ .cpu_features_add = features,
333
+ });
334
+ const path = b.path(root_src_file);
335
+ const exe = b.addExecutable(.{
336
+ .name = name,
337
+ .root_module = b.createModule(.{
338
+ .root_source_file = path,
339
+ .optimize = mode,
340
+ .target = freestanding,
341
+ .strip = strip,
342
+ .pic = pic,
343
+ }),
344
+ });
206
345
 
207
- const lint_exe =
208
- b.addExecutable(.{ .name = "lint", .root_source_file = .{ .path = "src/tools/lint.zig" } });
209
- if (tests.build) tests.step.dependOn(&lint_exe.step);
210
- const lint = lint_exe.run();
346
+ var file = try std.Io.Dir.cwd().openFile(b.graph.io, root_src_file, .{});
347
+ defer file.close(b.graph.io);
348
+ var reader = file.reader(b.graph.io, &.{});
349
+ const bytes = try reader.interface.allocRemaining(b.allocator, .unlimited);
350
+ exe.root_module.export_symbol_names = try exports(b, bytes);
351
+ exe.entry = .disabled;
211
352
 
212
- b.step("benchmark", "Run benchmark code").dependOn(&benchmark.step);
213
- b.step("fuzz", "Run fuzz tester").dependOn(&fuzz.step);
214
- b.step("lint", "Lint source files").dependOn(&lint.step);
215
- b.step("protocol", "Run protocol dump tool").dependOn(&protocol.step);
216
- b.step("serde", "Run serialization/deserialization tool").dependOn(&serde.step);
217
- b.step("test", "Run all tests").dependOn(&tests.step);
218
- b.step("tools", "Install tools").dependOn(&ToolsStep.create(b, &exes).step);
353
+ if (import) |i| {
354
+ exe.root_module.addImport("pkmn", i);
355
+ }
356
+ exe.stack_size = wasm_stack_size;
357
+ exe.root_module.addOptions("build_options", options);
358
+
359
+ const opt = b.findProgram(&.{"wasm-opt"}, &.{"./node_modules/.bin"}) catch null;
360
+ if (optimize != .Debug and opt != null) {
361
+ const out = b.fmt("build/lib/{s}.wasm", .{name});
362
+ const sh = b.addSystemCommand(&.{ opt.?, "--enable-bulk-memory", "--enable-simd", "-O4" });
363
+ sh.addArtifactArg(exe);
364
+ sh.addArg("-o");
365
+ sh.addFileArg(b.path(out));
366
+ b.getInstallStep().dependOn(&sh.step);
367
+ } else {
368
+ b.getInstallStep().dependOn(&b.addInstallArtifact(exe, .{
369
+ .dest_dir = .{ .override = std.Build.InstallDir{ .lib = {} } },
370
+ }).step);
371
+ }
219
372
  }
220
373
 
221
374
  fn maybeStrip(
222
375
  b: *std.Build,
223
- artifact: *std.Build.CompileStep,
376
+ artifact: *std.Build.Step.Compile,
224
377
  step: *std.Build.Step,
225
- strip: bool,
378
+ strip: ?bool,
226
379
  cmd: ?[]const u8,
227
380
  ) void {
228
- artifact.strip = strip;
229
- if (!strip or cmd == null) return;
381
+ if (!(strip orelse false) or cmd == null) return;
230
382
  // Using `strip -r -u` for dynamic libraries is supposed to work on macOS but doesn't...
231
383
  const mac = builtin.os.tag == .macos;
232
384
  if (mac and artifact.isDynamicLibrary()) return;
@@ -238,111 +390,129 @@ fn maybeStrip(
238
390
  }
239
391
 
240
392
  const Config = struct {
241
- target: std.zig.CrossTarget,
393
+ target: std.Build.ResolvedTarget,
242
394
  optimize: std.builtin.OptimizeMode,
243
- pic: bool,
244
- strip: bool,
395
+ pic: ?bool,
396
+ strip: ?bool,
245
397
  cmd: ?[]const u8,
246
398
  };
247
399
 
248
- const TESTS = .{
249
- "src/lib/common/test.zig",
250
- "src/lib/gen1/test.zig",
251
- "src/lib/gen2/test.zig",
252
- };
253
-
254
400
  const TestStep = struct {
255
401
  step: std.Build.Step,
256
402
  build: bool,
257
403
 
258
- pub fn create(b: *std.Build, options: *std.Build.OptionsStep, config: Config) *TestStep {
404
+ pub fn create(b: *std.Build, options: *std.Build.Step.Options, config: Config) *TestStep {
259
405
  const coverage = b.option([]const u8, "test-coverage", "Generate test coverage");
260
- const test_file = b.option([]const u8, "test-file", "Input file for test");
261
- const test_bin = b.option([]const u8, "test-bin", "Emit test binary to");
262
406
  const test_filter =
263
407
  b.option([]const u8, "test-filter", "Skip tests that do not match filter");
264
- const test_no_exec =
265
- b.option(bool, "test-no-exec", "Compiles test binary without running it") orelse false;
266
408
 
267
409
  const self = b.allocator.create(TestStep) catch @panic("OOM");
268
410
  const step = std.Build.Step.init(.{ .id = .custom, .name = "Run all tests", .owner = b });
269
- self.* = TestStep{ .step = step, .build = test_filter == null };
270
-
271
- const paths: []const []const u8 =
272
- if (test_file) |t| &.{t} else if (coverage != null) &.{"src/lib/test.zig"} else &TESTS;
273
- for (paths) |path| {
274
- const tests = b.addTest(.{
275
- .name = std.fs.path.basename(std.fs.path.dirname(path).?),
276
- .root_source_file = .{ .path = path },
411
+ self.* = .{ .step = step, .build = test_filter == null };
412
+
413
+ const path = b.path("src/lib/test.zig");
414
+ const tests = b.addTest(.{
415
+ .root_module = b.createModule(.{
416
+ .root_source_file = path,
277
417
  .optimize = config.optimize,
278
418
  .target = config.target,
279
- });
280
- tests.setMainPkgPath("./");
281
- tests.setFilter(test_filter);
282
- tests.addOptions("build_options", options);
283
- tests.single_threaded = true;
284
- maybeStrip(b, tests, &tests.step, config.strip, config.cmd);
285
- if (config.pic) tests.force_pic = config.pic;
286
- if (test_bin) |bin| {
287
- tests.name = std.fs.path.basename(bin);
288
- if (std.fs.path.dirname(bin)) |dir| tests.setOutputDir(dir);
289
- }
290
- if (coverage) |c| {
291
- tests.setExecCmd(&.{ "kcov", "--include-pattern=src/lib", c, null });
292
- }
293
- self.step.dependOn(if (test_no_exec) &tests.step else &tests.run().step);
419
+ .single_threaded = true,
420
+ .strip = config.strip,
421
+ .pic = config.pic,
422
+ }),
423
+ .filters = if (test_filter) |filter| &.{filter} else &.{},
424
+ });
425
+ tests.root_module.addOptions("build_options", options);
426
+
427
+ maybeStrip(b, tests, &tests.step, config.strip, config.cmd);
428
+ if (coverage) |c| {
429
+ tests.setExecCmd(&.{ "kcov", "--include-pattern=src/lib", c, null });
294
430
  }
431
+ self.step.dependOn(&b.addRunArtifact(tests).step);
432
+
295
433
  return self;
296
434
  }
297
435
  };
298
436
 
299
437
  const ToolConfig = struct {
438
+ options: Options,
439
+ module: ?*std.Build.Module,
300
440
  general: Config,
301
441
  tool: struct {
302
- showdown: bool,
303
- trace: bool,
304
442
  tests: ?*TestStep,
305
443
  name: ?[]const u8 = null,
306
- exes: *std.ArrayList(*std.Build.CompileStep),
444
+ exes: *ArrayList(*std.Build.Step.Compile),
307
445
  },
308
446
  };
309
447
 
310
- fn tool(b: *std.Build, path: []const u8, config: ToolConfig) *std.Build.RunStep {
448
+ fn tool(b: *std.Build, path: []const u8, config: ToolConfig) !?*std.Build.Step.Run {
449
+ if (!try exists(b, path)) return null;
311
450
  var name = config.tool.name orelse std.fs.path.basename(path);
312
451
  const index = std.mem.lastIndexOfScalar(u8, name, '.');
313
452
  if (index) |i| name = name[0..i];
314
- if (config.tool.showdown) name = b.fmt("{s}-showdown", .{name});
453
+ if (config.options.showdown orelse false) name = b.fmt("{s}-showdown", .{name});
315
454
 
316
455
  const exe = b.addExecutable(.{
317
456
  .name = name,
318
- .root_source_file = .{ .path = path },
319
- .target = config.general.target,
320
- .optimize = config.general.optimize,
457
+ .root_module = b.createModule(.{
458
+ .root_source_file = b.path(path),
459
+ .target = config.general.target,
460
+ .optimize = config.general.optimize,
461
+ .strip = config.general.strip,
462
+ .pic = config.general.pic,
463
+ }),
321
464
  });
322
- exe.addModule("pkmn", module(b, .{
323
- .showdown = config.tool.showdown,
324
- .trace = config.tool.trace,
325
- }));
326
- exe.single_threaded = true;
327
- if (config.general.pic) exe.force_pic = config.general.pic;
465
+
466
+ const import = if (config.module) |m| m else module(b, config.options);
467
+ exe.root_module.addImport("pkmn", import);
468
+ if (config.general.target.result.os.tag == .windows) {
469
+ exe.root_module.linkSystemLibrary("advapi32", .{});
470
+ }
471
+
328
472
  if (config.tool.tests) |ts| ts.step.dependOn(&exe.step);
329
- config.tool.exes.append(exe) catch @panic("OOM");
473
+ config.tool.exes.append(b.allocator, exe) catch @panic("OOM");
330
474
 
331
- const run = exe.run();
475
+ const run = b.addRunArtifact(exe);
332
476
  maybeStrip(b, exe, &run.step, config.general.strip, config.general.cmd);
333
477
  if (b.args) |args| run.addArgs(args);
334
478
 
335
479
  return run;
336
480
  }
337
481
 
482
+ fn exists(b: *std.Build, path: []const u8) !bool {
483
+ std.Io.Dir.cwd().access(b.graph.io, path, .{}) catch |err| switch (err) {
484
+ error.FileNotFound => return false,
485
+ else => |e| return e,
486
+ };
487
+ return true;
488
+ }
489
+
338
490
  const ToolsStep = struct {
339
491
  step: std.Build.Step,
340
492
 
341
- pub fn create(b: *std.Build, exes: *std.ArrayList(*std.Build.CompileStep)) *ToolsStep {
493
+ pub fn create(b: *std.Build, exes: *ArrayList(*std.Build.Step.Compile)) *ToolsStep {
342
494
  const self = b.allocator.create(ToolsStep) catch @panic("OOM");
343
495
  const step = std.Build.Step.init(.{ .id = .custom, .name = "Install tools", .owner = b });
344
- self.* = ToolsStep{ .step = step };
345
- for (exes.items) |t| self.step.dependOn(&b.addInstallArtifact(t).step);
496
+ self.* = .{ .step = step };
497
+ for (exes.items) |t| self.step.dependOn(&b.addInstallArtifact(t, .{}).step);
346
498
  return self;
347
499
  }
348
500
  };
501
+
502
+ pub fn exports(b: *std.Build, bytes: []const u8) ![][]const u8 {
503
+ var symbols: ArrayList([]const u8) = .empty;
504
+
505
+ var it = std.mem.splitSequence(u8, bytes, "export ");
506
+ _ = it.next();
507
+ while (it.next()) |s| {
508
+ if (std.mem.startsWith(u8, s, "const ")) {
509
+ const i = std.mem.indexOf(u8, s[6..], " ").?;
510
+ try symbols.append(b.allocator, s[6 .. 6 + i]);
511
+ } else { // "fn "
512
+ const i = std.mem.indexOf(u8, s[3..], "(").?;
513
+ try symbols.append(b.allocator, s[3 .. 3 + i]);
514
+ }
515
+ }
516
+
517
+ return symbols.items;
518
+ }