@pkmn/engine 0.1.0-dev.729b1e87 → 0.1.0-dev.73fa6bda

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 +155 -81
  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 +29 -0
  13. package/build/pkg/addon.js +80 -0
  14. package/build/pkg/addon.js.map +1 -0
  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 +28 -7
  19. package/build/pkg/data.js +3 -5
  20. package/build/pkg/data.js.map +1 -1
  21. package/build/pkg/gen1.d.ts +18 -20
  22. package/build/pkg/gen1.js +76 -51
  23. package/build/pkg/gen1.js.map +1 -1
  24. package/build/pkg/index.d.ts +57 -103
  25. package/build/pkg/index.js +23 -70
  26. package/build/pkg/index.js.map +1 -1
  27. package/build/pkg/protocol.d.ts +33 -3
  28. package/build/pkg/protocol.js +127 -104
  29. package/build/pkg/protocol.js.map +1 -1
  30. package/build/tools/debug.js +144 -0
  31. package/build/tools/debug.js.map +1 -0
  32. package/build/tools/display/debug.jsx +66 -0
  33. package/build/tools/display/debug.jsx.map +1 -0
  34. package/build/tools/display/index.js +152 -0
  35. package/build/tools/display/index.js.map +1 -0
  36. package/build/tools/display/ui.jsx +360 -0
  37. package/build/tools/display/ui.jsx.map +1 -0
  38. package/build/tools/display/util.js +157 -0
  39. package/build/tools/display/util.js.map +1 -0
  40. package/build.zig +342 -175
  41. package/package.json +94 -133
  42. package/src/bin/install-pkmn-engine +100 -42
  43. package/src/bin/pkmn-debug +62 -0
  44. package/src/lib/bindings/c.zig +200 -0
  45. package/src/lib/bindings/node.zig +117 -0
  46. package/src/lib/bindings/wasm.zig +50 -0
  47. package/src/lib/common/array.zig +70 -0
  48. package/src/lib/common/battle.zig +17 -0
  49. package/src/lib/common/data.zig +19 -31
  50. package/src/lib/common/debug.zig +47 -32
  51. package/src/lib/common/js.zig +114 -0
  52. package/src/lib/common/optional.zig +61 -0
  53. package/src/lib/common/options.zig +18 -3
  54. package/src/lib/common/protocol.zig +1072 -497
  55. package/src/lib/common/rational.zig +440 -0
  56. package/src/lib/common/rng.zig +31 -24
  57. package/src/lib/common/util.zig +44 -0
  58. package/src/lib/gen1/calc.zig +975 -0
  59. package/src/lib/gen1/chance.zig +1222 -0
  60. package/src/lib/gen1/data/moves.zig +203 -193
  61. package/src/lib/gen1/data/species.zig +6 -8
  62. package/src/lib/gen1/data/types.zig +12 -13
  63. package/src/lib/gen1/data.zig +105 -94
  64. package/src/lib/gen1/helpers.zig +138 -55
  65. package/src/lib/gen1/mechanics.zig +1439 -935
  66. package/src/lib/gen2/calc.zig +150 -0
  67. package/src/lib/gen2/chance.zig +896 -0
  68. package/src/lib/gen2/data/items.zig +15 -23
  69. package/src/lib/gen2/data/moves.zig +665 -573
  70. package/src/lib/gen2/data/species.zig +3 -5
  71. package/src/lib/gen2/data/types.zig +141 -36
  72. package/src/lib/gen2/data.zig +140 -104
  73. package/src/lib/gen2/generated.zig +801 -0
  74. package/src/lib/gen2/helpers.zig +58 -45
  75. package/src/lib/gen2/mechanics.zig +3582 -30
  76. package/src/lib/napi.h +2 -0
  77. package/src/lib/node.zig +14 -0
  78. package/src/lib/pkmn.zig +177 -22
  79. package/src/lib/wasm.zig +13 -0
  80. package/src/tools/display/dom.ts +108 -0
  81. package/src/tools/display/ui.css +354 -0
  82. package/src/lib/binding/node.zig +0 -229
  83. package/src/lib/binding/wasm.zig +0 -10
  84. package/src/lib/gen1/README.md +0 -778
  85. package/src/lib/gen2/README.md +0 -309
