@pkmn/engine 0.1.0-dev.3a273268 → 0.1.0-dev.3d09f03a

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