@pkmn/engine 0.1.0-dev.1220fbc5 → 0.1.0-dev.130de754

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 +46 -30
  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 +310 -203
  39. package/package.json +59 -132
  40. package/src/bin/install-pkmn-engine +70 -53
  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 +46 -30
  48. package/src/lib/common/js.zig +5 -9
  49. package/src/lib/common/optional.zig +21 -22
  50. package/src/lib/common/options.zig +6 -0
  51. package/src/lib/common/protocol.zig +573 -541
  52. package/src/lib/common/rational.zig +340 -254
  53. package/src/lib/common/rng.zig +11 -13
  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 +494 -308
  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 +49 -49
  61. package/src/lib/gen1/helpers.zig +115 -562
  62. package/src/lib/gen1/mechanics.zig +603 -489
  63. package/src/lib/gen2/calc.zig +35 -28
  64. package/src/lib/gen2/chance.zig +122 -518
  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 +40 -39
  70. package/src/lib/gen2/generated.zig +30 -47
  71. package/src/lib/gen2/helpers.zig +68 -39
  72. package/src/lib/gen2/mechanics.zig +853 -533
  73. package/src/lib/napi.h +2 -0
  74. package/src/lib/node.zig +14 -0
  75. package/src/lib/pkmn.zig +123 -29
  76. package/src/lib/wasm.zig +12 -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 -330
  84. package/src/test/display/pkmn.html.tmpl +0 -368
package/build.zig CHANGED
@@ -1,25 +1,13 @@
1
1
  const std = @import("std");
2
- const builtin = @import("builtin");
3
2
 
4
- pub const Options = struct {
5
- showdown: ?bool = null,
6
- log: ?bool = null,
7
- chance: ?bool = null,
8
- calc: ?bool = null,
9
- };
10
-
11
- pub fn module(b: *std.Build, options: Options) *std.Build.Module {
12
- const dirname = comptime std.fs.path.dirname(@src().file) orelse ".";
13
- const build_options = b.addOptions();
14
- build_options.addOption(?bool, "showdown", options.showdown);
15
- build_options.addOption(?bool, "log", options.log);
16
- build_options.addOption(?bool, "chance", options.chance);
17
- 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
- });
22
- }
3
+ const Debug: std.builtin.OptimizeMode =
4
+ if (@hasDecl(std.builtin.OptimizeMode, "Debug")) .debug else .Debug;
5
+ const ReleaseSafe: std.builtin.OptimizeMode =
6
+ if (@hasDecl(std.builtin.OptimizeMode, "ReleaseSafe")) .safe else .ReleaseSafe;
7
+ const ReleaseFast: std.builtin.OptimizeMode =
8
+ if (@hasDecl(std.builtin.OptimizeMode, "ReleaseFast")) .fast else .ReleaseFast;
9
+ const ReleaseSmall: std.builtin.OptimizeMode =
10
+ if (@hasDecl(std.builtin.OptimizeMode, "ReleaseSmall")) .small else .ReleaseSmall;
23
11
 
