@pkmn/engine 0.1.0-dev.d716e940 → 0.1.0-dev.d73cd76c

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 +114 -69
  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 -70
  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 +39 -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 +43 -37
  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 +428 -176
  40. package/package.json +72 -122
  41. package/src/bin/install-pkmn-engine +56 -25
  42. package/src/bin/pkmn-debug +1 -1
  43. package/src/lib/bindings/c.zig +201 -0
  44. package/src/lib/bindings/node.zig +117 -0
  45. package/src/lib/bindings/wasm.zig +51 -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 +58 -33
  50. package/src/lib/common/js.zig +114 -0
  51. package/src/lib/common/optional.zig +71 -0
  52. package/src/lib/common/options.zig +15 -0
  53. package/src/lib/common/protocol.zig +1037 -462
  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 +64 -0
  57. package/src/lib/gen1/calc.zig +975 -0
  58. package/src/lib/gen1/chance.zig +1246 -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 +98 -89
  63. package/src/lib/gen1/helpers.zig +159 -51
  64. package/src/lib/gen1/mechanics.zig +1211 -846
  65. package/src/lib/gen2/calc.zig +150 -0
  66. package/src/lib/gen2/chance.zig +914 -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 +81 -58
  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 +172 -17
  78. package/src/lib/wasm.zig +13 -0
  79. package/src/tools/display/dom.ts +108 -0
  80. package/src/tools/display/ui.css +354 -0
  81. package/build/test/display.js +0 -400
  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 -812
  86. package/src/lib/gen2/README.md +0 -309
  87. package/src/test/display/pkmn.html.tmpl +0 -368
package/build.zig CHANGED
@@ -1,16 +1,37 @@
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
- pub const Options = struct { showdown: ?bool = null, log: ?bool = null };
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
- pub fn module(b: *std.Build, options: Options) *std.Build.Module {
7
- 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 {
8
21
  const build_options = b.addOptions();
9
22
  build_options.addOption(?bool, "showdown", options.showdown);
10
23
  build_options.addOption(?bool, "log", options.log);
24
+ build_options.addOption(?bool, "chance", options.chance);
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() }};
11
30
  return b.createModule(.{
12
- .source_file = .{ .path = dirname ++ "/src/lib/pkmn.zig" },
13
- .dependencies = &.{.{ .name = "build_options", .module = build_options.createModule() }},
31
+ .root_source_file = root_source_file,
32
+ .target = target,
33
+ .optimize = optimize,
34
+ .imports = imports,
14
35
  });
15
36
  }
16
37
 
@@ -21,59 +42,107 @@ pub fn build(b: *std.Build) !void {
21
42
  const node_headers = b.option([]const u8, "node-headers", "Path to node-headers");
22
43
  const node_import_lib =
23
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;
24
49
  const wasm = b.option(bool, "wasm", "Build a WASM library") orelse false;
25
50
  const wasm_stack_size =
26
51
  b.option(u64, "wasm-stack-size", "The size of WASM stack") orelse std.wasm.page_size;
27
52
  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;
53
+ const strip = b.option(bool, "strip", "Strip debugging symbols from binary");
54
+ const pic = b.option(bool, "pic", "Force position independent code");
30
55
  const emit_asm = b.option(bool, "emit-asm", "Output .s (assembly code)") orelse false;
31
-
32
- const cmd = b.findProgram(&.{"strip"}, &.{}) catch null;
33
-
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, ":");
56
+ const emit_ll = b.option(bool, "emit-ll", "Output .ll (LLVM IR)") orelse false;
57
+
58
+ const cmd = if (@hasDecl(std.Build, "FindProgramOptions"))
59
+ b.findProgram(.{ .names = &.{"strip"} })
60
+ else
61
+ b.findProgram(&.{"strip"}, &.{}) catch null;
62
+
63
+ const json = @embedFile("package.json");
64
+ var parsed = try std.json.parseFromSlice(std.json.Value, b.allocator, json, .{});
65
+ defer parsed.deinit();
66
+ const version = parsed.value.object.get("version").?.string;
67
+ const description = parsed.value.object.get("description").?.string;
68
+ var repository = std.mem.splitScalar(u8, parsed.value.object.get("repository").?.string, ':');
41
69
  std.debug.assert(std.mem.eql(u8, repository.first(), "github"));
