@pkmn/engine 0.1.0-dev.77410aff → 0.1.0-dev.78dc891c

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 (84) hide show
  1. package/LICENSE +1 -3
  2. package/README.md +40 -28
  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/layout.json +15 -2
  16. package/build/pkg/data/protocol.json +23 -9
  17. package/build/pkg/data.d.ts +27 -6
  18. package/build/pkg/data.js.map +1 -1
  19. package/build/pkg/gen1.d.ts +15 -18
  20. package/build/pkg/gen1.js +28 -19
  21. package/build/pkg/gen1.js.map +1 -1
  22. package/build/pkg/index.d.ts +40 -90
  23. package/build/pkg/index.js +23 -70
  24. package/build/pkg/index.js.map +1 -1
  25. package/build/pkg/protocol.d.ts +29 -1
  26. package/build/pkg/protocol.js +109 -107
  27. package/build/pkg/protocol.js.map +1 -1
  28. package/build/tools/debug.js +84 -75
  29. package/build/tools/debug.js.map +1 -1
  30. package/build/tools/display/debug.jsx +66 -0
  31. package/build/tools/display/debug.jsx.map +1 -0
  32. package/build/tools/display/index.js +152 -0
  33. package/build/tools/display/index.js.map +1 -0
  34. package/build/tools/display/ui.jsx +360 -0
  35. package/build/tools/display/ui.jsx.map +1 -0
  36. package/build/tools/display/util.js +157 -0
  37. package/build/tools/display/util.js.map +1 -0
  38. package/build.zig +366 -148
  39. package/package.json +61 -133
  40. package/src/bin/install-pkmn-engine +47 -21
  41. package/src/bin/pkmn-debug +1 -1
  42. package/src/lib/bindings/c.zig +201 -0
  43. package/src/lib/{binding → bindings}/node.zig +9 -22
  44. package/src/lib/bindings/wasm.zig +51 -0
  45. package/src/lib/common/array.zig +70 -0
  46. package/src/lib/common/data.zig +4 -4
  47. package/src/lib/common/debug.zig +45 -29
  48. package/src/lib/common/js.zig +3 -7
  49. package/src/lib/common/optional.zig +27 -23
  50. package/src/lib/common/options.zig +6 -0
  51. package/src/lib/common/protocol.zig +205 -174
  52. package/src/lib/common/rational.zig +338 -255
  53. package/src/lib/common/rng.zig +7 -9
  54. package/src/lib/common/util.zig +53 -1
  55. package/src/lib/gen1/calc.zig +721 -172
  56. package/src/lib/gen1/chance.zig +486 -300
  57. package/src/lib/gen1/data/moves.zig +2 -4
  58. package/src/lib/gen1/data/species.zig +2 -4
  59. package/src/lib/gen1/data/types.zig +3 -4
  60. package/src/lib/gen1/data.zig +34 -34
  61. package/src/lib/gen1/helpers.zig +116 -561
  62. package/src/lib/gen1/mechanics.zig +402 -269
  63. package/src/lib/gen2/calc.zig +35 -28
  64. package/src/lib/gen2/chance.zig +111 -511
  65. package/src/lib/gen2/data/items.zig +1 -3
  66. package/src/lib/gen2/data/moves.zig +2 -4
  67. package/src/lib/gen2/data/species.zig +1 -3
  68. package/src/lib/gen2/data/types.zig +7 -5
  69. package/src/lib/gen2/data.zig +31 -30
  70. package/src/lib/gen2/generated.zig +18 -23
  71. package/src/lib/gen2/helpers.zig +68 -39
  72. package/src/lib/gen2/mechanics.zig +215 -157
  73. package/src/lib/napi.h +2 -0
  74. package/src/lib/node.zig +14 -0
  75. package/src/lib/pkmn.zig +125 -29
  76. package/src/lib/wasm.zig +13 -0
  77. package/src/tools/display/dom.ts +108 -0
  78. package/src/tools/display/ui.css +354 -0
  79. package/build/test/display.js +0 -400
  80. package/build/test/display.js.map +0 -1
  81. package/src/lib/binding/wasm.zig +0 -12
  82. package/src/lib/gen1/README.md +0 -894
  83. package/src/lib/gen2/README.md +0 -329
  84. package/src/test/display/pkmn.html.tmpl +0 -368
package/build.zig CHANGED
@@ -1,5 +1,18 @@
1
- const std = @import("std");
2
1
  const builtin = @import("builtin");
