@pkmn/engine 0.1.0-dev.437f6a52 → 0.1.0-dev.4438e918

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 (86) hide show
  1. package/LICENSE +1 -3
  2. package/README.md +104 -59
  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 +304 -142
  40. package/package.json +71 -121
  41. package/src/bin/install-pkmn-engine +48 -22
  42. package/src/bin/pkmn-debug +1 -1
  43. package/src/lib/bindings/c.zig +200 -0
  44. package/src/lib/{binding → bindings}/node.zig +15 -32
  45. package/src/lib/bindings/wasm.zig +50 -0
  46. package/src/lib/common/array.zig +70 -0
  47. package/src/lib/common/battle.zig +17 -0
  48. package/src/lib/common/data.zig +12 -24
  49. package/src/lib/common/debug.zig +33 -28
  50. package/src/lib/common/js.zig +114 -0
  51. package/src/lib/common/optional.zig +61 -0
  52. package/src/lib/common/options.zig +6 -0
  53. package/src/lib/common/protocol.zig +964 -450
  54. package/src/lib/common/rational.zig +440 -0
  55. package/src/lib/common/rng.zig +22 -15
  56. package/src/lib/common/util.zig +44 -0
  57. package/src/lib/gen1/calc.zig +975 -0
  58. package/src/lib/gen1/chance.zig +1222 -0
  59. package/src/lib/gen1/data/moves.zig +193 -183
  60. package/src/lib/gen1/data/species.zig +4 -6
  61. package/src/lib/gen1/data/types.zig +9 -10
  62. package/src/lib/gen1/data.zig +71 -81
  63. package/src/lib/gen1/helpers.zig +90 -410
  64. package/src/lib/gen1/mechanics.zig +712 -648
  65. package/src/lib/gen2/calc.zig +150 -0
  66. package/src/lib/gen2/chance.zig +896 -0
  67. package/src/lib/gen2/data/items.zig +13 -21
  68. package/src/lib/gen2/data/moves.zig +664 -572
  69. package/src/lib/gen2/data/species.zig +2 -4
  70. package/src/lib/gen2/data/types.zig +139 -34
  71. package/src/lib/gen2/data.zig +121 -96
  72. package/src/lib/gen2/generated.zig +801 -0
  73. package/src/lib/gen2/helpers.zig +53 -38
  74. package/src/lib/gen2/mechanics.zig +3580 -29
  75. package/src/lib/napi.h +2 -0
  76. package/src/lib/node.zig +14 -0
  77. package/src/lib/pkmn.zig +125 -29
  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/wasm.zig +0 -12
  84. package/src/lib/gen1/README.md +0 -814
  85. package/src/lib/gen2/README.md +0 -309
  86. 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,
@@ -9,16 +13,16 @@ pub const Options = struct {
9
13
  };
10
14
 
11
15
  pub fn module(b: *std.Build, options: Options) *std.Build.Module {
12
- const dirname = comptime std.fs.path.dirname(@src().file) orelse ".";
13
16
  const build_options = b.addOptions();
14
17
  build_options.addOption(?bool, "showdown", options.showdown);
15
18
  build_options.addOption(?bool, "log", options.log);
16
19
  build_options.addOption(?bool, "chance", options.chance);
17
20
  build_options.addOption(?bool, "calc", options.calc);
18
- return b.createModule(.{
19
- .source_file = .{ .path = dirname ++ "/src/lib/pkmn.zig" },
20
- .dependencies = &.{.{ .name = "build_options", .module = build_options.createModule() }},
21
- });
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 });
22
26
  }
23
27
 
