@pkmn/engine 0.1.0-dev.207fa86a → 0.1.0-dev.20de8262

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 (64) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +133 -71
  3. package/build/pkg/addon.d.ts +7 -0
  4. package/build/pkg/addon.js +112 -0
  5. package/build/pkg/addon.js.map +1 -0
  6. package/build/pkg/data/ids.json +12 -12
  7. package/build/pkg/data/layout.json +27 -5
  8. package/build/pkg/data/protocol.json +69 -12
  9. package/build/pkg/data.d.ts +4 -4
  10. package/build/pkg/data.js +3 -5
  11. package/build/pkg/data.js.map +1 -1
  12. package/build/pkg/gen1.d.ts +5 -4
  13. package/build/pkg/gen1.js +49 -33
  14. package/build/pkg/gen1.js.map +1 -1
  15. package/build/pkg/index.d.ts +18 -14
  16. package/build/pkg/index.js +1 -1
  17. package/build/pkg/index.js.map +1 -1
  18. package/build/pkg/protocol.d.ts +4 -2
  19. package/build/pkg/protocol.js +61 -40
  20. package/build/pkg/protocol.js.map +1 -1
  21. package/build/test/display.js +400 -0
  22. package/build/test/display.js.map +1 -0
  23. package/build/tools/debug.js +135 -0
  24. package/build/tools/debug.js.map +1 -0
  25. package/build.zig +166 -136
  26. package/package.json +87 -45
  27. package/src/bin/install-pkmn-engine +68 -36
  28. package/src/bin/pkmn-debug +62 -0
  29. package/src/lib/common/battle.zig +17 -0
  30. package/src/lib/common/data.zig +19 -31
  31. package/src/lib/common/debug.zig +74 -0
  32. package/src/lib/common/js.zig +118 -0
  33. package/src/lib/common/optional.zig +67 -0
  34. package/src/lib/common/options.zig +12 -3
  35. package/src/lib/common/protocol.zig +932 -395
  36. package/src/lib/common/rational.zig +357 -0
  37. package/src/lib/common/rng.zig +29 -20
  38. package/src/lib/common/util.zig +12 -0
  39. package/src/lib/gen1/README.md +309 -192
  40. package/src/lib/gen1/calc.zig +426 -0
  41. package/src/lib/gen1/chance.zig +1060 -0
  42. package/src/lib/gen1/data/moves.zig +202 -190
  43. package/src/lib/gen1/data/species.zig +4 -4
  44. package/src/lib/gen1/data/types.zig +10 -10
  45. package/src/lib/gen1/data.zig +107 -93
  46. package/src/lib/gen1/helpers.zig +577 -36
  47. package/src/lib/gen1/mechanics.zig +1222 -834
  48. package/src/lib/gen2/README.md +105 -85
  49. package/src/lib/gen2/calc.zig +143 -0
  50. package/src/lib/gen2/chance.zig +1314 -0
  51. package/src/lib/gen2/data/items.zig +14 -20
  52. package/src/lib/gen2/data/moves.zig +664 -570
  53. package/src/lib/gen2/data/species.zig +2 -2
  54. package/src/lib/gen2/data/types.zig +137 -34
  55. package/src/lib/gen2/data.zig +134 -96
  56. package/src/lib/gen2/generated.zig +806 -0
  57. package/src/lib/gen2/helpers.zig +16 -22
  58. package/src/lib/gen2/mechanics.zig +3506 -12
  59. package/src/lib/node.zig +131 -0
  60. package/src/lib/pkmn.zig +91 -9
  61. package/src/lib/wasm.zig +13 -0
  62. package/src/test/display/pkmn.html.tmpl +368 -0
  63. package/src/lib/binding/node.zig +0 -229
  64. package/src/lib/binding/wasm.zig +0 -10
package/build.zig CHANGED
@@ -1,18 +1,23 @@
1
1
  const std = @import("std");
2
2
  const builtin = @import("builtin");
3
3
 