package/build.zig CHANGED
@@ -1,19 +1,28 @@
1
- const std = @import("std");
2
1
  const builtin = @import("builtin");
2
+ const std = @import("std");
3
3
 
4
- const NativeTargetInfo = std.zig.system.NativeTargetInfo;
4
+ fn ArrayList(comptime T: type) type {
5
+ return std.array_list.Aligned(T, null);
6
+ }
5
7
 
6
- pub const Options = struct { showdown: ?bool = null, trace: ?bool = null };
8
+ pub const Options = struct {
9
+ showdown: ?bool = null,
10
+ log: ?bool = null,
11
+ chance: ?bool = null,
12
+ calc: ?bool = null,
13
+ };
7
14
 
8
15
  pub fn module(b: *std.Build, options: Options) *std.Build.Module {
9
- const dirname = comptime std.fs.path.dirname(@src().file) orelse ".";
10
16
  const build_options = b.addOptions();
11
17
  build_options.addOption(?bool, "showdown", options.showdown);
12
- build_options.addOption(?bool, "trace", options.trace);
13
- return b.createModule(.{
14
- .source_file = .{ .path = dirname ++ "/src/lib/pkmn.zig" },
15
- .dependencies = &.{.{ .name = "build_options", .module = build_options.createModule() }},
16
- });
18
+ build_options.addOption(?bool, "log", options.log);
19
+ build_options.addOption(?bool, "chance", options.chance);
20
+ build_options.addOption(?bool, "calc", options.calc);
21
+
22
+ const root_source_file = b.path("src/lib/pkmn.zig");
23
+ const imports: []const std.Build.Module.Import =
24
+ &.{.{ .name = "build_options", .module = build_options.createModule() }};
25
+ return b.createModule(.{ .root_source_file = root_source_file, .imports = imports });
17
26
  }
18
27
 
