macOS: gyp omits -isysroot when binding.gyp doesn't set SDKROOT, causing clang to resolve SDK via a path that ignores DEVELOPER_DIR/xcode-select
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 10.7k
- Forks
- 1.9k
- Avg merge
- 1d 4h
- Merged PRs (30d)
- 5
Description
Environment
- node-gyp version: 9.4.1
- Node.js version: v20.18.1
- OS: macOS 26 (Tahoe) 26.5.1
- Xcode: 16.2.0 (Apple clang 16.0.0),
xcode-select -p->/Applications/Xcode-16.2.0.app/Contents/Developer - Command Line Tools: 26.6 (installed via Software Update, independent of Xcode.app)
Bug description
On macOS, node-gyp rebuild invokes clang++ without either -isysroot or --sdk. This makes the clang driver fall back to its own internal call to xcrun --show-sdk-path (with no --sdk argument). Verified via xcrun -v that this specific call variant resolves via a cache key hard-coded to /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -- independent of xcode-select/DEVELOPER_DIR, and independent of whichever Xcode toolchain actually supplies the invoked clang++ binary. (By contrast, xcrun --sdk macosx --show-sdk-path -- i.e. with an explicit SDK name -- does respect DEVELOPER_DIR/xcode-select.)
On systems where the OS has been updated (bumping the standalone CommandLineTools SDK) but the selected Xcode.app has not, this pairs a newer SDK's libc++ headers (which reference builtins like __builtin_ctzg/__builtin_clzg, requiring Clang >= 19) with an older embedded clang (16.0.0 from Xcode 16.2), producing:
error: use of undeclared identifier '__builtin_ctzg'
Root cause in gyp's own code
gyp/pylib/gyp/xcode_emulation.py already contains logic that avoids this, by explicitly naming the SDK when querying xcrun, and by passing -isysroot:
# GetSDKVersionInfoItem
return GetStdoutQuiet(["xcrun", "--sdk", sdk, infoitem]) # explicit --sdk, respects DEVELOPER_DIR
# GetCflags
if "SDKROOT" in self._Settings() and sdk_root:
cflags.append("-isysroot %s" % sdk_root) # only appended if SDKROOT is explicitly set in xcode_settings
(Line numbers 525 / 581-582 in the 9.4.1 tag I actually hit this on; confirmed the exact same structure -- same gating -- is still present at line 522 / 578-579 in the current latest release, v13.0.1, published 2026-07-02. Also checked that release's CHANGELOG for any macOS SDK/clang-related fix; found none.)
This mirrors what CMake does (Modules/Platform/Darwin-Initialize.cmake: xcrun --sdk macosx --show-sdk-path), which correctly resolves the SDK paired with the active developer directory, and does not hit this issue on the same machine/OS update.
However, -isysroot is only emitted if the target's binding.gyp (or a shared .gypi) sets SDKROOT under xcode_settings. Neither node-gyp's own addon.gypi, node's cached common.gypi, nor common third-party native modules (checked diskusage, better-sqlite3, node-pty) set SDKROOT. As a result, no -isysroot is passed for ordinary builds, and clang falls through to the SDK-resolution path described above.
Steps to reproduce
- On a Mac where the standalone CommandLineTools SDK has been updated (via Software Update) to a version requiring Clang >= 19 builtins, while
xcode-selectstill points at an older Xcode with an older embedded clang. npm install/yarn installany package with a native addon (e.g.diskusage,better-sqlite3,node-pty).node-gyp rebuildfails with__builtin_ctzg/__builtin_clzgundeclared identifier errors, even thoughxcodebuild/CMake-based builds on the same machine succeed.
Expected behavior
The SDK-resolution path gyp uses by default (when SDKROOT is not explicitly configured) should pair the SDK with the actual compiler in use -- e.g. by always querying xcrun --sdk macosx --show-sdk-path instead of the no---sdk variant -- rather than relying on a system-wide default that can silently drift out of sync with the selected Xcode/CLT compiler.
Workaround (confirmed working)
Pin toolchain and SDK together, either direction:
DEVELOPER_DIR=/Library/Developer/CommandLineTools npm install # newer clang + newer SDK, paired
SDKROOT=/Applications/Xcode-16.2.0.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.2.sdk npm install # older clang + older SDK, paired
Related
- Same root cause, labeled
bugand fixed upstream: terasakisatoshi/CxxFork.jl#24 -- "Avoid blindly preferringMacOSX.sdk/ latest SDK symlinks when their libc++ headers are incompatible with the embedded Clang path." - Broader context on the OS/Xcode SDK version mismatch during the macOS 26 transition: Homebrew/brew#21216, winfunc/opcode#233
- Checked node-gyp's own issue tracker first (open+closed+PRs) for
builtin_ctzg,isysroot,SDKROOT,DEVELOPER_DIR,CommandLineTools,Tahoe/"macOS 26" -- closest existing issues (#2992, #3064, #3137) are about detecting the Xcode/CLT version, not this SDK/compiler pairing mismatch, so this appears to be a distinct report.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in gyp/pylib/gyp/xcode_emulation.py, especially GetSDKVersionInfoItem and GetCflags, then reproduce the failure with node-gyp rebuild on the mismatched macOS Xcode and Command Line Tools setup described. Done means ordinary native-addon builds resolve an SDK paired with the active compiler and no longer produce the reported builtin errors.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- nodejs, python
- Domain
- build-system, devtools
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100