42
70
 
43
71
  const showdown =
44
72
  b.option(bool, "showdown", "Enable Pokémon Showdown compatibility mode") orelse false;
45
73
  const log = b.option(bool, "log", "Enable protocol message logging") orelse false;
74
+ const chance = b.option(bool, "chance", "Enable update probability tracking") orelse false;
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");
46
77
 
47
78
  const options = b.addOptions();
48
79
  options.addOption(?bool, "showdown", showdown);
49
80
  options.addOption(?bool, "log", log);
81
+ options.addOption(?bool, "chance", chance);
82
+ options.addOption(?bool, "calc", calc);
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
+ }
50
100
 
51
101
  const name = if (showdown) "pkmn-showdown" else "pkmn";
52
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
+
53
110
  var c = false;
54
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));
55
119
  const addon = b.fmt("{s}.node", .{name});
56
- const lib = b.addSharedLibrary(.{
120
+ const path = b.path("src/lib/node.zig");
121
+ const lib = b.addLibrary(.{
122
+ .linkage = .dynamic,
57
123
  .name = addon,
58
- .root_source_file = .{ .path = "src/lib/binding/node.zig" },
59
- .optimize = optimize,
60
- .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
+ }),
61
131
  });
62
- lib.addOptions("build_options", options);
63
- lib.setMainPkgPath("./");
64
- lib.addSystemIncludePath(headers);
65
- 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;
66
135
  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");
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");
70
140
  std.process.exit(1);
71
141
  }
72
142
  lib.linker_allow_shlib_undefined = true;
73
143
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
74
- if (pic) lib.force_pic = pic;
75
144
  // 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):
145
+ // TODO: ziglang/zig#2231 - using the following used to work (though was hacky):
77
146
  //
78
147
  // lib.emit_bin = .{ .emit_to = b.fmt("build/lib/{s}", .{addon}) };
79
148
  // b.getInstallStep().dependOn(&lib.step);
@@ -82,110 +151,137 @@ pub fn build(b: *std.Build) !void {
82
151
  // rename the file ourself in install-pkmn-engine
83
152
  b.installArtifact(lib);
84
153
  } 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);
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);
110
161
  } else if (dynamic) {
111
- const lib = b.addSharedLibrary(.{
162
+ const path = b.path("src/lib/c.zig");
163
+ const lib = b.addLibrary(.{
164
+ .linkage = .dynamic,
112
165
  .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,
166
+ .root_module = b.createModule(.{
167
+ .root_source_file = path,
168
+ .optimize = optimize,
169
+ .target = target,
170
+ .strip = strip,
171
+ .pic = pic,
172
+ }),
117
173
  });
118
- lib.addOptions("build_options", options);
119
- lib.setMainPkgPath("./");
120
- lib.addIncludePath("src/include");
174
+ lib.root_module.addOptions("build_options", options);
175
+ lib.root_module.addIncludePath(b.path("src/include"));
121
176
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
122
- if (pic) lib.force_pic = pic;
123
177
  b.installArtifact(lib);
124
178
  c = true;
125
179
  } else {
126
- const lib = b.addStaticLibrary(.{
180
+ const path = b.path("src/lib/c.zig");
181
+ const lib = b.addLibrary(.{
182
+ .linkage = .static,
127
183
  .name = name,
128
- .root_source_file = .{ .path = "src/lib/binding/c.zig" },
129
- .optimize = optimize,
130
- .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
+ }),
131
191
  });
132
- lib.addOptions("build_options", options);
133
- lib.setMainPkgPath("./");
134
- lib.addIncludePath("src/include");
135
- 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
+ }
136
197
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
137
- if (pic) lib.force_pic = pic;
138
- if (emit_asm) lib.emit_asm = .emit;
139
- 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);
140
215
  c = true;
141
216
  }
142
217
 