19
28
  pub fn build(b: *std.Build) !void {
@@ -23,141 +32,193 @@ pub fn build(b: *std.Build) !void {
23
32
  const node_headers = b.option([]const u8, "node-headers", "Path to node-headers");
24
33
  const node_import_lib =
25
34
  b.option([]const u8, "node-import-library", "Path to node import library (Windows)");
35
+ const demo = if (try exists(b, "src/tools/demo.zig"))
36
+ b.option(bool, "demo", "Build the demo library") orelse false
37
+ else
38
+ false;
26
39
  const wasm = b.option(bool, "wasm", "Build a WASM library") orelse false;
27
40
  const wasm_stack_size =
28
41
  b.option(u64, "wasm-stack-size", "The size of WASM stack") orelse std.wasm.page_size;
29
42
  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, ":");
43
+ const strip = b.option(bool, "strip", "Strip debugging symbols from binary");
44
+ const pic = b.option(bool, "pic", "Force position independent code");
45
+ const emit_asm = b.option(bool, "emit-asm", "Output .s (assembly code)") orelse false;
46
+ const emit_ll = b.option(bool, "emit-ll", "Output .ll (LLVM IR)") orelse false;
47
+
48
+ const cmd = b.findProgram(&.{"strip"}, &.{}) catch null;
49
+
50
+ const json = @embedFile("package.json");
51
+ var parsed = try std.json.parseFromSlice(std.json.Value, b.allocator, json, .{});
52
+ defer parsed.deinit();
53
+ const version = parsed.value.object.get("version").?.string;
54
+ const description = parsed.value.object.get("description").?.string;
55
+ var repository = std.mem.splitScalar(u8, parsed.value.object.get("repository").?.string, ':');
42
56
  std.debug.assert(std.mem.eql(u8, repository.first(), "github"));
43
57
 
44
58
  const showdown =
45
59
  b.option(bool, "showdown", "Enable Pokémon Showdown compatibility mode") orelse false;
46
- const trace = b.option(bool, "trace", "Enable trace logs") orelse false;
60
+ const log = b.option(bool, "log", "Enable protocol message logging") orelse false;
61
+ const chance = b.option(bool, "chance", "Enable update probability tracking") orelse false;
62
+ const calc = b.option(bool, "calc", "Enable damage calculator support") orelse false;
63
+ const extra = b.option([]const []const u8, "option", "Enable extra option");
47
64
 
48
65
  const options = b.addOptions();
49
66
  options.addOption(?bool, "showdown", showdown);
50
- options.addOption(?bool, "trace", trace);
67
+ options.addOption(?bool, "log", log);
68
+ options.addOption(?bool, "chance", chance);
69
+ options.addOption(?bool, "calc", calc);
70
+
71
+ if (extra) |opts| {
72
+ for (opts) |opt| {
73
+ if (std.mem.indexOfScalar(u8, opt, '=')) |i| {
74
+ options.addOption(?bool, opt[0..i], if (std.mem.eql(u8, opt[i + 1 ..], "true"))
75
+ true
76
+ else if (std.mem.eql(u8, opt[i + 1 ..], "false"))
77
+ false
78
+ else {
79
+ std.log.err("Invalid option: {s}\n", .{opt});
80
+ return error.InvalidOption;
81
+ });
82
+ } else {
83
+ options.addOption(?bool, opt, true);
84
+ }
85
+ }
86
+ }
51
87
 
52
88
  const name = if (showdown) "pkmn-showdown" else "pkmn";
53
89
 
90
+ const pkmn: *std.Build.Module = b.addModule("pkmn", .{
91
+ .root_source_file = b.path("src/lib/pkmn.zig"),
92
+ .optimize = optimize,
93
+ .target = target,
94
+ .imports = &.{.{ .name = "build_options", .module = options.createModule() }},
95
+ });
96
+
54
97
  var c = false;
55
98
  if (node_headers) |headers| {
99
+ const translate_c = b.addTranslateC(.{
100
+ .root_source_file = b.path("src/lib/napi.h"),
101
+ .target = target,
102
+ .optimize = optimize,
103
+ });
104
+ translate_c.addSystemIncludePath(b.path(headers));
56
105
  const addon = b.fmt("{s}.node", .{name});
57
- const lib = b.addSharedLibrary(.{
106
+ const path = b.path("src/lib/node.zig");
107
+ const lib = b.addLibrary(.{
108
+ .linkage = .dynamic,
58
109
  .name = addon,
59
- .root_source_file = .{ .path = "src/lib/binding/node.zig" },
60
- .optimize = optimize,
61
- .target = target,
110
+ .root_module = b.createModule(.{
111
+ .root_source_file = path,
112
+ .optimize = optimize,
113
+ .target = target,
114
+ .strip = strip,
115
+ .pic = pic,
116
+ }),
62
117
  });
63
- lib.addOptions("build_options", options);
64
- lib.setMainPkgPath("./");
65
- lib.addSystemIncludePath(headers);
66
- lib.linkLibC();
118
+ lib.root_module.addOptions("build_options", options);
119
+ lib.root_module.addImport("napi", translate_c.createModule());
120
+ lib.root_module.link_libc = true;
67
121
  if (node_import_lib) |il| {
68
- lib.addObjectFile(il);
69
- } else if ((try NativeTargetInfo.detect(target)).target.os.tag == .windows) {
70
- try std.io.getStdErr().writeAll("Must provide --node-import-library path on Windows\n");
122
+ lib.root_module.addObjectFile(b.path(il));
123
+ } else if (target.result.os.tag == .windows) {
124
+ var err = std.Io.File.stderr().writer(b.graph.io, &.{});
125
+ try err.interface.writeAll("Must provide --node-import-library path on Windows\n");
71
126
  std.process.exit(1);
72
127
  }
73
128
  lib.linker_allow_shlib_undefined = true;
74
129
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
75
- if (pic) lib.force_pic = pic;
76
130
  // 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):
131
+ // TODO: ziglang/zig#2231 - using the following used to work (though was hacky):
78
132
  //
79
133
  // lib.emit_bin = .{ .emit_to = b.fmt("build/lib/{s}", .{addon}) };
80
134
  // b.getInstallStep().dependOn(&lib.step);
81
135
  //
82
136
  // But ziglang/zig#14647 broke this so we now need to do an install() and then manually
83
137
  // rename the file ourself in install-pkmn-engine
84
- lib.install();
138
+ b.installArtifact(lib);
85
139
  } else if (wasm) {
86
- const lib = b.addSharedLibrary(.{
87
- .name = name,
88
- .root_source_file = .{ .path = "src/lib/binding/wasm.zig" },
89
- .optimize = switch (optimize) {
90
- .ReleaseFast, .ReleaseSafe => .ReleaseSmall,
91
- else => optimize,
92
- },
93
- .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
94
- });
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;
105
- if (optimize != .Debug and opt != null) {
106
- const out = b.fmt("build/lib/{s}.wasm", .{name});
107
- const sh = b.addSystemCommand(&[_][]const u8{ opt.?, "-O4" });
108
- sh.addArtifactArg(lib);
109
- sh.addArg("-o");
110
- sh.addFileSourceArg(.{ .path = out });
111
- b.getInstallStep().dependOn(&sh.step);
112
- }
113
- lib.install();
140
+ const path = "src/lib/wasm.zig";
141
+ try buildWasm(b, name, path, optimize, strip, pic, wasm_stack_size, null, options);
142
+ } else if (demo) {
143
+ const path = "src/tools/demo.zig";
144
+ const n = if (showdown) "demo-showdown" else "demo";
145
+ const mod = pkmn;
146
+ try buildWasm(b, n, path, optimize, strip, pic, wasm_stack_size, mod, options);
114
147
  } else if (dynamic) {
115
- const lib = b.addSharedLibrary(.{
148
+ const path = b.path("src/lib/c.zig");
149
+ const lib = b.addLibrary(.{
150
+ .linkage = .dynamic,
116
151
  .name = name,
117
- .root_source_file = .{ .path = "src/lib/binding/c.zig" },
118
- .version = try std.builtin.Version.parse(version),
119
- .optimize = optimize,
120
- .target = target,
152
+ .root_module = b.createModule(.{
153
+ .root_source_file = path,
154
+ .optimize = optimize,
155
+ .target = target,
156
+ .strip = strip,
157
+ .pic = pic,
158
+ }),
121
159
  });
122
- lib.addOptions("build_options", options);
123
- lib.setMainPkgPath("./");
124
- lib.addIncludePath("src/include");
160
+ lib.root_module.addOptions("build_options", options);
161
+ lib.root_module.addIncludePath(b.path("src/include"));
125
162
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
126
- if (pic) lib.force_pic = pic;
127
- lib.install();
163
+ b.installArtifact(lib);
128
164
  c = true;
129
165
  } else {
130
- const lib = b.addStaticLibrary(.{
166
+ const path = b.path("src/lib/c.zig");
167
+ const lib = b.addLibrary(.{
168
+ .linkage = .static,
131
169
  .name = name,
132
- .root_source_file = .{ .path = "src/lib/binding/c.zig" },
133
- .optimize = optimize,
134
- .target = target,
170
+ .root_module = b.createModule(.{
171
+ .root_source_file = path,
172
+ .optimize = optimize,
173
+ .target = target,
174
+ .strip = strip,
175
+ .pic = pic,
176
+ }),
135
177
  });
136
- lib.addOptions("build_options", options);
137
- lib.setMainPkgPath("./");
138
- lib.addIncludePath("src/include");
139
- lib.bundle_compiler_rt = true;
178
+ lib.root_module.addOptions("build_options", options);
179
+ lib.root_module.addIncludePath(b.path("src/include"));
180
+ if (target.result.os.tag != .macos) {
181
+ lib.bundle_compiler_rt = true;
182
+ }
140
183
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
141
- if (pic) lib.force_pic = pic;
142
- lib.install();
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
+ }
198
+ b.installArtifact(lib);
143
199
  c = true;
144
200
  }
145
201
 
146
202
  if (c) {
147
- const header = b.addInstallFileWithDir(
148
- .{ .path = "src/include/pkmn.h" },
149
- .header,
150
- "pkmn.h",
151
- );
203
+ const path = b.path("src/include/pkmn.h");
204
+ const header = b.addInstallFileWithDir(path, .header, "pkmn.h");
152
205
  b.getInstallStep().dependOn(&header.step);
153
206
 
154
207
  const pc = b.fmt("lib{s}.pc", .{name});
155
- const file = try b.cache_root.join(b.allocator, &.{pc});
156
- const pkgconfig_file = try std.fs.cwd().createFile(file, .{});
208
+ const cwd = try std.process.currentPathAlloc(b.graph.io, b.allocator);
209
+ const file = try std.Io.Dir.path.relative(
210
+ b.allocator,
211
+ cwd,
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);
157
218
 
158
219
  const dirname = comptime std.fs.path.dirname(@src().file) orelse ".";
159
- const writer = pkgconfig_file.writer();
160
- try writer.print(
220
+ var writer = pkgconfig_file.writer(b.graph.io, &.{});
221
+ try writer.interface.print(
161
222
  \\prefix={0s}/{1s}
162
223
  \\includedir=${{prefix}}/include
163
224
  \\libdir=${{prefix}}/lib
@@ -169,26 +230,32 @@ pub fn build(b: *std.Build) !void {
169
230
  \\Cflags: -I${{includedir}}
170
231
  \\Libs: -L${{libdir}} -l{2s}
171
232
  , .{ dirname, b.install_path, name, repository.next().?, description, version });
172
- defer pkgconfig_file.close();
173
233
 
174
234
  b.installFile(file, b.fmt("share/pkgconfig/{s}", .{pc}));
175
235
  }
176
236
 
177
- const config = .{
237
+ const config: Config = .{
178
238
  .target = target,
179
239
  .optimize = optimize,
180
240
  .pic = pic,
181
241
  .strip = strip,
182
242
  .cmd = cmd,
183
243
  };
244
+
245
+ // TODO: tests can be run multiple times due to @imports
184
246
  const tests = TestStep.create(b, options, config);
185
247
 
186
- var exes = std.ArrayList(*std.Build.CompileStep).init(b.allocator);
248
+ var exes: ArrayList(*std.Build.Step.Compile) = .empty;
187
249
  const tools: ToolConfig = .{
250
+ .options = .{
251
+ .showdown = showdown,
252
+ .log = log,
253
+ .chance = chance,
254
+ .calc = calc,
255
+ },
256
+ .module = pkmn,
188
257
  .general = config,
189
258
  .tool = .{
190
- .showdown = showdown,
191
- .trace = trace,
192
259
  .tests = if (tests.build) tests else null,
193
260
  .exes = &exes,
194
261
  },
@@ -197,155 +264,255 @@ pub fn build(b: *std.Build) !void {
197
264
  var benchmark_config = tools;
198
265
  benchmark_config.general.optimize = .ReleaseFast;
199
266
  benchmark_config.general.strip = true;
200
- const benchmark = tool(b, "src/test/benchmark.zig", benchmark_config);
267
+ const benchmark = try tool(b, "src/test/benchmark.zig", benchmark_config);
201
268
 
202
269
  var fuzz_config = tools;
203
270
  fuzz_config.general.strip = false;
204
271
  fuzz_config.tool.name = "fuzz";
205
- 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
+ }
206
294
 
207
- const serde = tool(b, "src/tools/serde.zig", tools);
208
- 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
+ });
209
345
 
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();
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;
214
352
 
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);
220
- b.step("test", "Run all tests").dependOn(&tests.step);
221
- 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
+ }
222
372
  }
223
373
 
224
374
  fn maybeStrip(
225
375
  b: *std.Build,
226
- artifact: *std.Build.CompileStep,
376
+ artifact: *std.Build.Step.Compile,
227
377
  step: *std.Build.Step,
228
- strip: bool,
378
+ strip: ?bool,
229
379
  cmd: ?[]const u8,
230
380
  ) void {
231
- artifact.strip = strip;
232
- if (!strip or cmd == null) return;
381
+ if (!(strip orelse false) or cmd == null) return;
233
382
  // Using `strip -r -u` for dynamic libraries is supposed to work on macOS but doesn't...
234
383
  const mac = builtin.os.tag == .macos;
235
384
  if (mac and artifact.isDynamicLibrary()) return;
236
385
  // Assuming GNU strip, which complains "illegal pathname found in archive member"...
237
386
  if (!mac and artifact.isStaticLibrary()) return;
238
- const sh = b.addSystemCommand(&[_][]const u8{ cmd.?, if (mac) "-x" else "-s" });
387
+ const sh = b.addSystemCommand(&.{ cmd.?, if (mac) "-x" else "-s" });
239
388
  sh.addArtifactArg(artifact);
240
389
  step.dependOn(&sh.step);
241
390
  }
242
391
 
243
392
  const Config = struct {
244
- target: std.zig.CrossTarget,
393
+ target: std.Build.ResolvedTarget,
245
394
  optimize: std.builtin.OptimizeMode,
246
- pic: bool,
247
- strip: bool,
395
+ pic: ?bool,
396
+ strip: ?bool,
248
397
  cmd: ?[]const u8,
249
398
  };
250
399
 
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
400
  const TestStep = struct {
258
401
  step: std.Build.Step,
259
402
  build: bool,
260
403
 
261
- 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 {
262
405
  const coverage = b.option([]const u8, "test-coverage", "Generate test coverage");
263
- 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
406
  const test_filter =
266
407
  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
408
 
270
409
  const self = b.allocator.create(TestStep) catch @panic("OOM");
271
410
  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 },
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,
280
417
  .optimize = config.optimize,
281
418
  .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);
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 });
297
430
  }
431
+ self.step.dependOn(&b.addRunArtifact(tests).step);
432
+
298
433
  return self;
299
434
  }
300
435
  };
