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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/LICENSE +1 -3
  2. package/README.md +66 -32
  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 -5
  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 +42 -91
  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 -73
  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 +357 -135
  39. package/package.json +61 -132
  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 +15 -32
  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 +565 -552
  52. package/src/lib/common/rational.zig +340 -253
  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 +724 -175
  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 +29 -32
  61. package/src/lib/gen1/helpers.zig +113 -561
  62. package/src/lib/gen1/mechanics.zig +611 -488
  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 +27 -29
  70. package/src/lib/gen2/generated.zig +22 -29
  71. package/src/lib/gen2/helpers.zig +68 -39
  72. package/src/lib/gen2/mechanics.zig +504 -430
  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,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,
@@ -8,16 +12,26 @@ pub const Options = struct {
8
12
  calc: ?bool = null,
9
13
  };
10
14
 
11
- pub fn module(b: *std.Build, options: Options) *std.Build.Module {
12
- 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 {
13
21
  const build_options = b.addOptions();
14
22
  build_options.addOption(?bool, "showdown", options.showdown);
15
23
  build_options.addOption(?bool, "log", options.log);
16
24
  build_options.addOption(?bool, "chance", options.chance);
17
25
  build_options.addOption(?bool, "calc", options.calc);
26
+
27
+ const root_source_file = b.path("src/lib/pkmn.zig");
28
+ const imports: []const std.Build.Module.Import =
29
+ &.{.{ .name = "build_options", .module = build_options.createModule() }};
18
30
  return b.createModule(.{
19
- .source_file = .{ .path = dirname ++ "/src/lib/pkmn.zig" },
20
- .dependencies = &.{.{ .name = "build_options", .module = build_options.createModule() }},
31
+ .root_source_file = root_source_file,
32
+ .target = target,
33
+ .optimize = optimize,
34
+ .imports = imports,
21
35
  });
22
36
  }
23
37
 
@@ -28,23 +42,30 @@ pub fn build(b: *std.Build) !void {
28
42
  const node_headers = b.option([]const u8, "node-headers", "Path to node-headers");
29
43
  const node_import_lib =
30
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;
31
49
  const wasm = b.option(bool, "wasm", "Build a WASM library") orelse false;
32
50
  const wasm_stack_size =
33
51
  b.option(u64, "wasm-stack-size", "The size of WASM stack") orelse std.wasm.page_size;
34
52
  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;
53
+ const strip = b.option(bool, "strip", "Strip debugging symbols from binary");
54
+ const pic = b.option(bool, "pic", "Force position independent code");
37
55
  const emit_asm = b.option(bool, "emit-asm", "Output .s (assembly code)") orelse false;
38
56
  const emit_ll = b.option(bool, "emit-ll", "Output .ll (LLVM IR)") orelse false;
39
57
 
40
- 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;
41
62
 
42
63
  const json = @embedFile("package.json");
43
64
  var parsed = try std.json.parseFromSlice(std.json.Value, b.allocator, json, .{});
44
65
  defer parsed.deinit();
45
66
  const version = parsed.value.object.get("version").?.string;
46
67
  const description = parsed.value.object.get("description").?.string;
47
- var repository = std.mem.split(u8, parsed.value.object.get("repository").?.string, ":");
68
+ var repository = std.mem.splitScalar(u8, parsed.value.object.get("repository").?.string, ':');
48
69
  std.debug.assert(std.mem.eql(u8, repository.first(), "github"));
49
70
 
50
71
  const showdown =
@@ -52,6 +73,7 @@ pub fn build(b: *std.Build) !void {
52
73
  const log = b.option(bool, "log", "Enable protocol message logging") orelse false;
53
74
  const chance = b.option(bool, "chance", "Enable update probability tracking") orelse false;
54
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");
55
77
 
56
78
  const options = b.addOptions();
57
79
  options.addOption(?bool, "showdown", showdown);
@@ -59,32 +81,68 @@ pub fn build(b: *std.Build) !void {
59
81
  options.addOption(?bool, "chance", chance);
60
82
  options.addOption(?bool, "calc", calc);
61
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
+
62
101
  const name = if (showdown) "pkmn-showdown" else "pkmn";
63
102
 
103
+ const pkmn: *std.Build.Module = b.addModule("pkmn", .{
104
+ .root_source_file = b.path("src/lib/pkmn.zig"),
105
+ .optimize = optimize,
106
+ .target = target,
107
+ .imports = &.{.{ .name = "build_options", .module = options.createModule() }},
108
+ });
109
+
64
110
  var c = false;
65
111
  if (node_headers) |headers| {
112
+ const translate_c = b.addTranslateC(.{
113
+ .root_source_file = b.path("src/lib/napi.h"),
114
+ .target = target,
115
+ // TODO: workaround for ziglang/zig#35515
116
+ .optimize = .Debug,
117
+ });
118
+ translate_c.addSystemIncludePath(b.path(headers));
66
119
  const addon = b.fmt("{s}.node", .{name});
67
- const lib = b.addSharedLibrary(.{
120
+ const path = b.path("src/lib/node.zig");
121
+ const lib = b.addLibrary(.{
122
+ .linkage = .dynamic,
68
123
  .name = addon,
69
- .main_pkg_path = .{ .path = "." },
70
- .root_source_file = .{ .path = "src/lib/binding/node.zig" },
71
- .optimize = optimize,
72
- .target = target,
124
+ .root_module = b.createModule(.{
125
+ .root_source_file = path,
126
+ .optimize = optimize,
127
+ .target = target,
128
+ .strip = strip,
129
+ .pic = pic,
130
+ }),
73
131
  });
74
- lib.addOptions("build_options", options);
75
- lib.addSystemIncludePath(.{ .path = headers });
76
- 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;
77
135
  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");
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");
81
140
  std.process.exit(1);
82
141
  }
83
142
  lib.linker_allow_shlib_undefined = true;
84
143
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
85
- if (pic) lib.force_pic = pic;
86
144
  // 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):
145
+ // TODO: ziglang/zig#2231 - using the following used to work (though was hacky):
88
146
  //
89
147
  // lib.emit_bin = .{ .emit_to = b.fmt("build/lib/{s}", .{addon}) };
90
148
  // b.getInstallStep().dependOn(&lib.step);
@@ -93,59 +151,50 @@ pub fn build(b: *std.Build) !void {
93
151
  // rename the file ourself in install-pkmn-engine
94
152
  b.installArtifact(lib);
95
153
  } else if (wasm) {
96
- const lib = b.addSharedLibrary(.{
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
- lib.addOptions("build_options", options);
107
- lib.stack_size = wasm_stack_size;
108
- lib.rdynamic = true;
109
- lib.strip = strip;
110
- if (pic) lib.force_pic = pic;
111
- const opt = b.findProgram(&.{"wasm-opt"}, &.{"./node_modules/.bin"}) catch null;
112
- if (optimize != .Debug and opt != null) {
113
- const out = b.fmt("build/lib/{s}.wasm", .{name});
114
- const sh = b.addSystemCommand(&.{ opt.?, "-O4" });
115
- sh.addArtifactArg(lib);
116
- sh.addArg("-o");
117
- sh.addFileSourceArg(.{ .path = out });
118
- b.getInstallStep().dependOn(&sh.step);
119
- }
120
- b.installArtifact(lib);
154
+ const path = "src/lib/wasm.zig";
155
+ try buildWasm(b, name, path, optimize, strip, pic, wasm_stack_size, null, options);
156
+ } else if (demo) {
157
+ const path = "src/tools/demo.zig";
158
+ const n = if (showdown) "demo-showdown" else "demo";
159
+ const mod = pkmn;
160
+ try buildWasm(b, n, path, optimize, strip, pic, wasm_stack_size, mod, options);
121
161
  } else if (dynamic) {
122
- const lib = b.addSharedLibrary(.{
162
+ const path = b.path("src/lib/c.zig");
163
+ const lib = b.addLibrary(.{
164
+ .linkage = .dynamic,
123
165
  .name = name,
124
- .main_pkg_path = .{ .path = "." },
125
- .root_source_file = .{ .path = "src/lib/binding/c.zig" },
126
- .version = try std.SemanticVersion.parse(version),
127
- .optimize = optimize,
128
- .target = target,
166
+ .root_module = b.createModule(.{
167
+ .root_source_file = path,
168
+ .optimize = optimize,
169
+ .target = target,
170
+ .strip = strip,
171
+ .pic = pic,
172
+ }),
129
173
  });
130
- lib.addOptions("build_options", options);
131
- lib.addIncludePath(.{ .path = "src/include" });
174
+ lib.root_module.addOptions("build_options", options);
175
+ lib.root_module.addIncludePath(b.path("src/include"));
132
176
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
133
- if (pic) lib.force_pic = pic;
134
177
  b.installArtifact(lib);
135
178
  c = true;
136
179
  } else {
137
- const lib = b.addStaticLibrary(.{
180
+ const path = b.path("src/lib/c.zig");
181
+ const lib = b.addLibrary(.{
182
+ .linkage = .static,
138
183
  .name = name,
139
- .main_pkg_path = .{ .path = "." },
140
- .root_source_file = .{ .path = "src/lib/binding/c.zig" },
141
- .optimize = optimize,
142
- .target = target,
184
+ .root_module = b.createModule(.{
185
+ .root_source_file = path,
186
+ .optimize = optimize,
187
+ .target = target,
188
+ .strip = strip,
189
+ .pic = pic,
190
+ }),
143
191
  });
144
- lib.addOptions("build_options", options);
145
- lib.addIncludePath(.{ .path = "src/include" });
146
- 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
+ }
147
197
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
148
- if (pic) lib.force_pic = pic;
149
198
  if (emit_asm) {
150
199
  b.getInstallStep().dependOn(&b.addInstallFileWithDir(
151
200
  lib.getEmittedAsm(),
@@ -160,42 +209,59 @@ pub fn build(b: *std.Build) !void {
160
209
  b.fmt("{s}.ll", .{name}),
161
210
  ).step);
162
211
  }
163
- b.installArtifact(lib);
212
+ const install = b.addInstallArtifact(lib, .{});
213
+ b.getInstallStep().dependOn(&install.step);
214
+ maybeRanlib(b, lib, &install.step);
164
215
  c = true;
165
216
  }
166
217
 
167
218
  if (c) {
168
- const header = b.addInstallFileWithDir(
169
- .{ .path = "src/include/pkmn.h" },
170
- .header,
171
- "pkmn.h",
172
- );
219
+ const path = b.path("src/include/pkmn.h");
220
+ const header = b.addInstallFileWithDir(path, .header, "pkmn.h");
173
221
  b.getInstallStep().dependOn(&header.step);
174
222
 
175
- const pc = b.fmt("lib{s}.pc", .{name});
176
- const file = try b.cache_root.join(b.allocator, &.{pc});
177
- const pkgconfig_file = try std.fs.cwd().createFile(file, .{});
178
-
179
- const dirname = comptime std.fs.path.dirname(@src().file) orelse ".";
180
- const writer = pkgconfig_file.writer();
181
- try writer.print(
182
- \\prefix={0s}/{1s}
223
+ const content = try std.fmt.allocPrint(b.allocator,
224
+ \\prefix=${{pcfiledir}}/../..
183
225
  \\includedir=${{prefix}}/include
184
226
  \\libdir=${{prefix}}/lib
185
227
  \\
186
- \\Name: lib{2s}
187
- \\URL: https://github.com/{3s}
188
- \\Description: {4s}
189
- \\Version: {5s}
228
+ \\Name: lib{0s}
229
+ \\URL: https://github.com/{1s}
230
+ \\Description: {2s}
231
+ \\Version: {3s}
190
232
  \\Cflags: -I${{includedir}}
191
- \\Libs: -L${{libdir}} -l{2s}
192
- , .{ dirname, b.install_path, name, repository.next().?, description, version });
193
- defer pkgconfig_file.close();
233
+ \\Libs: -L${{libdir}} -l{0s}
234
+ , .{ name, repository.next().?, description, version });
194
235
 
195
- 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
+ }
196
262
  }
197
263
 
198
- const config = .{
264
+ const config: Config = .{
199
265
  .target = target,
200
266
  .optimize = optimize,
201
267
  .pic = pic,
@@ -206,7 +272,7 @@ pub fn build(b: *std.Build) !void {
206
272
  // TODO: tests can be run multiple times due to @imports
207
273
  const tests = TestStep.create(b, options, config);
208
274
 
209
- var exes = std.ArrayList(*std.Build.CompileStep).init(b.allocator);
275
+ var exes: ArrayList(*std.Build.Step.Compile) = .empty;
210
276
  const tools: ToolConfig = .{
211
277
  .options = .{
212
278
  .showdown = showdown,
@@ -245,22 +311,105 @@ pub fn build(b: *std.Build) !void {
245
311
  if (dump) |t| b.step("dump", "Run protocol dump tool").dependOn(&t.step);
246
312
  if (fuzz) |t| b.step("fuzz", "Run fuzz tester").dependOn(&t.step);
247
313
  if (serde) |t| b.step("serde", "Run serialization/deserialization tool").dependOn(&t.step);
248
- b.step("test", "Run all tests").dependOn(&tests.step);
249
- 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);
250
316
  if (transitions) |t| {
251
317
  b.step("transitions", "Visualize transitions algorithm search").dependOn(&t.step);
252
318
  }
253
319
  }
254
320
 
321
+ fn buildWasm(
322
+ b: *std.Build,
323
+ name: []const u8,
324
+ root_src_file: []const u8,
325
+ optimize: std.builtin.OptimizeMode,
326
+ strip: ?bool,
327
+ pic: ?bool,
328
+ wasm_stack_size: u64,
329
+ import: ?*std.Build.Module,
330
+ options: anytype,
331
+ ) !void {
332
+ const mode = switch (optimize) {
333
+ .ReleaseFast, .ReleaseSafe => .ReleaseSmall,
334
+ else => optimize,
335
+ };
336
+ // https://webassembly.org/features/
337
+ var features = std.Target.wasm.featureSet(&.{
338
+ .atomics,
339
+ .bulk_memory,
340
+ // .exception_handling,
341
+ .extended_const,
342
+ // .half_precision,
343
+ // .multimemory,
344
+ .mutable_globals,
345
+ .nontrapping_fptoint,
346
+ .reference_types,
347
+ // .relaxed_simd,
348
+ .sign_ext,
349
+ .simd128,
350
+ // .tail_call,
351
+ });
352
+ if (builtin.zig_version.order(try std.SemanticVersion.parse("0.12.0")) != .lt) {
353
+ features.addFeature(@intFromEnum(std.Target.wasm.Feature.multivalue));
354
+ }
355
+ const freestanding = b.resolveTargetQuery(.{
356
+ .cpu_arch = .wasm32,
357
+ .os_tag = .freestanding,
358
+ .cpu_features_add = features,
359
+ });
360
+ const path = b.path(root_src_file);
361
+ const exe = b.addExecutable(.{
362
+ .name = name,
363
+ .root_module = b.createModule(.{
364
+ .root_source_file = path,
365
+ .optimize = mode,
366
+ .target = freestanding,
367
+ .strip = strip,
368
+ .pic = pic,
369
+ }),
370
+ });
371
+
372
+ var file = try std.Io.Dir.cwd().openFile(b.graph.io, root_src_file, .{});
373
+ defer file.close(b.graph.io);
374
+ var reader = file.reader(b.graph.io, &.{});
375
+ const bytes = try reader.interface.allocRemaining(b.allocator, .unlimited);
376
+ exe.root_module.export_symbol_names = try exports(b, bytes);
377
+ exe.entry = .disabled;
378
+
379
+ if (import) |i| {
380
+ exe.root_module.addImport("pkmn", i);
381
+ }
382
+ exe.stack_size = wasm_stack_size;
383
+ exe.root_module.addOptions("build_options", options);
384
+
385
+ const opt = if (@hasDecl(std.Build, "FindProgramOptions")) blk: {
386
+ if (exists(b, "./node_modules/.bin/wasm-opt") catch false) {
387
+ break :blk "./node_modules/.bin/wasm-opt";
388
+ }
389
+ break :blk b.findProgram(.{ .names = &.{"wasm-opt"} });
390
+ } else b.findProgram(&.{"wasm-opt"}, &.{"./node_modules/.bin"}) catch null;
391
+ if (optimize != .Debug and opt != null) {
392
+ const out = b.fmt("build/lib/{s}.wasm", .{name});
393
+ const sh = b.addSystemCommand(&.{ opt.?, "--enable-bulk-memory", "--enable-simd", "-O4" });
394
+ sh.addArtifactArg(exe);
395
+ sh.addArg("-o");
396
+ sh.addFileArg(b.path(out));
397
+ b.getInstallStep().dependOn(&sh.step);
398
+ } else {
399
+ b.getInstallStep().dependOn(&b.addInstallArtifact(exe, .{
400
+ .dest_dir = .{ .override = std.Build.InstallDir{ .lib = {} } },
401
+ }).step);
402
+ }
403
+ }
404
+
255
405
  fn maybeStrip(
256
406
  b: *std.Build,
257
- artifact: *std.Build.CompileStep,
407
+ artifact: *std.Build.Step.Compile,
258
408
  step: *std.Build.Step,
259
- strip: bool,
409
+ strip: ?bool,
260
410
  cmd: ?[]const u8,
261
411
  ) void {
262
- artifact.strip = strip;
263
- if (!strip or cmd == null) return;
412
+ if (!(strip orelse false) or cmd == null) return;
264
413
  // Using `strip -r -u` for dynamic libraries is supposed to work on macOS but doesn't...
265
414
  const mac = builtin.os.tag == .macos;
266
415
  if (mac and artifact.isDynamicLibrary()) return;
@@ -272,47 +421,48 @@ fn maybeStrip(
272
421
  }
273
422
 
274
423
  const Config = struct {
275
- target: std.zig.CrossTarget,
424
+ target: std.Build.ResolvedTarget,
276
425
  optimize: std.builtin.OptimizeMode,
277
- pic: bool,
278
- strip: bool,
426
+ pic: ?bool,
427
+ strip: ?bool,
279
428
  cmd: ?[]const u8,
280
429
  };
281
430
 
282
431
  const TestStep = struct {
283
- step: std.Build.Step,
432
+ step: *std.Build.Step,
284
433
  build: bool,
285
434
 
286
- pub fn create(b: *std.Build, options: *std.Build.OptionsStep, config: Config) *TestStep {
435
+ pub fn create(b: *std.Build, options: *std.Build.Step.Options, config: Config) *TestStep {
287
436
  const coverage = b.option([]const u8, "test-coverage", "Generate test coverage");
288
- const test_file = b.option([]const u8, "test-file", "Input file for test");
289
437
  const test_filter =
290
438
  b.option([]const u8, "test-filter", "Skip tests that do not match filter");
291
439
 
292
440
  const self = b.allocator.create(TestStep) catch @panic("OOM");
293
- const step = std.Build.Step.init(.{ .id = .custom, .name = "Run all tests", .owner = b });
294
- self.* = .{ .step = step, .build = test_filter == null };
295
441
 
296
- const path = test_file orelse "src/lib/test.zig";
442
+ const path = b.path("src/lib/test.zig");
297
443
  const tests = b.addTest(.{
298
- .name = b.fmt("{s}-{s}", .{
299
- std.fs.path.basename(std.fs.path.dirname(path).?),
300
- std.fs.path.stem(std.fs.path.basename(path)),
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,
301
451
  }),
302
- .main_pkg_path = .{ .path = "." },
303
- .root_source_file = .{ .path = path },
304
- .optimize = config.optimize,
305
- .target = config.target,
306
- .filter = test_filter,
452
+ .filters = if (test_filter) |filter| &.{filter} else &.{},
307
453
  });
308
- tests.addOptions("build_options", options);
309
- tests.single_threaded = true;
454
+ tests.root_module.addOptions("build_options", options);
455
+
310
456
  maybeStrip(b, tests, &tests.step, config.strip, config.cmd);
311
- if (config.pic) tests.force_pic = config.pic;
312
457
  if (coverage) |c| {
313
- 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 };
314
465
  }
315
- self.step.dependOn(&b.addRunArtifact(tests).step);
316
466
 
317
467
  return self;
318
468
  }
@@ -324,15 +474,12 @@ const ToolConfig = struct {
324
474
  tool: struct {
325
475
  tests: ?*TestStep,
326
476
  name: ?[]const u8 = null,
327
- exes: *std.ArrayList(*std.Build.CompileStep),
477
+ exes: *ArrayList(*std.Build.Step.Compile),
328
478
  },
329
479
  };
330
480
 
331
- fn tool(b: *std.Build, path: []const u8, config: ToolConfig) !?*std.Build.RunStep {
332
- std.fs.cwd().access(path, .{}) catch |err| switch (err) {
333
- error.FileNotFound => return null,
334
- else => |e| return e,
335
- };
481
+ fn tool(b: *std.Build, path: []const u8, config: ToolConfig) !?*std.Build.Step.Run {
482
+ if (!try exists(b, path)) return null;
336
483
  var name = config.tool.name orelse std.fs.path.basename(path);
337
484
  const index = std.mem.lastIndexOfScalar(u8, name, '.');
338
485
  if (index) |i| name = name[0..i];
@@ -340,32 +487,107 @@ fn tool(b: *std.Build, path: []const u8, config: ToolConfig) !?*std.Build.RunSte
340
487
 
341
488
  const exe = b.addExecutable(.{
342
489
  .name = name,
343
- .root_source_file = .{ .path = path },
344
- .target = config.general.target,
345
- .optimize = config.general.optimize,
490
+ .root_module = b.createModule(.{
491
+ .root_source_file = b.path(path),
492
+ .target = config.general.target,
493
+ .optimize = config.general.optimize,
494
+ .strip = config.general.strip,
495
+ .pic = config.general.pic,
496
+ }),
346
497
  });
347
- exe.addModule("pkmn", module(b, config.options));
348
498
 
349
- exe.single_threaded = true;
350
- if (config.general.pic) exe.force_pic = config.general.pic;
499
+ const import = module(b, config.options, config.general.target, config.general.optimize);
500
+ exe.root_module.addImport("pkmn", import);
501
+ if (config.general.target.result.os.tag == .windows) {
502
+ exe.root_module.linkSystemLibrary("advapi32", .{});
503
+ }
504
+
351
505
  if (config.tool.tests) |ts| ts.step.dependOn(&exe.step);
352
- config.tool.exes.append(exe) catch @panic("OOM");
506
+ config.tool.exes.append(b.allocator, exe) catch @panic("OOM");
353
507
 
354
508
  const run = b.addRunArtifact(exe);
355
509
  maybeStrip(b, exe, &run.step, config.general.strip, config.general.cmd);
356
- 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
+ }
357
515
 
358
516
  return run;
359
517
  }
360
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
+
361
527
  const ToolsStep = struct {
362
- step: std.Build.Step,
528
+ step: *std.Build.Step,
363
529
 
364
- pub fn create(b: *std.Build, exes: *std.ArrayList(*std.Build.CompileStep)) *ToolsStep {
530
+ pub fn create(b: *std.Build, exes: *ArrayList(*std.Build.Step.Compile)) *ToolsStep {
365
531
  const self = b.allocator.create(ToolsStep) catch @panic("OOM");
366
- const step = std.Build.Step.init(.{ .id = .custom, .name = "Install tools", .owner = b });
367
- 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
+ }
368
553
  for (exes.items) |t| self.step.dependOn(&b.addInstallArtifact(t, .{}).step);
554
+
369
555
  return self;
370
556
  }
371
557
  };
558
+
559
+ pub fn exports(b: *std.Build, bytes: []const u8) ![][]const u8 {
560
+ var symbols: ArrayList([]const u8) = .empty;
561
+
562
+ var it = std.mem.splitSequence(u8, bytes, "export ");
563
+ _ = it.next();
564
+ while (it.next()) |s| {
565
+ if (std.mem.startsWith(u8, s, "const ")) {
566
+ const i = std.mem.indexOf(u8, s[6..], " ").?;
567
+ try symbols.append(b.allocator, s[6 .. 6 + i]);
568
+ } else { // "fn "
569
+ const i = std.mem.indexOf(u8, s[3..], "(").?;
570
+ try symbols.append(b.allocator, s[3 .. 3 + i]);
571
+ }
572
+ }
573
+
574
+ return symbols.items;
575
+ }
576
+
577
+ fn maybeRanlib(
578
+ b: *std.Build,
579
+ artifact: *std.Build.Step.Compile,
580
+ install_step: *std.Build.Step,
581
+ ) void {
582
+ if (builtin.os.tag != .macos) return;
583
+ if (artifact.linkage != .static) return;
584
+
585
+ const ranlib = if (comptime @hasDecl(std.Build, "FindProgramOptions"))
586
+ b.findProgram(.{ .names = &.{"ranlib"} }) orelse return
587
+ else
588
+ b.findProgram(&[_][]const u8{"ranlib"}, &[_][]const u8{}) catch return;
589
+
590
+ const sh = b.addSystemCommand(&[_][]const u8{ranlib});
591
+ sh.addFileArg(artifact.getEmittedBin());
592
+ install_step.dependOn(&sh.step);
593
+ }