//===--- 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 #include #include #include using namespace swift; #define DLSYM(LIBRARY, SYMBOL) \ reinterpret_cast(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( DLSYM(foundation, objc_msgSend)); NSProcessInfo *sharedProcessInfo = objcMsgSendProcessInfo(nsProcessInfo, @selector(processInfo)); if (!sharedProcessInfo) return {}; auto objcMsgSendVersion = reinterpret_cast( DLSYM(foundation, objc_msgSend_stret)); NSOperatingSystemVersion version = objcMsgSendVersion(sharedProcessInfo, @selector(operatingSystemVersion)); return clang::VersionTuple(static_cast(version.majorVersion), static_cast(version.minorVersion), static_cast(version.patchVersion)); #endif }