301
436
 
302
437
  const ToolConfig = struct {
438
+ options: Options,
439
+ module: ?*std.Build.Module,
303
440
  general: Config,
304
441
  tool: struct {
305
- showdown: bool,
306
- trace: bool,
307
442
  tests: ?*TestStep,
308
443
  name: ?[]const u8 = null,
309
- exes: *std.ArrayList(*std.Build.CompileStep),
444
+ exes: *ArrayList(*std.Build.Step.Compile),
310
445
  },
311
446
  };
312
447
 
313
- 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;
314
450
  var name = config.tool.name orelse std.fs.path.basename(path);
315
451
  const index = std.mem.lastIndexOfScalar(u8, name, '.');
316
452
  if (index) |i| name = name[0..i];
317
- if (config.tool.showdown) name = b.fmt("{s}-showdown", .{name});
453
+ if (config.options.showdown orelse false) name = b.fmt("{s}-showdown", .{name});
318
454
 
319
455
  const exe = b.addExecutable(.{
320
456
  .name = name,
321
- .root_source_file = .{ .path = path },
322
- .target = config.general.target,
323
- .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
+ }),
324
464
  });
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;
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
+
331
472
  if (config.tool.tests) |ts| ts.step.dependOn(&exe.step);
332
- config.tool.exes.append(exe) catch @panic("OOM");
473
+ config.tool.exes.append(b.allocator, exe) catch @panic("OOM");
333
474
 
334
- const run = exe.run();
475
+ const run = b.addRunArtifact(exe);
335
476
  maybeStrip(b, exe, &run.step, config.general.strip, config.general.cmd);
336
477
  if (b.args) |args| run.addArgs(args);
337
478
 
338
479
  return run;
339
480
  }
340
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
+
341
490
  const ToolsStep = struct {
342
491
  step: std.Build.Step,
343
492
 
344
- 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 {
345
494
  const self = b.allocator.create(ToolsStep) catch @panic("OOM");
346
495
  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);
496
+ self.* = .{ .step = step };
497
+ for (exes.items) |t| self.step.dependOn(&b.addInstallArtifact(t, .{}).step);
349
498
  return self;
350
499
  }
351
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
+ }