4
- const NativeTargetInfo = std.zig.system.NativeTargetInfo;
5
-
6
- pub const Options = struct { showdown: ?bool = null, trace: ?bool = null };
4
+ pub const Options = struct {
5
+ showdown: ?bool = null,
6
+ log: ?bool = null,
7
+ chance: ?bool = null,
8
+ calc: ?bool = null,
9
+ };
7
10
 
8
11
  pub fn module(b: *std.Build, options: Options) *std.Build.Module {
9
12
  const dirname = comptime std.fs.path.dirname(@src().file) orelse ".";
10
13
  const build_options = b.addOptions();
11
14
  build_options.addOption(?bool, "showdown", options.showdown);
12
- build_options.addOption(?bool, "trace", options.trace);
15
+ build_options.addOption(?bool, "log", options.log);
16
+ build_options.addOption(?bool, "chance", options.chance);
17
+ build_options.addOption(?bool, "calc", options.calc);
13
18
  return b.createModule(.{
14
- .source_file = .{ .path = dirname ++ "/src/lib/pkmn.zig" },
15
- .dependencies = &.{.{ .name = "build_options", .module = build_options.createModule() }},
19
+ .root_source_file = .{ .path = dirname ++ "/src/lib/pkmn.zig" },
20
+ .imports = &.{.{ .name = "build_options", .module = build_options.createModule() }},
16
21
  });
17
22
  }
18
23
 