143
218
  if (c) {
144
- const header = b.addInstallFileWithDir(
145
- .{ .path = "src/include/pkmn.h" },
146
- .header,
147
- "pkmn.h",
148
- );
219
+ const path = b.path("src/include/pkmn.h");
220
+ const header = b.addInstallFileWithDir(path, .header, "pkmn.h");
149
221
  b.getInstallStep().dependOn(&header.step);
150
222
 
151
- 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, .{});
154
-
155
- const dirname = comptime std.fs.path.dirname(@src().file) orelse ".";
156
- const writer = pkgconfig_file.writer();
157
- try writer.print(
158
- \\prefix={0s}/{1s}
223
+ const content = try std.fmt.allocPrint(b.allocator,
224
+ \\prefix=${{pcfiledir}}/../..
159
225
  \\includedir=${{prefix}}/include
160
226
  \\libdir=${{prefix}}/lib
161
227
  \\
162
- \\Name: lib{2s}
163
- \\URL: https://github.com/{3s}
164
- \\Description: {4s}
165
- \\Version: {5s}
228
+ \\Name: lib{0s}
229
+ \\URL: https://github.com/{1s}
230
+ \\Description: {2s}
231
+ \\Version: {3s}
166
232
  \\Cflags: -I${{includedir}}
167
- \\Libs: -L${{libdir}} -l{2s}
168
- , .{ dirname, b.install_path, name, repository.next().?, description, version });
169
- defer pkgconfig_file.close();
233
+ \\Libs: -L${{libdir}} -l{0s}
234
+ , .{ name, repository.next().?, description, version });
170
235
 
171
- 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
+ }
172
262
  }
173
263
 
174
- const config = .{
264
+ const config: Config = .{
175
265
  .target = target,
176
266
  .optimize = optimize,
177
267
  .pic = pic,
178
268
  .strip = strip,
179
269
  .cmd = cmd,
180
270
  };
271
+
272
+ // TODO: tests can be run multiple times due to @imports
181
273
  const tests = TestStep.create(b, options, config);
182
274
 
183
- var exes = std.ArrayList(*std.Build.CompileStep).init(b.allocator);
275
+ var exes: ArrayList(*std.Build.Step.Compile) = .empty;
184
276
  const tools: ToolConfig = .{
185
- .general = config,
186
- .tool = .{
277
+ .options = .{
187
278
  .showdown = showdown,
188
279
  .log = log,
280
+ .chance = chance,
281
+ .calc = calc,
282
+ },
283
+ .general = config,
284
+ .tool = .{
189
285
  .tests = if (tests.build) tests else null,
190
286
  .exes = &exes,
191
287
  },
@@ -194,39 +290,126 @@ pub fn build(b: *std.Build) !void {
194
290
  var benchmark_config = tools;
195
291
  benchmark_config.general.optimize = .ReleaseFast;
196
292
  benchmark_config.general.strip = true;
197
- const benchmark = tool(b, "src/test/benchmark.zig", benchmark_config);
293
+ const benchmark = try tool(b, "src/test/benchmark.zig", benchmark_config);
198
294
 
199
295
  var fuzz_config = tools;
200
296
  fuzz_config.general.strip = false;
201
297
  fuzz_config.tool.name = "fuzz";
202
- const fuzz = tool(b, "src/test/benchmark.zig", fuzz_config);
203
-
204
- const serde = tool(b, "src/tools/serde.zig", tools);
205
- const protocol = tool(b, "src/tools/protocol.zig", tools);
206
-
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);
211
-
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);
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
+ }
219
403
  }
220
404
 
