Index: include/clang/Basic/DiagnosticDriverKinds.td =================================================================== --- include/clang/Basic/DiagnosticDriverKinds.td +++ include/clang/Basic/DiagnosticDriverKinds.td @@ -405,4 +405,8 @@ def warn_drv_moutline_unsupported_opt : Warning< "The '%0' architecture does not support -moutline; flag ignored">, InGroup; + +def warn_drv_darwin_sdk_invalid_settings : Warning< + "SDK settings were ignored as 'SDKSettings.json' could not be parsed">, + InGroup>; } Index: include/clang/Basic/TargetInfo.h =================================================================== --- include/clang/Basic/TargetInfo.h +++ include/clang/Basic/TargetInfo.h @@ -1321,6 +1321,12 @@ return None; } + /// \returns The version of the SDK which was used during the compilation if + /// one was specified, or an empty version otherwise. + const llvm::VersionTuple &getSDKVersion() const { + return getTargetOpts().SDKVersion; + } + /// Check the target is valid after it is fully initialized. virtual bool validateTarget(DiagnosticsEngine &Diags) const { return true; Index: include/clang/Basic/TargetOptions.h =================================================================== --- include/clang/Basic/TargetOptions.h +++ include/clang/Basic/TargetOptions.h @@ -15,10 +15,11 @@ #ifndef LLVM_CLANG_BASIC_TARGETOPTIONS_H #define LLVM_CLANG_BASIC_TARGETOPTIONS_H -#include -#include #include "clang/Basic/OpenCLOptions.h" +#include "llvm/Support/VersionTuple.h" #include "llvm/Target/TargetOptions.h" +#include +#include namespace clang { @@ -73,6 +74,9 @@ // "default" for the case when the user has not explicitly specified a // code model. std::string CodeModel; + + /// The version of the SDK which was used during the compilation. + VersionTuple SDKVersion; }; } // end namespace clang Index: include/clang/Driver/CC1Options.td =================================================================== --- include/clang/Driver/CC1Options.td +++ include/clang/Driver/CC1Options.td @@ -27,6 +27,8 @@ HelpText<"Specify target triple (e.g. i686-apple-darwin9)">; def target_abi : Separate<["-"], "target-abi">, HelpText<"Target a particular ABI type">; +def target_sdk_version_EQ : Joined<["-"], "target-sdk-version=">, + HelpText<"The version of target SDK used for compilation">; } Index: include/clang/Driver/DarwinSDKInfo.h =================================================================== --- /dev/null +++ include/clang/Driver/DarwinSDKInfo.h @@ -0,0 +1,42 @@ +//===--- DarwinSDKInfo.h - SDK Information parser for darwin ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_DRIVER_DARWIN_SDK_INFO_H +#define LLVM_CLANG_DRIVER_DARWIN_SDK_INFO_H + +#include "clang/Basic/LLVM.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/VersionTuple.h" +#include "llvm/Support/VirtualFileSystem.h" + +namespace clang { +namespace driver { + +/// The information about the darwin SDK that was used during this compilation. +class DarwinSDKInfo { +public: + DarwinSDKInfo(llvm::VersionTuple Version) : Version(Version) {} + + const llvm::VersionTuple &getVersion() const { return Version; } + +private: + llvm::VersionTuple Version; +}; + +/// Parse the SDK information from the SDKSettings.json file. +/// +/// \returns an error if the SDKSettings.json file is invalid, None if the +/// SDK has no SDKSettings.json, or a valid \c DarwinSDKInfo otherwise. +Expected> parseDarwinSDKInfo(llvm::vfs::FileSystem &VFS, + StringRef SDKRootPath); + +} // end namespace driver +} // end namespace clang + +#endif // LLVM_CLANG_DRIVER_DARWIN_SDK_INFO_H Index: lib/CodeGen/ModuleBuilder.cpp =================================================================== --- lib/CodeGen/ModuleBuilder.cpp +++ lib/CodeGen/ModuleBuilder.cpp @@ -132,6 +132,9 @@ M->setTargetTriple(Ctx->getTargetInfo().getTriple().getTriple()); M->setDataLayout(Ctx->getTargetInfo().getDataLayout()); + const auto &SDKVersion = Ctx->getTargetInfo().getSDKVersion(); + if (!SDKVersion.empty()) + M->setSDKVersion(SDKVersion); Builder.reset(new CodeGen::CodeGenModule(Context, HeaderSearchOpts, PreprocessorOpts, CodeGenOpts, *M, Diags, CoverageInfo)); Index: lib/Driver/CMakeLists.txt =================================================================== --- lib/Driver/CMakeLists.txt +++ lib/Driver/CMakeLists.txt @@ -12,6 +12,7 @@ add_clang_library(clangDriver Action.cpp Compilation.cpp + DarwinSDKInfo.cpp Distro.cpp Driver.cpp DriverOptions.cpp Index: lib/Driver/DarwinSDKInfo.cpp =================================================================== --- /dev/null +++ lib/Driver/DarwinSDKInfo.cpp @@ -0,0 +1,44 @@ +//===--- DarwinSDKInfo.cpp - SDK Information parser for darwin - ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Driver/DarwinSDKInfo.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" + +using namespace clang::driver; +using namespace clang; + +Expected> +driver::parseDarwinSDKInfo(llvm::vfs::FileSystem &VFS, StringRef SDKRootPath) { + llvm::SmallString<256> Filepath = SDKRootPath; + llvm::sys::path::append(Filepath, "SDKSettings.json"); + llvm::ErrorOr> File = + VFS.getBufferForFile(Filepath); + if (!File) { + // If the file couldn't be read, assume it just doesn't exist. + return None; + } + Expected Result = + llvm::json::parse(File.get()->getBuffer()); + if (!Result) + return Result.takeError(); + + if (const auto *Obj = Result->getAsObject()) { + auto VersionString = Obj->getString("Version"); + if (VersionString) { + VersionTuple Version; + if (!Version.tryParse(*VersionString)) + return DarwinSDKInfo(Version); + } + } + return llvm::make_error("invalid SDKSettings.json", + llvm::inconvertibleErrorCode()); +} Index: lib/Driver/ToolChains/Darwin.h =================================================================== --- lib/Driver/ToolChains/Darwin.h +++ lib/Driver/ToolChains/Darwin.h @@ -11,9 +11,10 @@ #define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_DARWIN_H #include "Cuda.h" -#include "clang/Driver/XRayArgs.h" +#include "clang/Driver/DarwinSDKInfo.h" #include "clang/Driver/Tool.h" #include "clang/Driver/ToolChain.h" +#include "clang/Driver/XRayArgs.h" namespace clang { namespace driver { @@ -288,6 +289,9 @@ /// The OS version we are targeting. mutable VersionTuple TargetVersion; + /// The information about the darwin SDK that was used. + mutable Optional SDKInfo; + CudaInstallationDetector CudaInstallation; private: Index: lib/Driver/ToolChains/Darwin.cpp =================================================================== --- lib/Driver/ToolChains/Darwin.cpp +++ lib/Driver/ToolChains/Darwin.cpp @@ -1287,6 +1287,18 @@ return DarwinPlatform(InferredFromArch, getPlatformFromOS(OS), Value); } + /// Constructs an inferred SDKInfo value based on the version inferred from + /// the SDK path itself. Only works for values that were created by inferring + /// the platform from the SDKPath. + DarwinSDKInfo inferSDKInfo() { + assert(Kind == InferredFromSDK && "can infer SDK info only"); + llvm::VersionTuple Version; + bool IsValid = !Version.tryParse(OSVersion); + (void)IsValid; + assert(IsValid && "invalid SDK version"); + return DarwinSDKInfo(Version); + } + private: DarwinPlatform(SourceKind Kind, DarwinPlatformKind Platform, Arg *Argument) : Kind(Kind), Platform(Platform), Argument(Argument) {} @@ -1420,8 +1432,11 @@ } /// Tries to infer the deployment target from the SDK specified by -isysroot -/// (or SDKROOT). -Optional inferDeploymentTargetFromSDK(DerivedArgList &Args) { +/// (or SDKROOT). Uses the version specified in the SDKSettings.json file if +/// it's available. +Optional +inferDeploymentTargetFromSDK(DerivedArgList &Args, + const Optional &SDKInfo) { const Arg *A = Args.getLastArg(options::OPT_isysroot); if (!A) return None; @@ -1429,28 +1444,37 @@ 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, - /*IsSimulator=*/SDK.startswith("iPhoneSimulator")); - 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, - /*IsSimulator=*/SDK.startswith("WatchSimulator")); - else if (SDK.startswith("AppleTVOS") || SDK.startswith("AppleTVSimulator")) - return DarwinPlatform::createFromSDK( - Darwin::TvOS, Version, - /*IsSimulator=*/SDK.startswith("AppleTVSimulator")); - } + + std::string Version; + if (SDKInfo) { + // Get the version from the SDKSettings.json if it's available. + Version = SDKInfo->getVersion().getAsString(); + } else { + // 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) + Version = SDK.slice(StartVer, EndVer + 1); + } + if (Version.empty()) + return None; + + if (SDK.startswith("iPhoneOS") || SDK.startswith("iPhoneSimulator")) + return DarwinPlatform::createFromSDK( + Darwin::IPhoneOS, Version, + /*IsSimulator=*/SDK.startswith("iPhoneSimulator")); + 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, + /*IsSimulator=*/SDK.startswith("WatchSimulator")); + else if (SDK.startswith("AppleTVOS") || SDK.startswith("AppleTVSimulator")) + return DarwinPlatform::createFromSDK( + Darwin::TvOS, Version, + /*IsSimulator=*/SDK.startswith("AppleTVSimulator")); return None; } @@ -1525,6 +1549,22 @@ Args.getLastArg(options::OPT_target)); } +Optional parseSDKSettings(llvm::vfs::FileSystem &VFS, + const ArgList &Args, + const Driver &TheDriver) { + const Arg *A = Args.getLastArg(options::OPT_isysroot); + if (!A) + return None; + StringRef isysroot = A->getValue(); + auto SDKInfoOrErr = driver::parseDarwinSDKInfo(VFS, isysroot); + if (!SDKInfoOrErr) { + llvm::consumeError(SDKInfoOrErr.takeError()); + TheDriver.Diag(diag::warn_drv_darwin_sdk_invalid_settings); + return None; + } + return *SDKInfoOrErr; +} + } // namespace void Darwin::AddDeploymentTarget(DerivedArgList &Args) const { @@ -1549,6 +1589,10 @@ } } + // Read the SDKSettings.json file for more information, like the SDK version + // that we can pass down to the compiler. + SDKInfo = parseSDKSettings(getVFS(), Args, getDriver()); + // The OS and the version can be specified using the -target argument. Optional OSTarget = getDeploymentTargetFromTargetArg(Args, getTriple(), getDriver()); @@ -1594,16 +1638,22 @@ getDeploymentTargetFromEnvironmentVariables(getDriver(), getTriple()); if (OSTarget) { // Don't infer simulator from the arch when the SDK is also specified. - Optional SDKTarget = inferDeploymentTargetFromSDK(Args); + Optional SDKTarget = + inferDeploymentTargetFromSDK(Args, SDKInfo); if (SDKTarget) OSTarget->setEnvironment(SDKTarget->getEnvironment()); } } // 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); + // on -isysroot using SDKSettings.json if it exists. + if (!OSTarget) { + OSTarget = inferDeploymentTargetFromSDK(Args, SDKInfo); + /// If the target was successfully constructed from the SDK path, try to + /// infer the SDK info if the SDK doesn't have it. + if (OSTarget && !SDKInfo) + SDKInfo = OSTarget->inferSDKInfo(); + } // 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) @@ -2046,6 +2096,15 @@ options::OPT_fno_aligned_allocation) && isAlignedAllocationUnavailable()) CC1Args.push_back("-faligned-alloc-unavailable"); + + if (SDKInfo) { + /// Pass the SDK version to the compiler when the SDK information is + /// available. + std::string Arg; + llvm::raw_string_ostream OS(Arg); + OS << "-target-sdk-version=" << SDKInfo->getVersion(); + CC1Args.push_back(DriverArgs.MakeArgString(OS.str())); + } } DerivedArgList * Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -3192,6 +3192,14 @@ Opts.ForceEnableInt128 = Args.hasArg(OPT_fforce_enable_int128); Opts.NVPTXUseShortPointers = Args.hasFlag( options::OPT_fcuda_short_ptr, options::OPT_fno_cuda_short_ptr, false); + if (Arg *A = Args.getLastArg(options::OPT_target_sdk_version_EQ)) { + llvm::VersionTuple Version; + if (Version.tryParse(A->getValue())) + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(); + else + Opts.SDKVersion = Version; + } } bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, Index: test/CodeGen/darwin-sdk-version.c =================================================================== --- /dev/null +++ test/CodeGen/darwin-sdk-version.c @@ -0,0 +1,4 @@ +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.14 -target-sdk-version=10.14.1 -emit-llvm -o - %s | FileCheck %s + +// CHECK: !llvm.module.flags = !{!0 +// CHECK: !0 = !{i32 2, !"SDK Version", [3 x i32] [i32 10, i32 14, i32 1]} Index: test/Driver/Inputs/MacOSX10.14.sdk/SDKSettings.json =================================================================== --- /dev/null +++ test/Driver/Inputs/MacOSX10.14.sdk/SDKSettings.json @@ -0,0 +1 @@ +{"Version":"10.14"} Index: test/Driver/darwin-sdk-version.c =================================================================== --- /dev/null +++ test/Driver/darwin-sdk-version.c @@ -0,0 +1,36 @@ +// RUN: %clang -target x86_64-apple-macosx10.13 -isysroot %S/Inputs/MacOSX10.14.sdk -c -### %s 2>&1 \ +// RUN: | FileCheck %s +// RUN: env SDKROOT=%S/Inputs/MacOSX10.14.sdk %clang -target x86_64-apple-macosx10.13 -c -### %s 2>&1 \ +// RUN: | FileCheck %s +// +// RUN: rm -rf %t/SDKs/MacOSX10.10.sdk +// RUN: mkdir -p %t/SDKs/MacOSX10.10.sdk +// RUN: %clang -isysroot %t/SDKs/MacOSX10.10.sdk -c -### %s 2>&1 \ +// RUN: | FileCheck --check-prefix=INFER_SDK_VERSION %s +// RUN: cp %S/Inputs/MacOSX10.14.sdk/SDKSettings.json %t/SDKs/MacOSX10.10.sdk +// RUN: %clang -isysroot %t/SDKs/MacOSX10.10.sdk -c -### %s 2>&1 \ +// RUN: | FileCheck --check-prefix=INFER_DEPLOYMENT_TARGET_VERSION %s +// +// RUN: rm -rf %t/SDKs/MacOSX10.14.sdk +// RUN: mkdir -p %t/SDKs/MacOSX10.14.sdk +// RUN: %clang -target x86_64-apple-macosx10.13 -isysroot %t/SDKs/MacOSX10.14.sdk -c -### %s 2>&1 \ +// RUN: | FileCheck --check-prefix=NO_VERSION %s +// +// RUN: rm -rf %t/SDKs/MacOSX10.14.sdk +// RUN: mkdir -p %t/SDKs/MacOSX10.14.sdk +// RUN: echo '{broken json' > %t/SDKs/MacOSX10.14.sdk/SDKSettings.json +// RUN: %clang -target x86_64-apple-macosx10.13 -isysroot %t/SDKs/MacOSX10.14.sdk -c -### %s 2>&1 \ +// RUN: | FileCheck --check-prefixes=NO_VERSION,ERROR %s +// +// RUN: rm -rf %t/SDKs/MacOSX10.14.sdk +// RUN: mkdir -p %t/SDKs/MacOSX10.14.sdk +// RUN: echo '{"Version":1}' > %t/SDKs/MacOSX10.14.sdk/SDKSettings.json +// RUN: %clang -target x86_64-apple-macosx10.13 -isysroot %t/SDKs/MacOSX10.14.sdk -c -### %s 2>&1 \ +// RUN: | FileCheck --check-prefixes=NO_VERSION,ERROR %s + +// CHECK: -target-sdk-version=10.14 +// INFER_SDK_VERSION: "-triple" "x86_64-apple-macosx10.10.0" +// INFER_SDK_VERSION-SAME: -target-sdk-version=10.10 +// INFER_DEPLOYMENT_TARGET_VERSION: "-triple" "x86_64-apple-macosx10.14.0" +// NO_VERSION-NOT: target-sdk-version +// ERROR: warning: SDK settings were ignored as 'SDKSettings.json' could not be parsed