@pkmn/engine 0.1.0-dev.5ec64ea5 → 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 +337 -165
  40. package/package.json +71 -120
  41. package/src/bin/install-pkmn-engine +57 -26
  42. package/src/bin/pkmn-debug +3 -4
  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 -93
  63. package/src/lib/gen1/helpers.zig +138 -51
  64. package/src/lib/gen1/mechanics.zig +1364 -925
  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 -784
  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,138 +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");
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;
30
49
 
31
50
  const cmd = b.findProgram(&.{"strip"}, &.{}) catch null;
32
51
 
33
- var parser = std.json.Parser.init(b.allocator, false);
34
- defer parser.deinit();
35
- var tree = try parser.parse(@embedFile("package.json"));
36
- defer tree.deinit();
37
- const version = tree.root.Object.get("version").?.String;
38
- const description = tree.root.Object.get("description").?.String;
39
- 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, ":");
40
58
  std.debug.assert(std.mem.eql(u8, repository.first(), "github"));
41
59
 
42
60
  const showdown =
43
61
  b.option(bool, "showdown", "Enable Pokémon Showdown compatibility mode") orelse false;
44
- 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");
45
66
 
46
67
  const options = b.addOptions();
47
68
  options.addOption(?bool, "showdown", showdown);
48
- 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
+ }
49
89
 
50
90
  const name = if (showdown) "pkmn-showdown" else "pkmn";
51
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
+
52
99
  var c = false;
53
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));
54
107
  const addon = b.fmt("{s}.node", .{name});
55
- const lib = b.addSharedLibrary(.{
108
+ const path = b.path("src/lib/node.zig");
109
+ const lib = b.addLibrary(.{
110
+ .linkage = .dynamic,
56
111
  .name = addon,
57
- .root_source_file = .{ .path = "src/lib/binding/node.zig" },
58
- .optimize = optimize,
59
- .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
+ }),
60
119
  });
61
- lib.addOptions("build_options", options);
62
- lib.setMainPkgPath("./");
63
- lib.addSystemIncludePath(headers);
64
- 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;
65
123
  if (node_import_lib) |il| {
66
- lib.addObjectFile(il);
67
- } else if ((try std.zig.system.NativeTargetInfo.detect(target)).target.os.tag == .windows) {
68
- 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");
69
128
  std.process.exit(1);
70
129
  }
71
130
  lib.linker_allow_shlib_undefined = true;
72
131
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
73
- if (pic) lib.force_pic = pic;
74
132
  // Always emit to build/lib because this is where the driver code expects to find it
75
- // 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):
76
134
  //
77
135
  // lib.emit_bin = .{ .emit_to = b.fmt("build/lib/{s}", .{addon}) };
78
136
  // b.getInstallStep().dependOn(&lib.step);
79
137
  //
80
138
  // But ziglang/zig#14647 broke this so we now need to do an install() and then manually
81
139
  // rename the file ourself in install-pkmn-engine