221
405
  fn maybeStrip(
222
406
  b: *std.Build,
223
- artifact: *std.Build.CompileStep,
407
+ artifact: *std.Build.Step.Compile,
224
408
  step: *std.Build.Step,
225
- strip: bool,
409
+ strip: ?bool,
226
410
  cmd: ?[]const u8,
227
411
  ) void {
228
- artifact.strip = strip;
229
- if (!strip or cmd == null) return;
412
+ if (!(strip orelse false) or cmd == null) return;
230
413
  // Using `strip -r -u` for dynamic libraries is supposed to work on macOS but doesn't...
231
414
  const mac = builtin.os.tag == .macos;
232
415
  if (mac and artifact.isDynamicLibrary()) return;
@@ -238,104 +421,173 @@ fn maybeStrip(
238
421
  }
239
422
 
240
423
  const Config = struct {
241
- target: std.zig.CrossTarget,
424
+ target: std.Build.ResolvedTarget,
242
425
  optimize: std.builtin.OptimizeMode,
243
- pic: bool,
244
- strip: bool,
426
+ pic: ?bool,
427
+ strip: ?bool,
245
428
  cmd: ?[]const u8,
246
429
  };
247
430
 
248
- const TESTS = .{
249
- "src/lib/common/test.zig",
250
- "src/lib/gen1/test.zig",
251
- "src/lib/gen2/test.zig",
252
- };
253
-
254
431
  const TestStep = struct {
255
- step: std.Build.Step,
432
+ step: *std.Build.Step,
256
433
  build: bool,
257
434
 
258
- 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 {
259
436
  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
437
  const test_filter =
262
438
  b.option([]const u8, "test-filter", "Skip tests that do not match filter");
263
439
 
264
440
  const self = b.allocator.create(TestStep) catch @panic("OOM");
265
- 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 },
441
+
442
+ const path = b.path("src/lib/test.zig");
443
+ const tests = b.addTest(.{
444
+ .root_module = b.createModule(.{
445
+ .root_source_file = path,
274
446
  .optimize = config.optimize,
275
447
  .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);
448
+ .single_threaded = true,
449
+ .strip = config.strip,
450
+ .pic = config.pic,
451
+ }),
452
+ .filters = if (test_filter) |filter| &.{filter} else &.{},
453
+ });
454
+ tests.root_module.addOptions("build_options", options);
455
+
456
+ maybeStrip(b, tests, &tests.step, config.strip, config.cmd);
457
+ if (coverage) |c| {
458
+ const kcov_run = b.addSystemCommand(&.{ "kcov", "--include-pattern=src/lib", c });
459
+ kcov_run.addArtifactArg(tests);
460
+ kcov_run.enableTestRunnerMode();
461
+ self.* = .{ .step = &kcov_run.step, .build = test_filter == null };
462
+ } else {
463
+ const run_step = b.addRunArtifact(tests);
464
+ self.* = .{ .step = &run_step.step, .build = test_filter == null };
287
465
  }
466
+
288
467
  return self;
289
468
  }
290
469
  };
291
470
 
292
471
  const ToolConfig = struct {
472
+ options: Options,
293
473
  general: Config,
294
474
  tool: struct {
295
- showdown: bool,
296
- log: bool,
297
475
  tests: ?*TestStep,
298
476
  name: ?[]const u8 = null,
299
- exes: *std.ArrayList(*std.Build.CompileStep),
477
+ exes: *ArrayList(*std.Build.Step.Compile),
300
478
  },
301
479
  };
302
480
 