24
28
  pub fn build(b: *std.Build) !void {
@@ -28,13 +32,18 @@ pub fn build(b: *std.Build) !void {
28
32
  const node_headers = b.option([]const u8, "node-headers", "Path to node-headers");
29
33
  const node_import_lib =
30
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;
31
39
  const wasm = b.option(bool, "wasm", "Build a WASM library") orelse false;
32
40
  const wasm_stack_size =
33
41
  b.option(u64, "wasm-stack-size", "The size of WASM stack") orelse std.wasm.page_size;
34
42
  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;
43
+ const strip = b.option(bool, "strip", "Strip debugging symbols from binary");
44
+ const pic = b.option(bool, "pic", "Force position independent code");
37
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;
38
47
 
39
48
  const cmd = b.findProgram(&.{"strip"}, &.{}) catch null;
40
49
 
@@ -43,7 +52,7 @@ pub fn build(b: *std.Build) !void {
43
52
  defer parsed.deinit();
44
53
  const version = parsed.value.object.get("version").?.string;
45
54
  const description = parsed.value.object.get("description").?.string;
46
- var repository = std.mem.split(u8, parsed.value.object.get("repository").?.string, ":");
55
+ var repository = std.mem.splitScalar(u8, parsed.value.object.get("repository").?.string, ':');
47
56
  std.debug.assert(std.mem.eql(u8, repository.first(), "github"));
48
57
 
49
58
  const showdown =
@@ -51,6 +60,7 @@ pub fn build(b: *std.Build) !void {
51
60
  const log = b.option(bool, "log", "Enable protocol message logging") orelse false;
52
61
  const chance = b.option(bool, "chance", "Enable update probability tracking") orelse false;
53
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");
54
64
 
55
65
  const options = b.addOptions();
56
66
  options.addOption(?bool, "showdown", showdown);
@@ -58,32 +68,67 @@ pub fn build(b: *std.Build) !void {
58
68
  options.addOption(?bool, "chance", chance);
59
69
  options.addOption(?bool, "calc", calc);
60
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
+ }
87
+
61
88
  const name = if (showdown) "pkmn-showdown" else "pkmn";
62
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
+
63
97
  var c = false;
64
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));
65
105
  const addon = b.fmt("{s}.node", .{name});
66
- const lib = b.addSharedLibrary(.{
106
+ const path = b.path("src/lib/node.zig");
107
+ const lib = b.addLibrary(.{
108
+ .linkage = .dynamic,
67
109
  .name = addon,
68
- .root_source_file = .{ .path = "src/lib/binding/node.zig" },
69
- .optimize = optimize,
70
- .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
+ }),
71
117
  });
72
- lib.addOptions("build_options", options);
73
- lib.setMainPkgPath("./");
74
- lib.addSystemIncludePath(headers);
75
- 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;
76
121
  if (node_import_lib) |il| {
77
- lib.addObjectFile(il);
78
- } else if ((try std.zig.system.NativeTargetInfo.detect(target)).target.os.tag == .windows) {
79
- 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");
80
126
  std.process.exit(1);
81
127
  }
82
128
  lib.linker_allow_shlib_undefined = true;
83
129
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
84
- if (pic) lib.force_pic = pic;
85
130
  // Always emit to build/lib because this is where the driver code expects to find it
86
- // TODO(ziglang/zig#2231): using the following used to work (though was hacky):
131
+ // TODO: ziglang/zig#2231 - using the following used to work (though was hacky):
87
132
  //
88
133
  // lib.emit_bin = .{ .emit_to = b.fmt("build/lib/{s}", .{addon}) };
89
134
  // b.getInstallStep().dependOn(&lib.step);
@@ -92,79 +137,88 @@ pub fn build(b: *std.Build) !void {
92
137
  // rename the file ourself in install-pkmn-engine
93
138
  b.installArtifact(lib);
94
139
  } else if (wasm) {
95
- const lib = b.addSharedLibrary(.{
96
- .name = name,
97
- .root_source_file = .{ .path = "src/lib/binding/wasm.zig" },
98
- .optimize = switch (optimize) {
99
- .ReleaseFast, .ReleaseSafe => .ReleaseSmall,
100
- else => optimize,
101
- },
102
- .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
103
- });
104
- lib.addOptions("build_options", options);
105
- lib.setMainPkgPath("./");
106
- lib.stack_size = wasm_stack_size;
107
- lib.rdynamic = true;
108
- lib.strip = strip;
109
- if (pic) lib.force_pic = pic;
110
- const opt = b.findProgram(&.{"wasm-opt"}, &.{"./node_modules/.bin"}) catch null;
111
- if (optimize != .Debug and opt != null) {
112
- const out = b.fmt("build/lib/{s}.wasm", .{name});
113
- const sh = b.addSystemCommand(&.{ opt.?, "-O4" });
114
- sh.addArtifactArg(lib);
115
- sh.addArg("-o");
116
- sh.addFileSourceArg(.{ .path = out });
117
- b.getInstallStep().dependOn(&sh.step);
118
- }
119
- b.installArtifact(lib);
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);
120
147
  } else if (dynamic) {
121
- const lib = b.addSharedLibrary(.{
148
+ const path = b.path("src/lib/c.zig");
149
+ const lib = b.addLibrary(.{
150
+ .linkage = .dynamic,
122
151
  .name = name,
123
- .root_source_file = .{ .path = "src/lib/binding/c.zig" },
124
- .version = try std.SemanticVersion.parse(version),
125
- .optimize = optimize,
126
- .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
+ }),
127
159
  });
128
- lib.addOptions("build_options", options);
129
- lib.setMainPkgPath("./");
130
- lib.addIncludePath("src/include");
160
+ lib.root_module.addOptions("build_options", options);
161
+ lib.root_module.addIncludePath(b.path("src/include"));
131
162
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
132
- if (pic) lib.force_pic = pic;
133
163
  b.installArtifact(lib);
134
164
  c = true;
135
165
  } else {
136
- const lib = b.addStaticLibrary(.{
166
+ const path = b.path("src/lib/c.zig");
167
+ const lib = b.addLibrary(.{
168
+ .linkage = .static,
137
169
  .name = name,
138
- .root_source_file = .{ .path = "src/lib/binding/c.zig" },
139
- .optimize = optimize,
140
- .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
+ }),
141
177
  });
142
- lib.addOptions("build_options", options);
143
- lib.setMainPkgPath("./");
144
- lib.addIncludePath("src/include");
145
- 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
+ }
146
183
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
147
- if (pic) lib.force_pic = pic;
148
- if (emit_asm) lib.emit_asm = .emit;
184
+ if (emit_asm) {
185
+ b.getInstallStep().dependOn(&b.addInstallFileWithDir(
186
+ lib.getEmittedAsm(),
187
+ .prefix,
188
+ b.fmt("{s}.s", .{name}),
189
+ ).step);
190
+ }
191
+ if (emit_ll) {
192
+ b.getInstallStep().dependOn(&b.addInstallFileWithDir(
193
+ lib.getEmittedLlvmIr(),
194
+ .prefix,
195
+ b.fmt("{s}.ll", .{name}),
196
+ ).step);
197
+ }
149
198
  b.installArtifact(lib);
150
199
  c = true;
151
200
  }