82
- lib.install();
140
+ b.installArtifact(lib);
83
141
  } else if (wasm) {
84
- const lib = b.addSharedLibrary(.{
85
- .name = name,
86
- .root_source_file = .{ .path = "src/lib/binding/wasm.zig" },
87
- .optimize = switch (optimize) {
88
- .ReleaseFast, .ReleaseSafe => .ReleaseSmall,
89
- else => optimize,
90
- },
91
- .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
92
- });
93
- lib.addOptions("build_options", options);
94
- lib.setMainPkgPath("./");
95
- lib.stack_size = wasm_stack_size;
96
- lib.rdynamic = true;
97
- lib.strip = strip;
98
- if (pic) lib.force_pic = pic;
99
- const opt = b.findProgram(&.{"wasm-opt"}, &.{"./node_modules/.bin"}) catch null;
100
- if (optimize != .Debug and opt != null) {
101
- const out = b.fmt("build/lib/{s}.wasm", .{name});
102
- const sh = b.addSystemCommand(&.{ opt.?, "-O4" });
103
- sh.addArtifactArg(lib);
104
- sh.addArg("-o");
105
- sh.addFileSourceArg(.{ .path = out });
106
- b.getInstallStep().dependOn(&sh.step);
107
- }
108
- 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);
109
149
  } else if (dynamic) {
110
- const lib = b.addSharedLibrary(.{
150
+ const path = b.path("src/lib/c.zig");
151
+ const lib = b.addLibrary(.{
152
+ .linkage = .dynamic,
111
153
  .name = name,
112
- .root_source_file = .{ .path = "src/lib/binding/c.zig" },
113
- .version = try std.builtin.Version.parse(version),
114
- .optimize = optimize,
115
- .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
+ }),
116
161
  });
117
- lib.addOptions("build_options", options);
118
- lib.setMainPkgPath("./");
119
- lib.addIncludePath("src/include");
162
+ lib.root_module.addOptions("build_options", options);
163
+ lib.root_module.addIncludePath(b.path("src/include"));
120
164
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
121
- if (pic) lib.force_pic = pic;
122
- lib.install();
165
+ b.installArtifact(lib);
123
166
  c = true;
124
167
  } else {
125
- const lib = b.addStaticLibrary(.{
168
+ const path = b.path("src/lib/c.zig");
169
+ const lib = b.addLibrary(.{
170
+ .linkage = .static,
126
171
  .name = name,
127
- .root_source_file = .{ .path = "src/lib/binding/c.zig" },
128
- .optimize = optimize,
129
- .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
+ }),
130
179
  });
131
- lib.addOptions("build_options", options);
132
- lib.setMainPkgPath("./");
133
- lib.addIncludePath("src/include");
180
+ lib.root_module.addOptions("build_options", options);
181
+ lib.root_module.addIncludePath(b.path("src/include"));
134
182
  lib.bundle_compiler_rt = true;
135
183
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
136
- if (pic) lib.force_pic = pic;
137
- 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);
138
199
  c = true;
139
200
  }
140
201
 
141
202
  if (c) {
142
- const header = b.addInstallFileWithDir(
143
- .{ .path = "src/include/pkmn.h" },
144
- .header,
145
- "pkmn.h",
146
- );
203
+ const path = b.path("src/include/pkmn.h");
204
+ const header = b.addInstallFileWithDir(path, .header, "pkmn.h");
147
205
  b.getInstallStep().dependOn(&header.step);
148
206
 
149
207
  const pc = b.fmt("lib{s}.pc", .{name});
150
- const file = try b.cache_root.join(b.allocator, &.{pc});
151
- 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);
152
218
 
153
219
  const dirname = comptime std.fs.path.dirname(@src().file) orelse ".";
