@pkmn/engine 0.1.0-dev.6ef582f1 → 0.1.0-dev.72514829

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