@pkmn/engine 0.1.0-dev.36b5d56b → 0.1.0-dev.388b6109

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 (62) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +26 -19
  3. package/build/pkg/addon/browser.js +4 -1
  4. package/build/pkg/addon/browser.js.map +1 -1
  5. package/build/pkg/addon/node.js +20 -7
  6. package/build/pkg/addon/node.js.map +1 -1
  7. package/build/pkg/addon/wasm.js +41 -9
  8. package/build/pkg/addon/wasm.js.map +1 -1
  9. package/build/pkg/addon.d.ts +7 -7
  10. package/build/pkg/addon.js +8 -17
  11. package/build/pkg/addon.js.map +1 -1
  12. package/build/pkg/data/layout.json +15 -2
  13. package/build/pkg/data/protocol.json +23 -9
  14. package/build/pkg/data.js.map +1 -1
  15. package/build/pkg/gen1.d.ts +15 -18
  16. package/build/pkg/gen1.js +28 -19
  17. package/build/pkg/gen1.js.map +1 -1
  18. package/build/pkg/index.d.ts +37 -100
  19. package/build/pkg/index.js +23 -75
  20. package/build/pkg/index.js.map +1 -1
  21. package/build/pkg/protocol.d.ts +11 -2
  22. package/build/pkg/protocol.js.map +1 -1
  23. package/build/tools/debug.js +54 -11
  24. package/build/tools/debug.js.map +1 -1
  25. package/build/tools/display/debug.jsx +19 -94
  26. package/build/tools/display/debug.jsx.map +1 -1
  27. package/build/tools/display/index.js +18 -7
  28. package/build/tools/display/index.js.map +1 -1
  29. package/build/tools/display/ui.jsx +20 -9
  30. package/build/tools/display/ui.jsx.map +1 -1
  31. package/build/tools/display/util.js +140 -1
  32. package/build/tools/display/util.js.map +1 -1
  33. package/build.zig +309 -233
  34. package/package.json +31 -130
  35. package/src/bin/install-pkmn-engine +25 -9
  36. package/src/lib/bindings/c.zig +201 -0
  37. package/src/lib/bindings/node.zig +117 -0
  38. package/src/lib/bindings/wasm.zig +51 -0
  39. package/src/lib/common/array.zig +5 -12
  40. package/src/lib/common/debug.zig +45 -60
  41. package/src/lib/common/js.zig +3 -9
  42. package/src/lib/common/optional.zig +26 -26
  43. package/src/lib/common/protocol.zig +224 -189
  44. package/src/lib/common/rational.zig +275 -284
  45. package/src/lib/common/util.zig +34 -31
  46. package/src/lib/gen1/calc.zig +139 -117
  47. package/src/lib/gen1/chance.zig +149 -88
  48. package/src/lib/gen1/data/types.zig +2 -2
  49. package/src/lib/gen1/data.zig +5 -5
  50. package/src/lib/gen1/helpers.zig +30 -22
  51. package/src/lib/gen1/mechanics.zig +50 -43
  52. package/src/lib/gen2/calc.zig +3 -6
  53. package/src/lib/gen2/chance.zig +38 -26
  54. package/src/lib/gen2/data/types.zig +6 -3
  55. package/src/lib/gen2/data.zig +8 -7
  56. package/src/lib/gen2/helpers.zig +27 -19
  57. package/src/lib/gen2/mechanics.zig +36 -25
  58. package/src/lib/napi.h +2 -0
  59. package/src/lib/node.zig +3 -118
  60. package/src/lib/pkmn.zig +94 -19
  61. package/src/lib/wasm.zig +10 -9
  62. package/src/tools/display/dom.ts +43 -31
package/build.zig CHANGED
@@ -1,6 +1,10 @@
1
1
  const builtin = @import("builtin");
2
2
  const std = @import("std");
3
3
 
