@@ -179,6 +179,55 @@ def parse_version_tuple(text: str) -> tuple[int, ...]:
179179 return tuple (digits )
180180
181181
182+ # When building with pixi or NixOS, the compiler is sandboxed inside its own
183+ # sysroot. System LLVM at /usr is outside that sysroot, so linking against it
184+ # pulls in libraries built for the host's libc/libstdc++ which may be
185+ # ABI-incompatible with the ones inside the sandbox. Detect this and refuse to
186+ # use any LLVM install that falls outside the compiler's sysroot.
187+ def compiler_sysroot () -> Path | None :
188+ """Return the ``--sysroot`` from clang++'s per-target config, if any."""
189+ compiler = shutil .which ("clang++" )
190+ if not compiler :
191+ return None
192+
193+ try :
194+ triple = subprocess .check_output ([compiler , "-dumpmachine" ], text = True ).strip ()
195+ except (subprocess .CalledProcessError , OSError ):
196+ return None
197+
198+ if not triple :
199+ return None
200+
201+ cfg = Path (compiler ).with_name (f"{ triple } .cfg" )
202+ if not cfg .is_file ():
203+ return None
204+
205+ try :
206+ for line in cfg .read_text (encoding = "utf-8" ).splitlines ():
207+ if not line .startswith ("--sysroot=" ):
208+ continue
209+ sysroot = Path (line .split ("=" , 1 )[1 ]).resolve ()
210+ if sysroot .exists ():
211+ return sysroot
212+ except OSError :
213+ return None
214+
215+ return None
216+
217+
218+ def llvm_install_matches_compiler (prefix : Path ) -> bool :
219+ """True when *prefix* is inside the compiler's sysroot (or no sysroot)."""
220+ sysroot = compiler_sysroot ()
221+ if sysroot is None :
222+ return True
223+
224+ try :
225+ prefix .resolve ().relative_to (sysroot )
226+ return True
227+ except ValueError :
228+ return False
229+
230+
182231def system_llvm_ok (required_version : str , build_type : str ) -> Path | None :
183232 if build_type .lower ().startswith ("debug" ):
184233 return None
@@ -194,7 +243,12 @@ def system_llvm_ok(required_version: str, build_type: str) -> Path | None:
194243 found = parse_version_tuple (version )
195244 if not found or found < required :
196245 return None
197- return Path (prefix )
246+
247+ prefix_path = Path (prefix )
248+ if not llvm_install_matches_compiler (prefix_path ):
249+ return None
250+
251+ return prefix_path
198252
199253
200254def github_api (url : str , token : str | None ) -> dict :
@@ -287,13 +341,21 @@ def main() -> None:
287341 if args .install_path :
288342 candidate = Path (args .install_path )
289343 if candidate .exists ():
290- log (f"Using provided LLVM install at { candidate } " )
344+ if llvm_install_matches_compiler (candidate ):
345+ log (f"Using provided LLVM install at { candidate } " )
346+ install_path = candidate
347+ else :
348+ log (
349+ f"Provided LLVM install at { candidate } is outside the active compiler sysroot; "
350+ "will install a bundled LLVM instead"
351+ )
352+ needs_install = True
291353 else :
292354 log (
293355 f"Provided LLVM install path does not exist; will install to { candidate } "
294356 )
295357 needs_install = True
296- install_path = candidate
358+ install_path = candidate
297359 else :
298360 detected = system_llvm_ok (args .version , build_type )
299361 if detected :
0 commit comments