154
- const writer = pkgconfig_file.writer();
155
- try writer.print(
220
+ var writer = pkgconfig_file.writer(b.graph.io, &.{});
221
+ try writer.interface.print(
156
222
  \\prefix={0s}/{1s}
157
223
  \\includedir=${{prefix}}/include
158
224
  \\libdir=${{prefix}}/lib
@@ -164,26 +230,32 @@ pub fn build(b: *std.Build) !void {
164
230
  \\Cflags: -I${{includedir}}
165
231
  \\Libs: -L${{libdir}} -l{2s}
166
232
  , .{ dirname, b.install_path, name, repository.next().?, description, version });
167
- defer pkgconfig_file.close();
168
233
 
169
234
  b.installFile(file, b.fmt("share/pkgconfig/{s}", .{pc}));
170
235
  }
171
236
 
172
- const config = .{
237
+ const config: Config = .{
173
238
  .target = target,
174
239
  .optimize = optimize,
175
240
  .pic = pic,
176
241
  .strip = strip,
177
242
  .cmd = cmd,
178
243
  };
244
+
245
+ // TODO: tests can be run multiple times due to @imports
179
246
  const tests = TestStep.create(b, options, config);
180
247
 
181
- var exes = std.ArrayList(*std.Build.CompileStep).init(b.allocator);
248
+ var exes: ArrayList(*std.Build.Step.Compile) = .empty;
182
249
  const tools: ToolConfig = .{
250
+ .options = .{
251
+ .showdown = showdown,
252
+ .log = log,
253
+ .chance = chance,
254
+ .calc = calc,
255
+ },
256
+ .module = pkmn,
183
257
  .general = config,
184
258
  .tool = .{
185
- .showdown = showdown,
186
- .trace = trace,
187
259
  .tests = if (tests.build) tests else null,
188
260
  .exes = &exes,
189
261
  },
@@ -192,39 +264,121 @@ pub fn build(b: *std.Build) !void {
192
264
  var benchmark_config = tools;
193
265
  benchmark_config.general.optimize = .ReleaseFast;
194
266
  benchmark_config.general.strip = true;
195
- const benchmark = tool(b, "src/test/benchmark.zig", benchmark_config);
267
+ const benchmark = try tool(b, "src/test/benchmark.zig", benchmark_config);
196
268
 
197
269
  var fuzz_config = tools;
198
270
  fuzz_config.general.strip = false;
199
271
  fuzz_config.tool.name = "fuzz";
200
- 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
+ }
201
294
 
202
- const serde = tool(b, "src/tools/serde.zig", tools);
203
- 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
+ });
204
345
 
205
- const lint_exe =
206
- b.addExecutable(.{ .name = "lint", .root_source_file = .{ .path = "src/tools/lint.zig" } });
207
- if (tests.build) tests.step.dependOn(&lint_exe.step);
208
- 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;
209
352
 
210
- b.step("benchmark", "Run benchmark code").dependOn(&benchmark.step);
211
- b.step("fuzz", "Run fuzz tester").dependOn(&fuzz.step);
212
- b.step("lint", "Lint source files").dependOn(&lint.step);
213
- b.step("protocol", "Run protocol dump tool").dependOn(&protocol.step);
214
- b.step("serde", "Run serialization/deserialization tool").dependOn(&serde.step);
215
- b.step("test", "Run all tests").dependOn(&tests.step);
216
- 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
+ }
217
372
  }
218
373
 
219
374
  fn maybeStrip(
220
375
  b: *std.Build,
221
- artifact: *std.Build.CompileStep,
376
+ artifact: *std.Build.Step.Compile,
222
377
  step: *std.Build.Step,
223
- strip: bool,
378
+ strip: ?bool,
224
379
  cmd: ?[]const u8,
225
380
  ) void {
226
- artifact.strip = strip;
227
- if (!strip or cmd == null) return;
381
+ if (!(strip orelse false) or cmd == null) return;
228
382
  // Using `strip -r -u` for dynamic libraries is supposed to work on macOS but doesn't...
229
383
  const mac = builtin.os.tag == .macos;
230
384
  if (mac and artifact.isDynamicLibrary()) return;
@@ -236,111 +390,129 @@ fn maybeStrip(
236
390
  }
237
391
 
238
392
  const Config = struct {
239
- target: std.zig.CrossTarget,
393
+ target: std.Build.ResolvedTarget,
240
394
  optimize: std.builtin.OptimizeMode,
241
- pic: bool,
242
- strip: bool,
395
+ pic: ?bool,
396
+ strip: ?bool,
243
397
  cmd: ?[]const u8,
244
398
  };
245
399
 
246
- const TESTS = .{
247
- "src/lib/common/test.zig",
248
- "src/lib/gen1/test.zig",
249
- "src/lib/gen2/test.zig",
250
- };
251
-
252
400
  const TestStep = struct {
253
401
  step: std.Build.Step,
254
402
  build: bool,
255
403
 
256
- 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 {
257
405
  const coverage = b.option([]const u8, "test-coverage", "Generate test coverage");
258
- const test_file = b.option([]const u8, "test-file", "Input file for test");
259
- const test_bin = b.option([]const u8, "test-bin", "Emit test binary to");
260
406
  const test_filter =
261
407
  b.option([]const u8, "test-filter", "Skip tests that do not match filter");
262
- const test_no_exec =
263
- b.option(bool, "test-no-exec", "Compiles test binary without running it") orelse false;
264
408
 
265
409
  const self = b.allocator.create(TestStep) catch @panic("OOM");
266
410
  const step = std.Build.Step.init(.{ .id = .custom, .name = "Run all tests", .owner = b });
267
- self.* = TestStep{ .step = step, .build = test_filter == null };
268
-
269
- const paths: []const []const u8 =
270
- if (test_file) |t| &.{t} else if (coverage != null) &.{"src/lib/test.zig"} else &TESTS;
271
- for (paths) |path| {
272
- const tests = b.addTest(.{
273
- .name = std.fs.path.basename(std.fs.path.dirname(path).?),
274
- .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,
275
417
  .optimize = config.optimize,
276
418
  .target = config.target,
277
- });
278
- tests.setMainPkgPath("./");
279
- tests.setFilter(test_filter);
280
- tests.addOptions("build_options", options);
281
- tests.single_threaded = true;
282
- maybeStrip(b, tests, &tests.step, config.strip, config.cmd);
283
- if (config.pic) tests.force_pic = config.pic;
284
- if (test_bin) |bin| {
285
- tests.name = std.fs.path.basename(bin);
286
- if (std.fs.path.dirname(bin)) |dir| tests.setOutputDir(dir);
287
- }
288
- if (coverage) |c| {
289
- tests.setExecCmd(&.{ "kcov", "--include-pattern=src/lib", c, null });
290
- }
291
- 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 });
292
430
  }
431
+ self.step.dependOn(&b.addRunArtifact(tests).step);
432
+
293
433
  return self;
294
434
  }
