[mac] minos version used by clang is dependent on host macOS version

Hi,

I’ve been comparing clang’s output determinism on different host OSs.

When using the macOS 12.3 SDK on macOS 12.6 like so:

path/to/bin/clang -arch x86_64 -isysroot path/to/MacOSX12.3.sdk -c empty.cpp -o empty.o

then the resulting object file has minos set to 12.0 and sdk to 12.3:

% otool -l empty.o | rg '12\.'
    minos 12.0
      sdk 12.3

If I do the exact same thing on macOS 13, I instead get 12.3 for both minos and sdk.

The 12.3 comes from the SDK’s SDKSettings.json file.

The 12.0 is because ⚙ D34175 [driver][macOS] Pick the system version for the deployment target if the SDK is newer than the system added code to compare the version number in SDKSettings.json to llvm::sys::getProcessTriple() and use the smaller of the two. The patch description doesn’t say why it does this, but I guess it’s for people working on clang, so that if they compile a file locally on an older macOS version than the SDK version, they don’t end up with a binary they can run.

I’m on 12.6, but Triple::getMacOSXVersion() currently drops the minor version, which is why the 12.3 becomes 12.0.

To me, getting different output depending on host OS is a bit surprising. I’m wondering if we could improve the situation here.

Options:

  1. Make Triple::getMacOSXVersion() not ignore the minor version. Then behavior on macOS 12.6 and macOS 13 would at least be identical (but behavior on macOS 12.1 would still be different – but that arguably makes some sense).

  2. If ⚙ D34175 [driver][macOS] Pick the system version for the deployment target if the SDK is newer than the system is indeed just for compiler hackers (and not for end users), getting the maximum macOS version at cmake time instead of at runtime would solve the problem that patch tries to solve, while also having the effect of giving the same clang binary on different macOS versions the same behavior.

  3. Revert ⚙ D34175 [driver][macOS] Pick the system version for the deployment target if the SDK is newer than the system altogether – passing -mmacos-version-min= in the (presumably?) rare case of using a newer SDK on an older macOS version isn’t that terrible maybe, and it makes clang’s behavior les surprising. (But since I’m not sure which problem that patch is meant to solve exactly, I’m not sure this is a good idea.)

  4. Do nothing.

Thoughts?

@dexonsmith @akyrtzi @amara (and @arphaman but they don’t seem to be on discourse maybe).

CC @jansvoboda11

1 Like

Triple::getMacOSXVersion() ignores the minor version because the Darwin kernel versions don’t have a direct correspondence to the specific macOS version. We could lookup the actual macOS version but that’s expensive and requires us to link CoreFoundation to parse a system Plist which we wouldn’t be able to do various reasons.
We can tweak the current behavior to be smarter about the minor version discrepancy. For instance, if the major version number that we get from getProcessTriple and the SDK version match, we can do a runtime check using __isPlatformVersionAtLeast to see if the OS version we’re running on is >= SDK version. Then we know we could safely take the SDK version for our deployment target.