@bloomengine/engine 0.3.3 → 0.4.0

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.
@@ -39,6 +39,28 @@ fn build_jolt() {
39
39
  .join("third_party")
40
40
  .join("bloom_jolt");
41
41
 
42
+ let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
43
+ let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
44
+
45
+ println!("cargo:rerun-if-env-changed=BLOOM_JOLT_PREBUILT_DIR");
46
+ println!("cargo:rerun-if-env-changed=BLOOM_JOLT_FROM_SOURCE");
47
+
48
+ // Prebuilt fast path — if `@bloomengine/jolt-prebuilt` is available
49
+ // (via env var or a node_modules sibling), link its archives and
50
+ // skip the multi-minute cmake build of JoltPhysics entirely. The
51
+ // env var BLOOM_JOLT_FROM_SOURCE=1 forces the cmake fallback even
52
+ // when prebuilts are present, useful for hacking on the C++ shim.
53
+ let from_source = std::env::var_os("BLOOM_JOLT_FROM_SOURCE").is_some();
54
+ if !from_source {
55
+ if let Some(prebuilt_dir) =
56
+ find_prebuilt_dir(&manifest_dir, &target_os, &target_arch)
57
+ {
58
+ link_prebuilt(&prebuilt_dir, &target_os);
59
+ emit_cxx_runtime_link(&target_os);
60
+ return;
61
+ }
62
+ }
63
+
42
64
  if !shim_dir.join("CMakeLists.txt").exists() {
43
65
  panic!(
44
66
  "bloom_jolt shim not found at {}; did the third_party submodules init?",
@@ -60,8 +82,6 @@ fn build_jolt() {
60
82
  // 2. The Jolt build is expensive and identical across every cargo target
61
83
  // hash — caching it once per profile lets `cargo clean` not nuke a
62
84
  // multi-minute compile.
63
- let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
64
- let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
65
85
  let dst = shim_dir
66
86
  .join("build")
67
87
  .join(format!("{}-{}", target_os, target_arch));
@@ -87,10 +107,121 @@ fn build_jolt() {
87
107
  println!("cargo:rustc-link-lib=static=bloom_jolt");
88
108
  println!("cargo:rustc-link-lib=static=Jolt");
89
109
 
90
- // C++ standard library — required because we're linking static archives
91
- // that pull in libc++ / libstdc++ symbols.
92
- let target = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
93
- match target.as_str() {
110
+ emit_cxx_runtime_link(&target_os);
111
+ }
112
+
113
+ /// Locate the `@bloomengine/jolt-prebuilt` package's lib dir for the
114
+ /// active build target. Returns `Some(dir)` only when both expected
115
+ /// archives are present — a half-shipped package falls through to the
116
+ /// cmake build instead of producing a confusing linker error.
117
+ ///
118
+ /// Resolution order:
119
+ /// 1. `BLOOM_JOLT_PREBUILT_DIR` env var — points at a directory
120
+ /// containing per-target subdirs (`<dir>/<os>-<arch>/lib*.a`).
121
+ /// Used for local spike testing and for CI matrix jobs that
122
+ /// stage archives outside `node_modules`.
123
+ /// 2. Walk up from `CARGO_MANIFEST_DIR` looking for any
124
+ /// `node_modules/@bloomengine/jolt-prebuilt/lib/<os>-<arch>/`.
125
+ /// Matches what npm's resolver does when `@bloomengine/engine`
126
+ /// depends on `@bloomengine/jolt-prebuilt` — the consumer's
127
+ /// install creates this directory next to the engine package.
128
+ #[cfg(feature = "jolt")]
129
+ fn find_prebuilt_dir(
130
+ manifest_dir: &std::path::Path,
131
+ target_os: &str,
132
+ target_arch: &str,
133
+ ) -> Option<std::path::PathBuf> {
134
+ // Map Rust's target_arch to the npm/Node convention used in this
135
+ // package's directory layout. Keep this in sync with the matrix in
136
+ // npm/jolt-prebuilt/README.md.
137
+ let arch_token = match target_arch {
138
+ "aarch64" => "arm64",
139
+ "x86_64" => "x64",
140
+ "arm" => "armv7",
141
+ other => other,
142
+ };
143
+ let target_token = format!("{}-{}", target_os, arch_token);
144
+
145
+ let candidates = std::iter::empty::<std::path::PathBuf>()
146
+ .chain(
147
+ std::env::var_os("BLOOM_JOLT_PREBUILT_DIR")
148
+ .map(|v| std::path::PathBuf::from(v).join(&target_token)),
149
+ )
150
+ .chain(walk_up_for_node_modules(manifest_dir).map(|nm| {
151
+ nm.join("@bloomengine")
152
+ .join("jolt-prebuilt")
153
+ .join("lib")
154
+ .join(&target_token)
155
+ }));
156
+
157
+ let (lib_prefix, lib_ext) = if target_os == "windows" {
158
+ ("", "lib")
159
+ } else {
160
+ ("lib", "a")
161
+ };
162
+
163
+ for dir in candidates {
164
+ let bloom_jolt = dir.join(format!("{}bloom_jolt.{}", lib_prefix, lib_ext));
165
+ let jolt = dir.join(format!("{}Jolt.{}", lib_prefix, lib_ext));
166
+ if bloom_jolt.exists() && jolt.exists() {
167
+ return Some(dir);
168
+ }
169
+ }
170
+ None
171
+ }
172
+
173
+ /// Iterator over every ancestor `node_modules/` reachable from
174
+ /// `start`. Yields each directory once, parent-first, so a closer
175
+ /// prebuilt installation wins over a higher one (matching node's
176
+ /// resolution algorithm).
177
+ #[cfg(feature = "jolt")]
178
+ fn walk_up_for_node_modules(
179
+ start: &std::path::Path,
180
+ ) -> impl Iterator<Item = std::path::PathBuf> + '_ {
181
+ let mut next: Option<&std::path::Path> = Some(start);
182
+ std::iter::from_fn(move || {
183
+ while let Some(cur) = next {
184
+ next = cur.parent();
185
+ let candidate = cur.join("node_modules");
186
+ if candidate.is_dir() {
187
+ return Some(candidate);
188
+ }
189
+ }
190
+ None
191
+ })
192
+ }
193
+
194
+ #[cfg(feature = "jolt")]
195
+ fn link_prebuilt(dir: &std::path::Path, target_os: &str) {
196
+ // Rerun if any archive in the prebuilt dir changes — covers the
197
+ // case where a new release of jolt-prebuilt drops in new bytes.
198
+ let (lib_prefix, lib_ext) = if target_os == "windows" {
199
+ ("", "lib")
200
+ } else {
201
+ ("lib", "a")
202
+ };
203
+ println!(
204
+ "cargo:rerun-if-changed={}",
205
+ dir.join(format!("{}bloom_jolt.{}", lib_prefix, lib_ext))
206
+ .display()
207
+ );
208
+ println!(
209
+ "cargo:rerun-if-changed={}",
210
+ dir.join(format!("{}Jolt.{}", lib_prefix, lib_ext)).display()
211
+ );
212
+
213
+ println!("cargo:rustc-link-search=native={}", dir.display());
214
+ println!("cargo:rustc-link-lib=static=bloom_jolt");
215
+ println!("cargo:rustc-link-lib=static=Jolt");
216
+ }
217
+
218
+ /// Emit the C++ standard library link directive that matches the
219
+ /// archives we just linked. Required because the static archives pull
220
+ /// in libc++ / libstdc++ symbols that the Rust toolchain doesn't
221
+ /// resolve on its own.
222
+ #[cfg(feature = "jolt")]
223
+ fn emit_cxx_runtime_link(target_os: &str) {
224
+ match target_os {
94
225
  "macos" | "ios" | "tvos" | "watchos" => {
95
226
  println!("cargo:rustc-link-lib=dylib=c++");
96
227
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bloomengine/engine",
3
- "version": "0.3.3",
3
+ "version": "0.4.0",
4
4
  "description": "Bloom Engine: native TypeScript game engine compiled by Perry",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",