Index: lib/Driver/ToolChains/Darwin.h =================================================================== --- lib/Driver/ToolChains/Darwin.h +++ lib/Driver/ToolChains/Darwin.h @@ -268,14 +268,17 @@ enum DarwinPlatformKind { MacOS, IPhoneOS, - IPhoneOSSimulator, TvOS, - TvOSSimulator, WatchOS, - WatchOSSimulator + LastDarwinPlatform = WatchOS + }; + enum DarwinEnvironmentKind { + NativeEnvironment, + Simulator, }; mutable DarwinPlatformKind TargetPlatform; + mutable DarwinEnvironmentKind TargetEnvironment; /// The OS version we are targeting. mutable VersionTuple TargetVersion; @@ -317,32 +320,34 @@ // FIXME: Eliminate these ...Target functions and derive separate tool chains // for these targets and put version in constructor. - void setTarget(DarwinPlatformKind Platform, unsigned Major, unsigned Minor, - unsigned Micro) const { + void setTarget(DarwinPlatformKind Platform, DarwinEnvironmentKind Environment, + unsigned Major, unsigned Minor, unsigned Micro) const { // FIXME: For now, allow reinitialization as long as values don't // change. This will go away when we move away from argument translation. if (TargetInitialized && TargetPlatform == Platform && + TargetEnvironment == Environment && TargetVersion == VersionTuple(Major, Minor, Micro)) return; assert(!TargetInitialized && "Target already initialized!"); TargetInitialized = true; TargetPlatform = Platform; + TargetEnvironment = Environment; TargetVersion = VersionTuple(Major, Minor, Micro); - if (Platform == IPhoneOSSimulator || Platform == TvOSSimulator || - Platform == WatchOSSimulator) + if (Environment == Simulator) const_cast(this)->setTripleEnvironment(llvm::Triple::Simulator); } bool isTargetIPhoneOS() const { assert(TargetInitialized && "Target not initialized!"); - return TargetPlatform == IPhoneOS || TargetPlatform == TvOS; + return (TargetPlatform == IPhoneOS || TargetPlatform == TvOS) && + TargetEnvironment == NativeEnvironment; } bool isTargetIOSSimulator() const { assert(TargetInitialized && "Target not initialized!"); - return TargetPlatform == IPhoneOSSimulator || - TargetPlatform == TvOSSimulator; + return (TargetPlatform == IPhoneOS || TargetPlatform == TvOS) && + TargetEnvironment == Simulator; } bool isTargetIOSBased() const { @@ -352,32 +357,32 @@ bool isTargetTvOS() const { assert(TargetInitialized && "Target not initialized!"); - return TargetPlatform == TvOS; + return TargetPlatform == TvOS && TargetEnvironment == NativeEnvironment; } bool isTargetTvOSSimulator() const { assert(TargetInitialized && "Target not initialized!"); - return TargetPlatform == TvOSSimulator; + return TargetPlatform == TvOS && TargetEnvironment == Simulator; } bool isTargetTvOSBased() const { assert(TargetInitialized && "Target not initialized!"); - return TargetPlatform == TvOS || TargetPlatform == TvOSSimulator; + return TargetPlatform == TvOS; } bool isTargetWatchOS() const { assert(TargetInitialized && "Target not initialized!"); - return TargetPlatform == WatchOS; + return TargetPlatform == WatchOS && TargetEnvironment == NativeEnvironment; } bool isTargetWatchOSSimulator() const { assert(TargetInitialized && "Target not initialized!"); - return TargetPlatform == WatchOSSimulator; + return TargetPlatform == WatchOS && TargetEnvironment == Simulator; } bool isTargetWatchOSBased() const { assert(TargetInitialized && "Target not initialized!"); - return TargetPlatform == WatchOS || TargetPlatform == WatchOSSimulator; + return TargetPlatform == WatchOS; } bool isTargetMacOS() const { @@ -413,10 +418,11 @@ Action::OffloadKind DeviceOffloadKind) const override; StringRef getPlatformFamily() const; - static StringRef getSDKName(StringRef isysroot); StringRef getOSLibraryNameSuffix() const; public: + static StringRef getSDKName(StringRef isysroot); + /// } /// @name ToolChain Implementation /// { Index: lib/Driver/ToolChains/Darwin.cpp =================================================================== --- lib/Driver/ToolChains/Darwin.cpp +++ lib/Driver/ToolChains/Darwin.cpp @@ -941,13 +941,10 @@ case DarwinPlatformKind::MacOS: return "MacOSX"; case DarwinPlatformKind::IPhoneOS: - case DarwinPlatformKind::IPhoneOSSimulator: return "iPhone"; case DarwinPlatformKind::TvOS: - case DarwinPlatformKind::TvOSSimulator: return "AppleTV"; case DarwinPlatformKind::WatchOS: - case DarwinPlatformKind::WatchOSSimulator: return "Watch"; } llvm_unreachable("Unsupported platform"); @@ -971,17 +968,11 @@ case DarwinPlatformKind::MacOS: return "osx"; case DarwinPlatformKind::IPhoneOS: - return "ios"; - case DarwinPlatformKind::IPhoneOSSimulator: - return "iossim"; + return TargetEnvironment == NativeEnvironment ? "ios" : "iossim"; case DarwinPlatformKind::TvOS: - return "tvos"; - case DarwinPlatformKind::TvOSSimulator: - return "tvossim"; + return TargetEnvironment == NativeEnvironment ? "tvos" : "tvossim"; case DarwinPlatformKind::WatchOS: - return "watchos"; - case DarwinPlatformKind::WatchOSSimulator: - return "watchossim"; + return TargetEnvironment == NativeEnvironment ? "watchos" : "watchossim"; } llvm_unreachable("Unsupported platform"); } @@ -1172,28 +1163,131 @@ return MacOSSDKVersion; } -void Darwin::AddDeploymentTarget(DerivedArgList &Args) const { - const OptTable &Opts = getDriver().getOpts(); +namespace { - // Support allowing the SDKROOT environment variable used by xcrun and other - // Xcode tools to define the default sysroot, by making it the default for - // isysroot. - if (const Arg *A = Args.getLastArg(options::OPT_isysroot)) { - // Warn if the path does not exist. - if (!getVFS().exists(A->getValue())) - getDriver().Diag(clang::diag::warn_missing_sysroot) << A->getValue(); - } else { - if (char *env = ::getenv("SDKROOT")) { - // We only use this value as the default if it is an absolute path, - // exists, and it is not the root path. - if (llvm::sys::path::is_absolute(env) && getVFS().exists(env) && - StringRef(env) != "/") { - Args.append(Args.MakeSeparateArg( - nullptr, Opts.getOption(options::OPT_isysroot), env)); - } +/// The Darwin OS that was selected or inferred from arguments / environment. +struct DarwinPlatform { + enum SourceKind { + /// The OS was specified using the -target argument. + TargetArg, + /// The OS was specified using the -m-version-min argument. + OSVersionArg, + /// The OS was specified using the OS_DEPLOYMENT_TARGET environment. + DeploymentTargetEnv, + /// The OS was inferred from the SDK. + InferredFromSDK, + /// The OS was inferred from the -arch. + InferredFromArch + }; + + using DarwinPlatformKind = Darwin::DarwinPlatformKind; + + DarwinPlatformKind getPlatform() const { return Platform; } + + StringRef getOSVersion() const { + if (Kind == OSVersionArg) + return Argument->getValue(); + return OSVersion; + } + + /// Returns true if the target OS was explicitly specified. + bool isExplicitlySpecified() const { return Kind <= DeploymentTargetEnv; } + + /// Adds the -m-version-min argument to the compiler invocation. + void addOSVersionMinArgument(DerivedArgList &Args, const OptTable &Opts) { + if (Argument) + return; + assert(Kind != TargetArg && Kind != OSVersionArg && "Invalid kind"); + options::ID Opt; + switch (Platform) { + case DarwinPlatformKind::MacOS: + Opt = options::OPT_mmacosx_version_min_EQ; + break; + case DarwinPlatformKind::IPhoneOS: + Opt = options::OPT_miphoneos_version_min_EQ; + break; + case DarwinPlatformKind::TvOS: + Opt = options::OPT_mtvos_version_min_EQ; + break; + case DarwinPlatformKind::WatchOS: + Opt = options::OPT_mwatchos_version_min_EQ; + break; + } + Argument = Args.MakeJoinedArg(nullptr, Opts.getOption(Opt), OSVersion); + Args.append(Argument); + } + + /// Returns the OS version with the argument / environment variable that + /// specified it. + std::string getAsString(DerivedArgList &Args, const OptTable &Opts) { + switch (Kind) { + case TargetArg: + case OSVersionArg: + case InferredFromSDK: + case InferredFromArch: + assert(Argument && "OS version argument not yet inferred"); + return Argument->getAsString(Args); + case DeploymentTargetEnv: + return (llvm::Twine(EnvVarName) + "=" + OSVersion).str(); } } + static DarwinPlatform createOSVersionArg(DarwinPlatformKind Platform, + Arg *A) { + return DarwinPlatform(OSVersionArg, Platform, A); + } + static DarwinPlatform createDeploymentTargetEnv(DarwinPlatformKind Platform, + StringRef EnvVarName, + StringRef Value) { + DarwinPlatform Result(DeploymentTargetEnv, Platform, Value); + Result.EnvVarName = EnvVarName; + return Result; + } + static DarwinPlatform createFromSDK(DarwinPlatformKind Platform, + StringRef Value) { + return DarwinPlatform(InferredFromSDK, Platform, Value); + } + static DarwinPlatform createFromArch(llvm::Triple::OSType OS, + StringRef Value) { + DarwinPlatformKind Platform; + switch (OS) { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + Platform = DarwinPlatformKind::MacOS; + break; + case llvm::Triple::IOS: + Platform = DarwinPlatformKind::IPhoneOS; + break; + case llvm::Triple::TvOS: + Platform = DarwinPlatformKind::TvOS; + break; + case llvm::Triple::WatchOS: + Platform = DarwinPlatformKind::WatchOS; + break; + default: + llvm_unreachable("Unable to infer Darwin variant"); + } + return DarwinPlatform(InferredFromArch, Platform, Value); + } + +private: + DarwinPlatform(SourceKind Kind, DarwinPlatformKind Platform, Arg *Argument) + : Kind(Kind), Platform(Platform), Argument(Argument) {} + DarwinPlatform(SourceKind Kind, DarwinPlatformKind Platform, StringRef Value) + : Kind(Kind), Platform(Platform), OSVersion(Value), Argument(nullptr) {} + + SourceKind Kind; + DarwinPlatformKind Platform; + std::string OSVersion; + Arg *Argument; + StringRef EnvVarName; +}; + +/// Returns the deployment target that's specified using the -m-version-min +/// argument. +Optional +getDeploymentTargetFromOSVersionArg(DerivedArgList &Args, + const Driver &TheDriver) { Arg *OSXVersion = Args.getLastArg(options::OPT_mmacosx_version_min_EQ); Arg *iOSVersion = Args.getLastArg(options::OPT_miphoneos_version_min_EQ, options::OPT_mios_simulator_version_min_EQ); @@ -1203,224 +1297,249 @@ Arg *WatchOSVersion = Args.getLastArg(options::OPT_mwatchos_version_min_EQ, options::OPT_mwatchos_simulator_version_min_EQ); + if (OSXVersion) { + if (iOSVersion || TvOSVersion || WatchOSVersion) { + TheDriver.Diag(diag::err_drv_argument_not_allowed_with) + << OSXVersion->getAsString(Args) + << (iOSVersion ? iOSVersion + : TvOSVersion ? TvOSVersion : WatchOSVersion) + ->getAsString(Args); + } + return DarwinPlatform::createOSVersionArg(Darwin::MacOS, OSXVersion); + } else if (iOSVersion) { + if (TvOSVersion || WatchOSVersion) { + TheDriver.Diag(diag::err_drv_argument_not_allowed_with) + << iOSVersion->getAsString(Args) + << (TvOSVersion ? TvOSVersion : WatchOSVersion)->getAsString(Args); + } + return DarwinPlatform::createOSVersionArg(Darwin::IPhoneOS, iOSVersion); + } else if (TvOSVersion) { + if (WatchOSVersion) { + TheDriver.Diag(diag::err_drv_argument_not_allowed_with) + << TvOSVersion->getAsString(Args) + << WatchOSVersion->getAsString(Args); + } + return DarwinPlatform::createOSVersionArg(Darwin::TvOS, TvOSVersion); + } else if (WatchOSVersion) + return DarwinPlatform::createOSVersionArg(Darwin::WatchOS, WatchOSVersion); + return None; +} + +/// Returns the deployment target that's specified using the +/// OS_DEPLOYMENT_TARGET environment variable. +Optional +getDeploymentTargetFromEnvironmentVariables(const Driver &TheDriver, + const llvm::Triple &Triple) { + std::string Targets[Darwin::LastDarwinPlatform + 1]; + const char *EnvVars[] = { + "MACOSX_DEPLOYMENT_TARGET", + "IPHONEOS_DEPLOYMENT_TARGET", + "TVOS_DEPLOYMENT_TARGET", + "WATCHOS_DEPLOYMENT_TARGET", + }; + static_assert(llvm::array_lengthof(EnvVars) == Darwin::LastDarwinPlatform + 1, + "Missing platform"); + for (const auto &I : llvm::enumerate(llvm::makeArrayRef(EnvVars))) { + if (char *Env = ::getenv(I.value())) + Targets[I.index()] = Env; + } + + // Do not allow conflicts with the watchOS target. + if (!Targets[Darwin::WatchOS].empty() && + (!Targets[Darwin::IPhoneOS].empty() || !Targets[Darwin::TvOS].empty())) { + TheDriver.Diag(diag::err_drv_conflicting_deployment_targets) + << "WATCHOS_DEPLOYMENT_TARGET" + << (!Targets[Darwin::IPhoneOS].empty() ? "IPHONEOS_DEPLOYMENT_TARGET" + : "TVOS_DEPLOYMENT_TARGET"); + } - unsigned Major, Minor, Micro; - bool HadExtra; + // Do not allow conflicts with the tvOS target. + if (!Targets[Darwin::TvOS].empty() && !Targets[Darwin::IPhoneOS].empty()) { + TheDriver.Diag(diag::err_drv_conflicting_deployment_targets) + << "TVOS_DEPLOYMENT_TARGET" + << "IPHONEOS_DEPLOYMENT_TARGET"; + } - // The iOS deployment target that is explicitly specified via a command line - // option or an environment variable. - std::string ExplicitIOSDeploymentTargetStr; - - if (iOSVersion) - ExplicitIOSDeploymentTargetStr = iOSVersion->getAsString(Args); - - if (OSXVersion && (iOSVersion || TvOSVersion || WatchOSVersion)) { - getDriver().Diag(diag::err_drv_argument_not_allowed_with) - << OSXVersion->getAsString(Args) - << (iOSVersion ? iOSVersion : - TvOSVersion ? TvOSVersion : WatchOSVersion)->getAsString(Args); - iOSVersion = TvOSVersion = WatchOSVersion = nullptr; - } else if (iOSVersion && (TvOSVersion || WatchOSVersion)) { - getDriver().Diag(diag::err_drv_argument_not_allowed_with) - << iOSVersion->getAsString(Args) - << (TvOSVersion ? TvOSVersion : WatchOSVersion)->getAsString(Args); - TvOSVersion = WatchOSVersion = nullptr; - } else if (TvOSVersion && WatchOSVersion) { - getDriver().Diag(diag::err_drv_argument_not_allowed_with) - << TvOSVersion->getAsString(Args) - << WatchOSVersion->getAsString(Args); - WatchOSVersion = nullptr; - } else if (!OSXVersion && !iOSVersion && !TvOSVersion && !WatchOSVersion) { - // If no deployment target was specified on the command line, check for - // environment defines. - std::string OSXTarget; - std::string iOSTarget; - std::string TvOSTarget; - std::string WatchOSTarget; - - if (char *env = ::getenv("MACOSX_DEPLOYMENT_TARGET")) - OSXTarget = env; - if (char *env = ::getenv("IPHONEOS_DEPLOYMENT_TARGET")) - iOSTarget = env; - if (char *env = ::getenv("TVOS_DEPLOYMENT_TARGET")) - TvOSTarget = env; - if (char *env = ::getenv("WATCHOS_DEPLOYMENT_TARGET")) - WatchOSTarget = env; - - if (!iOSTarget.empty()) - ExplicitIOSDeploymentTargetStr = - std::string("IPHONEOS_DEPLOYMENT_TARGET=") + iOSTarget; - - // If there is no command-line argument to specify the Target version and - // no environment variable defined, see if we can set the default based - // on -isysroot. - if (OSXTarget.empty() && iOSTarget.empty() && WatchOSTarget.empty() && - TvOSTarget.empty() && Args.hasArg(options::OPT_isysroot)) { - if (const Arg *A = Args.getLastArg(options::OPT_isysroot)) { - StringRef isysroot = A->getValue(); - StringRef SDK = getSDKName(isysroot); - if (SDK.size() > 0) { - // Slice the version number out. - // Version number is between the first and the last number. - size_t StartVer = SDK.find_first_of("0123456789"); - size_t EndVer = SDK.find_last_of("0123456789"); - if (StartVer != StringRef::npos && EndVer > StartVer) { - StringRef Version = SDK.slice(StartVer, EndVer + 1); - if (SDK.startswith("iPhoneOS") || - SDK.startswith("iPhoneSimulator")) - iOSTarget = Version; - else if (SDK.startswith("MacOSX")) - OSXTarget = getSystemOrSDKMacOSVersion(Version); - else if (SDK.startswith("WatchOS") || - SDK.startswith("WatchSimulator")) - WatchOSTarget = Version; - else if (SDK.startswith("AppleTVOS") || - SDK.startswith("AppleTVSimulator")) - TvOSTarget = Version; - } - } - } - } + // Allow conflicts among OSX and iOS for historical reasons, but choose the + // default platform. + if (!Targets[Darwin::MacOS].empty() && + (!Targets[Darwin::IPhoneOS].empty() || + !Targets[Darwin::WatchOS].empty() || !Targets[Darwin::TvOS].empty())) { + if (Triple.getArch() == llvm::Triple::arm || + Triple.getArch() == llvm::Triple::aarch64 || + Triple.getArch() == llvm::Triple::thumb) + Targets[Darwin::MacOS] = ""; + else + Targets[Darwin::IPhoneOS] = Targets[Darwin::WatchOS] = + Targets[Darwin::TvOS] = ""; + } - // If no OS targets have been specified, try to guess platform from -target - // or arch name and compute the version from the triple. - if (OSXTarget.empty() && iOSTarget.empty() && TvOSTarget.empty() && - WatchOSTarget.empty()) { - llvm::Triple::OSType OSTy = llvm::Triple::UnknownOS; - - // Set the OSTy based on -target if -arch isn't present. - if (Args.hasArg(options::OPT_target) && !Args.hasArg(options::OPT_arch)) { - OSTy = getTriple().getOS(); - } else { - StringRef MachOArchName = getMachOArchName(Args); - if (MachOArchName == "armv7" || MachOArchName == "armv7s" || - MachOArchName == "arm64") - OSTy = llvm::Triple::IOS; - else if (MachOArchName == "armv7k") - OSTy = llvm::Triple::WatchOS; - else if (MachOArchName != "armv6m" && MachOArchName != "armv7m" && - MachOArchName != "armv7em") - OSTy = llvm::Triple::MacOSX; - } + for (const auto &Target : llvm::enumerate(llvm::makeArrayRef(Targets))) { + if (!Target.value().empty()) + return DarwinPlatform::createDeploymentTargetEnv( + (Darwin::DarwinPlatformKind)Target.index(), EnvVars[Target.index()], + Target.value()); + } + return None; +} + +/// Tries to infer the deployment target from the SDK specified by -isysroot +/// (or SDKROOT). +Optional inferDeploymentTargetFromSDK(DerivedArgList &Args) { + const Arg *A = Args.getLastArg(options::OPT_isysroot); + if (!A) + return None; + StringRef isysroot = A->getValue(); + StringRef SDK = Darwin::getSDKName(isysroot); + if (!SDK.size()) + return None; + // Slice the version number out. + // Version number is between the first and the last number. + size_t StartVer = SDK.find_first_of("0123456789"); + size_t EndVer = SDK.find_last_of("0123456789"); + if (StartVer != StringRef::npos && EndVer > StartVer) { + StringRef Version = SDK.slice(StartVer, EndVer + 1); + if (SDK.startswith("iPhoneOS") || SDK.startswith("iPhoneSimulator")) + return DarwinPlatform::createFromSDK(Darwin::IPhoneOS, Version); + else if (SDK.startswith("MacOSX")) + return DarwinPlatform::createFromSDK(Darwin::MacOS, + getSystemOrSDKMacOSVersion(Version)); + else if (SDK.startswith("WatchOS") || SDK.startswith("WatchSimulator")) + return DarwinPlatform::createFromSDK(Darwin::WatchOS, Version); + else if (SDK.startswith("AppleTVOS") || SDK.startswith("AppleTVSimulator")) + return DarwinPlatform::createFromSDK(Darwin::TvOS, Version); + } + return None; +} +std::string getOSVersion(llvm::Triple::OSType OS, const llvm::Triple &Triple, + const Driver &TheDriver) { + unsigned Major, Minor, Micro; + switch (OS) { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + if (!Triple.getMacOSXVersion(Major, Minor, Micro)) + TheDriver.Diag(diag::err_drv_invalid_darwin_version) + << Triple.getOSName(); + break; + case llvm::Triple::IOS: + Triple.getiOSVersion(Major, Minor, Micro); + break; + case llvm::Triple::TvOS: + Triple.getOSVersion(Major, Minor, Micro); + break; + case llvm::Triple::WatchOS: + Triple.getWatchOSVersion(Major, Minor, Micro); + break; + default: + llvm_unreachable("Unexpected OS type"); + break; + } - if (OSTy != llvm::Triple::UnknownOS) { - unsigned Major, Minor, Micro; - std::string *OSTarget; - - switch (OSTy) { - case llvm::Triple::Darwin: - case llvm::Triple::MacOSX: - if (!getTriple().getMacOSXVersion(Major, Minor, Micro)) - getDriver().Diag(diag::err_drv_invalid_darwin_version) - << getTriple().getOSName(); - OSTarget = &OSXTarget; - break; - case llvm::Triple::IOS: - getTriple().getiOSVersion(Major, Minor, Micro); - OSTarget = &iOSTarget; - break; - case llvm::Triple::TvOS: - getTriple().getOSVersion(Major, Minor, Micro); - OSTarget = &TvOSTarget; - break; - case llvm::Triple::WatchOS: - getTriple().getWatchOSVersion(Major, Minor, Micro); - OSTarget = &WatchOSTarget; - break; - default: - llvm_unreachable("Unexpected OS type"); - break; - } + std::string OSVersion; + llvm::raw_string_ostream(OSVersion) << Major << '.' << Minor << '.' << Micro; + return OSVersion; +} - llvm::raw_string_ostream(*OSTarget) << Major << '.' << Minor << '.' - << Micro; - } - } +/// Tries to infer the target OS from the -arch. +Optional +inferDeploymentTargetFromArch(DerivedArgList &Args, const Darwin &Toolchain, + const llvm::Triple &Triple, + const Driver &TheDriver) { + llvm::Triple::OSType OSTy = llvm::Triple::UnknownOS; + + // Set the OSTy based on -target if -arch isn't present. + if (Args.hasArg(options::OPT_target) && !Args.hasArg(options::OPT_arch)) { + OSTy = Triple.getOS(); + } else { + StringRef MachOArchName = Toolchain.getMachOArchName(Args); + if (MachOArchName == "armv7" || MachOArchName == "armv7s" || + MachOArchName == "arm64") + OSTy = llvm::Triple::IOS; + else if (MachOArchName == "armv7k") + OSTy = llvm::Triple::WatchOS; + else if (MachOArchName != "armv6m" && MachOArchName != "armv7m" && + MachOArchName != "armv7em") + OSTy = llvm::Triple::MacOSX; + } + + if (OSTy == llvm::Triple::UnknownOS) + return None; + return DarwinPlatform::createFromArch(OSTy, + getOSVersion(OSTy, Triple, TheDriver)); +} - // Do not allow conflicts with the watchOS target. - if (!WatchOSTarget.empty() && (!iOSTarget.empty() || !TvOSTarget.empty())) { - getDriver().Diag(diag::err_drv_conflicting_deployment_targets) - << "WATCHOS_DEPLOYMENT_TARGET" - << (!iOSTarget.empty() ? "IPHONEOS_DEPLOYMENT_TARGET" : - "TVOS_DEPLOYMENT_TARGET"); - } +} // namespace - // Do not allow conflicts with the tvOS target. - if (!TvOSTarget.empty() && !iOSTarget.empty()) { - getDriver().Diag(diag::err_drv_conflicting_deployment_targets) - << "TVOS_DEPLOYMENT_TARGET" - << "IPHONEOS_DEPLOYMENT_TARGET"; - } +void Darwin::AddDeploymentTarget(DerivedArgList &Args) const { + const OptTable &Opts = getDriver().getOpts(); - // Allow conflicts among OSX and iOS for historical reasons, but choose the - // default platform. - if (!OSXTarget.empty() && (!iOSTarget.empty() || - !WatchOSTarget.empty() || - !TvOSTarget.empty())) { - if (getTriple().getArch() == llvm::Triple::arm || - getTriple().getArch() == llvm::Triple::aarch64 || - getTriple().getArch() == llvm::Triple::thumb) - OSXTarget = ""; - else - iOSTarget = WatchOSTarget = TvOSTarget = ""; - } - - if (!OSXTarget.empty()) { - const Option O = Opts.getOption(options::OPT_mmacosx_version_min_EQ); - OSXVersion = Args.MakeJoinedArg(nullptr, O, OSXTarget); - Args.append(OSXVersion); - } else if (!iOSTarget.empty()) { - const Option O = Opts.getOption(options::OPT_miphoneos_version_min_EQ); - iOSVersion = Args.MakeJoinedArg(nullptr, O, iOSTarget); - Args.append(iOSVersion); - } else if (!TvOSTarget.empty()) { - const Option O = Opts.getOption(options::OPT_mtvos_version_min_EQ); - TvOSVersion = Args.MakeJoinedArg(nullptr, O, TvOSTarget); - Args.append(TvOSVersion); - } else if (!WatchOSTarget.empty()) { - const Option O = Opts.getOption(options::OPT_mwatchos_version_min_EQ); - WatchOSVersion = Args.MakeJoinedArg(nullptr, O, WatchOSTarget); - Args.append(WatchOSVersion); + // Support allowing the SDKROOT environment variable used by xcrun and other + // Xcode tools to define the default sysroot, by making it the default for + // isysroot. + if (const Arg *A = Args.getLastArg(options::OPT_isysroot)) { + // Warn if the path does not exist. + if (!getVFS().exists(A->getValue())) + getDriver().Diag(clang::diag::warn_missing_sysroot) << A->getValue(); + } else { + if (char *env = ::getenv("SDKROOT")) { + // We only use this value as the default if it is an absolute path, + // exists, and it is not the root path. + if (llvm::sys::path::is_absolute(env) && getVFS().exists(env) && + StringRef(env) != "/") { + Args.append(Args.MakeSeparateArg( + nullptr, Opts.getOption(options::OPT_isysroot), env)); + } } } - DarwinPlatformKind Platform; - if (OSXVersion) - Platform = MacOS; - else if (iOSVersion) - Platform = IPhoneOS; - else if (TvOSVersion) - Platform = TvOS; - else if (WatchOSVersion) - Platform = WatchOS; - else - llvm_unreachable("Unable to infer Darwin variant"); + // The OS target can be specified using the -mversion-min argument. + Optional OSTarget = + getDeploymentTargetFromOSVersionArg(Args, getDriver()); + // If no deployment target was specified on the command line, check for + // environment defines. + if (!OSTarget) + OSTarget = + getDeploymentTargetFromEnvironmentVariables(getDriver(), getTriple()); + // If there is no command-line argument to specify the Target version and + // no environment variable defined, see if we can set the default based + // on -isysroot. + if (!OSTarget) + OSTarget = inferDeploymentTargetFromSDK(Args); + // If no OS targets have been specified, try to guess platform from -target + // or arch name and compute the version from the triple. + if (!OSTarget) + OSTarget = + inferDeploymentTargetFromArch(Args, *this, getTriple(), getDriver()); + + assert(OSTarget && "Unable to infer Darwin variant"); + OSTarget->addOSVersionMinArgument(Args, Opts); + DarwinPlatformKind Platform = OSTarget->getPlatform(); + unsigned Major, Minor, Micro; + bool HadExtra; // Set the tool chain target information. if (Platform == MacOS) { - assert((!iOSVersion && !TvOSVersion && !WatchOSVersion) && - "Unknown target platform!"); - if (!Driver::GetReleaseVersion(OSXVersion->getValue(), Major, Minor, Micro, - HadExtra) || + if (!Driver::GetReleaseVersion(OSTarget->getOSVersion(), Major, Minor, + Micro, HadExtra) || HadExtra || Major != 10 || Minor >= 100 || Micro >= 100) getDriver().Diag(diag::err_drv_invalid_version_number) - << OSXVersion->getAsString(Args); + << OSTarget->getAsString(Args, Opts); } else if (Platform == IPhoneOS) { - assert(iOSVersion && "Unknown target platform!"); - if (!Driver::GetReleaseVersion(iOSVersion->getValue(), Major, Minor, Micro, - HadExtra) || + if (!Driver::GetReleaseVersion(OSTarget->getOSVersion(), Major, Minor, + Micro, HadExtra) || HadExtra || Major >= 100 || Minor >= 100 || Micro >= 100) getDriver().Diag(diag::err_drv_invalid_version_number) - << iOSVersion->getAsString(Args); + << OSTarget->getAsString(Args, Opts); + ; // For 32-bit targets, the deployment target for iOS has to be earlier than // iOS 11. if (getTriple().isArch32Bit() && Major >= 11) { // If the deployment target is explicitly specified, print a diagnostic. - if (!ExplicitIOSDeploymentTargetStr.empty()) { + if (OSTarget->isExplicitlySpecified()) { getDriver().Diag(diag::warn_invalid_ios_deployment_target) - << ExplicitIOSDeploymentTargetStr; - // Otherwise, set it to 10.99.99. + << OSTarget->getAsString(Args, Opts); + // Otherwise, set it to 10.99.99. } else { Major = 10; Minor = 99; @@ -1428,32 +1547,27 @@ } } } else if (Platform == TvOS) { - if (!Driver::GetReleaseVersion(TvOSVersion->getValue(), Major, Minor, - Micro, HadExtra) || HadExtra || - Major >= 100 || Minor >= 100 || Micro >= 100) + if (!Driver::GetReleaseVersion(OSTarget->getOSVersion(), Major, Minor, + Micro, HadExtra) || + HadExtra || Major >= 100 || Minor >= 100 || Micro >= 100) getDriver().Diag(diag::err_drv_invalid_version_number) - << TvOSVersion->getAsString(Args); + << OSTarget->getAsString(Args, Opts); } else if (Platform == WatchOS) { - if (!Driver::GetReleaseVersion(WatchOSVersion->getValue(), Major, Minor, - Micro, HadExtra) || HadExtra || - Major >= 10 || Minor >= 100 || Micro >= 100) + if (!Driver::GetReleaseVersion(OSTarget->getOSVersion(), Major, Minor, + Micro, HadExtra) || + HadExtra || Major >= 10 || Minor >= 100 || Micro >= 100) getDriver().Diag(diag::err_drv_invalid_version_number) - << WatchOSVersion->getAsString(Args); + << OSTarget->getAsString(Args, Opts); } else llvm_unreachable("unknown kind of Darwin platform"); + DarwinEnvironmentKind Environment = NativeEnvironment; // Recognize iOS targets with an x86 architecture as the iOS simulator. - if (iOSVersion && (getTriple().getArch() == llvm::Triple::x86 || - getTriple().getArch() == llvm::Triple::x86_64)) - Platform = IPhoneOSSimulator; - if (TvOSVersion && (getTriple().getArch() == llvm::Triple::x86 || - getTriple().getArch() == llvm::Triple::x86_64)) - Platform = TvOSSimulator; - if (WatchOSVersion && (getTriple().getArch() == llvm::Triple::x86 || - getTriple().getArch() == llvm::Triple::x86_64)) - Platform = WatchOSSimulator; + if (Platform != MacOS && (getTriple().getArch() == llvm::Triple::x86 || + getTriple().getArch() == llvm::Triple::x86_64)) + Environment = Simulator; - setTarget(Platform, Major, Minor, Micro); + setTarget(Platform, Environment, Major, Minor, Micro); if (const Arg *A = Args.getLastArg(options::OPT_isysroot)) { StringRef SDK = getSDKName(A->getValue()); @@ -1777,15 +1891,12 @@ OS = llvm::Triple::MacOSX; break; case IPhoneOS: - case IPhoneOSSimulator: OS = llvm::Triple::IOS; break; - case TvOS: - case TvOSSimulator: // Earlier than 11.0. + case TvOS: // Earlier than 11.0. OS = llvm::Triple::TvOS; break; - case WatchOS: - case WatchOSSimulator: // Earlier than 4.0. + case WatchOS: // Earlier than 4.0. OS = llvm::Triple::WatchOS; break; } Index: test/Driver/darwin-version.c =================================================================== --- test/Driver/darwin-version.c +++ test/Driver/darwin-version.c @@ -139,3 +139,8 @@ // RUN: %clang -target x86_64-apple-watchos4.0 -c %s -### 2>&1 | \ // RUN: FileCheck --check-prefix=CHECK-VERSION-WATCHOS-TARGET %s // CHECK-VERSION-WATCHOS-TARGET: "x86_64-apple-watchos4.0.0-simulator" + +// RUN: env MACOSX_DEPLOYMENT_TARGET=1000.1000 \ +// RUN: %clang -c %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK-VERSION-INVALID-ENV %s +// CHECK-VERSION-INVALID-ENV: invalid version number in 'MACOSX_DEPLOYMENT_TARGET=1000.1000'