@pkmn/engine 0.1.0-dev.7963b917 → 0.1.0-dev.79957401

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/LICENSE +1 -3
  2. package/README.md +70 -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/ids.json +12 -12
  16. package/build/pkg/data/layout.json +17 -4
  17. package/build/pkg/data/protocol.json +84 -13
  18. package/build/pkg/data.d.ts +27 -5
  19. package/build/pkg/data.js.map +1 -1
  20. package/build/pkg/gen1.d.ts +15 -18
  21. package/build/pkg/gen1.js +30 -21
  22. package/build/pkg/gen1.js.map +1 -1
  23. package/build/pkg/index.d.ts +42 -91
  24. package/build/pkg/index.js +23 -70
  25. package/build/pkg/index.js.map +1 -1
  26. package/build/pkg/protocol.d.ts +29 -1
  27. package/build/pkg/protocol.js +123 -102
  28. package/build/pkg/protocol.js.map +1 -1
  29. package/build/tools/debug.js +84 -73
  30. package/build/tools/debug.js.map +1 -1
  31. package/build/tools/display/debug.jsx +66 -0
  32. package/build/tools/display/debug.jsx.map +1 -0
  33. package/build/tools/display/index.js +152 -0
  34. package/build/tools/display/index.js.map +1 -0
  35. package/build/tools/display/ui.jsx +360 -0
  36. package/build/tools/display/ui.jsx.map +1 -0
  37. package/build/tools/display/util.js +157 -0
  38. package/build/tools/display/util.js.map +1 -0
  39. package/build.zig +397 -149
  40. package/package.json +62 -123
  41. package/src/bin/install-pkmn-engine +43 -22
  42. package/src/bin/pkmn-debug +1 -1
  43. package/src/lib/bindings/c.zig +201 -0
  44. package/src/lib/{binding → bindings}/node.zig +15 -32
  45. package/src/lib/bindings/wasm.zig +51 -0
  46. package/src/lib/common/array.zig +70 -0
  47. package/src/lib/common/data.zig +12 -24
  48. package/src/lib/common/debug.zig +46 -30
  49. package/src/lib/common/js.zig +3 -7
  50. package/src/lib/common/optional.zig +30 -24
  51. package/src/lib/common/options.zig +6 -0
  52. package/src/lib/common/protocol.zig +961 -450
  53. package/src/lib/common/rational.zig +341 -254
  54. package/src/lib/common/rng.zig +26 -19
  55. package/src/lib/common/util.zig +53 -1
  56. package/src/lib/gen1/calc.zig +726 -178
  57. package/src/lib/gen1/chance.zig +526 -324
  58. package/src/lib/gen1/data/moves.zig +193 -183
  59. package/src/lib/gen1/data/species.zig +4 -6
  60. package/src/lib/gen1/data/types.zig +9 -10
  61. package/src/lib/gen1/data.zig +78 -90
  62. package/src/lib/gen1/helpers.zig +114 -422
  63. package/src/lib/gen1/mechanics.zig +699 -615
  64. package/src/lib/gen2/calc.zig +150 -0
  65. package/src/lib/gen2/chance.zig +914 -0
  66. package/src/lib/gen2/data/items.zig +13 -21
  67. package/src/lib/gen2/data/moves.zig +664 -572
  68. package/src/lib/gen2/data/species.zig +2 -4
  69. package/src/lib/gen2/data/types.zig +139 -34
  70. package/src/lib/gen2/data.zig +131 -103
  71. package/src/lib/gen2/generated.zig +801 -0
  72. package/src/lib/gen2/helpers.zig +76 -51
  73. package/src/lib/gen2/mechanics.zig +3569 -29
  74. package/src/lib/napi.h +2 -0
  75. package/src/lib/node.zig +14 -0
  76. package/src/lib/pkmn.zig +125 -29
  77. package/src/lib/wasm.zig +13 -0
  78. package/src/tools/display/dom.ts +108 -0
  79. package/src/tools/display/ui.css +354 -0
  80. package/build/test/display.js +0 -400
  81. package/build/test/display.js.map +0 -1
  82. package/src/lib/binding/wasm.zig +0 -12
  83. package/src/lib/gen1/README.md +0 -812
  84. package/src/lib/gen2/README.md +0 -308
  85. 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
- .root_source_file = .{ .path = "src/lib/binding/node.zig" },
70
- .optimize = optimize,
71
- .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
+ }),
72
140
  });