@@ -27,27 +32,32 @@ pub fn build(b: *std.Build) !void {
27
32
  const wasm_stack_size =
28
33
  b.option(u64, "wasm-stack-size", "The size of WASM stack") orelse std.wasm.page_size;
29
34
  const dynamic = b.option(bool, "dynamic", "Build a dynamic library") orelse false;
30
- const strip = b.option(bool, "strip", "Strip debugging symbols from binary") orelse false;
31
- const pic = b.option(bool, "pic", "Force position independent code") orelse false;
32
-
33
- const cmd = b.findProgram(&[_][]const u8{"strip"}, &[_][]const u8{}) catch null;
34
-
35
- var parser = std.json.Parser.init(b.allocator, false);
36
- defer parser.deinit();
37
- var tree = try parser.parse(@embedFile("package.json"));
38
- defer tree.deinit();
39
- const version = tree.root.Object.get("version").?.String;
40
- const description = tree.root.Object.get("description").?.String;
41
- var repository = std.mem.split(u8, tree.root.Object.get("repository").?.String, ":");
35
+ const strip = b.option(bool, "strip", "Strip debugging symbols from binary");
36
+ const pic = b.option(bool, "pic", "Force position independent code");
37
+ const emit_asm = b.option(bool, "emit-asm", "Output .s (assembly code)") orelse false;
38
+ const emit_ll = b.option(bool, "emit-ll", "Output .ll (LLVM IR)") orelse false;
39
+
40
+ const cmd = b.findProgram(&.{"strip"}, &.{}) catch null;
41
+
42
+ const json = @embedFile("package.json");
43
+ var parsed = try std.json.parseFromSlice(std.json.Value, b.allocator, json, .{});
44
+ defer parsed.deinit();
45
+ const version = parsed.value.object.get("version").?.string;
46
+ const description = parsed.value.object.get("description").?.string;
47
+ var repository = std.mem.split(u8, parsed.value.object.get("repository").?.string, ":");
42
48
  std.debug.assert(std.mem.eql(u8, repository.first(), "github"));
43
49
 
44
50
  const showdown =
45
51
  b.option(bool, "showdown", "Enable Pokémon Showdown compatibility mode") orelse false;
46
- const trace = b.option(bool, "trace", "Enable trace logs") orelse false;
52
+ const log = b.option(bool, "log", "Enable protocol message logging") orelse false;
53
+ const chance = b.option(bool, "chance", "Enable update probability tracking") orelse false;
54
+ const calc = b.option(bool, "calc", "Enable damage calculator support") orelse false;
47
55
 
48
56
  const options = b.addOptions();
49
57
  options.addOption(?bool, "showdown", showdown);
50
- options.addOption(?bool, "trace", trace);
58
+ options.addOption(?bool, "log", log);
59
+ options.addOption(?bool, "chance", chance);
60
+ options.addOption(?bool, "calc", calc);
51
61
 
52
62
  const name = if (showdown) "pkmn-showdown" else "pkmn";
53
63
 
@@ -56,90 +66,111 @@ pub fn build(b: *std.Build) !void {
56
66
  const addon = b.fmt("{s}.node", .{name});
57
67
  const lib = b.addSharedLibrary(.{
58
68
  .name = addon,
59
- .root_source_file = .{ .path = "src/lib/binding/node.zig" },
69
+ .root_source_file = .{ .path = "src/lib/node.zig" },
60
70
  .optimize = optimize,
61
71
  .target = target,
72
+ .strip = strip,
73
+ .pic = pic,
62
74
  });
63
- lib.addOptions("build_options", options);
64
- lib.setMainPkgPath("./");
65
- lib.addSystemIncludePath(headers);
75
+ lib.root_module.addOptions("build_options", options);
76
+ lib.addSystemIncludePath(.{ .path = headers });
66
77
  lib.linkLibC();
67
78
  if (node_import_lib) |il| {
68
- lib.addObjectFile(il);
69
- } else if ((try NativeTargetInfo.detect(target)).target.os.tag == .windows) {
79
+ lib.addObjectFile(.{ .path = il });
80
+ } else if (target.result.os.tag == .windows) {
70
81
  try std.io.getStdErr().writeAll("Must provide --node-import-library path on Windows\n");
71
82
  std.process.exit(1);
72
83
  }
73
84
  lib.linker_allow_shlib_undefined = true;
74
85
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
75
- if (pic) lib.force_pic = pic;
76
86
  // Always emit to build/lib because this is where the driver code expects to find it
77
- // TODO(ziglang/zig#2231): using the following used to work (though be hacky):
87
+ // TODO(ziglang/zig#2231): using the following used to work (though was hacky):
78
88
  //
79
89
  // lib.emit_bin = .{ .emit_to = b.fmt("build/lib/{s}", .{addon}) };
80
90
  // b.getInstallStep().dependOn(&lib.step);
81
91
  //
82
92
  // But ziglang/zig#14647 broke this so we now need to do an install() and then manually
83
93
  // rename the file ourself in install-pkmn-engine
84
- lib.install();
94
+ b.installArtifact(lib);
85
95
  } else if (wasm) {
86
- const lib = b.addSharedLibrary(.{
96
+ const exe = b.addExecutable(.{
87
97
  .name = name,
88
- .root_source_file = .{ .path = "src/lib/binding/wasm.zig" },
98
+ .root_source_file = .{ .path = "src/lib/wasm.zig" },
89
99
  .optimize = switch (optimize) {
90
100
  .ReleaseFast, .ReleaseSafe => .ReleaseSmall,
91
101
  else => optimize,
92
102
  },
93
- .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
103
+ .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
104
+ .strip = strip,
105
+ .pic = pic,
94
106
  });
95
- lib.addOptions("build_options", options);
96
- lib.setMainPkgPath("./");
97
- lib.stack_size = wasm_stack_size;
98
- lib.rdynamic = true;
99
- lib.strip = strip;
100
- if (pic) lib.force_pic = pic;
101
- const opt = b.findProgram(
102
- &[_][]const u8{"wasm-opt"},
103
- &[_][]const u8{"./node_modules/.bin"},
104
- ) catch null;
107
+ exe.root_module.addOptions("build_options", options);
108
+ exe.root_module.export_symbol_names = &[_][]const u8{
109
+ "SHOWDOWN",
110
+ "LOG",
111
+ "CHANCE",
112
+ "CALC",
113
+ "GEN1_CHOICES_SIZE",
114
+ "GEN1_LOGS_SIZE",
115
+ };
116
+ exe.entry = .disabled;
117
+ exe.stack_size = wasm_stack_size;
118
+ const opt = b.findProgram(&.{"wasm-opt"}, &.{"./node_modules/.bin"}) catch null;
105
119
  if (optimize != .Debug and opt != null) {
106
120
  const out = b.fmt("build/lib/{s}.wasm", .{name});
107
- const sh = b.addSystemCommand(&[_][]const u8{ opt.?, "-O4" });
108
- sh.addArtifactArg(lib);
121
+ const sh = b.addSystemCommand(&.{ opt.?, "-O4" });
122
+ sh.addArtifactArg(exe);
109
123
  sh.addArg("-o");
110
- sh.addFileSourceArg(.{ .path = out });
124
+ sh.addFileArg(.{ .path = out });
111
125
  b.getInstallStep().dependOn(&sh.step);
126
+ } else {
127
+ b.getInstallStep().dependOn(&b.addInstallArtifact(exe, .{
128
+ .dest_dir = .{ .override = std.Build.InstallDir{ .lib = {} } },
129
+ }).step);
112
130
  }
113
- lib.install();
114
131
  } else if (dynamic) {
115
132
  const lib = b.addSharedLibrary(.{
116
133
  .name = name,
117
- .root_source_file = .{ .path = "src/lib/binding/c.zig" },
118
- .version = try std.builtin.Version.parse(version),
134
+ .root_source_file = .{ .path = "src/lib/c.zig" },
135
+ .version = try std.SemanticVersion.parse(version),
119
136
  .optimize = optimize,
120
137
  .target = target,
138
+ .strip = strip,
139
+ .pic = pic,
121
140
  });
122
- lib.addOptions("build_options", options);
123
- lib.setMainPkgPath("./");
124
- lib.addIncludePath("src/include");
141
+ lib.root_module.addOptions("build_options", options);
142
+ lib.addIncludePath(.{ .path = "src/include" });
125
143
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
126
- if (pic) lib.force_pic = pic;
127
- lib.install();
144
+ b.installArtifact(lib);
128
145
  c = true;
129
146
  } else {
130
147
  const lib = b.addStaticLibrary(.{
131
148
  .name = name,
132
- .root_source_file = .{ .path = "src/lib/binding/c.zig" },
149
+ .root_source_file = .{ .path = "src/lib/c.zig" },
133
150
  .optimize = optimize,
134
151
  .target = target,
152
+ .strip = strip,
153
+ .pic = pic,
135
154
  });
136
- lib.addOptions("build_options", options);
137
- lib.setMainPkgPath("./");
138
- lib.addIncludePath("src/include");
155
+ lib.root_module.addOptions("build_options", options);
156
+ lib.addIncludePath(.{ .path = "src/include" });
139
157
  lib.bundle_compiler_rt = true;
140
158
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
141
- if (pic) lib.force_pic = pic;
142
- lib.install();
159
+ if (emit_asm) {
160
+ b.getInstallStep().dependOn(&b.addInstallFileWithDir(
161
+ lib.getEmittedAsm(),
162
+ .prefix,
163
+ b.fmt("{s}.s", .{name}),
164
+ ).step);
165
+ }
166
+ if (emit_ll) {
167
+ b.getInstallStep().dependOn(&b.addInstallFileWithDir(
168
+ lib.getEmittedLlvmIr(),
169
+ .prefix,
170
+ b.fmt("{s}.ll", .{name}),
171
+ ).step);
172
+ }
173
+ b.installArtifact(lib);
143
174
  c = true;
144
175
  }
145
176
 
@@ -181,14 +212,20 @@ pub fn build(b: *std.Build) !void {
181
212
  .strip = strip,
182
213
  .cmd = cmd,
183
214
  };
215
+
216
+ // TODO: tests can be run multiple times due to @imports
184
217
  const tests = TestStep.create(b, options, config);
185
218
 
186
- var exes = std.ArrayList(*std.Build.CompileStep).init(b.allocator);
219
+ var exes = std.ArrayList(*std.Build.Step.Compile).init(b.allocator);
187
220
  const tools: ToolConfig = .{
221
+ .options = .{
222
+ .showdown = showdown,
223
+ .log = log,
224
+ .chance = chance,
225
+ .calc = calc,
226
+ },
188
227
  .general = config,
189
228
  .tool = .{
190
- .showdown = showdown,
191
- .trace = trace,
192
229
  .tests = if (tests.build) tests else null,
193
230
  .exes = &exes,
194
231
  },
@@ -197,141 +234,134 @@ pub fn build(b: *std.Build) !void {
197
234
  var benchmark_config = tools;
198
235
  benchmark_config.general.optimize = .ReleaseFast;
199
236
  benchmark_config.general.strip = true;
200
- const benchmark = tool(b, "src/test/benchmark.zig", benchmark_config);
237
+ const benchmark = try tool(b, "src/test/benchmark.zig", benchmark_config);
201
238
 
202
239
  var fuzz_config = tools;
203
240
  fuzz_config.general.strip = false;
204
241
  fuzz_config.tool.name = "fuzz";
205
- const fuzz = tool(b, "src/test/benchmark.zig", fuzz_config);
206
-
207
- const serde = tool(b, "src/tools/serde.zig", tools);
208
- const protocol = tool(b, "src/tools/protocol.zig", tools);
209
-
210
- const lint_exe =
211
- b.addExecutable(.{ .name = "lint", .root_source_file = .{ .path = "src/tools/lint.zig" } });
212
- if (tests.build) tests.step.dependOn(&lint_exe.step);
213
- const lint = lint_exe.run();
214
-
215
- b.step("benchmark", "Run benchmark code").dependOn(&benchmark.step);
216
- b.step("fuzz", "Run fuzz tester").dependOn(&fuzz.step);
217
- b.step("lint", "Lint source files").dependOn(&lint.step);
218
- b.step("protocol", "Run protocol dump tool").dependOn(&protocol.step);
219
- b.step("serde", "Run serialization/deserialization tool").dependOn(&serde.step);
242
+ const fuzz = try tool(b, "src/test/fuzz.zig", fuzz_config);
243
+
244
+ const analyze = try tool(b, "src/tools/analyze.zig", tools);
245
+ const dump = try tool(b, "src/tools/dump.zig", tools);
246
+ const transitions = try tool(b, "src/tools/transitions.zig", tools);
247
+
248
+ // FIXME: serde randomly fails to build in some release configurations
249
+ var hack = tools;
250
+ if (optimize != .Debug) hack.tool.tests = null;
251
+ const serde = try tool(b, "src/tools/serde.zig", hack);
252
+
253
+ if (analyze) |t| b.step("analyze", "Run LLVM analysis tool").dependOn(&t.step);
254
+ if (benchmark) |t| b.step("benchmark", "Run benchmark code").dependOn(&t.step);
255
+ if (dump) |t| b.step("dump", "Run protocol dump tool").dependOn(&t.step);
256
+ if (fuzz) |t| b.step("fuzz", "Run fuzz tester").dependOn(&t.step);
257
+ if (serde) |t| b.step("serde", "Run serialization/deserialization tool").dependOn(&t.step);
220
258
  b.step("test", "Run all tests").dependOn(&tests.step);
221
259
  b.step("tools", "Install tools").dependOn(&ToolsStep.create(b, &exes).step);
260
+ if (transitions) |t| {
261
+ b.step("transitions", "Visualize transitions algorithm search").dependOn(&t.step);
262
+ }
222
263
  }
223
264
 
224
265
  fn maybeStrip(
225
266
  b: *std.Build,
226
- artifact: *std.Build.CompileStep,
267
+ artifact: *std.Build.Step.Compile,
227
268
  step: *std.Build.Step,
228
- strip: bool,
269
+ strip: ?bool,
229
270
  cmd: ?[]const u8,
230
271
  ) void {
231
- artifact.strip = strip;
232
- if (!strip or cmd == null) return;
272
+ if (!(strip orelse false) or cmd == null) return;
233
273
  // Using `strip -r -u` for dynamic libraries is supposed to work on macOS but doesn't...
234
274
  const mac = builtin.os.tag == .macos;
235
275
  if (mac and artifact.isDynamicLibrary()) return;
236
276
  // Assuming GNU strip, which complains "illegal pathname found in archive member"...
237
277
  if (!mac and artifact.isStaticLibrary()) return;
238
- const sh = b.addSystemCommand(&[_][]const u8{ cmd.?, if (mac) "-x" else "-s" });
278
+ const sh = b.addSystemCommand(&.{ cmd.?, if (mac) "-x" else "-s" });
239
279
  sh.addArtifactArg(artifact);
240
280
  step.dependOn(&sh.step);
241
281
  }
242
282
 
243
283
  const Config = struct {
244
- target: std.zig.CrossTarget,
284
+ target: std.Build.ResolvedTarget,
245
285
  optimize: std.builtin.OptimizeMode,
246
- pic: bool,
247
- strip: bool,
286
+ pic: ?bool,
287
+ strip: ?bool,
248
288
  cmd: ?[]const u8,
249
289
  };
250
290
 
251
- const TESTS = [_][]const u8{
252
- "src/lib/common/test.zig",
253
- "src/lib/gen1/test.zig",
254
- "src/lib/gen2/test.zig",
255
- };
256
-
257
291
  const TestStep = struct {
258
292
  step: std.Build.Step,
259
293
  build: bool,
260
294
 
261
- pub fn create(b: *std.Build, options: *std.Build.OptionsStep, config: Config) *TestStep {
295
+ pub fn create(b: *std.Build, options: *std.Build.Step.Options, config: Config) *TestStep {
262
296
  const coverage = b.option([]const u8, "test-coverage", "Generate test coverage");
263
297
  const test_file = b.option([]const u8, "test-file", "Input file for test");
264
- const test_bin = b.option([]const u8, "test-bin", "Emit test binary to");
265
298
  const test_filter =
266
299
  b.option([]const u8, "test-filter", "Skip tests that do not match filter");
267
- const test_no_exec =
268
- b.option(bool, "test-no-exec", "Compiles test binary without running it") orelse false;
269
300
 
270
301
  const self = b.allocator.create(TestStep) catch @panic("OOM");
271
302
  const step = std.Build.Step.init(.{ .id = .custom, .name = "Run all tests", .owner = b });
272
- self.* = TestStep{ .step = step, .build = test_filter == null };
273
-
274
- const paths: []const []const u8 =
275
- if (test_file) |t| &.{t} else if (coverage != null) &.{"src/lib/test.zig"} else &TESTS;
276
- for (paths) |path| {
277
- const tests = b.addTest(.{
278
- .name = std.fs.path.basename(std.fs.path.dirname(path).?),
279
- .root_source_file = .{ .path = path },
280
- .optimize = config.optimize,
281
- .target = config.target,
282
- });
283
- tests.setMainPkgPath("./");
284
- tests.setFilter(test_filter);
285
- tests.addOptions("build_options", options);
286
- tests.single_threaded = true;
287
- maybeStrip(b, tests, &tests.step, config.strip, config.cmd);
288
- if (config.pic) tests.force_pic = config.pic;
289
- if (test_bin) |bin| {
290
- tests.name = std.fs.path.basename(bin);
291
- if (std.fs.path.dirname(bin)) |dir| tests.setOutputDir(dir);
292
- }
293
- if (coverage) |c| {
294
- tests.setExecCmd(&.{ "kcov", "--include-pattern=src/lib", c, null });
295
- }
296
- self.step.dependOn(if (test_no_exec) &tests.step else &tests.run().step);
303
+ self.* = .{ .step = step, .build = test_filter == null };
304
+
305
+ const path = test_file orelse "src/lib/test.zig";
306
+ const tests = b.addTest(.{
307
+ .name = b.fmt("{s}-{s}", .{
308
+ std.fs.path.basename(std.fs.path.dirname(path).?),
309
+ std.fs.path.stem(std.fs.path.basename(path)),
310
+ }),
311
+ .root_source_file = .{ .path = path },
312
+ .optimize = config.optimize,
313
+ .target = config.target,
314
+ .filter = test_filter,
315
+ .single_threaded = true,
316
+ .strip = config.strip,
317
+ .pic = config.pic,
318
+ });
319
+ tests.root_module.addOptions("build_options", options);
320
+ maybeStrip(b, tests, &tests.step, config.strip, config.cmd);
321
+ if (coverage) |c| {
322
+ tests.setExecCmd(&.{ "kcov", "--include-pattern=src/lib", c, null });
297
323
  }
324
+ self.step.dependOn(&b.addRunArtifact(tests).step);
325
+
298
326
  return self;
299
327
  }
300
328
  };
301
329
 
302
330
  const ToolConfig = struct {
331
+ options: Options,
303
332
  general: Config,
304
333
  tool: struct {
305
- showdown: bool,
306
- trace: bool,
307
334
  tests: ?*TestStep,
308
335
  name: ?[]const u8 = null,
309
- exes: *std.ArrayList(*std.Build.CompileStep),
336
+ exes: *std.ArrayList(*std.Build.Step.Compile),
310
337
  },
311
338
  };
312
339
 
313
- fn tool(b: *std.Build, path: []const u8, config: ToolConfig) *std.Build.RunStep {
340
+ fn tool(b: *std.Build, path: []const u8, config: ToolConfig) !?*std.Build.Step.Run {
341
+ std.fs.cwd().access(path, .{}) catch |err| switch (err) {
342
+ error.FileNotFound => return null,
343
+ else => |e| return e,
344
+ };
314
345
  var name = config.tool.name orelse std.fs.path.basename(path);
315
346
  const index = std.mem.lastIndexOfScalar(u8, name, '.');
316
347
  if (index) |i| name = name[0..i];
317
- if (config.tool.showdown) name = b.fmt("{s}-showdown", .{name});
348
+ if (config.options.showdown orelse false) name = b.fmt("{s}-showdown", .{name});
318
349
 
319
350
  const exe = b.addExecutable(.{
320
351
  .name = name,
321
352
  .root_source_file = .{ .path = path },
322
353
  .target = config.general.target,
323
354
  .optimize = config.general.optimize,
355
+ .single_threaded = true,
356
+ .strip = config.general.strip,
357
+ .pic = config.general.pic,
324
358
  });
325
- exe.addModule("pkmn", module(b, .{
326
- .showdown = config.tool.showdown,
327
- .trace = config.tool.trace,
328
- }));
329
- exe.single_threaded = true;
330
- if (config.general.pic) exe.force_pic = config.general.pic;
359
+ exe.root_module.addImport("pkmn", module(b, config.options));
360
+
331
361
  if (config.tool.tests) |ts| ts.step.dependOn(&exe.step);
332
362
  config.tool.exes.append(exe) catch @panic("OOM");
333
363
 
334
- const run = exe.run();
364
+ const run = b.addRunArtifact(exe);
335
365
  maybeStrip(b, exe, &run.step, config.general.strip, config.general.cmd);
336
366
  if (b.args) |args| run.addArgs(args);
337
367
 
@@ -341,11 +371,11 @@ fn tool(b: *std.Build, path: []const u8, config: ToolConfig) *std.Build.RunStep
341
371
  const ToolsStep = struct {
342
372
  step: std.Build.Step,
343
373
 
344
- pub fn create(b: *std.Build, exes: *std.ArrayList(*std.Build.CompileStep)) *ToolsStep {
374
+ pub fn create(b: *std.Build, exes: *std.ArrayList(*std.Build.Step.Compile)) *ToolsStep {
345
375
  const self = b.allocator.create(ToolsStep) catch @panic("OOM");
346
376
  const step = std.Build.Step.init(.{ .id = .custom, .name = "Install tools", .owner = b });
347
- self.* = ToolsStep{ .step = step };
348
- for (exes.items) |t| self.step.dependOn(&b.addInstallArtifact(t).step);
377
+ self.* = .{ .step = step };
378
+ for (exes.items) |t| self.step.dependOn(&b.addInstallArtifact(t, .{}).step);
349
379
  return self;
350
380
  }
351
381
  };