152
201
 
153
202
  if (c) {
154
- const header = b.addInstallFileWithDir(
155
- .{ .path = "src/include/pkmn.h" },
156
- .header,
157
- "pkmn.h",
158
- );
203
+ const path = b.path("src/include/pkmn.h");
204
+ const header = b.addInstallFileWithDir(path, .header, "pkmn.h");
159
205
  b.getInstallStep().dependOn(&header.step);
160
206
 
161
207
  const pc = b.fmt("lib{s}.pc", .{name});
162
- const file = try b.cache_root.join(b.allocator, &.{pc});
163
- 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);
164
218
 
165
219
  const dirname = comptime std.fs.path.dirname(@src().file) orelse ".";
166
- const writer = pkgconfig_file.writer();
167
- try writer.print(
220
+ var writer = pkgconfig_file.writer(b.graph.io, &.{});
221
+ try writer.interface.print(
168
222
  \\prefix={0s}/{1s}
169
223
  \\includedir=${{prefix}}/include
170
224
  \\libdir=${{prefix}}/lib
@@ -176,12 +230,11 @@ pub fn build(b: *std.Build) !void {
176
230
  \\Cflags: -I${{includedir}}
177
231
  \\Libs: -L${{libdir}} -l{2s}
178
232
  , .{ dirname, b.install_path, name, repository.next().?, description, version });
179
- defer pkgconfig_file.close();
180
233
 
181
234
  b.installFile(file, b.fmt("share/pkgconfig/{s}", .{pc}));
182
235
  }
183
236
 
184
- const config = .{
237
+ const config: Config = .{
185
238
  .target = target,
186
239
  .optimize = optimize,
187
240
  .pic = pic,
@@ -192,7 +245,7 @@ pub fn build(b: *std.Build) !void {
192
245
  // TODO: tests can be run multiple times due to @imports
193
246
  const tests = TestStep.create(b, options, config);
194
247
 
195
- var exes = std.ArrayList(*std.Build.CompileStep).init(b.allocator);
248
+ var exes: ArrayList(*std.Build.Step.Compile) = .empty;
196
249
  const tools: ToolConfig = .{
197
250
  .options = .{
198
251
  .showdown = showdown,
@@ -200,6 +253,7 @@ pub fn build(b: *std.Build) !void {
200
253
  .chance = chance,
201
254
  .calc = calc,
202
255
  },
256
+ .module = pkmn,
203
257
  .general = config,
204
258
  .tool = .{
205
259
  .tests = if (tests.build) tests else null,
@@ -210,41 +264,121 @@ pub fn build(b: *std.Build) !void {
210
264
  var benchmark_config = tools;
211
265
  benchmark_config.general.optimize = .ReleaseFast;
212
266
  benchmark_config.general.strip = true;
213
- const benchmark = tool(b, "src/test/benchmark.zig", benchmark_config);
267
+ const benchmark = try tool(b, "src/test/benchmark.zig", benchmark_config);
214
268
 
215
269
  var fuzz_config = tools;
216
270
  fuzz_config.general.strip = false;
217
271
  fuzz_config.tool.name = "fuzz";
218
- const fuzz = tool(b, "src/test/fuzz.zig", fuzz_config);
219
-
220
- const dump = tool(b, "src/tools/dump.zig", tools);
221
- const serde = tool(b, "src/tools/serde.zig", tools);
222
- const transitions = tool(b, "src/tools/transitions.zig", tools);
223
-
224
- const lint_exe =
225
- b.addExecutable(.{ .name = "lint", .root_source_file = .{ .path = "src/tools/lint.zig" } });
226
- if (tests.build) tests.step.dependOn(&lint_exe.step);
227
- const lint = b.addRunArtifact(lint_exe);
228
-
229
- b.step("benchmark", "Run benchmark code").dependOn(&benchmark.step);
230
- b.step("dump", "Run protocol dump tool").dependOn(&dump.step);
231
- b.step("fuzz", "Run fuzz tester").dependOn(&fuzz.step);
232
- b.step("lint", "Lint source files").dependOn(&lint.step);
233
- b.step("serde", "Run serialization/deserialization tool").dependOn(&serde.step);
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);
234
288
  b.step("test", "Run all tests").dependOn(&tests.step);
235
289
  b.step("tools", "Install tools").dependOn(&ToolsStep.create(b, &exes).step);
236
- b.step("transitions", "Visualize transitions algorithm search").dependOn(&transitions.step);
290
+ if (transitions) |t| {
291
+ b.step("transitions", "Visualize transitions algorithm search").dependOn(&t.step);
292
+ }
293
+ }
294
+
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
+ });
345
+
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;
352
+
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
+ }
237
372
  }
238
373
 
239
374
  fn maybeStrip(
240
375
  b: *std.Build,
241
- artifact: *std.Build.CompileStep,
376
+ artifact: *std.Build.Step.Compile,
242
377
  step: *std.Build.Step,
243
- strip: bool,
378
+ strip: ?bool,
244
379
  cmd: ?[]const u8,
245
380
  ) void {
246
- artifact.strip = strip;
247
- if (!strip or cmd == null) return;
381
+ if (!(strip orelse false) or cmd == null) return;
248
382
  // Using `strip -r -u` for dynamic libraries is supposed to work on macOS but doesn't...
249
383
  const mac = builtin.os.tag == .macos;
250
384
  if (mac and artifact.isDynamicLibrary()) return;
@@ -256,68 +390,63 @@ fn maybeStrip(
256
390
  }
257
391
 
258
392
  const Config = struct {
259
- target: std.zig.CrossTarget,
393
+ target: std.Build.ResolvedTarget,
260
394
  optimize: std.builtin.OptimizeMode,
261
- pic: bool,
262
- strip: bool,
395
+ pic: ?bool,
396
+ strip: ?bool,
263
397
  cmd: ?[]const u8,
264
398
  };
265
399
 
266
- const TESTS = .{
267
- "src/lib/common/test.zig",
268
- "src/lib/gen1/test.zig",
269
- "src/lib/gen2/test.zig",
270
- };
271
-
272
400
  const TestStep = struct {
273
401
  step: std.Build.Step,
274
402
  build: bool,
275
403
 
276
- 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 {
277
405
  const coverage = b.option([]const u8, "test-coverage", "Generate test coverage");
278
- const test_file = b.option([]const u8, "test-file", "Input file for test");
279
406
  const test_filter =
280
407
  b.option([]const u8, "test-filter", "Skip tests that do not match filter");
281
408
 
282
409
  const self = b.allocator.create(TestStep) catch @panic("OOM");
283
410
  const step = std.Build.Step.init(.{ .id = .custom, .name = "Run all tests", .owner = b });
284
- self.* = TestStep{ .step = step, .build = test_filter == null };
285
-
286
- const paths: []const []const u8 =
287
- if (test_file) |t| &.{t} else if (coverage != null) &.{"src/lib/test.zig"} else &TESTS;
288
- for (paths) |path| {
289
- const tests = b.addTest(.{
290
- .name = std.fs.path.basename(std.fs.path.dirname(path).?),
291
- .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,
292
417
  .optimize = config.optimize,
293
418
  .target = config.target,
294
- .filter = test_filter,
295
- });
296
- tests.setMainPkgPath("./");
297
- tests.addOptions("build_options", options);
298
- tests.single_threaded = true;
299
- maybeStrip(b, tests, &tests.step, config.strip, config.cmd);
300
- if (config.pic) tests.force_pic = config.pic;
301
- if (coverage) |c| {
302
- tests.setExecCmd(&.{ "kcov", "--include-pattern=src/lib", c, null });
303
- }
304
- self.step.dependOn(&b.addRunArtifact(tests).step);
419
+ .single_threaded = true,
420
+ .strip = config.strip,
421
+ .pic = config.pic,
422
+ }),
423
+ .filters = if (test_filter) |filter| &.{filter} else &.{},
424
+ });
425
+ tests.root_module.addOptions("build_options", options);
426
+
427
+ maybeStrip(b, tests, &tests.step, config.strip, config.cmd);
428
+ if (coverage) |c| {
429
+ tests.setExecCmd(&.{ "kcov", "--include-pattern=src/lib", c, null });
305
430
  }
431
+ self.step.dependOn(&b.addRunArtifact(tests).step);
432
+
306
433
  return self;
307
434
  }
308
435
  };
