blob: ad1db82c717cf7dec4dc979837f5ae7e9fd0fb6d [file] [log] [blame]
//===--- 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;
}