@pkmn/engine 0.1.0-dev.4eeb346e → 0.1.0-dev.51ebf68b

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 +137 -72
  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 +332 -155
  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 +100 -89
  63. package/src/lib/gen1/helpers.zig +138 -51
  64. package/src/lib/gen1/mechanics.zig +1293 -849
  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 -383
  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,59 +34,103 @@ 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);
@@ -82,79 +139,86 @@ pub fn build(b: *std.Build) !void {
82
139
  // rename the file ourself in install-pkmn-engine
83
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
- b.installArtifact(lib);
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
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;
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
+ }
139
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 = b.addRunArtifact(lint_exe);
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,88 +390,87 @@ 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
406
  const test_filter =
262
407
  b.option([]const u8, "test-filter", "Skip tests that do not match filter");
263
408
 
264
409
  const self = b.allocator.create(TestStep) catch @panic("OOM");
265
410
  const step = std.Build.Step.init(.{ .id = .custom, .name = "Run all tests", .owner = b });
266
- self.* = TestStep{ .step = step, .build = test_filter == null };
267
-
268
- const paths: []const []const u8 =
269
- if (test_file) |t| &.{t} else if (coverage != null) &.{"src/lib/test.zig"} else &TESTS;
270
- for (paths) |path| {
271
- const tests = b.addTest(.{
272
- .name = std.fs.path.basename(std.fs.path.dirname(path).?),
273
- .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,
274
417
  .optimize = config.optimize,
275
418
  .target = config.target,
276
- .filter = test_filter,
277
- });
278
- tests.setMainPkgPath("./");
279
- tests.addOptions("build_options", options);
280
- tests.single_threaded = true;
281
- maybeStrip(b, tests, &tests.step, config.strip, config.cmd);
282
- if (config.pic) tests.force_pic = config.pic;
283
- if (coverage) |c| {
284
- tests.setExecCmd(&.{ "kcov", "--include-pattern=src/lib", c, null });
285
- }
286
- self.step.dependOn(&b.addRunArtifact(tests).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 });
287
430
  }
431
+ self.step.dependOn(&b.addRunArtifact(tests).step);
432
+
288
433
  return self;
289
434
  }
290
435
  };
291
436
 
292
437
  const ToolConfig = struct {
438
+ options: Options,
439
+ module: ?*std.Build.Module,
293
440
  general: Config,
294
441
  tool: struct {
295
- showdown: bool,
296
- trace: bool,
297
442
  tests: ?*TestStep,
298
443
  name: ?[]const u8 = null,
299
- exes: *std.ArrayList(*std.Build.CompileStep),
444
+ exes: *ArrayList(*std.Build.Step.Compile),
300
445
  },
301
446
  };
302
447
 
303
- 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;
304
450
  var name = config.tool.name orelse std.fs.path.basename(path);
305
451
  const index = std.mem.lastIndexOfScalar(u8, name, '.');
306
452
  if (index) |i| name = name[0..i];
307
- if (config.tool.showdown) name = b.fmt("{s}-showdown", .{name});
453
+ if (config.options.showdown orelse false) name = b.fmt("{s}-showdown", .{name});
308
454
 
309
455
  const exe = b.addExecutable(.{
310
456
  .name = name,
311
- .root_source_file = .{ .path = path },
312
- .target = config.general.target,
313
- .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
+ }),
314
464
  });
315
- exe.addModule("pkmn", module(b, .{
316
- .showdown = config.tool.showdown,
317
- .trace = config.tool.trace,
318
- }));
319
- exe.single_threaded = true;
320
- 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
+
321
472
  if (config.tool.tests) |ts| ts.step.dependOn(&exe.step);
322
- config.tool.exes.append(exe) catch @panic("OOM");
473
+ config.tool.exes.append(b.allocator, exe) catch @panic("OOM");
323
474
 
324
475
  const run = b.addRunArtifact(exe);
325
476
  maybeStrip(b, exe, &run.step, config.general.strip, config.general.cmd);
@@ -328,14 +479,40 @@ fn tool(b: *std.Build, path: []const u8, config: ToolConfig) *std.Build.RunStep
328
479
  return run;
329
480
  }
330
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
+
331
490
  const ToolsStep = struct {
332
491
  step: std.Build.Step,
333
492
 
334
- 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 {
335
494
  const self = b.allocator.create(ToolsStep) catch @panic("OOM");
336
495
  const step = std.Build.Step.init(.{ .id = .custom, .name = "Install tools", .owner = b });
337
- self.* = ToolsStep{ .step = step };
338
- 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);
339
498
  return self;
340
499
  }
341
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
+ }