2
+ const std = @import("std");
3
+
4
+ const Debug: std.builtin.OptimizeMode =
5
+ if (@hasDecl(std.builtin.OptimizeMode, "Debug")) .debug else .Debug;
6
+ const ReleaseSafe: std.builtin.OptimizeMode =
7
+ if (@hasDecl(std.builtin.OptimizeMode, "ReleaseSafe")) .safe else .ReleaseSafe;
8
+ const ReleaseFast: std.builtin.OptimizeMode =
9
+ if (@hasDecl(std.builtin.OptimizeMode, "ReleaseFast")) .fast else .ReleaseFast;
10
+ const ReleaseSmall: std.builtin.OptimizeMode =
11
+ if (@hasDecl(std.builtin.OptimizeMode, "ReleaseSmall")) .small else .ReleaseSmall;
12
+
13
+ fn ArrayList(comptime T: type) type {
14
+ return std.array_list.Aligned(T, null);
15
+ }
3
16
 
4
17
  pub const Options = struct {
5
18
  showdown: ?bool = null,
@@ -8,16 +21,26 @@ pub const Options = struct {
8
21
  calc: ?bool = null,
9
22
  };
10
23
 
11
- pub fn module(b: *std.Build, options: Options) *std.Build.Module {
12
- const dirname = comptime std.fs.path.dirname(@src().file) orelse ".";
24
+ pub fn module(
25
+ b: *std.Build,
26
+ options: Options,
27
+ target: std.Build.ResolvedTarget,
28
+ optimize: std.builtin.OptimizeMode,
29
+ ) *std.Build.Module {
13
30
  const build_options = b.addOptions();
14
31
  build_options.addOption(?bool, "showdown", options.showdown);
15
32
  build_options.addOption(?bool, "log", options.log);
16
33
  build_options.addOption(?bool, "chance", options.chance);
17
34
  build_options.addOption(?bool, "calc", options.calc);
35
+
36
+ const root_source_file = b.path("src/lib/pkmn.zig");
37
+ const imports: []const std.Build.Module.Import =
38
+ &.{.{ .name = "build_options", .module = build_options.createModule() }};
18
39
  return b.createModule(.{
19
- .source_file = .{ .path = dirname ++ "/src/lib/pkmn.zig" },
20
- .dependencies = &.{.{ .name = "build_options", .module = build_options.createModule() }},
40
+ .root_source_file = root_source_file,
41
+ .target = target,
42
+ .optimize = optimize,
43
+ .imports = imports,
21
44
  });
22
45
  }
23
46
 
@@ -28,23 +51,30 @@ pub fn build(b: *std.Build) !void {
28
51
  const node_headers = b.option([]const u8, "node-headers", "Path to node-headers");
29
52
  const node_import_lib =
30
53
  b.option([]const u8, "node-import-library", "Path to node import library (Windows)");
54
+ const demo = if (try exists(b, "src/tools/demo.zig"))
55
+ b.option(bool, "demo", "Build the demo library") orelse false
56
+ else
57
+ false;
31
58
  const wasm = b.option(bool, "wasm", "Build a WASM library") orelse false;
32
59
  const wasm_stack_size =
33
60
  b.option(u64, "wasm-stack-size", "The size of WASM stack") orelse std.wasm.page_size;
34
61
  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;
62
+ const strip = b.option(bool, "strip", "Strip debugging symbols from binary");
63
+ const pic = b.option(bool, "pic", "Force position independent code");
37
64
  const emit_asm = b.option(bool, "emit-asm", "Output .s (assembly code)") orelse false;
38
65
  const emit_ll = b.option(bool, "emit-ll", "Output .ll (LLVM IR)") orelse false;
39
66
 
40
- const cmd = b.findProgram(&.{"strip"}, &.{}) catch null;
67
+ const cmd = if (@hasDecl(std.Build, "FindProgramOptions"))
68
+ b.findProgram(.{ .names = &.{"strip"} })
69
+ else
70
+ b.findProgram(&.{"strip"}, &.{}) catch null;
41
71
 
42
72
  const json = @embedFile("package.json");
43
73
  var parsed = try std.json.parseFromSlice(std.json.Value, b.allocator, json, .{});
44
74
  defer parsed.deinit();
45
75
  const version = parsed.value.object.get("version").?.string;
46
76
  const description = parsed.value.object.get("description").?.string;
47
- var repository = std.mem.split(u8, parsed.value.object.get("repository").?.string, ":");
77
+ var repository = std.mem.splitScalar(u8, parsed.value.object.get("repository").?.string, ':');
48
78
  std.debug.assert(std.mem.eql(u8, repository.first(), "github"));
49
79
 
50
80
  const showdown =
@@ -52,6 +82,7 @@ pub fn build(b: *std.Build) !void {
52
82
  const log = b.option(bool, "log", "Enable protocol message logging") orelse false;
53
83
  const chance = b.option(bool, "chance", "Enable update probability tracking") orelse false;
54
84
  const calc = b.option(bool, "calc", "Enable damage calculator support") orelse false;
85
+ const extra = b.option([]const []const u8, "option", "Enable extra option");
55
86
 
56
87
  const options = b.addOptions();
57
88
  options.addOption(?bool, "showdown", showdown);
@@ -59,32 +90,68 @@ pub fn build(b: *std.Build) !void {
59
90
  options.addOption(?bool, "chance", chance);
60
91
  options.addOption(?bool, "calc", calc);
61
92
 
93
+ if (extra) |opts| {
94
+ for (opts) |opt| {
95
+ if (std.mem.indexOfScalar(u8, opt, '=')) |i| {
96
+ options.addOption(?bool, opt[0..i], if (std.mem.eql(u8, opt[i + 1 ..], "true"))
97
+ true
98
+ else if (std.mem.eql(u8, opt[i + 1 ..], "false"))
99
+ false
100
+ else {
101
+ std.log.err("Invalid option: {s}\n", .{opt});
102
+ return error.InvalidOption;
103
+ });
104
+ } else {
105
+ options.addOption(?bool, opt, true);
106
+ }
107
+ }
108
+ }
109
+
62
110
  const name = if (showdown) "pkmn-showdown" else "pkmn";
63
111
 
112
+ const pkmn: *std.Build.Module = b.addModule("pkmn", .{
113
+ .root_source_file = b.path("src/lib/pkmn.zig"),
114
+ .optimize = optimize,
115
+ .target = target,
116
+ .imports = &.{.{ .name = "build_options", .module = options.createModule() }},
117
+ });
118
+
64
119
  var c = false;
65
120
  if (node_headers) |headers| {
121
+ const translate_c = b.addTranslateC(.{
122
+ .root_source_file = b.path("src/lib/napi.h"),
123
+ .target = target,
124
+ // TODO: workaround for ziglang/zig#35515
125
+ .optimize = Debug,
126
+ });
127
+ translate_c.addSystemIncludePath(b.path(headers));
66
128
  const addon = b.fmt("{s}.node", .{name});
67
- const lib = b.addSharedLibrary(.{
129
+ const path = b.path("src/lib/node.zig");
130
+ const lib = b.addLibrary(.{
131
+ .linkage = .dynamic,
68
132
  .name = addon,
69
- .main_pkg_path = .{ .path = "." },
70
- .root_source_file = .{ .path = "src/lib/binding/node.zig" },
71
- .optimize = optimize,
72
- .target = target,
133
+ .root_module = b.createModule(.{
134
+ .root_source_file = path,
135
+ .optimize = optimize,
136
+ .target = target,
137
+ .strip = strip,
138
+ .pic = pic,
139
+ }),
73
140
  });
74
- lib.addOptions("build_options", options);
75
- lib.addSystemIncludePath(.{ .path = headers });
76
- lib.linkLibC();
141
+ lib.root_module.addOptions("build_options", options);
142
+ lib.root_module.addImport("napi", translate_c.createModule());
143
+ lib.root_module.link_libc = true;
77
144
  if (node_import_lib) |il| {
78
- lib.addObjectFile(.{ .path = il });
79
- } else if ((try std.zig.system.NativeTargetInfo.detect(target)).target.os.tag == .windows) {
80
- try std.io.getStdErr().writeAll("Must provide --node-import-library path on Windows\n");
145
+ lib.root_module.addObjectFile(b.path(il));
146
+ } else if (target.result.os.tag == .windows) {
147
+ var err = std.Io.File.stderr().writer(b.graph.io, &.{});
148
+ try err.interface.writeAll("Must provide --node-import-library path on Windows\n");
81
149
  std.process.exit(1);
82
150
  }
83
151
  lib.linker_allow_shlib_undefined = true;
84
152
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
85
- if (pic) lib.force_pic = pic;
86
153
  // Always emit to build/lib because this is where the driver code expects to find it
87
- // TODO(ziglang/zig#2231): using the following used to work (though was hacky):
154
+ // TODO: ziglang/zig#2231 - using the following used to work (though was hacky):
88
155
  //
89
156
  // lib.emit_bin = .{ .emit_to = b.fmt("build/lib/{s}", .{addon}) };
90
157
  // b.getInstallStep().dependOn(&lib.step);
@@ -93,70 +160,50 @@ pub fn build(b: *std.Build) !void {
93
160
  // rename the file ourself in install-pkmn-engine
94
161
  b.installArtifact(lib);
95
162
  } else if (wasm) {
96
- const exe = b.addExecutable(.{
97
- .name = name,
98
- .main_pkg_path = .{ .path = "." },
99
- .root_source_file = .{ .path = "src/lib/binding/wasm.zig" },
100
- .optimize = switch (optimize) {
101
- .ReleaseFast, .ReleaseSafe => .ReleaseSmall,
102
- else => optimize,
103
- },
104
- .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
105
- });
106
- exe.addOptions("build_options", options);
107
- exe.export_symbol_names = &[_][]const u8{
108
- "SHOWDOWN",
109
- "LOG",
110
- "CHANCE",
111
- "CALC",
112
- "GEN1_CHOICES_SIZE",
113
- "GEN1_LOGS_SIZE",
114
- };
115
- exe.entry = .disabled;
116
- exe.stack_size = wasm_stack_size;
117
- exe.strip = strip;
118
- if (pic) exe.force_pic = pic;
119
- const opt = b.findProgram(&.{"wasm-opt"}, &.{"./node_modules/.bin"}) catch null;
120
- if (optimize != .Debug and opt != null) {
121
- const out = b.fmt("build/lib/{s}.wasm", .{name});
122
- const sh = b.addSystemCommand(&.{ opt.?, "-O4" });
123
- sh.addArtifactArg(exe);
124
- sh.addArg("-o");
125
- sh.addFileSourceArg(.{ .path = out });
126
- b.getInstallStep().dependOn(&sh.step);
127
- } else {
128
- b.getInstallStep().dependOn(&b.addInstallArtifact(exe, .{
129
- .dest_dir = .{ .override = std.Build.InstallDir{ .lib = {} } },
130
- }).step);
131
- }
163
+ const path = "src/lib/wasm.zig";
164
+ try buildWasm(b, name, path, optimize, strip, pic, wasm_stack_size, null, options);
165
+ } else if (demo) {
166
+ const path = "src/tools/demo.zig";
167
+ const n = if (showdown) "demo-showdown" else "demo";
168
+ const mod = pkmn;
169
+ try buildWasm(b, n, path, optimize, strip, pic, wasm_stack_size, mod, options);
132
170
  } else if (dynamic) {
133
- const lib = b.addSharedLibrary(.{
171
+ const path = b.path("src/lib/c.zig");
172
+ const lib = b.addLibrary(.{
173
+ .linkage = .dynamic,
134
174
  .name = name,
135
- .main_pkg_path = .{ .path = "." },
136
- .root_source_file = .{ .path = "src/lib/binding/c.zig" },
137
- .version = try std.SemanticVersion.parse(version),
138
- .optimize = optimize,
139
- .target = target,
175
+ .root_module = b.createModule(.{
176
+ .root_source_file = path,
177
+ .optimize = optimize,
178
+ .target = target,
179
+ .strip = strip,
180
+ .pic = pic,
181
+ }),
140
182
  });
141
- lib.addOptions("build_options", options);
142
- lib.addIncludePath(.{ .path = "src/include" });
183
+ lib.root_module.addOptions("build_options", options);
184
+ lib.root_module.addIncludePath(b.path("src/include"));
143
185
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
144
- if (pic) lib.force_pic = pic;
145
186
  b.installArtifact(lib);
146
187
  c = true;
147
188
  } else {
148
- const lib = b.addStaticLibrary(.{
189
+ const path = b.path("src/lib/c.zig");
190
+ const lib = b.addLibrary(.{
191
+ .linkage = .static,
149
192
  .name = name,
150
- .main_pkg_path = .{ .path = "." },
151
- .root_source_file = .{ .path = "src/lib/binding/c.zig" },
152
- .optimize = optimize,
153
- .target = target,
193
+ .root_module = b.createModule(.{
194
+ .root_source_file = path,
195
+ .optimize = optimize,
196
+ .target = target,
197
+ .strip = strip,
198
+ .pic = pic,
199
+ }),
154
200
  });
155
- lib.addOptions("build_options", options);
156
- lib.addIncludePath(.{ .path = "src/include" });
157
- lib.bundle_compiler_rt = true;
201
+ lib.root_module.addOptions("build_options", options);
202
+ lib.root_module.addIncludePath(b.path("src/include"));
203
+ if (target.result.os.tag != .macos) {
204
+ lib.bundle_compiler_rt = true;
205
+ }
158
206
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
159
- if (pic) lib.force_pic = pic;
160
207
  if (emit_asm) {
161
208
  b.getInstallStep().dependOn(&b.addInstallFileWithDir(
162
209
  lib.getEmittedAsm(),
@@ -171,42 +218,59 @@ pub fn build(b: *std.Build) !void {
171
218
  b.fmt("{s}.ll", .{name}),
172
219
  ).step);
173
220
  }
174
- b.installArtifact(lib);
221
+ const install = b.addInstallArtifact(lib, .{});
222
+ b.getInstallStep().dependOn(&install.step);
223
+ maybeRanlib(b, lib, &install.step);
175
224
  c = true;
176
225
  }
177
226
 
178
227
  if (c) {
179
- const header = b.addInstallFileWithDir(
180
- .{ .path = "src/include/pkmn.h" },
181
- .header,
182
- "pkmn.h",
183
- );
228
+ const path = b.path("src/include/pkmn.h");
229
+ const header = b.addInstallFileWithDir(path, .header, "pkmn.h");
184
230
  b.getInstallStep().dependOn(&header.step);
185
231
 
186
- const pc = b.fmt("lib{s}.pc", .{name});
187
- const file = try b.cache_root.join(b.allocator, &.{pc});
188
- const pkgconfig_file = try std.fs.cwd().createFile(file, .{});
189
-
190
- const dirname = comptime std.fs.path.dirname(@src().file) orelse ".";
191
- const writer = pkgconfig_file.writer();
192
- try writer.print(
193
- \\prefix={0s}/{1s}
232
+ const content = try std.fmt.allocPrint(b.allocator,
233
+ \\prefix=${{pcfiledir}}/../..
194
234
  \\includedir=${{prefix}}/include
195
235
  \\libdir=${{prefix}}/lib
196
236
  \\
197
- \\Name: lib{2s}
198
- \\URL: https://github.com/{3s}
199
- \\Description: {4s}
200
- \\Version: {5s}
237
+ \\Name: lib{0s}
238
+ \\URL: https://github.com/{1s}
239
+ \\Description: {2s}
240
+ \\Version: {3s}
201
241
  \\Cflags: -I${{includedir}}
202
- \\Libs: -L${{libdir}} -l{2s}
203
- , .{ dirname, b.install_path, name, repository.next().?, description, version });
204
- defer pkgconfig_file.close();
242
+ \\Libs: -L${{libdir}} -l{0s}
243
+ , .{ name, repository.next().?, description, version });
205
244
 
206
- b.installFile(file, b.fmt("share/pkgconfig/{s}", .{pc}));
245
+ const pc = b.fmt("lib{s}.pc", .{name});
246
+ if (@hasDecl(std.Build, "FindProgramOptions")) {
247
+ const write_file = b.addWriteFiles();
248
+ const pkgconfig = write_file.add(pc, content);
249
+ b.getInstallStep().dependOn(&b.addInstallFileWithDir(
250
+ pkgconfig,
251
+ .prefix,
252
+ b.fmt("share/pkgconfig/{s}", .{pc}),
253
+ ).step);
254
+ } else {
255
+ const cwd = try std.process.currentPathAlloc(b.graph.io, b.allocator);
256
+ const file = try std.Io.Dir.path.relative(
257
+ b.allocator,
258
+ cwd,
259
+ &b.graph.environ_map,
260
+ cwd,
261
+ try b.cache_root.join(b.allocator, &.{pc}),
262
+ );
263
+ const pkgconfig = try std.Io.Dir.cwd().createFile(b.graph.io, file, .{});
264
+ defer pkgconfig.close(b.graph.io);
265
+
266
+ var writer = pkgconfig.writer(b.graph.io, &.{});
267
+ try writer.interface.writeAll(content);
268
+
269
+ b.installFile(file, b.fmt("share/pkgconfig/{s}", .{pc}));
270
+ }
207
271
  }
208
272
 
209
- const config = .{
273
+ const config: Config = .{
210
274
  .target = target,
211
275
  .optimize = optimize,
212
276
  .pic = pic,
@@ -217,7 +281,7 @@ pub fn build(b: *std.Build) !void {
217
281
  // TODO: tests can be run multiple times due to @imports
218
282
  const tests = TestStep.create(b, options, config);
219
283
 
220
- var exes = std.ArrayList(*std.Build.CompileStep).init(b.allocator);
284
+ var exes: ArrayList(*std.Build.Step.Compile) = .empty;
221
285
  const tools: ToolConfig = .{
222
286
  .options = .{
223
287
  .showdown = showdown,
@@ -233,7 +297,7 @@ pub fn build(b: *std.Build) !void {
233
297
  };
234
298
 
235
299
  var benchmark_config = tools;
236
- benchmark_config.general.optimize = .ReleaseFast;
300
+ benchmark_config.general.optimize = ReleaseFast;
237
301
  benchmark_config.general.strip = true;
238
302
  const benchmark = try tool(b, "src/test/benchmark.zig", benchmark_config);
239
303
 
@@ -248,7 +312,7 @@ pub fn build(b: *std.Build) !void {
248
312
 
249
313
  // FIXME: serde randomly fails to build in some release configurations
250
314
  var hack = tools;
251
- if (optimize != .Debug) hack.tool.tests = null;
315
+ if (optimize != Debug) hack.tool.tests = null;
252
316
  const serde = try tool(b, "src/tools/serde.zig", hack);
253
317
 
254
318
  if (analyze) |t| b.step("analyze", "Run LLVM analysis tool").dependOn(&t.step);
@@ -256,22 +320,103 @@ pub fn build(b: *std.Build) !void {
256
320
  if (dump) |t| b.step("dump", "Run protocol dump tool").dependOn(&t.step);
257
321
  if (fuzz) |t| b.step("fuzz", "Run fuzz tester").dependOn(&t.step);
258
322
  if (serde) |t| b.step("serde", "Run serialization/deserialization tool").dependOn(&t.step);
259
- b.step("test", "Run all tests").dependOn(&tests.step);
260
- b.step("tools", "Install tools").dependOn(&ToolsStep.create(b, &exes).step);
323
+ b.step("test", "Run all tests").dependOn(tests.step);
324
+ b.step("tools", "Install tools").dependOn(ToolsStep.create(b, &exes).step);
261
325
  if (transitions) |t| {
262
326
  b.step("transitions", "Visualize transitions algorithm search").dependOn(&t.step);
263
327
  }
264
328
  }
265
329
 
330
+ fn buildWasm(
331
+ b: *std.Build,
332
+ name: []const u8,
333
+ root_src_file: []const u8,
334
+ optimize: std.builtin.OptimizeMode,
335
+ strip: ?bool,
336
+ pic: ?bool,
337
+ wasm_stack_size: u64,
338
+ import: ?*std.Build.Module,
339
+ options: anytype,
340
+ ) !void {
341
+ const mode = switch (optimize) {
342
+ ReleaseFast, ReleaseSafe => ReleaseSmall,
343
+ else => optimize,
344
+ };
345
+ // https://webassembly.org/features/
346
+ const features = std.Target.wasm.featureSet(&.{
347
+ .atomics,
348
+ .bulk_memory,
349
+ // .exception_handling,
350
+ .extended_const,
351
+ // .half_precision,
352
+ // .multimemory,
353
+ .multivalue,
354
+ .mutable_globals,
355
+ .nontrapping_fptoint,
356
+ .reference_types,
357
+ // .relaxed_simd,
358
+ .sign_ext,
359
+ .simd128,
360
+ // .tail_call,
361
+ });
362
+ const freestanding = b.resolveTargetQuery(.{
363
+ .cpu_arch = .wasm32,
364
+ .os_tag = .freestanding,
365
+ .cpu_features_add = features,
366
+ });
367
+ const path = b.path(root_src_file);
368
+ const exe = b.addExecutable(.{
369
+ .name = name,
370
+ .root_module = b.createModule(.{
371
+ .root_source_file = path,
372
+ .optimize = mode,
373
+ .target = freestanding,
374
+ .strip = strip,
375
+ .pic = pic,
376
+ }),
377
+ });
378
+
379
+ var file = try std.Io.Dir.cwd().openFile(b.graph.io, root_src_file, .{});
380
+ defer file.close(b.graph.io);
381
+ var reader = file.reader(b.graph.io, &.{});
382
+ const bytes = try reader.interface.allocRemaining(b.allocator, .unlimited);
383
+ exe.root_module.export_symbol_names = try exports(b, bytes);
384
+ exe.entry = .disabled;
385
+
386
+ if (import) |i| {
387
+ exe.root_module.addImport("pkmn", i);
388
+ }
389
+ exe.stack_size = wasm_stack_size;
390
+ exe.root_module.addOptions("build_options", options);
391
+
392
+ const opt = if (@hasDecl(std.Build, "FindProgramOptions")) blk: {
393
+ if (exists(b, "./node_modules/.bin/wasm-opt") catch false) {
394
+ break :blk "./node_modules/.bin/wasm-opt";
395
+ }
396
+ break :blk b.findProgram(.{ .names = &.{"wasm-opt"} });
397
+ } else b.findProgram(&.{"wasm-opt"}, &.{"./node_modules/.bin"}) catch null;
398
+ if (optimize != Debug and opt != null) {
399
+ const out = b.fmt("build/lib/{s}.wasm", .{name});
400
+ const sh = b.addSystemCommand(&.{ opt.?, "--enable-bulk-memory", "--enable-simd", "-O4" });
401
+ sh.addArtifactArg(exe);
402
+ sh.addArg("-o");
403
+ sh.addFileArg(b.path(out));
404
+ b.getInstallStep().dependOn(&sh.step);
405
+ } else {
406
+ b.getInstallStep().dependOn(&b.addInstallArtifact(exe, .{
407
+ .dest_dir = .{ .override = std.Build.InstallDir{ .lib = {} } },
408
+ }).step);
409
+ }
410
+ }
411
+
266
412
  fn maybeStrip(
267
413
  b: *std.Build,
268
- artifact: *std.Build.CompileStep,
414
+ artifact: *std.Build.Step.Compile,
269
415
  step: *std.Build.Step,
270
- strip: bool,
416
+ strip: ?bool,
271
417
  cmd: ?[]const u8,
272
418
  ) void {
273
- artifact.strip = strip;
274
- if (!strip or cmd == null) return;
419
+ if (!(strip orelse false) or cmd == null) return;
275
420
  // Using `strip -r -u` for dynamic libraries is supposed to work on macOS but doesn't...
276
421
  const mac = builtin.os.tag == .macos;
277
422
  if (mac and artifact.isDynamicLibrary()) return;
@@ -283,47 +428,48 @@ fn maybeStrip(
283
428
  }
284
429
 
285
430
  const Config = struct {
286
- target: std.zig.CrossTarget,
431
+ target: std.Build.ResolvedTarget,
287
432
  optimize: std.builtin.OptimizeMode,
288
- pic: bool,
289
- strip: bool,
433
+ pic: ?bool,
434
+ strip: ?bool,
290
435
  cmd: ?[]const u8,
291
436
  };
292
437
 
293
438
  const TestStep = struct {
294
- step: std.Build.Step,
439
+ step: *std.Build.Step,
295
440
  build: bool,
296
441
 
297
- pub fn create(b: *std.Build, options: *std.Build.OptionsStep, config: Config) *TestStep {
442
+ pub fn create(b: *std.Build, options: *std.Build.Step.Options, config: Config) *TestStep {
298
443
  const coverage = b.option([]const u8, "test-coverage", "Generate test coverage");
299
- const test_file = b.option([]const u8, "test-file", "Input file for test");
300
444
  const test_filter =
301
445
  b.option([]const u8, "test-filter", "Skip tests that do not match filter");
302
446
 
303
447
  const self = b.allocator.create(TestStep) catch @panic("OOM");
304
- const step = std.Build.Step.init(.{ .id = .custom, .name = "Run all tests", .owner = b });
305
- self.* = .{ .step = step, .build = test_filter == null };
306
448
 
307
- const path = test_file orelse "src/lib/test.zig";
449
+ const path = b.path("src/lib/test.zig");
308
450
  const tests = b.addTest(.{
309
- .name = b.fmt("{s}-{s}", .{
310
- std.fs.path.basename(std.fs.path.dirname(path).?),
311
- std.fs.path.stem(std.fs.path.basename(path)),
451
+ .root_module = b.createModule(.{
452
+ .root_source_file = path,
453
+ .optimize = config.optimize,
454
+ .target = config.target,
455
+ .single_threaded = true,
456
+ .strip = config.strip,
457
+ .pic = config.pic,
312
458
  }),
313
- .main_pkg_path = .{ .path = "." },
314
- .root_source_file = .{ .path = path },
315
- .optimize = config.optimize,
316
- .target = config.target,
317
- .filter = test_filter,
459
+ .filters = if (test_filter) |filter| &.{filter} else &.{},
318
460
  });
319
- tests.addOptions("build_options", options);
320
- tests.single_threaded = true;
461
+ tests.root_module.addOptions("build_options", options);
462
+
321
463
  maybeStrip(b, tests, &tests.step, config.strip, config.cmd);
322
- if (config.pic) tests.force_pic = config.pic;
323
464
  if (coverage) |c| {
324
- tests.setExecCmd(&.{ "kcov", "--include-pattern=src/lib", c, null });
465
+ const kcov_run = b.addSystemCommand(&.{ "kcov", "--include-pattern=src/lib", c });
466
+ kcov_run.addArtifactArg(tests);
467
+ kcov_run.enableTestRunnerMode();
468
+ self.* = .{ .step = &kcov_run.step, .build = test_filter == null };
469
+ } else {
470
+ const run_step = b.addRunArtifact(tests);
471
+ self.* = .{ .step = &run_step.step, .build = test_filter == null };
325
472
  }
326
- self.step.dependOn(&b.addRunArtifact(tests).step);
327
473
 
328
474
  return self;
329
475
  }
@@ -335,15 +481,12 @@ const ToolConfig = struct {
335
481
  tool: struct {
336
482
  tests: ?*TestStep,
337
483
  name: ?[]const u8 = null,
338
- exes: *std.ArrayList(*std.Build.CompileStep),
484
+ exes: *ArrayList(*std.Build.Step.Compile),
339
485
  },
340
486
  };
341
487
 
342
- fn tool(b: *std.Build, path: []const u8, config: ToolConfig) !?*std.Build.RunStep {
343
- std.fs.cwd().access(path, .{}) catch |err| switch (err) {
344
- error.FileNotFound => return null,
345
- else => |e| return e,
346
- };
488
+ fn tool(b: *std.Build, path: []const u8, config: ToolConfig) !?*std.Build.Step.Run {
489
+ if (!try exists(b, path)) return null;
347
490
  var name = config.tool.name orelse std.fs.path.basename(path);
348
491
  const index = std.mem.lastIndexOfScalar(u8, name, '.');
349
492
  if (index) |i| name = name[0..i];
@@ -351,32 +494,107 @@ fn tool(b: *std.Build, path: []const u8, config: ToolConfig) !?*std.Build.RunSte
351
494
 
352
495
  const exe = b.addExecutable(.{
353
496
  .name = name,
354
- .root_source_file = .{ .path = path },
355
- .target = config.general.target,
356
- .optimize = config.general.optimize,
497
+ .root_module = b.createModule(.{
498
+ .root_source_file = b.path(path),
499
+ .target = config.general.target,
500
+ .optimize = config.general.optimize,
501
+ .strip = config.general.strip,
502
+ .pic = config.general.pic,
503
+ }),
357
504
  });
358
- exe.addModule("pkmn", module(b, config.options));
359
505
 
360
- exe.single_threaded = true;
361
- if (config.general.pic) exe.force_pic = config.general.pic;
506
+ const import = module(b, config.options, config.general.target, config.general.optimize);
507
+ exe.root_module.addImport("pkmn", import);
508
+ if (config.general.target.result.os.tag == .windows) {
509
+ exe.root_module.linkSystemLibrary("advapi32", .{});
510
+ }
511
+
362
512
  if (config.tool.tests) |ts| ts.step.dependOn(&exe.step);
363
- config.tool.exes.append(exe) catch @panic("OOM");
513
+ config.tool.exes.append(b.allocator, exe) catch @panic("OOM");
364
514
 
365
515
  const run = b.addRunArtifact(exe);
366
516
  maybeStrip(b, exe, &run.step, config.general.strip, config.general.cmd);
367
- if (b.args) |args| run.addArgs(args);
517
+ if (@hasDecl(std.Build, "FindProgramOptions")) {
518
+ run.addPassthruArgs();
519
+ } else {
520
+ if (b.args) |args| run.addArgs(args);
521
+ }
368
522
 
369
523
  return run;
370
524
  }
371
525
 
526
+ fn exists(b: *std.Build, path: []const u8) !bool {
527
+ std.Io.Dir.cwd().access(b.graph.io, path, .{}) catch |err| switch (err) {
528
+ error.FileNotFound => return false,
529
+ else => |e| return e,
530
+ };
531
+ return true;
532
+ }
533
+
372
534
  const ToolsStep = struct {
373
- step: std.Build.Step,
535
+ step: *std.Build.Step,
374
536
 
375
- pub fn create(b: *std.Build, exes: *std.ArrayList(*std.Build.CompileStep)) *ToolsStep {
537
+ pub fn create(b: *std.Build, exes: *ArrayList(*std.Build.Step.Compile)) *ToolsStep {
376
538
  const self = b.allocator.create(ToolsStep) catch @panic("OOM");
377
- const step = std.Build.Step.init(.{ .id = .custom, .name = "Install tools", .owner = b });
378
- self.* = .{ .step = step };
539
+
540
+ if (@hasDecl(std.Build, "FindProgramOptions")) {
541
+ const step = b.allocator.create(std.Build.Step.TopLevel) catch @panic("OOM");
542
+ step.* = .{
543
+ .step = std.Build.Step.init(.{
544
+ .tag = .top_level,
545
+ .name = "Install tools",
546
+ .owner = b,
547
+ }),
548
+ .description = "Install tools",
549
+ };
550
+ self.* = .{ .step = &step.step };
551
+ } else {
552
+ const step = b.allocator.create(std.Build.Step) catch @panic("OOM");
553
+ step.* = std.Build.Step.init(.{
554
+ .id = .custom,
555
+ .name = "Install tools",
556
+ .owner = b,
557
+ });
558
+ self.* = .{ .step = step };
559
+ }
379
560
  for (exes.items) |t| self.step.dependOn(&b.addInstallArtifact(t, .{}).step);
561
+
380
562
  return self;
381
563
  }
382
564
  };
565
+
566
+ pub fn exports(b: *std.Build, bytes: []const u8) ![][]const u8 {
567
+ var symbols: ArrayList([]const u8) = .empty;
568
+
569
+ var it = std.mem.splitSequence(u8, bytes, "export ");
570
+ _ = it.next();
571
+ while (it.next()) |s| {
572
+ if (std.mem.startsWith(u8, s, "const ")) {
573
+ const i = std.mem.indexOf(u8, s[6..], " ").?;
574
+ try symbols.append(b.allocator, s[6 .. 6 + i]);
575
+ } else { // "fn "
576
+ const i = std.mem.indexOf(u8, s[3..], "(").?;
577
+ try symbols.append(b.allocator, s[3 .. 3 + i]);
578
+ }
579
+ }
580
+
581
+ return symbols.items;
582
+ }
583
+
584
+ fn maybeRanlib(
585
+ b: *std.Build,
586
+ artifact: *std.Build.Step.Compile,
587
+ install_step: *std.Build.Step,
588
+ ) void {
589
+ if (builtin.os.tag != .macos) return;
590
+ if (artifact.linkage != .static) return;
591
+
592
+ const ranlib = if (comptime @hasDecl(std.Build, "FindProgramOptions"))
593
+ b.findProgram(.{ .names = &.{"ranlib"} }) orelse return
594
+ else
595
+ b.findProgram(&[_][]const u8{"ranlib"}, &[_][]const u8{}) catch return;
596
+
597
+ const sh = b.addSystemCommand(&[_][]const u8{ranlib});
598
+ sh.addFileArg(artifact.getEmittedBin());
599
+ install_step.dependOn(&sh.step);
600
+ }