4
+ fn ArrayList(comptime T: type) type {
5
+ return std.array_list.Aligned(T, null);
6
+ }
7
+
4
8
  pub const Options = struct {
5
9
  showdown: ?bool = null,
6
10
  log: ?bool = null,
@@ -8,71 +12,68 @@ pub const Options = struct {
8
12
  calc: ?bool = null,
9
13
  };
10
14
 
11
- const Step = if (@hasDecl(std.Build, "Step")) std.Build.Step else .{
12
- .Compile = std.Build.CompileStep,
13
- .Options = std.Build.OptionsStep,
14
- .Run = std.Build.RunStep,
15
- };
16
- const has_path = @hasField(std.Build.LazyPath, "path");
17
- const root_module = @hasField(Step.Compile, "root_module");
18
- const force_pic = !@hasField(std.Build.SharedLibraryOptions, "pic");
19
- const ResolvedTarget =
20
- if (@hasDecl(std.Build, "ResolvedTarget")) std.Build.ResolvedTarget else std.zig.CrossTarget;
21
-
22
- pub fn module(b: *std.Build, options: Options) *std.Build.Module {
23
- 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 {
24
21
  const build_options = b.addOptions();
25
22
  build_options.addOption(?bool, "showdown", options.showdown);
26
23
  build_options.addOption(?bool, "log", options.log);
27
24
  build_options.addOption(?bool, "chance", options.chance);
28
25
  build_options.addOption(?bool, "calc", options.calc);
29
26
 
30
- const root_source_file = .{ .cwd_relative = dirname ++ "/src/lib/pkmn.zig" };
31
- const imports = &.{.{ .name = "build_options", .module = build_options.createModule() }};
32
- return b.createModule(if (@hasDecl(std.Build, "CreateModuleOptions"))
33
- .{ .source_file = root_source_file, .dependencies = imports }
34
- else
35
- .{ .root_source_file = root_source_file, .imports = imports });
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() }};
30
+ return b.createModule(.{
31
+ .root_source_file = root_source_file,
32
+ .target = target,
33
+ .optimize = optimize,
34
+ .imports = imports,
35
+ });
36
36
  }
37
37
 
