mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
In immediate mode, detect the host OS version on Apple platforms.
This allows script mode to pick up the current version of macOS instead of defaulting to 10.9, making it unnecessary to write #available. A -target flag can still override this if you're trying to write a portable script. The logic is a little tortured to avoid having to actually link to Foundation.framework or libobjc. Finishes rdar://problem/29433205.
This commit is contained in:
31
lib/Frontend/AppleHostVersionDetection.h
Normal file
31
lib/Frontend/AppleHostVersionDetection.h
Normal file
@@ -0,0 +1,31 @@
|
||||
//===--- AppleHostVersionDetection.h - NSProcessInfo interface --*- c++ -*-===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2016 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SWIFT_FRONTEND_APPLEHOSTVERSIONDETECTION_H
|
||||
#define SWIFT_FRONTEND_APPLEHOSTVERSIONDETECTION_H
|
||||
|
||||
#import "clang/Basic/VersionTuple.h"
|
||||
|
||||
namespace swift {
|
||||
|
||||
/// Returns a string in a form suitable for llvm::Triple's OS component
|
||||
/// representing the current host OS.
|
||||
///
|
||||
/// Returns an empty version if the host OS version cannot be detected.
|
||||
///
|
||||
/// Note that this will load additional code into the process to detect the
|
||||
/// OS version properly.
|
||||
clang::VersionTuple inferAppleHostOSVersion();
|
||||
|
||||
} // end namespace swift
|
||||
|
||||
#endif
|
||||
81
lib/Frontend/AppleHostVersionDetection.mm
Normal file
81
lib/Frontend/AppleHostVersionDetection.mm
Normal file
@@ -0,0 +1,81 @@
|
||||
//===--- AppleHostVersionDetection.mm - Interface to NSProcessInfo --------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2016 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;
|
||||
|
||||
// Note that these conditions must come in this order. TARGET_OS_MAC is set on
|
||||
// nearly all Apple platforms; it's in contrast to things like TARGET_OS_WIN32.
|
||||
#if TARGET_OS_IPHONE
|
||||
# define REQUIRED_CF_VERSION kCFCoreFoundationVersionNumber_iOS_8_0
|
||||
#elif TARGET_OS_MAC
|
||||
# define REQUIRED_CF_VERSION kCFCoreFoundationVersionNumber10_10
|
||||
#else
|
||||
# error "Unknown Apple platform"
|
||||
#endif
|
||||
|
||||
#define DLSYM(LIBRARY, SYMBOL) \
|
||||
reinterpret_cast<decltype(SYMBOL) *>(dlsym(LIBRARY, #SYMBOL))
|
||||
|
||||
clang::VersionTuple swift::inferAppleHostOSVersion() {
|
||||
// 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 < REQUIRED_CF_VERSION)
|
||||
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));
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
if(APPLE)
|
||||
set(AppleHostVersionDetection AppleHostVersionDetection.mm)
|
||||
else()
|
||||
set(AppleHostVersionDetection)
|
||||
endif()
|
||||
|
||||
add_swift_library(swiftFrontend STATIC
|
||||
CompilerInvocation.cpp
|
||||
DiagnosticVerifier.cpp
|
||||
@@ -5,6 +11,7 @@ add_swift_library(swiftFrontend STATIC
|
||||
FrontendOptions.cpp
|
||||
PrintingDiagnosticConsumer.cpp
|
||||
SerializedDiagnosticConsumer.cpp
|
||||
${AppleHostVersionDetection}
|
||||
DEPENDS SwiftOptions
|
||||
LINK_LIBRARIES
|
||||
swiftSIL
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
|
||||
#include "swift/Frontend/Frontend.h"
|
||||
|
||||
#if __APPLE__
|
||||
# include "AppleHostVersionDetection.h"
|
||||
#endif
|
||||
|
||||
#include "swift/Strings.h"
|
||||
#include "swift/AST/DiagnosticsFrontend.h"
|
||||
#include "swift/Basic/Platform.h"
|
||||
@@ -904,6 +908,21 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
|
||||
Target = llvm::Triple(A->getValue());
|
||||
TargetArg = A->getValue();
|
||||
}
|
||||
#if __APPLE__
|
||||
else if (FrontendOpts.actionIsImmediate()) {
|
||||
clang::VersionTuple currentOSVersion = inferAppleHostOSVersion();
|
||||
if (currentOSVersion.getMajor() != 0) {
|
||||
llvm::Triple::OSType currentOS = Target.getOS();
|
||||
if (currentOS == llvm::Triple::Darwin)
|
||||
currentOS = llvm::Triple::MacOSX;
|
||||
|
||||
SmallString<16> newOSBuf;
|
||||
llvm::raw_svector_ostream newOS(newOSBuf);
|
||||
newOS << llvm::Triple::getOSTypeName(currentOS) << currentOSVersion;
|
||||
Target.setOSName(newOS.str());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Opts.EnableObjCInterop = Target.isOSDarwin();
|
||||
if (auto A = Args.getLastArg(OPT_enable_objc_interop,
|
||||
|
||||
6
test/Interpreter/Inputs/availability_host_os.h
Normal file
6
test/Interpreter/Inputs/availability_host_os.h
Normal file
@@ -0,0 +1,6 @@
|
||||
__attribute__((availability(macosx,introduced=10.9)))
|
||||
static inline int mavericks() { return 9; }
|
||||
__attribute__((availability(macosx,introduced=10.10)))
|
||||
static inline int yosemite() { return 10; }
|
||||
__attribute__((availability(macosx,introduced=10.99)))
|
||||
static inline int todosSantos() { return 99; }
|
||||
16
test/Interpreter/availability_host_os.swift
Normal file
16
test/Interpreter/availability_host_os.swift
Normal file
@@ -0,0 +1,16 @@
|
||||
// Note: This deliberately uses the script interpreter rather than build/run.
|
||||
// RUN: %swift_driver -import-objc-header %S/Inputs/availability_host_os.h -DFAIL -Xfrontend -verify %s
|
||||
// RUN: %swift_driver -import-objc-header %S/Inputs/availability_host_os.h %s | %FileCheck %s
|
||||
// RUN: not %swift -typecheck -import-objc-header %S/Inputs/availability_host_os.h %s 2>&1 | %FileCheck -check-prefix=CHECK-NOT-INFERRED %s
|
||||
|
||||
// REQUIRES: OS=macosx
|
||||
// REQUIRES: executable_test
|
||||
|
||||
print(mavericks()) // CHECK: {{^9$}}
|
||||
print(yosemite()) // CHECK-NEXT: {{^10$}}
|
||||
// CHECK-NOT-INFERRED: 'yosemite()' is only available on OS X 10.10 or newer
|
||||
|
||||
#if FAIL
|
||||
print(todosSantos()) // expected-error {{'todosSantos()' is only available on OS X 10.99 or newer}}
|
||||
// expected-note@-1 {{add 'if #available' version check}}
|
||||
#endif
|
||||
Reference in New Issue
Block a user