blob: a65d0741b7aa3589fcb6346a52996ee97604894e [file] [log] [blame]
//===--- AppleHostVersionDetection.mm - Interface to NSProcessInfo --------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "AppleHostVersionDetection.h"
#define OBJC_OLD_DISPATCH_PROTOTYPES 0
#import <Foundation/NSProcessInfo.h>
#include <objc/message.h>
#include <objc/runtime.h>
#include <dlfcn.h>
using namespace swift;
#define DLSYM(LIBRARY, SYMBOL) \
reinterpret_cast<decltype(SYMBOL) *>(dlsym(LIBRARY, #SYMBOL))
clang::VersionTuple swift::inferAppleHostOSVersion() {
#if !TARGET_OS_OSX
// For now, only support this on macOS. It wouldn't take too much work to
// port it to other Apple platforms, but no one is using that right now.
return {};
#else
// Simulate [[NSProcessInfo processInfo] operatingSystemVersion].
// DYLD_PRINT_STATISTICS shows that the cost of linking Foundation when we
// don't need to is a non-trivial percentage of our pre-main startup time.
// Which, to be fair, is pretty small anyway, but even so.
// Use RTLD_GLOBAL here, even though we don't need it, because the JIT might
// end up importing Foundation later, and at that point it /does/ need to be
// global. (This is arguably a bug in macOS's implementation of dlopen.)
auto *foundation =
dlopen("/System/Library/Frameworks/Foundation.framework/Foundation",
RTLD_LAZY | RTLD_GLOBAL);
if (!foundation)
return {};
auto *cfVersionPtr = DLSYM(foundation, kCFCoreFoundationVersionNumber);
if (!cfVersionPtr || *cfVersionPtr < kCFCoreFoundationVersionNumber10_10)
return {};
auto objcGetClass = DLSYM(foundation, objc_getClass);
if (!objcGetClass)
return {};
Class nsProcessInfo = objcGetClass("NSProcessInfo");
if (!nsProcessInfo)
return {};
auto objcMsgSendProcessInfo =
reinterpret_cast<NSProcessInfo *(*)(Class, SEL)>(
DLSYM(foundation, objc_msgSend));
NSProcessInfo *sharedProcessInfo =
objcMsgSendProcessInfo(nsProcessInfo, @selector(processInfo));
if (!sharedProcessInfo)
return {};
auto objcMsgSendVersion =
reinterpret_cast<NSOperatingSystemVersion(*)(NSProcessInfo *, SEL)>(
DLSYM(foundation, objc_msgSend_stret));
NSOperatingSystemVersion version =
objcMsgSendVersion(sharedProcessInfo, @selector(operatingSystemVersion));
return clang::VersionTuple(static_cast<unsigned>(version.majorVersion),
static_cast<unsigned>(version.minorVersion),
static_cast<unsigned>(version.patchVersion));
#endif
}