73
- lib.addOptions("build_options", options);
74
- lib.setMainPkgPath("./");
75
- lib.addSystemIncludePath(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(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,97 +160,117 @@ 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 lib = b.addSharedLibrary(.{
97
- .name = name,
98
- .root_source_file = .{ .path = "src/lib/binding/wasm.zig" },
99
- .optimize = switch (optimize) {
100
- .ReleaseFast, .ReleaseSafe => .ReleaseSmall,
101
- else => optimize,
102
- },
103
- .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
104
- });
105
- lib.addOptions("build_options", options);
106
- lib.setMainPkgPath("./");
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);
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);
121
170
  } else if (dynamic) {
122
- const lib = b.addSharedLibrary(.{
171
+ const path = b.path("src/lib/c.zig");
172
+ const lib = b.addLibrary(.{
173
+ .linkage = .dynamic,
123
174
  .name = name,
124
- .root_source_file = .{ .path = "src/lib/binding/c.zig" },
125
- .version = try std.SemanticVersion.parse(version),
126
- .optimize = optimize,
127
- .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
+ }),
128
182
  });
129
- lib.addOptions("build_options", options);
130
- lib.setMainPkgPath("./");
131
- lib.addIncludePath("src/include");
183
+ lib.root_module.addOptions("build_options", options);
184
+ lib.root_module.addIncludePath(b.path("src/include"));
132
185
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
133
- if (pic) lib.force_pic = pic;
134
186
  b.installArtifact(lib);
135
187
  c = true;
136
188
  } else {
137
- const lib = b.addStaticLibrary(.{
189
+ const path = b.path("src/lib/c.zig");
190
+ const lib = b.addLibrary(.{
191
+ .linkage = .static,
138
192
  .name = name,
139
- .root_source_file = .{ .path = "src/lib/binding/c.zig" },
140
- .optimize = optimize,
141
- .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
+ }),
142
200
  });
143
- lib.addOptions("build_options", options);
144
- lib.setMainPkgPath("./");
145
- lib.addIncludePath("src/include");
146
- 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
+ }
147
206
  maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
148
- if (pic) lib.force_pic = pic;
149
- if (emit_asm) lib.emit_asm = .emit;
150
- if (emit_ll) lib.emit_llvm_ir = .emit;
151
- b.installArtifact(lib);
207
+ if (emit_asm) {
208
+ b.getInstallStep().dependOn(&b.addInstallFileWithDir(
209
+ lib.getEmittedAsm(),
210
+ .prefix,
211
+ b.fmt("{s}.s", .{name}),
212
+ ).step);
213
+ }
214
+ if (emit_ll) {
215
+ b.getInstallStep().dependOn(&b.addInstallFileWithDir(
216
+ lib.getEmittedLlvmIr(),
217
+ .prefix,
218
+ b.fmt("{s}.ll", .{name}),
219
+ ).step);
220
+ }
221
+ const install = b.addInstallArtifact(lib, .{});
222
+ b.getInstallStep().dependOn(&install.step);
223
+ maybeRanlib(b, lib, &install.step);
152
224
  c = true;
153
225
  }
154
226
 
155
227
  if (c) {
156
- const header = b.addInstallFileWithDir(
157
- .{ .path = "src/include/pkmn.h" },
158
- .header,
159
- "pkmn.h",
160
- );
228
+ const path = b.path("src/include/pkmn.h");
229
+ const header = b.addInstallFileWithDir(path, .header, "pkmn.h");
161
230
  b.getInstallStep().dependOn(&header.step);
162
231
 
163
- const pc = b.fmt("lib{s}.pc", .{name});
164
- const file = try b.cache_root.join(b.allocator, &.{pc});
165
- const pkgconfig_file = try std.fs.cwd().createFile(file, .{});
166
-
167
- const dirname = comptime std.fs.path.dirname(@src().file) orelse ".";
168
- const writer = pkgconfig_file.writer();
169
- try writer.print(
170
- \\prefix={0s}/{1s}
232
+ const content = try std.fmt.allocPrint(b.allocator,
233
+ \\prefix=${{pcfiledir}}/../..
171
234
  \\includedir=${{prefix}}/include
172
235
  \\libdir=${{prefix}}/lib
173
236
  \\
174
- \\Name: lib{2s}
175
- \\URL: https://github.com/{3s}
176
- \\Description: {4s}
177
- \\Version: {5s}
237
+ \\Name: lib{0s}
238
+ \\URL: https://github.com/{1s}
239
+ \\Description: {2s}
240
+ \\Version: {3s}
178
241
  \\Cflags: -I${{includedir}}
179
- \\Libs: -L${{libdir}} -l{2s}
180
- , .{ dirname, b.install_path, name, repository.next().?, description, version });
181
- defer pkgconfig_file.close();
242
+ \\Libs: -L${{libdir}} -l{0s}
243
+ , .{ name, repository.next().?, description, version });
182
244
 
183
- 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
+ }
184
271
  }