303
- fn tool(b: *std.Build, path: []const u8, config: ToolConfig) *std.Build.RunStep {
481
+ fn tool(b: *std.Build, path: []const u8, config: ToolConfig) !?*std.Build.Step.Run {
482
+ if (!try exists(b, path)) return null;
304
483
  var name = config.tool.name orelse std.fs.path.basename(path);
305
484
  const index = std.mem.lastIndexOfScalar(u8, name, '.');
306
485
  if (index) |i| name = name[0..i];
307
- if (config.tool.showdown) name = b.fmt("{s}-showdown", .{name});
486
+ if (config.options.showdown orelse false) name = b.fmt("{s}-showdown", .{name});
308
487
 
309
488
  const exe = b.addExecutable(.{
310
489
  .name = name,
311
- .root_source_file = .{ .path = path },
312
- .target = config.general.target,
313
- .optimize = config.general.optimize,
490
+ .root_module = b.createModule(.{
491
+ .root_source_file = b.path(path),
492
+ .target = config.general.target,
493
+ .optimize = config.general.optimize,
494
+ .strip = config.general.strip,
495
+ .pic = config.general.pic,
496
+ }),
314
497
  });
315
- exe.addModule("pkmn", module(b, .{
316
- .showdown = config.tool.showdown,
317
- .log = config.tool.log,
318
- }));
319
- exe.single_threaded = true;
320
- if (config.general.pic) exe.force_pic = config.general.pic;
498
+
499
+ const import = module(b, config.options, config.general.target, config.general.optimize);
500
+ exe.root_module.addImport("pkmn", import);
501
+ if (config.general.target.result.os.tag == .windows) {
502
+ exe.root_module.linkSystemLibrary("advapi32", .{});
503
+ }
504
+
321
505
  if (config.tool.tests) |ts| ts.step.dependOn(&exe.step);
322
- config.tool.exes.append(exe) catch @panic("OOM");
506
+ config.tool.exes.append(b.allocator, exe) catch @panic("OOM");
323
507
 
324
508
  const run = b.addRunArtifact(exe);
325
509
  maybeStrip(b, exe, &run.step, config.general.strip, config.general.cmd);
326
- if (b.args) |args| run.addArgs(args);
510
+ if (@hasDecl(std.Build, "FindProgramOptions")) {
511
+ run.addPassthruArgs();
512
+ } else {
513
+ if (b.args) |args| run.addArgs(args);
514
+ }
327
515
 
328
516
  return run;
329
517
  }
330
518
 
519
+ fn exists(b: *std.Build, path: []const u8) !bool {
520
+ std.Io.Dir.cwd().access(b.graph.io, path, .{}) catch |err| switch (err) {
521
+ error.FileNotFound => return false,
522
+ else => |e| return e,
523
+ };
524
+ return true;
525
+ }
526
+
331
527
  const ToolsStep = struct {
332
- step: std.Build.Step,
528
+ step: *std.Build.Step,
333
529
 
334
- pub fn create(b: *std.Build, exes: *std.ArrayList(*std.Build.CompileStep)) *ToolsStep {
530
+ pub fn create(b: *std.Build, exes: *ArrayList(*std.Build.Step.Compile)) *ToolsStep {
335
531
  const self = b.allocator.create(ToolsStep) catch @panic("OOM");
336
- 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);
532
+
533
+ if (@hasDecl(std.Build, "FindProgramOptions")) {
534
+ const step = b.allocator.create(std.Build.Step.TopLevel) catch @panic("OOM");
535
+ step.* = .{
536
+ .step = std.Build.Step.init(.{
537
+ .tag = .top_level,
538
+ .name = "Install tools",
539
+ .owner = b,
540
+ }),
541
+ .description = "Install tools",
542
+ };
543
+ self.* = .{ .step = &step.step };
544
+ } else {
545
+ const step = b.allocator.create(std.Build.Step) catch @panic("OOM");
546
+ step.* = std.Build.Step.init(.{
547
+ .id = .custom,
548
+ .name = "Install tools",
549
+ .owner = b,
550
+ });
551
+ self.* = .{ .step = step };
552
+ }
553
+ for (exes.items) |t| self.step.dependOn(&b.addInstallArtifact(t, .{}).step);
554
+
339
555
  return self;
340
556
  }
341
557
  };
558
+
559
+ pub fn exports(b: *std.Build, bytes: []const u8) ![][]const u8 {
560
+ var symbols: ArrayList([]const u8) = .empty;
561
+
562
+ var it = std.mem.splitSequence(u8, bytes, "export ");
563
+ _ = it.next();
564
+ while (it.next()) |s| {
565
+ if (std.mem.startsWith(u8, s, "const ")) {
566
+ const i = std.mem.indexOf(u8, s[6..], " ").?;
567
+ try symbols.append(b.allocator, s[6 .. 6 + i]);
568
+ } else { // "fn "
569
+ const i = std.mem.indexOf(u8, s[3..], "(").?;
570
+ try symbols.append(b.allocator, s[3 .. 3 + i]);
571
+ }
572
+ }
573
+
574
+ return symbols.items;
575
+ }
576
+
577
+ fn maybeRanlib(
578
+ b: *std.Build,
579
+ artifact: *std.Build.Step.Compile,
580
+ install_step: *std.Build.Step,
581
+ ) void {
582
+ if (builtin.os.tag != .macos) return;
583
+ if (artifact.linkage != .static) return;
584
+
585
+ const ranlib = if (comptime @hasDecl(std.Build, "FindProgramOptions"))
586
+ b.findProgram(.{ .names = &.{"ranlib"} }) orelse return
587
+ else
588
+ b.findProgram(&[_][]const u8{"ranlib"}, &[_][]const u8{}) catch return;
589
+
590
+ const sh = b.addSystemCommand(&[_][]const u8{ranlib});
591
+ sh.addFileArg(artifact.getEmittedBin());
592
+ install_step.dependOn(&sh.step);
593
+ }