| //===--- Platform.cpp - Implement platform-related helpers ----------------===// |
| // |
| // 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 "swift/Basic/Platform.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/ADT/StringSwitch.h" |
| #include "llvm/ADT/Triple.h" |
| #include "llvm/Support/VersionTuple.h" |
| |
| using namespace swift; |
| |
| bool swift::tripleIsiOSSimulator(const llvm::Triple &triple) { |
| return (triple.isiOS() && |
| !tripleIsMacCatalystEnvironment(triple) && |
| triple.isSimulatorEnvironment()); |
| } |
| |
| bool swift::tripleIsAppleTVSimulator(const llvm::Triple &triple) { |
| return (triple.isTvOS() && triple.isSimulatorEnvironment()); |
| } |
| |
| bool swift::tripleIsWatchSimulator(const llvm::Triple &triple) { |
| return (triple.isWatchOS() && triple.isSimulatorEnvironment()); |
| } |
| |
| bool swift::tripleIsMacCatalystEnvironment(const llvm::Triple &triple) { |
| return triple.isiOS() && !triple.isTvOS() && |
| triple.getEnvironment() == llvm::Triple::MacABI; |
| } |
| |
| bool swift::tripleInfersSimulatorEnvironment(const llvm::Triple &triple) { |
| switch (triple.getOS()) { |
| case llvm::Triple::IOS: |
| case llvm::Triple::TvOS: |
| case llvm::Triple::WatchOS: |
| return !triple.hasEnvironment() && |
| (triple.getArch() == llvm::Triple::x86 || |
| triple.getArch() == llvm::Triple::x86_64) && |
| !tripleIsMacCatalystEnvironment(triple); |
| |
| default: |
| return false; |
| } |
| } |
| |
| bool swift::triplesAreValidForZippering(const llvm::Triple &target, |
| const llvm::Triple &targetVariant) { |
| // The arch and vendor must match. |
| if (target.getArchName() != targetVariant.getArchName() || |
| target.getArch() != targetVariant.getArch() || |
| target.getSubArch() != targetVariant.getSubArch() || |
| target.getVendor() != targetVariant.getVendor()) { |
| return false; |
| } |
| |
| // Allow a macOS target and an iOS-macabi target variant |
| // This is typically the case when zippering a library originally |
| // developed for macOS. |
| if (target.isMacOSX() && tripleIsMacCatalystEnvironment(targetVariant)) { |
| return true; |
| } |
| |
| // Allow an iOS-macabi target and a macOS target variant. This would |
| // be the case when zippering a library originally developed for |
| // iOS. |
| if (targetVariant.isMacOSX() && tripleIsMacCatalystEnvironment(target)) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool swift::tripleRequiresRPathForSwiftInOS(const llvm::Triple &triple) { |
| if (triple.isMacOSX()) { |
| // macOS 10.14.4 contains a copy of Swift, but the linker will still use an |
| // rpath-based install name until 10.15. |
| return triple.isMacOSXVersionLT(10, 15); |
| } |
| |
| if (triple.isiOS()) { |
| return triple.isOSVersionLT(12, 2); |
| } |
| |
| if (triple.isWatchOS()) { |
| return triple.isOSVersionLT(5, 2); |
| } |
| |
| // Other platforms don't have Swift installed as part of the OS by default. |
| return false; |
| } |
| |
| DarwinPlatformKind swift::getDarwinPlatformKind(const llvm::Triple &triple) { |
| if (triple.isiOS()) { |
| if (triple.isTvOS()) { |
| if (tripleIsAppleTVSimulator(triple)) |
| return DarwinPlatformKind::TvOSSimulator; |
| return DarwinPlatformKind::TvOS; |
| } |
| |
| if (tripleIsiOSSimulator(triple)) |
| return DarwinPlatformKind::IPhoneOSSimulator; |
| return DarwinPlatformKind::IPhoneOS; |
| } |
| |
| if (triple.isWatchOS()) { |
| if (tripleIsWatchSimulator(triple)) |
| return DarwinPlatformKind::WatchOSSimulator; |
| return DarwinPlatformKind::WatchOS; |
| } |
| |
| if (triple.isMacOSX()) |
| return DarwinPlatformKind::MacOS; |
| |
| llvm_unreachable("Unsupported Darwin platform"); |
| } |
| |
| static StringRef getPlatformNameForDarwin(const DarwinPlatformKind platform) { |
| switch (platform) { |
| case DarwinPlatformKind::MacOS: |
| return "macosx"; |
| case DarwinPlatformKind::IPhoneOS: |
| return "iphoneos"; |
| case DarwinPlatformKind::IPhoneOSSimulator: |
| return "iphonesimulator"; |
| case DarwinPlatformKind::TvOS: |
| return "appletvos"; |
| case DarwinPlatformKind::TvOSSimulator: |
| return "appletvsimulator"; |
| case DarwinPlatformKind::WatchOS: |
| return "watchos"; |
| case DarwinPlatformKind::WatchOSSimulator: |
| return "watchsimulator"; |
| } |
| llvm_unreachable("Unsupported Darwin platform"); |
| } |
| |
| StringRef swift::getPlatformNameForTriple(const llvm::Triple &triple) { |
| switch (triple.getOS()) { |
| case llvm::Triple::UnknownOS: |
| llvm_unreachable("unknown OS"); |
| case llvm::Triple::Ananas: |
| case llvm::Triple::CloudABI: |
| case llvm::Triple::DragonFly: |
| case llvm::Triple::Emscripten: |
| case llvm::Triple::Fuchsia: |
| case llvm::Triple::KFreeBSD: |
| case llvm::Triple::Lv2: |
| case llvm::Triple::NetBSD: |
| case llvm::Triple::Solaris: |
| case llvm::Triple::Minix: |
| case llvm::Triple::RTEMS: |
| case llvm::Triple::NaCl: |
| case llvm::Triple::CNK: |
| case llvm::Triple::AIX: |
| case llvm::Triple::CUDA: |
| case llvm::Triple::NVCL: |
| case llvm::Triple::AMDHSA: |
| case llvm::Triple::ELFIAMCU: |
| case llvm::Triple::Mesa3D: |
| case llvm::Triple::Contiki: |
| case llvm::Triple::AMDPAL: |
| case llvm::Triple::HermitCore: |
| case llvm::Triple::Hurd: |
| return ""; |
| case llvm::Triple::Darwin: |
| case llvm::Triple::MacOSX: |
| case llvm::Triple::IOS: |
| case llvm::Triple::TvOS: |
| case llvm::Triple::WatchOS: |
| return getPlatformNameForDarwin(getDarwinPlatformKind(triple)); |
| case llvm::Triple::Linux: |
| return triple.isAndroid() ? "android" : "linux"; |
| case llvm::Triple::FreeBSD: |
| return "freebsd"; |
| case llvm::Triple::OpenBSD: |
| return "openbsd"; |
| case llvm::Triple::Win32: |
| switch (triple.getEnvironment()) { |
| case llvm::Triple::Cygnus: |
| return "cygwin"; |
| case llvm::Triple::GNU: |
| return "mingw"; |
| case llvm::Triple::MSVC: |
| case llvm::Triple::Itanium: |
| return "windows"; |
| default: |
| llvm_unreachable("unsupported Windows environment"); |
| } |
| case llvm::Triple::PS4: |
| return "ps4"; |
| case llvm::Triple::Haiku: |
| return "haiku"; |
| case llvm::Triple::WASI: |
| return "wasi"; |
| } |
| llvm_unreachable("unsupported OS"); |
| } |
| |
| StringRef swift::getMajorArchitectureName(const llvm::Triple &Triple) { |
| if (Triple.isOSLinux()) { |
| switch (Triple.getSubArch()) { |
| case llvm::Triple::SubArchType::ARMSubArch_v7: |
| return "armv7"; |
| case llvm::Triple::SubArchType::ARMSubArch_v6: |
| return "armv6"; |
| default: |
| break; |
| } |
| } |
| return Triple.getArchName(); |
| } |
| |
| // The code below is responsible for normalizing target triples into the form |
| // used to name target-specific swiftmodule, swiftinterface, and swiftdoc files. |
| // If two triples have incompatible ABIs or can be distinguished by Swift #if |
| // declarations, they should normalize to different values. |
| // |
| // This code is only really used on platforms with toolchains supporting fat |
| // binaries (a single binary containing multiple architectures). On these |
| // platforms, this code should strip unnecessary details from target triple |
| // components and map synonyms to canonical values. Even values which don't need |
| // any special canonicalization should be documented here as comments. |
| // |
| // (Fallback behavior does not belong here; it should be implemented in code |
| // that calls this function, most importantly in SerializedModuleLoaderBase.) |
| // |
| // If you're trying to refer to this code to understand how Swift behaves and |
| // you're unfamiliar with LLVM internals, here's a cheat sheet for reading it: |
| // |
| // * llvm::Triple is the type for a target name. It's a bit of a misnomer, |
| // because it can contain three or four values: arch-vendor-os[-environment]. |
| // |
| // * In .Cases and .Case, the last argument is the value the arguments before it |
| // map to. That is, `.Cases("bar", "baz", "foo")` will return "foo" if it sees |
| // "bar" or "baz". |
| // |
| // * llvm::Optional is similar to a Swift Optional: it either contains a value |
| // or represents the absence of one. `None` is equivalent to `nil`; leading |
| // `*` is equivalent to trailing `!`; conversion to `bool` is a not-`None` |
| // check. |
| |
| static StringRef |
| getArchForAppleTargetSpecificModuleTriple(const llvm::Triple &triple) { |
| auto tripleArchName = triple.getArchName(); |
| |
| return llvm::StringSwitch<StringRef>(tripleArchName) |
| .Cases("arm64", "aarch64", "arm64") |
| .Cases("x86_64", "amd64", "x86_64") |
| .Cases("i386", "i486", "i586", "i686", "i786", "i886", "i986", |
| "i386") |
| .Cases("unknown", "", "unknown") |
| // These values are also supported, but are handled by the default case below: |
| // .Case ("armv7s", "armv7s") |
| // .Case ("armv7k", "armv7k") |
| // .Case ("armv7", "armv7") |
| // .Case ("arm64e", "arm64e") |
| .Default(tripleArchName); |
| } |
| |
| static StringRef |
| getVendorForAppleTargetSpecificModuleTriple(const llvm::Triple &triple) { |
| // We unconditionally normalize to "apple" because it's relatively common for |
| // build systems to omit the vendor name or use an incorrect one like |
| // "unknown". Most parts of the compiler ignore the vendor, so you might not |
| // notice such a mistake. |
| // |
| // Please don't depend on this behavior--specify 'apple' if you're building |
| // for an Apple platform. |
| |
| assert(triple.isOSDarwin() && |
| "shouldn't normalize non-Darwin triple to 'apple'"); |
| |
| return "apple"; |
| } |
| |
| static StringRef |
| getOSForAppleTargetSpecificModuleTriple(const llvm::Triple &triple) { |
| auto tripleOSName = triple.getOSName(); |
| |
| // Truncate the OS name before the first digit. "Digit" here is ASCII '0'-'9'. |
| auto tripleOSNameNoVersion = tripleOSName.take_until(llvm::isDigit); |
| |
| return llvm::StringSwitch<StringRef>(tripleOSNameNoVersion) |
| .Cases("macos", "macosx", "darwin", "macos") |
| .Cases("unknown", "", "unknown") |
| // These values are also supported, but are handled by the default case below: |
| // .Case ("ios", "ios") |
| // .Case ("tvos", "tvos") |
| // .Case ("watchos", "watchos") |
| .Default(tripleOSNameNoVersion); |
| } |
| |
| static Optional<StringRef> |
| getEnvironmentForAppleTargetSpecificModuleTriple(const llvm::Triple &triple) { |
| auto tripleEnvironment = triple.getEnvironmentName(); |
| return llvm::StringSwitch<Optional<StringRef>>(tripleEnvironment) |
| .Cases("unknown", "", None) |
| // These values are also supported, but are handled by the default case below: |
| // .Case ("simulator", StringRef("simulator")) |
| // .Case ("macabi", StringRef("macabi")) |
| .Default(tripleEnvironment); |
| } |
| |
| llvm::Triple swift::getTargetSpecificModuleTriple(const llvm::Triple &triple) { |
| // isOSDarwin() returns true for all Darwin-style OSes, including macOS, iOS, |
| // etc. |
| if (triple.isOSDarwin()) { |
| StringRef newArch = getArchForAppleTargetSpecificModuleTriple(triple); |
| |
| StringRef newVendor = getVendorForAppleTargetSpecificModuleTriple(triple); |
| |
| StringRef newOS = getOSForAppleTargetSpecificModuleTriple(triple); |
| |
| Optional<StringRef> newEnvironment = |
| getEnvironmentForAppleTargetSpecificModuleTriple(triple); |
| |
| if (!newEnvironment) |
| // Generate an arch-vendor-os triple. |
| return llvm::Triple(newArch, newVendor, newOS); |
| |
| // Generate an arch-vendor-os-environment triple. |
| return llvm::Triple(newArch, newVendor, newOS, *newEnvironment); |
| } |
| |
| // android - drop the API level. That is not pertinent to the module; the API |
| // availability is handled by the clang importer. |
| if (triple.isAndroid()) { |
| StringRef environment = |
| llvm::Triple::getEnvironmentTypeName(triple.getEnvironment()); |
| |
| return llvm::Triple(triple.getArchName(), triple.getVendorName(), |
| triple.getOSName(), environment); |
| } |
| |
| // Other platforms get no normalization. |
| return triple; |
| } |
| |
| llvm::Triple swift::getUnversionedTriple(const llvm::Triple &triple) { |
| StringRef unversionedOSName = triple.getOSName().take_until(llvm::isDigit); |
| if (triple.getEnvironment()) { |
| StringRef environment = |
| llvm::Triple::getEnvironmentTypeName(triple.getEnvironment()); |
| |
| return llvm::Triple(triple.getArchName(), triple.getVendorName(), |
| unversionedOSName, environment); |
| } |
| |
| return llvm::Triple(triple.getArchName(), triple.getVendorName(), |
| unversionedOSName); |
| } |
| |
| Optional<llvm::VersionTuple> |
| swift::getSwiftRuntimeCompatibilityVersionForTarget( |
| const llvm::Triple &Triple) { |
| unsigned Major, Minor, Micro; |
| |
| if (Triple.getArchName() == "arm64e") |
| return llvm::VersionTuple(5, 3); |
| |
| if (Triple.isMacOSX()) { |
| Triple.getMacOSXVersion(Major, Minor, Micro); |
| if (Major == 10) { |
| if (Triple.isAArch64() && Minor <= 16) |
| return llvm::VersionTuple(5, 3); |
| |
| if (Minor <= 14) { |
| return llvm::VersionTuple(5, 0); |
| } else if (Minor <= 15) { |
| if (Micro <= 3) { |
| return llvm::VersionTuple(5, 1); |
| } else { |
| return llvm::VersionTuple(5, 2); |
| } |
| } |
| } else if (Major == 11) { |
| return llvm::VersionTuple(5, 3); |
| } |
| } else if (Triple.isiOS()) { // includes tvOS |
| Triple.getiOSVersion(Major, Minor, Micro); |
| |
| // arm64 simulators and macCatalyst are introduced in iOS 14.0/tvOS 14.0 |
| // with Swift 5.3 |
| if (Triple.isAArch64() && Major <= 14 && |
| (Triple.isSimulatorEnvironment() || Triple.isMacCatalystEnvironment())) |
| return llvm::VersionTuple(5, 3); |
| |
| if (Major <= 12) { |
| return llvm::VersionTuple(5, 0); |
| } else if (Major <= 13) { |
| if (Minor <= 3) { |
| return llvm::VersionTuple(5, 1); |
| } else { |
| return llvm::VersionTuple(5, 2); |
| } |
| } |
| } else if (Triple.isWatchOS()) { |
| Triple.getWatchOSVersion(Major, Minor, Micro); |
| if (Major <= 5) { |
| return llvm::VersionTuple(5, 0); |
| } else if (Major <= 6) { |
| if (Minor <= 1) { |
| return llvm::VersionTuple(5, 1); |
| } else { |
| return llvm::VersionTuple(5, 2); |
| } |
| } |
| } |
| |
| return None; |
| } |
| |
| |
| /// Remap the given version number via the version map, or produce \c None if |
| /// there is no mapping for this version. |
| static Optional<llvm::VersionTuple> remapVersion( |
| const llvm::StringMap<llvm::VersionTuple> &versionMap, |
| llvm::VersionTuple version) { |
| // The build number is never used in the lookup. |
| version = version.withoutBuild(); |
| |
| // Look for this specific version. |
| auto known = versionMap.find(version.getAsString()); |
| if (known != versionMap.end()) |
| return known->second; |
| |
| // If an extra ".0" was specified (in the subminor version), drop that |
| // and look again. |
| if (!version.getSubminor() || *version.getSubminor() != 0) |
| return None; |
| |
| version = llvm::VersionTuple(version.getMajor(), *version.getMinor()); |
| known = versionMap.find(version.getAsString()); |
| if (known != versionMap.end()) |
| return known->second; |
| |
| // If another extra ".0" wa specified (in the minor version), drop that |
| // and look again. |
| if (!version.getMinor() || *version.getMinor() != 0) |
| return None; |
| |
| version = llvm::VersionTuple(version.getMajor()); |
| known = versionMap.find(version.getAsString()); |
| if (known != versionMap.end()) |
| return known->second; |
| |
| return None; |
| } |
| |
| llvm::VersionTuple |
| swift::getTargetSDKVersion(clang::driver::DarwinSDKInfo &SDKInfo, |
| const llvm::Triple &triple) { |
| // Retrieve the SDK version. |
| auto SDKVersion = SDKInfo.getVersion(); |
| |
| // For the Mac Catalyst environment, we have a macOS SDK with a macOS |
| // SDK version. Map that to the corresponding iOS version number to pass |
| // down to the linker. |
| if (tripleIsMacCatalystEnvironment(triple)) { |
| return remapVersion( |
| SDKInfo.getVersionMap().MacOS2iOSMacMapping, SDKVersion) |
| .getValueOr(llvm::VersionTuple(0, 0, 0)); |
| } |
| |
| return SDKVersion; |
| } |
| |
| static std::string getPlistEntry(const llvm::Twine &Path, StringRef KeyName) { |
| auto BufOrErr = llvm::MemoryBuffer::getFile(Path); |
| if (!BufOrErr) { |
| // FIXME: diagnose properly |
| return {}; |
| } |
| |
| std::string Key = "<key>"; |
| Key += KeyName; |
| Key += "</key>"; |
| |
| StringRef Lines = BufOrErr.get()->getBuffer(); |
| while (!Lines.empty()) { |
| StringRef CurLine; |
| std::tie(CurLine, Lines) = Lines.split('\n'); |
| if (CurLine.find(Key) != StringRef::npos) { |
| std::tie(CurLine, Lines) = Lines.split('\n'); |
| unsigned Begin = CurLine.find("<string>") + strlen("<string>"); |
| unsigned End = CurLine.find("</string>"); |
| return CurLine.substr(Begin, End - Begin).str(); |
| } |
| } |
| |
| return {}; |
| } |
| |
| std::string swift::getSDKBuildVersionFromPlist(StringRef Path) { |
| return getPlistEntry(Path, "ProductBuildVersion"); |
| } |
| |
| std::string swift::getSDKBuildVersion(StringRef Path) { |
| return getSDKBuildVersionFromPlist((llvm::Twine(Path) + |
| "/System/Library/CoreServices/SystemVersion.plist").str()); |
| } |
| |
| std::string swift::getSDKName(StringRef Path) { |
| std::string Name = getPlistEntry(llvm::Twine(Path)+"/SDKSettings.plist", |
| "CanonicalName"); |
| if (Name.empty() && Path.endswith(".sdk")) { |
| Name = llvm::sys::path::filename(Path).drop_back(strlen(".sdk")).str(); |
| } |
| return Name; |
| } |