24
12
  pub fn build(b: *std.Build) !void {
25
13
  const target = b.standardTargetOptions(.{});
@@ -28,23 +16,27 @@ pub fn build(b: *std.Build) !void {
28
16
  const node_headers = b.option([]const u8, "node-headers", "Path to node-headers");
29
17
  const node_import_lib =
30
18
  b.option([]const u8, "node-import-library", "Path to node import library (Windows)");
19
+ const demo = if (try exists(b, "src/tools/demo.zig"))
20
+ b.option(bool, "demo", "Build the demo library") orelse false
21
+ else
22
+ false;
31
23
  const wasm = b.option(bool, "wasm", "Build a WASM library") orelse false;
32
24
  const wasm_stack_size =
33
25
  b.option(u64, "wasm-stack-size", "The size of WASM stack") orelse std.wasm.page_size;
34
26
  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;
27
+ const strip = b.option(bool, "strip", "Strip debugging symbols from binary");
28
+ const unwind_tables: ?std.builtin.UnwindTables =
29
+ if ((strip orelse false) and target.result.os.tag != .windows) .none else null;
30
+ const pic = b.option(bool, "pic", "Force position independent code");
37
31
  const emit_asm = b.option(bool, "emit-asm", "Output .s (assembly code)") orelse false;
38
32
  const emit_ll = b.option(bool, "emit-ll", "Output .ll (LLVM IR)") orelse false;
39
33
 
40
- const cmd = b.findProgram(&.{"strip"}, &.{}) catch null;
41
-
42
34
  const json = @embedFile("package.json");
43
35
  var parsed = try std.json.parseFromSlice(std.json.Value, b.allocator, json, .{});
44
36
  defer parsed.deinit();
45
37
  const version = parsed.value.object.get("version").?.string;
46
38
  const description = parsed.value.object.get("description").?.string;
47
- var repository = std.mem.split(u8, parsed.value.object.get("repository").?.string, ":");
39
+ var repository = std.mem.splitScalar(u8, parsed.value.object.get("repository").?.string, ':');
48
40
  std.debug.assert(std.mem.eql(u8, repository.first(), "github"));
49
41
 
50
42
  const showdown =
@@ -52,6 +44,7 @@ pub fn build(b: *std.Build) !void {
52
44
  const log = b.option(bool, "log", "Enable protocol message logging") orelse false;
53
45
  const chance = b.option(bool, "chance", "Enable update probability tracking") orelse false;
54
46
  const calc = b.option(bool, "calc", "Enable damage calculator support") orelse false;
47
+ const extra = b.option([]const []const u8, "option", "Enable extra option");
55
48
 
56
49
  const options = b.addOptions();
57
50
  options.addOption(?bool, "showdown", showdown);
@@ -59,93 +52,117 @@ pub fn build(b: *std.Build) !void {
59
52
  options.addOption(?bool, "chance", chance);
60
53
  options.addOption(?bool, "calc", calc);
61
54
 
55
+ if (extra) |opts| {
56
+ for (opts) |opt| {
57
+ if (std.mem.indexOfScalar(u8, opt, '=')) |i| {
58
+ options.addOption(?bool, opt[0..i], if (std.mem.eql(u8, opt[i + 1 ..], "true"))
59
+ true
60
+ else if (std.mem.eql(u8, opt[i + 1 ..], "false"))
61
+ false
62
+ else {
63
+ std.log.err("Invalid option: {s}\n", .{opt});
64
+ return error.InvalidOption;
65
+ });
66
+ } else {
67
+ options.addOption(?bool, opt, true);
68
+ }
69
+ }
70
+ }
71
+
62
72
  const name = if (showdown) "pkmn-showdown" else "pkmn";
63
73
 
74
+ const pkmn = b.addModule("pkmn", .{
75
+ .root_source_file = b.path("src/lib/pkmn.zig"),
76
+ .imports = &.{.{ .name = "build_options", .module = options.createModule() }},
77
+ });
78
+
64
79
  var c = false;
65
80
  if (node_headers) |headers| {
81
+ const translate_c = b.addTranslateC(.{
82
+ .root_source_file = b.path("src/lib/napi.h"),
83
+ .target = target,
84
+ // TODO: workaround for ziglang/zig#35515
85
+ .optimize = Debug,
86
+ });
87
+ translate_c.addSystemIncludePath(b.path(headers));
66
88
  const addon = b.fmt("{s}.node", .{name});
67
- const lib = b.addSharedLibrary(.{
89
+ const path = b.path("src/lib/node.zig");
90
+ const lib = b.addLibrary(.{
91
+ .linkage = .dynamic,
68
92
  .name = addon,
69
- .main_pkg_path = .{ .path = "." },
70
- .root_source_file = .{ .path = "src/lib/binding/node.zig" },
71
- .optimize = optimize,
72
- .target = target,
93
+ .root_module = b.createModule(.{
94
+ .root_source_file = path,
95
+ .optimize = optimize,
96
+ .target = target,
97
+ .strip = strip,
98
+ .unwind_tables = unwind_tables,
99
+ .pic = pic,
100
+ }),
73
101
  });
74
- lib.addOptions("build_options", options);
75
- lib.addSystemIncludePath(.{ .path = headers });
76
- lib.linkLibC();
102
+ lib.discard_local_symbols = strip orelse false;
103
+ lib.root_module.addOptions("build_options", options);
104
+ lib.root_module.addImport("napi", translate_c.createModule());
105
+ lib.root_module.link_libc = true;
77
106
  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");
107
+ lib.root_module.addObjectFile(b.path(il));
108
+ } else if (target.result.os.tag == .windows) {
109
+ var err = std.Io.File.stderr().writer(b.graph.io, &.{});
110
+ try err.interface.writeAll("Must provide --node-import-library path on Windows\n");
81
111
  std.process.exit(1);
82
112
  }
83
113
  lib.linker_allow_shlib_undefined = true;
84
- maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
85
- if (pic) lib.force_pic = pic;
86
- // 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):
88
- //
89
- // lib.emit_bin = .{ .emit_to = b.fmt("build/lib/{s}", .{addon}) };
90
- // b.getInstallStep().dependOn(&lib.step);
91
- //
92
- // But ziglang/zig#14647 broke this so we now need to do an install() and then manually
93
- // rename the file ourself in install-pkmn-engine
94
- b.installArtifact(lib);
114
+ b.getInstallStep().dependOn(&b.addInstallArtifact(lib, .{
115
+ .dest_dir = .{ .override = .lib },
116
+ .dest_sub_path = addon,
117
+ .implib_dir = .disabled,
118
+ .pdb_dir = .disabled,
119
+ }).step);
95
120
  } 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);
121
+ const path = "src/lib/wasm.zig";
122
+ try buildWasm(b, name, path, optimize, strip, pic, wasm_stack_size, null, options);
123
+ } else if (demo) {
124
+ const path = "src/tools/demo.zig";
125
+ const n = if (showdown) "demo-showdown" else "demo";
126
+ try buildWasm(b, n, path, optimize, strip, pic, wasm_stack_size, pkmn, options);
121
127
  } else if (dynamic) {
122
- const lib = b.addSharedLibrary(.{
128
+ const path = b.path("src/lib/c.zig");
129
+ const lib = b.addLibrary(.{
130
+ .linkage = .dynamic,
123
131
  .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,
132
+ .root_module = b.createModule(.{
133
+ .root_source_file = path,
134
+ .optimize = optimize,
135
+ .target = target,
136
+ .strip = strip,
137
+ .unwind_tables = unwind_tables,
138
+ .pic = pic,
139
+ }),
129
140
  });
130
- lib.addOptions("build_options", options);
131
- lib.addIncludePath(.{ .path = "src/include" });
132
- maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
133
- if (pic) lib.force_pic = pic;
141
+ lib.discard_local_symbols = strip orelse false;
142
+ lib.root_module.addOptions("build_options", options);
143
+ lib.root_module.addIncludePath(b.path("src/include"));
134
144
  b.installArtifact(lib);
135
145
  c = true;
136
146
  } else {
137
- const lib = b.addStaticLibrary(.{
147
+ const path = b.path("src/lib/c.zig");
148
+ const lib = b.addLibrary(.{
149
+ .linkage = .static,
138
150
  .name = name,
139
- .main_pkg_path = .{ .path = "." },
140
- .root_source_file = .{ .path = "src/lib/binding/c.zig" },
141
- .optimize = optimize,
142
- .target = target,
151
+ .root_module = b.createModule(.{
152
+ .root_source_file = path,
153
+ .optimize = optimize,
154
+ .target = target,
155
+ .strip = strip,
156
+ .unwind_tables = unwind_tables,
157
+ .pic = pic,
158
+ }),
143
159
  });
144
- lib.addOptions("build_options", options);
145
- lib.addIncludePath(.{ .path = "src/include" });
146
- lib.bundle_compiler_rt = true;
147
- maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
148
- if (pic) lib.force_pic = pic;
160
+ lib.discard_local_symbols = strip orelse false;
161
+ lib.root_module.addOptions("build_options", options);
162
+ lib.root_module.addIncludePath(b.path("src/include"));
163
+ if (target.result.os.tag != .macos) {
164
+ lib.bundle_compiler_rt = true;
165
+ }
149
166
  if (emit_asm) {
150
167
  b.getInstallStep().dependOn(&b.addInstallFileWithDir(
151
168
  lib.getEmittedAsm(),
@@ -165,207 +182,297 @@ pub fn build(b: *std.Build) !void {
165
182
  }
166
183
 
167
184
  if (c) {
168
- const header = b.addInstallFileWithDir(
169
- .{ .path = "src/include/pkmn.h" },
170
- .header,
171
- "pkmn.h",
172
- );
185
+ const path = b.path("src/include/pkmn.h");
186
+ const header = b.addInstallFileWithDir(path, .header, "pkmn.h");
173
187
  b.getInstallStep().dependOn(&header.step);
174
188
 
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}
189
+ const content = try std.fmt.allocPrint(b.allocator,
190
+ \\prefix=${{pcfiledir}}/../..
183
191
  \\includedir=${{prefix}}/include
184
192
  \\libdir=${{prefix}}/lib
185
193
  \\
186
- \\Name: lib{2s}
187
- \\URL: https://github.com/{3s}
188
- \\Description: {4s}
189
- \\Version: {5s}
194
+ \\Name: lib{0s}
195
+ \\URL: https://github.com/{1s}
196
+ \\Description: {2s}
197
+ \\Version: {3s}
190
198
  \\Cflags: -I${{includedir}}
191
- \\Libs: -L${{libdir}} -l{2s}
192
- , .{ dirname, b.install_path, name, repository.next().?, description, version });
193
- defer pkgconfig_file.close();
199
+ \\Libs: -L${{libdir}} -l{0s}
200
+ , .{ name, repository.next().?, description, version });
194
201
 
195
- b.installFile(file, b.fmt("share/pkgconfig/{s}", .{pc}));
202
+ const pc = b.fmt("lib{s}.pc", .{name});
203
+ const write_file = b.addWriteFiles();
204
+ const pkgconfig = write_file.add(pc, content);
205
+ b.getInstallStep().dependOn(&b.addInstallFileWithDir(
206
+ pkgconfig,
207
+ .prefix,
208
+ b.fmt("share/pkgconfig/{s}", .{pc}),
209
+ ).step);
196
210
  }
197
211
 
198
- const config = .{
212
+ const config: Config = .{
199
213
  .target = target,
200
214
  .optimize = optimize,
201
215
  .pic = pic,
202
216
  .strip = strip,
203
- .cmd = cmd,
217
+ .unwind_tables = unwind_tables,
204
218
  };
205
219
 
206
220
  // TODO: tests can be run multiple times due to @imports
207
221
  const tests = TestStep.create(b, options, config);
208
222
 
209
- var exes = std.ArrayList(*std.Build.CompileStep).init(b.allocator);
223
+ var exes: std.ArrayList(*std.Build.Step.Compile) = .empty;
210
224
  const tools: ToolConfig = .{
211
- .options = .{
212
- .showdown = showdown,
213
- .log = log,
214
- .chance = chance,
215
- .calc = calc,
216
- },
225
+ .showdown = showdown,
226
+ .module = pkmn,
217
227
  .general = config,
218
228
  .tool = .{
219
- .tests = if (tests.build) tests else null,
229
+ .tests = if (tests.build) tests.step else null,
220
230
  .exes = &exes,
221
231
  },
222
232
  };
223
233
 
224
234
  var benchmark_config = tools;
225
- benchmark_config.general.optimize = .ReleaseFast;
235
+ benchmark_config.general.optimize = ReleaseFast;
226
236
  benchmark_config.general.strip = true;
237
+ benchmark_config.general.unwind_tables = if (target.result.os.tag != .windows) .none else null;
227
238
  const benchmark = try tool(b, "src/test/benchmark.zig", benchmark_config);
228
239
 
229
240
  var fuzz_config = tools;
230
241
  fuzz_config.general.strip = false;
242
+ fuzz_config.general.unwind_tables = null;
231
243
  fuzz_config.tool.name = "fuzz";
232
244
  const fuzz = try tool(b, "src/test/fuzz.zig", fuzz_config);
233
245
 
234
246
  const analyze = try tool(b, "src/tools/analyze.zig", tools);
235
247
  const dump = try tool(b, "src/tools/dump.zig", tools);
248
+ const serde = try tool(b, "src/tools/serde.zig", tools);
236
249
  const transitions = try tool(b, "src/tools/transitions.zig", tools);
237
250
 
238
- // FIXME: serde randomly fails to build in some release configurations
239
- var hack = tools;
240
- if (optimize != .Debug) hack.tool.tests = null;
241
- const serde = try tool(b, "src/tools/serde.zig", hack);
242
-
243
251
  if (analyze) |t| b.step("analyze", "Run LLVM analysis tool").dependOn(&t.step);
244
252
  if (benchmark) |t| b.step("benchmark", "Run benchmark code").dependOn(&t.step);
245
253
  if (dump) |t| b.step("dump", "Run protocol dump tool").dependOn(&t.step);
246
254
  if (fuzz) |t| b.step("fuzz", "Run fuzz tester").dependOn(&t.step);
247
255
  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);
256
+ b.step("test", "Run all tests").dependOn(tests.step);
257
+ const install_tools = b.step("tools", "Install tools");
258
+ for (exes.items) |t| install_tools.dependOn(&b.addInstallArtifact(t, .{}).step);
250
259
  if (transitions) |t| {
251
260
  b.step("transitions", "Visualize transitions algorithm search").dependOn(&t.step);
252
261
  }
253
262
  }
254
263
 
255
- fn maybeStrip(
264
+ fn buildWasm(
256
265
  b: *std.Build,
257
- artifact: *std.Build.CompileStep,
258
- step: *std.Build.Step,
259
- strip: bool,
260
- cmd: ?[]const u8,
261
- ) void {
262
- artifact.strip = strip;
263
- if (!strip or cmd == null) return;
264
- // Using `strip -r -u` for dynamic libraries is supposed to work on macOS but doesn't...
265
- const mac = builtin.os.tag == .macos;
266
- if (mac and artifact.isDynamicLibrary()) return;
267
- // Assuming GNU strip, which complains "illegal pathname found in archive member"...
268
- if (!mac and artifact.isStaticLibrary()) return;
269
- const sh = b.addSystemCommand(&.{ cmd.?, if (mac) "-x" else "-s" });
270
- sh.addArtifactArg(artifact);
271
- step.dependOn(&sh.step);
266
+ name: []const u8,
267
+ root_src_file: []const u8,
268
+ optimize: std.builtin.OptimizeMode,
269
+ strip: ?bool,
270
+ pic: ?bool,
271
+ wasm_stack_size: u64,
272
+ import: ?*std.Build.Module,
273
+ options: *std.Build.Step.Options,
274
+ ) !void {
275
+ const mode = switch (optimize) {
276
+ ReleaseFast, ReleaseSafe => ReleaseSmall,
277
+ else => optimize,
278
+ };
279
+ // https://webassembly.org/features/
280
+ const features = std.Target.wasm.featureSet(&.{
281
+ .atomics,
282
+ .bulk_memory,
283
+ // .exception_handling,
284
+ .extended_const,
285
+ // .half_precision,
286
+ // .multimemory,
287
+ .multivalue,
288
+ .mutable_globals,
289
+ .nontrapping_fptoint,
290
+ .reference_types,
291
+ // .relaxed_simd,
292
+ .sign_ext,
293
+ .simd128,
294
+ // .tail_call,
295
+ });
296
+ const freestanding = b.resolveTargetQuery(.{
297
+ .cpu_arch = .wasm32,
298
+ .os_tag = .freestanding,
299
+ .cpu_features_add = features,
300
+ });
301
+ const path = b.path(root_src_file);
302
+ const exe = b.addExecutable(.{
303
+ .name = name,
304
+ .root_module = b.createModule(.{
305
+ .root_source_file = path,
306
+ .optimize = mode,
307
+ .target = freestanding,
308
+ .strip = strip,
309
+ .unwind_tables = if (strip orelse false) .none else null,
310
+ .pic = pic,
311
+ }),
312
+ });
313
+ exe.discard_local_symbols = strip orelse false;
314
+
315
+ if (@hasDecl(std.Build, "FindProgramOptions")) b.dependOnFileContents(path);
316
+ const root = if (@hasField(std.Build, "root")) b.root.root_dir.handle else b.build_root.handle;
317
+ var file = try root.openFile(b.graph.io, root_src_file, .{});
318
+ defer file.close(b.graph.io);
319
+ var reader = file.reader(b.graph.io, &.{});
320
+ const bytes = try reader.interface.allocRemaining(b.allocator, .unlimited);
321
+ exe.root_module.export_symbol_names = try exports(b, bytes);
322
+ exe.entry = .disabled;
323
+
324
+ if (import) |i| {
325
+ exe.root_module.addImport("pkmn", i);
326
+ } else {
327
+ exe.root_module.addOptions("build_options", options);
328
+ }
329
+ exe.stack_size = wasm_stack_size;
330
+
331
+ const opt = if (optimize == Debug)
332
+ null
333
+ else if (@hasDecl(std.Build, "FindProgramOptions")) blk: {
334
+ if (exists(b, "./node_modules/.bin/wasm-opt") catch false) {
335
+ break :blk "./node_modules/.bin/wasm-opt";
336
+ }
337
+ break :blk b.findProgram(.{ .names = &.{"wasm-opt"} });
338
+ } else b.findProgram(&.{"wasm-opt"}, &.{"./node_modules/.bin"}) catch null;
339
+ if (opt) |wasm_opt| {
340
+ const out = b.fmt("{s}.wasm", .{name});
341
+ const sh = b.addSystemCommand(&.{
342
+ wasm_opt,
343
+ "--enable-bulk-memory",
344
+ "--enable-simd",
345
+ "-O4",
346
+ });
347
+ sh.addArtifactArg(exe);
348
+ sh.addArg("-o");
349
+ b.getInstallStep().dependOn(&b.addInstallFileWithDir(
350
+ sh.addOutputFileArg(out),
351
+ .lib,
352
+ out,
353
+ ).step);
354
+ } else {
355
+ b.getInstallStep().dependOn(&b.addInstallArtifact(exe, .{
356
+ .dest_dir = .{ .override = .lib },
357
+ }).step);
358
+ }
272
359
  }
273
360
 
274
361
  const Config = struct {
275
- target: std.zig.CrossTarget,
362
+ target: std.Build.ResolvedTarget,
276
363
  optimize: std.builtin.OptimizeMode,
277
- pic: bool,
278
- strip: bool,
279
- cmd: ?[]const u8,
364
+ pic: ?bool,
365
+ strip: ?bool,
366
+ unwind_tables: ?std.builtin.UnwindTables,
280
367
  };
281
368
 
282
369
  const TestStep = struct {
283
- step: std.Build.Step,
370
+ step: *std.Build.Step,
284
371
  build: bool,
285
372
 
286
- pub fn create(b: *std.Build, options: *std.Build.OptionsStep, config: Config) *TestStep {
373
+ pub fn create(b: *std.Build, options: *std.Build.Step.Options, config: Config) TestStep {
287
374
  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
375
  const test_filter =
290
376
  b.option([]const u8, "test-filter", "Skip tests that do not match filter");
291
377
 
292
- 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
-
296
- const path = test_file orelse "src/lib/test.zig";
378
+ const path = b.path("src/lib/test.zig");
297
379
  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)),
380
+ .root_module = b.createModule(.{
381
+ .root_source_file = path,
382
+ .optimize = config.optimize,
383
+ .target = config.target,
384
+ .single_threaded = true,
385
+ .strip = config.strip,
386
+ .unwind_tables = config.unwind_tables,
387
+ .pic = config.pic,
301
388
  }),
302
- .main_pkg_path = .{ .path = "." },
303
- .root_source_file = .{ .path = path },
304
- .optimize = config.optimize,
305
- .target = config.target,
306
- .filter = test_filter,
389
+ .filters = if (test_filter) |filter| &.{filter} else &.{},
307
390
  });
308
- tests.addOptions("build_options", options);
309
- tests.single_threaded = true;
310
- maybeStrip(b, tests, &tests.step, config.strip, config.cmd);
311
- if (config.pic) tests.force_pic = config.pic;
391
+ tests.discard_local_symbols = config.strip orelse false;
392
+ tests.root_module.addOptions("build_options", options);
393
+
312
394
  if (coverage) |c| {
313
- tests.setExecCmd(&.{ "kcov", "--include-pattern=src/lib", c, null });
395
+ const kcov_run = b.addSystemCommand(&.{ "kcov", "--include-pattern=src/lib", c });
396
+ kcov_run.addArtifactArg(tests);
397
+ kcov_run.enableTestRunnerMode();
398
+ return .{ .step = &kcov_run.step, .build = test_filter == null };
399
+ } else {
400
+ const run_step = b.addRunArtifact(tests);
401
+ return .{ .step = &run_step.step, .build = test_filter == null };
314
402
  }
315
- self.step.dependOn(&b.addRunArtifact(tests).step);
316
-
317
- return self;
318
403
  }
319
404
  };
320
405
 
321
406
  const ToolConfig = struct {
322
- options: Options,
407
+ showdown: ?bool,
408
+ module: *std.Build.Module,
323
409
  general: Config,
324
410
  tool: struct {
325
- tests: ?*TestStep,
411
+ tests: ?*std.Build.Step,
326
412
  name: ?[]const u8 = null,
327
- exes: *std.ArrayList(*std.Build.CompileStep),
413
+ exes: *std.ArrayList(*std.Build.Step.Compile),
328
414
  },
329
415
  };
330
416
 
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
- };
417
+ fn tool(b: *std.Build, path: []const u8, config: ToolConfig) !?*std.Build.Step.Run {
418
+ if (!try exists(b, path)) return null;
336
419
  var name = config.tool.name orelse std.fs.path.basename(path);
337
420
  const index = std.mem.lastIndexOfScalar(u8, name, '.');
338
421
  if (index) |i| name = name[0..i];
339
- if (config.options.showdown orelse false) name = b.fmt("{s}-showdown", .{name});
422
+ if (config.showdown orelse false) name = b.fmt("{s}-showdown", .{name});
340
423
 
341
424
  const exe = b.addExecutable(.{
342
425
  .name = name,
343
- .root_source_file = .{ .path = path },
344
- .target = config.general.target,
345
- .optimize = config.general.optimize,
426
+ .root_module = b.createModule(.{
427
+ .root_source_file = b.path(path),
428
+ .target = config.general.target,
429
+ .optimize = config.general.optimize,
430
+ .single_threaded = true,
431
+ .strip = config.general.strip,
432
+ .unwind_tables = config.general.unwind_tables,
433
+ .pic = config.general.pic,
434
+ }),
346
435
  });
347
- exe.addModule("pkmn", module(b, config.options));
436
+ exe.discard_local_symbols = config.general.strip orelse false;
437
+ exe.root_module.addImport("pkmn", config.module);
348
438
 
349
- exe.single_threaded = true;
350
- if (config.general.pic) exe.force_pic = config.general.pic;
351
- if (config.tool.tests) |ts| ts.step.dependOn(&exe.step);
352
- config.tool.exes.append(exe) catch @panic("OOM");
439
+ if (config.tool.tests) |ts| ts.dependOn(&exe.step);
440
+ config.tool.exes.append(b.allocator, exe) catch @panic("OOM");
353
441
 
354
442
  const run = b.addRunArtifact(exe);
355
- maybeStrip(b, exe, &run.step, config.general.strip, config.general.cmd);
356
- if (b.args) |args| run.addArgs(args);
443
+ if (@hasDecl(std.Build, "FindProgramOptions")) {
444
+ run.addPassthruArgs();
445
+ } else {
446
+ if (b.args) |args| run.addArgs(args);
447
+ }
357
448
 
358
449
  return run;
359
450
  }
360
451
 
361
- const ToolsStep = struct {
362
- step: std.Build.Step,
452
+ fn exists(b: *std.Build, path: []const u8) !bool {
453
+ const root = if (@hasField(std.Build, "root")) b.root.root_dir.handle else b.build_root.handle;
454
+ root.access(b.graph.io, path, .{}) catch |err| switch (err) {
455
+ error.FileNotFound => return false,
456
+ else => |e| return e,
457
+ };
458
+ if (@hasDecl(std.Build, "FindProgramOptions")) b.dependOnFileMetadata(b.path(path));
459
+ return true;
460
+ }
363
461
 
364
- pub fn create(b: *std.Build, exes: *std.ArrayList(*std.Build.CompileStep)) *ToolsStep {
365
- 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 };
368
- for (exes.items) |t| self.step.dependOn(&b.addInstallArtifact(t, .{}).step);
369
- return self;
462
+ pub fn exports(b: *std.Build, bytes: []const u8) ![][]const u8 {
463
+ var symbols: std.ArrayList([]const u8) = .empty;
464
+
465
+ var it = std.mem.splitSequence(u8, bytes, "export ");
466
+ _ = it.next();
467
+ while (it.next()) |s| {
468
+ if (std.mem.startsWith(u8, s, "const ")) {
469
+ const i = std.mem.indexOf(u8, s[6..], " ").?;
470
+ try symbols.append(b.allocator, s[6 .. 6 + i]);
471
+ } else { // "fn "
472
+ const i = std.mem.indexOf(u8, s[3..], "(").?;
473
+ try symbols.append(b.allocator, s[3 .. 3 + i]);
474
+ }
370
475
  }
371
- };
476
+
477
+ return symbols.items;
478
+ }