Skip to content

Commit 9410b3c

Browse files
committed
fix(build): reject system LLVM outside compiler sysroot
When building with pixi or NixOS, clang++ has a dedicated sysroot. System LLVM at /usr is outside it, causing ABI mismatches with the sandboxed libc/libstdc++. Detect this and fall back to bundled LLVM. Co-Authored-By: perdixky <perdixky@users.noreply.github.com>
1 parent d42d9d5 commit 9410b3c

File tree

2 files changed

+69
-5
lines changed

2 files changed

+69
-5
lines changed

cmake/llvm.cmake

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,10 @@ function(setup_llvm LLVM_VERSION)
5050
# set llvm include and lib path
5151
add_library(llvm-libs INTERFACE IMPORTED)
5252

53-
# add to include directories
54-
target_include_directories(llvm-libs INTERFACE "${LLVM_INSTALL_PATH}/include")
53+
target_include_directories(llvm-libs INTERFACE
54+
"${LLVM_INSTALL_PATH}/include"
55+
"${CMAKE_CURRENT_BINARY_DIR}/include"
56+
)
5557

5658
if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT WIN32)
5759
target_link_directories(llvm-libs INTERFACE "${LLVM_INSTALL_PATH}/lib")

scripts/setup-llvm.py

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
182231
def 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

200254
def 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

Comments
 (0)