309
436
 
310
437
  const ToolConfig = struct {
311
438
  options: Options,
439
+ module: ?*std.Build.Module,
312
440
  general: Config,
313
441
  tool: struct {
314
442
  tests: ?*TestStep,
315
443
  name: ?[]const u8 = null,
316
- exes: *std.ArrayList(*std.Build.CompileStep),
444
+ exes: *ArrayList(*std.Build.Step.Compile),
317
445
  },
318
446
  };
319
447
 
320
- 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;
321
450
  var name = config.tool.name orelse std.fs.path.basename(path);
322
451
  const index = std.mem.lastIndexOfScalar(u8, name, '.');
323
452
  if (index) |i| name = name[0..i];
@@ -325,16 +454,23 @@ fn tool(b: *std.Build, path: []const u8, config: ToolConfig) *std.Build.RunStep
325
454
 
326
455
  const exe = b.addExecutable(.{
327
456
  .name = name,
328
- .root_source_file = .{ .path = path },
329
- .target = config.general.target,
330
- .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
+ }),
331
464
  });
332
- exe.addModule("pkmn", module(b, config.options));
333
465
 
334
- exe.single_threaded = true;
335
- if (config.general.pic) exe.force_pic = config.general.pic;
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
+
336
472
  if (config.tool.tests) |ts| ts.step.dependOn(&exe.step);
337
- config.tool.exes.append(exe) catch @panic("OOM");
473
+ config.tool.exes.append(b.allocator, exe) catch @panic("OOM");
338
474
 
339
475
  const run = b.addRunArtifact(exe);
340
476
  maybeStrip(b, exe, &run.step, config.general.strip, config.general.cmd);
@@ -343,14 +479,40 @@ fn tool(b: *std.Build, path: []const u8, config: ToolConfig) *std.Build.RunStep
343
479
  return run;
344
480
  }
345
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
+
346
490
  const ToolsStep = struct {
347
491
  step: std.Build.Step,
348
492
 
349
- 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 {
350
494
  const self = b.allocator.create(ToolsStep) catch @panic("OOM");
351
495
  const step = std.Build.Step.init(.{ .id = .custom, .name = "Install tools", .owner = b });
352
- self.* = ToolsStep{ .step = step };
353
- 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);
354
498
  return self;
355
499
  }
356
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
+ }