38
38
  pub fn build(b: *std.Build) !void {
39
39
  const target = b.standardTargetOptions(.{});
40
40
  const optimize = b.standardOptimizeOption(.{});
41
41
 
42
- const default: ?bool = if (force_pic) false else null;
43
42
  const node_headers = b.option([]const u8, "node-headers", "Path to node-headers");
44
43
  const node_import_lib =
45
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;
46
49
  const wasm = b.option(bool, "wasm", "Build a WASM library") orelse false;
47
50
  const wasm_stack_size =
48
51
  b.option(u64, "wasm-stack-size", "The size of WASM stack") orelse std.wasm.page_size;
49
52
  const dynamic = b.option(bool, "dynamic", "Build a dynamic library") orelse false;
50
- const strip = b.option(bool, "strip", "Strip debugging symbols from binary") orelse default;
51
- const pic = b.option(bool, "pic", "Force position independent code") orelse default;
53
+ const strip = b.option(bool, "strip", "Strip debugging symbols from binary");
54
+ const pic = b.option(bool, "pic", "Force position independent code");
52
55
  const emit_asm = b.option(bool, "emit-asm", "Output .s (assembly code)") orelse false;
53
56
  const emit_ll = b.option(bool, "emit-ll", "Output .ll (LLVM IR)") orelse false;
54
57
 
55
- const cmd = b.findProgram(&.{"strip"}, &.{}) catch null;
58
+ const cmd = if (@hasDecl(std.Build, "FindProgramOptions"))
59
+ b.findProgram(.{ .names = &.{"strip"} })
60
+ else
61
+ b.findProgram(&.{"strip"}, &.{}) catch null;
56
62
 
57
63
  const json = @embedFile("package.json");
58
64
  var parsed = try std.json.parseFromSlice(std.json.Value, b.allocator, json, .{});
59
65
  defer parsed.deinit();
60
66
  const version = parsed.value.object.get("version").?.string;
61
67
  const description = parsed.value.object.get("description").?.string;
62
- var repository = std.mem.splitSequence(u8, parsed.value.object.get("repository").?.string, ":");
68
+ var repository = std.mem.splitScalar(u8, parsed.value.object.get("repository").?.string, ':');
63
69
  std.debug.assert(std.mem.eql(u8, repository.first(), "github"));
64
70
 
65
- const fast = try std.SemanticVersion.parse("0.12.0-dev.866+3a47bc715");
66
- if (optimize != .Debug and fast.order(builtin.zig_version) == .lt) {
67
- std.log.warn("Release builds are ~10-20% slower than before Zig " ++
68
- "v0.12.0-dev.866+3a47bc715 due to ziglang/zig#17768", .{});
69
- }
70
-
71
71
  const showdown =
72
72
  b.option(bool, "showdown", "Enable Pokémon Showdown compatibility mode") orelse false;
73
73
  const log = b.option(bool, "log", "Enable protocol message logging") orelse false;
74
74
  const chance = b.option(bool, "chance", "Enable update probability tracking") orelse false;
75
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");
76
77
 
77
78
  const options = b.addOptions();
78
79
  options.addOption(?bool, "showdown", showdown);
@@ -80,13 +81,27 @@ pub fn build(b: *std.Build) !void {
80
81
  options.addOption(?bool, "chance", chance);
81
82
  options.addOption(?bool, "calc", calc);
82
83
 
84
+ if (extra) |opts| {
85
+ for (opts) |opt| {
86
+ if (std.mem.indexOfScalar(u8, opt, '=')) |i| {
87
+ options.addOption(?bool, opt[0..i], if (std.mem.eql(u8, opt[i + 1 ..], "true"))
88
+ true
89
+ else if (std.mem.eql(u8, opt[i + 1 ..], "false"))
90
+ false
91
+ else {
92
+ std.log.err("Invalid option: {s}\n", .{opt});
93
+ return error.InvalidOption;
94
+ });
95
+ } else {
96
+ options.addOption(?bool, opt, true);
97
+ }
98
+ }
99
+ }
100
+
83
101
  const name = if (showdown) "pkmn-showdown" else "pkmn";
84
102
 
85
- const pkmn = if (@hasDecl(std.Build, "CreateModuleOptions")) null else b.addModule("pkmn", .{
86
- .root_source_file = if (has_path)
87
- .{ .path = "src/lib/pkmn.zig" }
88
- else
89
- b.path("src/lib/pkmn.zig"),
103
+ const pkmn: *std.Build.Module = b.addModule("pkmn", .{
104
+ .root_source_file = b.path("src/lib/pkmn.zig"),
90
105
  .optimize = optimize,
91
106
  .target = target,
92
107
  .imports = &.{.{ .name = "build_options", .module = options.createModule() }},
@@ -94,33 +109,38 @@ pub fn build(b: *std.Build) !void {
94
109
 
95
110
  var c = false;
96
111
  if (node_headers) |headers| {
97
- const addon = b.fmt("{s}.node", .{name});
98
- const path = if (has_path) .{ .path = "src/lib/node.zig" } else b.path("src/lib/node.zig");
99
- const lib = b.addSharedLibrary(if (force_pic) .{
100
- .name = addon,
101
- .root_source_file = path,
102
- .optimize = optimize,
112
+ const translate_c = b.addTranslateC(.{
113
+ .root_source_file = b.path("src/lib/napi.h"),
103
114
  .target = target,
104
- } else .{
115
+ // TODO: workaround for ziglang/zig#35515
116
+ .optimize = .Debug,
117
+ });
118
+ translate_c.addSystemIncludePath(b.path(headers));
119
+ const addon = b.fmt("{s}.node", .{name});
120
+ const path = b.path("src/lib/node.zig");
121
+ const lib = b.addLibrary(.{
122
+ .linkage = .dynamic,
105
123
  .name = addon,
106
- .root_source_file = path,
107
- .optimize = optimize,
108
- .target = target,
109
- .strip = strip,
110
- .pic = pic,
124
+ .root_module = b.createModule(.{
125
+ .root_source_file = path,
126
+ .optimize = optimize,
127
+ .target = target,
128
+ .strip = strip,
129
+ .pic = pic,
130
+ }),
111
131
  });
112
- (if (root_module) lib.root_module else lib).addOptions("build_options", options);
113
- lib.addSystemIncludePath(if (has_path) .{ .path = headers } else b.path(headers));
114
- 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;
115
135
  if (node_import_lib) |il| {
116
- lib.addObjectFile(if (has_path) .{ .path = il } else b.path(il));
117
- } else if (detect(target).os.tag == .windows) {
118
- 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");
119
140
  std.process.exit(1);
120
141
  }
121
142
  lib.linker_allow_shlib_undefined = true;
122
143
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
123
- if (force_pic and pic orelse false) lib.force_pic = pic;
124
144
  // Always emit to build/lib because this is where the driver code expects to find it
125
145
  // TODO: ziglang/zig#2231 - using the following used to work (though was hacky):
126
146
  //
@@ -131,50 +151,50 @@ pub fn build(b: *std.Build) !void {
131
151
  // rename the file ourself in install-pkmn-engine
132
152
  b.installArtifact(lib);
133
153
  } else if (wasm) {
134
- try buildWasm(b, name, "src/lib/wasm.zig", optimize, strip, pic, wasm_stack_size, options);
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);
135
161
  } else if (dynamic) {
136
- const path = if (has_path) .{ .path = "src/lib/c.zig" } else b.path("src/lib/c.zig");
137
- const lib = b.addSharedLibrary(if (force_pic) .{
162
+ const path = b.path("src/lib/c.zig");
163
+ const lib = b.addLibrary(.{
164
+ .linkage = .dynamic,
138
165
  .name = name,
139
- .root_source_file = path,
140
- .version = try std.SemanticVersion.parse(version),
141
- .optimize = optimize,
142
- .target = target,
143
- } else .{
144
- .name = name,
145
- .root_source_file = path,
146
- .version = try std.SemanticVersion.parse(version),
147
- .optimize = optimize,
148
- .target = target,
149
- .strip = strip,
150
- .pic = pic,
166
+ .root_module = b.createModule(.{
167
+ .root_source_file = path,
168
+ .optimize = optimize,
169
+ .target = target,
170
+ .strip = strip,
171
+ .pic = pic,
172
+ }),
151
173
  });
152
- (if (root_module) lib.root_module else lib).addOptions("build_options", options);
153
- lib.addIncludePath(if (has_path) .{ .path = "src/include" } else b.path("src/include"));
174
+ lib.root_module.addOptions("build_options", options);
175
+ lib.root_module.addIncludePath(b.path("src/include"));
154
176
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
155
- if (force_pic and pic orelse false) lib.force_pic = pic;
156
177
  b.installArtifact(lib);
157
178
  c = true;
158
179
  } else {
159
- const path = if (has_path) .{ .path = "src/lib/c.zig" } else b.path("src/lib/c.zig");
160
- const lib = b.addStaticLibrary(if (force_pic) .{
180
+ const path = b.path("src/lib/c.zig");
181
+ const lib = b.addLibrary(.{
182
+ .linkage = .static,
161
183
  .name = name,
162
- .root_source_file = path,
163
- .optimize = optimize,
164
- .target = target,
165
- } else .{
166
- .name = name,
167
- .root_source_file = path,
168
- .optimize = optimize,
169
- .target = target,
170
- .strip = strip,
171
- .pic = pic,
184
+ .root_module = b.createModule(.{
185
+ .root_source_file = path,
186
+ .optimize = optimize,
187
+ .target = target,
188
+ .strip = strip,
189
+ .pic = pic,
190
+ }),
172
191
  });
173
- (if (root_module) lib.root_module else lib).addOptions("build_options", options);
174
- lib.addIncludePath(if (has_path) .{ .path = "src/include" } else b.path("src/include"));
175
- 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
+ }
176
197
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
177
- if (force_pic and pic orelse false) lib.force_pic = pic;
178
198
  if (emit_asm) {
179
199
  b.getInstallStep().dependOn(&b.addInstallFileWithDir(
180
200
  lib.getEmittedAsm(),
@@ -189,44 +209,59 @@ pub fn build(b: *std.Build) !void {
189
209
  b.fmt("{s}.ll", .{name}),
190
210
  ).step);
191
211
  }
192
- b.installArtifact(lib);
212
+ const install = b.addInstallArtifact(lib, .{});
213
+ b.getInstallStep().dependOn(&install.step);
214
+ maybeRanlib(b, lib, &install.step);
193
215
  c = true;
194
216
  }
195
217
 
196
218
  if (c) {
197
- const path =
198
- if (has_path) .{ .path = "src/include/pkmn.h" } else b.path("src/include/pkmn.h");
219
+ const path = b.path("src/include/pkmn.h");
199
220
  const header = b.addInstallFileWithDir(path, .header, "pkmn.h");
200
221
  b.getInstallStep().dependOn(&header.step);
201
222
 
202
- const pc = b.fmt("lib{s}.pc", .{name});
203
- const file = try std.fs.path.relative(
204
- b.allocator,
205
- try std.process.getCwdAlloc(b.allocator),
206
- try b.cache_root.join(b.allocator, &.{pc}),
207
- );
208
- const pkgconfig_file = try std.fs.cwd().createFile(file, .{});
209
-
210
- const dirname = comptime std.fs.path.dirname(@src().file) orelse ".";
211
- const writer = pkgconfig_file.writer();
212
- try writer.print(
213
- \\prefix={0s}/{1s}
223
+ const content = try std.fmt.allocPrint(b.allocator,
224
+ \\prefix=${{pcfiledir}}/../..
214
225
  \\includedir=${{prefix}}/include
215
226
  \\libdir=${{prefix}}/lib
216
227
  \\
217
- \\Name: lib{2s}
218
- \\URL: https://github.com/{3s}
219
- \\Description: {4s}
220
- \\Version: {5s}
228
+ \\Name: lib{0s}
229
+ \\URL: https://github.com/{1s}
230
+ \\Description: {2s}
231
+ \\Version: {3s}
221
232
  \\Cflags: -I${{includedir}}
222
- \\Libs: -L${{libdir}} -l{2s}
223
- , .{ dirname, b.install_path, name, repository.next().?, description, version });
224
- defer pkgconfig_file.close();
233
+ \\Libs: -L${{libdir}} -l{0s}
234
+ , .{ name, repository.next().?, description, version });
225
235
 
226
- 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
+ }
227
262
  }
228
263
 
229
- const config = .{
264
+ const config: Config = .{
230
265
  .target = target,
231
266
  .optimize = optimize,
232
267
  .pic = pic,
@@ -237,7 +272,7 @@ pub fn build(b: *std.Build) !void {
237
272
  // TODO: tests can be run multiple times due to @imports
238
273
  const tests = TestStep.create(b, options, config);
239
274
 
240
- var exes = std.ArrayList(*Step.Compile).init(b.allocator);
275
+ var exes: ArrayList(*std.Build.Step.Compile) = .empty;
241
276
  const tools: ToolConfig = .{
242
277
  .options = .{
243
278
  .showdown = showdown,
@@ -245,7 +280,6 @@ pub fn build(b: *std.Build) !void {
245
280
  .chance = chance,
246
281
  .calc = calc,
247
282
  },
248
- .module = pkmn,
249
283
  .general = config,
250
284
  .tool = .{
251
285
  .tests = if (tests.build) tests else null,
@@ -277,8 +311,8 @@ pub fn build(b: *std.Build) !void {
277
311
  if (dump) |t| b.step("dump", "Run protocol dump tool").dependOn(&t.step);
278
312
  if (fuzz) |t| b.step("fuzz", "Run fuzz tester").dependOn(&t.step);
279
313
  if (serde) |t| b.step("serde", "Run serialization/deserialization tool").dependOn(&t.step);
280
- b.step("test", "Run all tests").dependOn(&tests.step);
281
- b.step("tools", "Install tools").dependOn(&ToolsStep.create(b, &exes).step);
314
+ b.step("test", "Run all tests").dependOn(tests.step);
315
+ b.step("tools", "Install tools").dependOn(ToolsStep.create(b, &exes).step);
282
316
  if (transitions) |t| {
283
317
  b.step("transitions", "Visualize transitions algorithm search").dependOn(&t.step);
284
318
  }
@@ -292,65 +326,77 @@ fn buildWasm(
292
326
  strip: ?bool,
293
327
  pic: ?bool,
294
328
  wasm_stack_size: u64,
329
+ import: ?*std.Build.Module,
295
330
  options: anytype,
296
331
  ) !void {
297
- const entry = @hasDecl(Step.Compile, "Entry");
298
332
  const mode = switch (optimize) {
299
333
  .ReleaseFast, .ReleaseSafe => .ReleaseSmall,
300
334
  else => optimize,
301
335
  };
302
- const freestanding = if (@hasDecl(std.Build, "resolveTargetQuery"))
303
- b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding })
304
- else
305
- ResolvedTarget{ .cpu_arch = .wasm32, .os_tag = .freestanding };
306
- const path = if (has_path) .{ .path = root_src_file } else b.path(root_src_file);
307
- const bin = if (entry) bin: {
308
- const exe = b.addExecutable(if (force_pic) .{
309
- .name = name,
310
- .root_source_file = path,
311
- .optimize = mode,
312
- .target = freestanding,
313
- } else .{
314
- .name = name,
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(.{
315
364
  .root_source_file = path,
316
365
  .optimize = mode,
317
366
  .target = freestanding,
318
367
  .strip = strip,
319
368
  .pic = pic,
320
- });
321
- (if (root_module) exe.root_module else exe).export_symbol_names = try exports(b);
322
- exe.entry = .disabled;
323
- break :bin exe;
324
- } else bin: {
325
- const lib = b.addSharedLibrary(.{
326
- .name = name,
327
- .root_source_file = path,
328
- .optimize = mode,
329
- .target = freestanding,
330
- });
331
- lib.rdynamic = true;
332
- break :bin lib;
333
- };
334
- bin.stack_size = wasm_stack_size;
335
- if (@hasDecl(@TypeOf(bin.*), "strip")) bin.strip = strip orelse false;
336
- if (force_pic and pic orelse false) bin.force_pic = pic;
337
- (if (root_module) bin.root_module else bin).addOptions("build_options", options);
369
+ }),
370
+ });
338
371
 
339
- const opt = b.findProgram(&.{"wasm-opt"}, &.{"./node_modules/.bin"}) catch null;
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;
340
391
  if (optimize != .Debug and opt != null) {
341
392
  const out = b.fmt("build/lib/{s}.wasm", .{name});
342
- const sh = b.addSystemCommand(&.{ opt.?, "-O4" });
343
- sh.addArtifactArg(bin);
393
+ const sh = b.addSystemCommand(&.{ opt.?, "--enable-bulk-memory", "--enable-simd", "-O4" });
394
+ sh.addArtifactArg(exe);
344
395
  sh.addArg("-o");
345
- if (@hasDecl(@TypeOf(sh.*), "addFileSourceArg")) {
346
- sh.addFileSourceArg(if (has_path) .{ .path = out } else b.path(out));
347
- } else {
348
- sh.addFileArg(if (has_path) .{ .path = out } else b.path(out));
349
- }
396
+ sh.addFileArg(b.path(out));
350
397
  b.getInstallStep().dependOn(&sh.step);
351
- if (!entry) b.installArtifact(bin);
352
- } else if (entry) {
353
- b.getInstallStep().dependOn(&b.addInstallArtifact(bin, .{
398
+ } else {
399
+ b.getInstallStep().dependOn(&b.addInstallArtifact(exe, .{
354
400
  .dest_dir = .{ .override = std.Build.InstallDir{ .lib = {} } },
355
401
  }).step);
356
402
  }
@@ -358,12 +404,11 @@ fn buildWasm(
358
404
 
359
405
  fn maybeStrip(
360
406
  b: *std.Build,
361
- artifact: *Step.Compile,
407
+ artifact: *std.Build.Step.Compile,
362
408
  step: *std.Build.Step,
363
409
  strip: ?bool,
364
410
  cmd: ?[]const u8,
365
411
  ) void {
366
- if (@hasDecl(@TypeOf(artifact.*), "strip")) artifact.strip = strip orelse false;
367
412
  if (!(strip orelse false) or cmd == null) return;
368
413
  // Using `strip -r -u` for dynamic libraries is supposed to work on macOS but doesn't...
369
414
  const mac = builtin.os.tag == .macos;
@@ -376,7 +421,7 @@ fn maybeStrip(
376
421
  }
377
422
 
378
423
  const Config = struct {
379
- target: ResolvedTarget,
424
+ target: std.Build.ResolvedTarget,
380
425
  optimize: std.builtin.OptimizeMode,
381
426
  pic: ?bool,
382
427
  strip: ?bool,
@@ -384,44 +429,40 @@ const Config = struct {
384
429
  };
385
430
 
386
431
  const TestStep = struct {
387
- step: std.Build.Step,
432
+ step: *std.Build.Step,
388
433
  build: bool,
389
434
 
390
- pub fn create(b: *std.Build, options: *Step.Options, config: Config) *TestStep {
435
+ pub fn create(b: *std.Build, options: *std.Build.Step.Options, config: Config) *TestStep {
391
436
  const coverage = b.option([]const u8, "test-coverage", "Generate test coverage");
392
437
  const test_filter =
393
438
  b.option([]const u8, "test-filter", "Skip tests that do not match filter");
394
439
 
395
440
  const self = b.allocator.create(TestStep) catch @panic("OOM");
396
- const step = std.Build.Step.init(.{ .id = .custom, .name = "Run all tests", .owner = b });
397
- self.* = .{ .step = step, .build = test_filter == null };
398
441
 
399
- const path = if (has_path) .{ .path = "src/lib/test.zig" } else b.path("src/lib/test.zig");
400
- const tests = b.addTest(if (force_pic) .{
401
- .root_source_file = path,
402
- .optimize = config.optimize,
403
- .target = config.target,
404
- .filter = test_filter,
405
- } else .{
406
- .root_source_file = path,
407
- .optimize = config.optimize,
408
- .target = config.target,
409
- .filter = test_filter,
410
- .single_threaded = true,
411
- .strip = config.strip,
412
- .pic = config.pic,
442
+ const path = b.path("src/lib/test.zig");
443
+ const tests = b.addTest(.{
444
+ .root_module = b.createModule(.{
445
+ .root_source_file = path,
446
+ .optimize = config.optimize,
447
+ .target = config.target,
448
+ .single_threaded = true,
449
+ .strip = config.strip,
450
+ .pic = config.pic,
451
+ }),
452
+ .filters = if (test_filter) |filter| &.{filter} else &.{},
413
453
  });
414
- (if (root_module) tests.root_module else tests).addOptions("build_options", options);
454
+ tests.root_module.addOptions("build_options", options);
415
455
 
416
456
  maybeStrip(b, tests, &tests.step, config.strip, config.cmd);
417
- if (force_pic) {
418
- tests.single_threaded = true;
419
- if (config.pic orelse false) tests.force_pic = config.pic;
420
- }
421
457
  if (coverage) |c| {
422
- tests.setExecCmd(&.{ "kcov", "--include-pattern=src/lib", c, null });
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 };
423
465
  }
424
- self.step.dependOn(&b.addRunArtifact(tests).step);
425
466
 
426
467
  return self;
427
468
  }
@@ -429,89 +470,124 @@ const TestStep = struct {
429
470
 
430
471
  const ToolConfig = struct {
431
472
  options: Options,
432
- module: ?*std.Build.Module,
433
473
  general: Config,
434
474
  tool: struct {
435
475
  tests: ?*TestStep,
436
476
  name: ?[]const u8 = null,
437
- exes: *std.ArrayList(*Step.Compile),
477
+ exes: *ArrayList(*std.Build.Step.Compile),
438
478
  },
439
479
  };
440
480
 
441
- fn tool(b: *std.Build, path: []const u8, config: ToolConfig) !?*Step.Run {
442
- std.fs.cwd().access(path, .{}) catch |err| switch (err) {
443
- error.FileNotFound => return null,
444
- else => |e| return e,
445
- };
481
+ fn tool(b: *std.Build, path: []const u8, config: ToolConfig) !?*std.Build.Step.Run {
482
+ if (!try exists(b, path)) return null;
446
483
  var name = config.tool.name orelse std.fs.path.basename(path);
447
484
  const index = std.mem.lastIndexOfScalar(u8, name, '.');
448
485
  if (index) |i| name = name[0..i];
449
486
  if (config.options.showdown orelse false) name = b.fmt("{s}-showdown", .{name});
450
487
 
451
- const exe = b.addExecutable(if (force_pic) .{
452
- .name = name,
453
- .root_source_file = if (has_path) .{ .path = path } else b.path(path),
454
- .target = config.general.target,
455
- .optimize = config.general.optimize,
456
- } else .{
488
+ const exe = b.addExecutable(.{
457
489
  .name = name,
458
- .root_source_file = if (has_path) .{ .path = path } else b.path(path),
459
- .target = config.general.target,
460
- .optimize = config.general.optimize,
461
- .single_threaded = true,
462
- .strip = config.general.strip,
463
- .pic = config.general.pic,
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
+ }),
464
497
  });
465
498
 
466
- if (root_module) {
467
- const import = if (config.module) |m| m else module(b, config.options);
468
- exe.root_module.addImport("pkmn", import);
469
- } else {
470
- exe.addModule("pkmn", module(b, config.options));
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", .{});
471
503
  }
472
504
 
473
- if (force_pic) {
474
- exe.single_threaded = true;
475
- if (config.general.pic orelse false) exe.force_pic = config.general.pic;
476
- }
477
505
  if (config.tool.tests) |ts| ts.step.dependOn(&exe.step);
478
- config.tool.exes.append(exe) catch @panic("OOM");
506
+ config.tool.exes.append(b.allocator, exe) catch @panic("OOM");
479
507
 
480
508
  const run = b.addRunArtifact(exe);
481
509
  maybeStrip(b, exe, &run.step, config.general.strip, config.general.cmd);
482
- 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
+ }
483
515
 
484
516
  return run;
485
517
  }
486
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
+
487
527
  const ToolsStep = struct {
488
- step: std.Build.Step,
528
+ step: *std.Build.Step,
489
529
 
490
- pub fn create(b: *std.Build, exes: *std.ArrayList(*Step.Compile)) *ToolsStep {
530
+ pub fn create(b: *std.Build, exes: *ArrayList(*std.Build.Step.Compile)) *ToolsStep {
491
531
  const self = b.allocator.create(ToolsStep) catch @panic("OOM");
492
- const step = std.Build.Step.init(.{ .id = .custom, .name = "Install tools", .owner = b });
493
- self.* = .{ .step = 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
+ }
494
553
  for (exes.items) |t| self.step.dependOn(&b.addInstallArtifact(t, .{}).step);
554
+
495
555
  return self;
496
556
  }
497
557
  };
498
558
 
499
- fn detect(target: anytype) std.Target {
500
- if (@hasField(@TypeOf(target), "result")) return target.result;
501
- return (std.zig.system.NativeTargetInfo.detect(target) catch unreachable).target;
502
- }
503
-
504
- const WASM = @embedFile("src/lib/wasm.zig");
559
+ pub fn exports(b: *std.Build, bytes: []const u8) ![][]const u8 {
560
+ var symbols: ArrayList([]const u8) = .empty;
505
561
 
506
- fn exports(b: *std.Build) ![][]const u8 {
507
- var symbols = std.ArrayList([]const u8).init(b.allocator);
508
-
509
- var it = std.mem.splitSequence(u8, WASM, "export const ");
562
+ var it = std.mem.splitSequence(u8, bytes, "export ");
510
563
  _ = it.next();
511
564
  while (it.next()) |s| {
512
- const i = std.mem.indexOf(u8, s, " ").?;
513
- try symbols.append(s[0..i]);
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
+ }
514
572
  }
515
573
 
516
574
  return symbols.items;
517
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
+ }