295
435
  };
296
436
 
297
437
  const ToolConfig = struct {
438
+ options: Options,
439
+ module: ?*std.Build.Module,
298
440
  general: Config,
299
441
  tool: struct {
300
- showdown: bool,
301
- trace: bool,
302
442
  tests: ?*TestStep,
303
443
  name: ?[]const u8 = null,
304
- exes: *std.ArrayList(*std.Build.CompileStep),
444
+ exes: *ArrayList(*std.Build.Step.Compile),
305
445
  },
306
446
  };
307
447
 
308
- 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;
309
450
  var name = config.tool.name orelse std.fs.path.basename(path);
310
451
  const index = std.mem.lastIndexOfScalar(u8, name, '.');
311
452
  if (index) |i| name = name[0..i];
312
- if (config.tool.showdown) name = b.fmt("{s}-showdown", .{name});
453
+ if (config.options.showdown orelse false) name = b.fmt("{s}-showdown", .{name});
313
454
 
314
455
  const exe = b.addExecutable(.{
315
456
  .name = name,
316
- .root_source_file = .{ .path = path },
317
- .target = config.general.target,
318
- .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
+ }),
319
464
  });
320
- exe.addModule("pkmn", module(b, .{
321
- .showdown = config.tool.showdown,
322
- .trace = config.tool.trace,
323
- }));
324
- exe.single_threaded = true;
325
- 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
+
326
472
  if (config.tool.tests) |ts| ts.step.dependOn(&exe.step);
327
- config.tool.exes.append(exe) catch @panic("OOM");
473
+ config.tool.exes.append(b.allocator, exe) catch @panic("OOM");
328
474
 
329
- const run = exe.run();
475
+ const run = b.addRunArtifact(exe);
330
476
  maybeStrip(b, exe, &run.step, config.general.strip, config.general.cmd);
331
477
  if (b.args) |args| run.addArgs(args);
332
478
 
333
479
  return run;
334
480
  }
335
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
+
336
490
  const ToolsStep = struct {
337
491
  step: std.Build.Step,
338
492
 
339
- 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 {
340
494
  const self = b.allocator.create(ToolsStep) catch @panic("OOM");
341
495
  const step = std.Build.Step.init(.{ .id = .custom, .name = "Install tools", .owner = b });
342
- self.* = ToolsStep{ .step = step };
343
- 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);
344
498
  return self;
345
499
  }
346
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
+ }