185
272
 
186
- const config = .{
273
+ const config: Config = .{
187
274
  .target = target,
188
275
  .optimize = optimize,
189
276
  .pic = pic,
@@ -194,7 +281,7 @@ pub fn build(b: *std.Build) !void {
194
281
  // TODO: tests can be run multiple times due to @imports
195
282
  const tests = TestStep.create(b, options, config);
196
283
 
197
- var exes = std.ArrayList(*std.Build.CompileStep).init(b.allocator);
284
+ var exes: ArrayList(*std.Build.Step.Compile) = .empty;
198
285
  const tools: ToolConfig = .{
199
286
  .options = .{
200
287
  .showdown = showdown,
@@ -210,39 +297,126 @@ pub fn build(b: *std.Build) !void {
210
297
  };
211
298
 
212
299
  var benchmark_config = tools;
213
- benchmark_config.general.optimize = .ReleaseFast;
300
+ benchmark_config.general.optimize = ReleaseFast;
214
301
  benchmark_config.general.strip = true;
215
- const benchmark = tool(b, "src/test/benchmark.zig", benchmark_config);
302
+ const benchmark = try tool(b, "src/test/benchmark.zig", benchmark_config);
216
303
 
217
304
  var fuzz_config = tools;
218
305
  fuzz_config.general.strip = false;
219
306
  fuzz_config.tool.name = "fuzz";
220
- const fuzz = tool(b, "src/test/fuzz.zig", fuzz_config);
221
-
222
- const copies = tool(b, "src/tools/copies.zig", tools);
223
- const dump = tool(b, "src/tools/dump.zig", tools);
224
- const serde = tool(b, "src/tools/serde.zig", tools);
225
- const transitions = tool(b, "src/tools/transitions.zig", tools);
226
-
227
- b.step("copies", "Run copy detector").dependOn(&copies.step);
228
- b.step("benchmark", "Run benchmark code").dependOn(&benchmark.step);
229
- b.step("dump", "Run protocol dump tool").dependOn(&dump.step);
230
- b.step("fuzz", "Run fuzz tester").dependOn(&fuzz.step);
231
- b.step("serde", "Run serialization/deserialization tool").dependOn(&serde.step);
232
- b.step("test", "Run all tests").dependOn(&tests.step);
233
- b.step("tools", "Install tools").dependOn(&ToolsStep.create(b, &exes).step);
234
- b.step("transitions", "Visualize transitions algorithm search").dependOn(&transitions.step);
307
+ const fuzz = try tool(b, "src/test/fuzz.zig", fuzz_config);
308
+
309
+ const analyze = try tool(b, "src/tools/analyze.zig", tools);
310
+ const dump = try tool(b, "src/tools/dump.zig", tools);
311
+ const transitions = try tool(b, "src/tools/transitions.zig", tools);
312
+
313
+ // FIXME: serde randomly fails to build in some release configurations
314
+ var hack = tools;
315
+ if (optimize != Debug) hack.tool.tests = null;
316
+ const serde = try tool(b, "src/tools/serde.zig", hack);
317
+
318
+ if (analyze) |t| b.step("analyze", "Run LLVM analysis tool").dependOn(&t.step);
319
+ if (benchmark) |t| b.step("benchmark", "Run benchmark code").dependOn(&t.step);
320
+ if (dump) |t| b.step("dump", "Run protocol dump tool").dependOn(&t.step);
321
+ if (fuzz) |t| b.step("fuzz", "Run fuzz tester").dependOn(&t.step);
322
+ if (serde) |t| b.step("serde", "Run serialization/deserialization tool").dependOn(&t.step);
323
+ b.step("test", "Run all tests").dependOn(tests.step);
324
+ b.step("tools", "Install tools").dependOn(ToolsStep.create(b, &exes).step);
325
+ if (transitions) |t| {
326
+ b.step("transitions", "Visualize transitions algorithm search").dependOn(&t.step);
327
+ }
328
+ }
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
+ }
235
410
  }
236
411
 
237
412
  fn maybeStrip(
238
413
  b: *std.Build,
239
- artifact: *std.Build.CompileStep,
414
+ artifact: *std.Build.Step.Compile,
240
415
  step: *std.Build.Step,
241
- strip: bool,
416
+ strip: ?bool,
242
417
  cmd: ?[]const u8,
243
418
  ) void {
244
- artifact.strip = strip;
245
- if (!strip or cmd == null) return;
419
+ if (!(strip orelse false) or cmd == null) return;
246
420
  // Using `strip -r -u` for dynamic libraries is supposed to work on macOS but doesn't...
247
421
  const mac = builtin.os.tag == .macos;
248
422
  if (mac and artifact.isDynamicLibrary()) return;
@@ -254,47 +428,48 @@ fn maybeStrip(
254
428
  }
255
429
 
256
430
  const Config = struct {
257
- target: std.zig.CrossTarget,
431
+ target: std.Build.ResolvedTarget,
258
432
  optimize: std.builtin.OptimizeMode,
259
- pic: bool,
260
- strip: bool,
433
+ pic: ?bool,
434
+ strip: ?bool,
261
435
  cmd: ?[]const u8,
262
436
  };
263
437
 
264
438
  const TestStep = struct {
265
- step: std.Build.Step,
439
+ step: *std.Build.Step,
266
440
  build: bool,
267
441
 
268
- 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 {
269
443
  const coverage = b.option([]const u8, "test-coverage", "Generate test coverage");
270
- const test_file = b.option([]const u8, "test-file", "Input file for test");
271
444
  const test_filter =
272
445
  b.option([]const u8, "test-filter", "Skip tests that do not match filter");
273
446
 
274
447
  const self = b.allocator.create(TestStep) catch @panic("OOM");
275
- const step = std.Build.Step.init(.{ .id = .custom, .name = "Run all tests", .owner = b });
276
- self.* = TestStep{ .step = step, .build = test_filter == null };
277
448
 
278
- const path = test_file orelse "src/lib/test.zig";
449
+ const path = b.path("src/lib/test.zig");
279
450
  const tests = b.addTest(.{
280
- .name = b.fmt("{s}-{s}", .{
281
- std.fs.path.basename(std.fs.path.dirname(path).?),
282
- 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,
283
458
  }),
284
- .root_source_file = .{ .path = path },
285
- .optimize = config.optimize,
286
- .target = config.target,
287
- .filter = test_filter,
459
+ .filters = if (test_filter) |filter| &.{filter} else &.{},
288
460
  });
289
- tests.setMainPkgPath("./");
290
- tests.addOptions("build_options", options);
291
- tests.single_threaded = true;
461
+ tests.root_module.addOptions("build_options", options);
462
+
292
463
  maybeStrip(b, tests, &tests.step, config.strip, config.cmd);
293
- if (config.pic) tests.force_pic = config.pic;
294
464
  if (coverage) |c| {
295
- 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 };
296
472
  }
297
- self.step.dependOn(&b.addRunArtifact(tests).step);
298
473
 
299
474
  return self;
300
475
  }
@@ -306,11 +481,12 @@ const ToolConfig = struct {
306
481
  tool: struct {
307
482
  tests: ?*TestStep,
308
483
  name: ?[]const u8 = null,
309
- exes: *std.ArrayList(*std.Build.CompileStep),
484
+ exes: *ArrayList(*std.Build.Step.Compile),
310
485
  },
311
486
  };
312
487
 
313
- fn tool(b: *std.Build, path: []const u8, config: ToolConfig) *std.Build.RunStep {
488
+ fn tool(b: *std.Build, path: []const u8, config: ToolConfig) !?*std.Build.Step.Run {
489
+ if (!try exists(b, path)) return null;
314
490
  var name = config.tool.name orelse std.fs.path.basename(path);
315
491
  const index = std.mem.lastIndexOfScalar(u8, name, '.');
316
492
  if (index) |i| name = name[0..i];
@@ -318,32 +494,104 @@ fn tool(b: *std.Build, path: []const u8, config: ToolConfig) *std.Build.RunStep
318
494
 
319
495
  const exe = b.addExecutable(.{
320
496
  .name = name,
321
- .root_source_file = .{ .path = path },
322
- .target = config.general.target,
323
- .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
+ }),
324
504
  });
325
- exe.addModule("pkmn", module(b, config.options));
326
505
 
327
- exe.single_threaded = true;
328
- 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
+
329
509
  if (config.tool.tests) |ts| ts.step.dependOn(&exe.step);
330
- config.tool.exes.append(exe) catch @panic("OOM");
510
+ config.tool.exes.append(b.allocator, exe) catch @panic("OOM");
331
511
 
332
512
  const run = b.addRunArtifact(exe);
333
513
  maybeStrip(b, exe, &run.step, config.general.strip, config.general.cmd);
334
- if (b.args) |args| run.addArgs(args);
514
+ if (@hasDecl(std.Build, "FindProgramOptions")) {
515
+ run.addPassthruArgs();
516
+ } else {
517
+ if (b.args) |args| run.addArgs(args);
518
+ }
335
519
 
336
520
  return run;
337
521
  }
338
522
 
523
+ fn exists(b: *std.Build, path: []const u8) !bool {
524
+ std.Io.Dir.cwd().access(b.graph.io, path, .{}) catch |err| switch (err) {
525
+ error.FileNotFound => return false,
526
+ else => |e| return e,
527
+ };
528
+ return true;
529
+ }
530
+
339
531
  const ToolsStep = struct {
340
- step: std.Build.Step,
532
+ step: *std.Build.Step,
341
533
 
342
- pub fn create(b: *std.Build, exes: *std.ArrayList(*std.Build.CompileStep)) *ToolsStep {
534
+ pub fn create(b: *std.Build, exes: *ArrayList(*std.Build.Step.Compile)) *ToolsStep {
343
535
  const self = b.allocator.create(ToolsStep) catch @panic("OOM");
344
- const step = std.Build.Step.init(.{ .id = .custom, .name = "Install tools", .owner = b });
345
- self.* = ToolsStep{ .step = step };
346
- for (exes.items) |t| self.step.dependOn(&b.addInstallArtifact(t).step);
536
+
537
+ if (@hasDecl(std.Build, "FindProgramOptions")) {
538
+ const step = b.allocator.create(std.Build.Step.TopLevel) catch @panic("OOM");
539
+ step.* = .{
540
+ .step = std.Build.Step.init(.{
541
+ .tag = .top_level,
542
+ .name = "Install tools",
543
+ .owner = b,
544
+ }),
545
+ .description = "Install tools",
546
+ };
547
+ self.* = .{ .step = &step.step };
548
+ } else {
549
+ const step = b.allocator.create(std.Build.Step) catch @panic("OOM");
550
+ step.* = std.Build.Step.init(.{
551
+ .id = .custom,
552
+ .name = "Install tools",
553
+ .owner = b,
554
+ });
555
+ self.* = .{ .step = step };
556
+ }
557
+ for (exes.items) |t| self.step.dependOn(&b.addInstallArtifact(t, .{}).step);
558
+
347
559
  return self;
348
560
  }
349
561
  };
562
+
563
+ pub fn exports(b: *std.Build, bytes: []const u8) ![][]const u8 {
564
+ var symbols: ArrayList([]const u8) = .empty;
565
+
566
+ var it = std.mem.splitSequence(u8, bytes, "export ");
567
+ _ = it.next();
568
+ while (it.next()) |s| {
569
+ if (std.mem.startsWith(u8, s, "const ")) {
570
+ const i = std.mem.indexOf(u8, s[6..], " ").?;
571
+ try symbols.append(b.allocator, s[6 .. 6 + i]);
572
+ } else { // "fn "
573
+ const i = std.mem.indexOf(u8, s[3..], "(").?;
574
+ try symbols.append(b.allocator, s[3 .. 3 + i]);
575
+ }
576
+ }
577
+
578
+ return symbols.items;
579
+ }
580
+
581
+ fn maybeRanlib(
582
+ b: *std.Build,
583
+ artifact: *std.Build.Step.Compile,
584
+ install_step: *std.Build.Step,
585
+ ) void {
586
+ if (builtin.os.tag != .macos) return;
587
+ if (artifact.linkage != .static) return;
588
+
589
+ const ranlib = if (comptime @hasDecl(std.Build, "FindProgramOptions"))
590
+ b.findProgram(.{ .names = &.{"ranlib"} }) orelse return
591
+ else
592
+ b.findProgram(&[_][]const u8{"ranlib"}, &[_][]const u8{}) catch return;
593
+
594
+ const sh = b.addSystemCommand(&[_][]const u8{ranlib});
595
+ sh.addFileArg(artifact.getEmittedBin());
596
+ install_step.dependOn(&sh.step);
597
+ }