@pkmn/engine 0.1.0-dev.a324c50f → 0.1.0-dev.a5846b2d

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