diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -3824,6 +3824,20 @@ Operating System Features and Limitations ----------------------------------------- +Apple +^^^^^ + +On Apple platforms, the standard headers and libraries are not provided by +the base system and are instead part of the Xcode SDK application. The location +of the SDK is determined any of the following ways: + +- If passed to Clang, the ``-isysroot`` option specifies the path to the SDK. + +- If the sysroot isn't provided, the ``SDKROOT`` environment variable is checked. + This variable is set by various Xcode tools. + +- Otherwise, Clang uses Xcode's ``xcrun`` tool to find the SDK. + Windows ^^^^^^^ diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4091,6 +4091,8 @@ def no_integrated_cpp : Flag<["-", "--"], "no-integrated-cpp">, Flags<[NoXarchOption]>; def no_pedantic : Flag<["-", "--"], "no-pedantic">, Group; def no__dead__strip__inits__and__terms : Flag<["-"], "no_dead_strip_inits_and_terms">; +def no_detect_xcode : Flag<["--"], "no-detect-xcode">, + Flags<[NoXarchOption]>, HelpText<"Disable detection of the Xcode SDK">; def nobuiltininc : Flag<["-"], "nobuiltininc">, Flags<[CC1Option, CoreOption]>, HelpText<"Disable builtin #include directories">, MarshallingInfoNegativeFlag>; diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp --- a/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -18,15 +18,22 @@ #include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/Options.h" #include "clang/Driver/SanitizerArgs.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Option/ArgList.h" #include "llvm/ProfileData/InstrProf.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileUtilities.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/TargetParser.h" #include "llvm/Support/Threading.h" #include "llvm/Support/VirtualFileSystem.h" #include // ::getenv +#include // std::unique_ptr using namespace clang::driver; using namespace clang::driver::tools; @@ -2078,21 +2085,90 @@ void Darwin::AddDeploymentTarget(DerivedArgList &Args) const { const OptTable &Opts = getDriver().getOpts(); - // 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. + // On Apple platforms, standard headers and libraries are not provided with + // the base system (e.g. in /usr/{include,lib}). Instead, they are provided + // in various SDKs for the different Apple platforms. Clang needs to know + // where that SDK lives, and there are a couple ways this can be achieved: + // + // (1) If `-isysroot ` is passed explicitly, use that. 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)); + } + + // (2) If the SDKROOT environment variable is defined and points to a valid + // path, use that. $SDKROOT is set by `xcrun` and other Xcode tools, so + // running `xcrun clang` will work by going through this path. + 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)); + } + } + + // (3) Otherwise, we try to guess the path of the default SDK by running + // `xcrun --show-sdk-path`. This won't always be correct, but if the + // user wants to use the non-default SDK, they should specify it + // explicitly with methods (1) or (2) above. + else if (!Args.hasArg(options::OPT_no_detect_xcode)) { + llvm::SmallString<64> OutputFile; + llvm::sys::fs::createTemporaryFile("print-sdk-path", "" /* No Suffix */, + OutputFile); + llvm::FileRemover OutputRemover(OutputFile.c_str()); + std::optional Redirects[] = {{""}, OutputFile.str(), {""}}; + + Optional SDKName = std::nullopt; + switch (getTriple().getOS()) { + case llvm::Triple::OSType::WatchOS: + if (getTriple().isSimulatorEnvironment()) + SDKName = "watchsimulator"; + else + SDKName = "watchos"; + break; + case llvm::Triple::OSType::TvOS: + if (getTriple().isSimulatorEnvironment()) + SDKName = "appletvsimulator"; + else + SDKName = "appletvos"; + break; + case llvm::Triple::OSType::IOS: + if (getTriple().isSimulatorEnvironment()) + SDKName = "iphonesimulator"; + else + SDKName = "iphoneos"; + break; + case llvm::Triple::OSType::MacOSX: + SDKName = "macosx"; + break; + case llvm::Triple::OSType::DriverKit: + SDKName = "driverkit"; + break; + case llvm::Triple::OSType::Darwin: + break; + default: + llvm_unreachable("unknown kind of Darwin platform"); + } + + if (SDKName) { + int Result = llvm::sys::ExecuteAndWait( + "/usr/bin/xcrun", + {"/usr/bin/xcrun", "--sdk", *SDKName, "--show-sdk-path"}, + /* Inherit environment from parent process */ std::nullopt, Redirects, + /* SecondsToWait */ 0, /*MemoryLimit*/ 0); + if (Result == 0) { + llvm::ErrorOr> OutputBuf = + llvm::MemoryBuffer::getFile(OutputFile.c_str(), /* IsText */ true); + if (OutputBuf) { + llvm::StringRef SdkPath = (*OutputBuf)->getBuffer().trim(); + if (getVFS().exists(SdkPath)) { + Args.append(Args.MakeSeparateArg( + nullptr, Opts.getOption(options::OPT_isysroot), SdkPath)); + } + } } } } diff --git a/clang/test/Driver/darwin-ld-platform-version-macos.c b/clang/test/Driver/darwin-ld-platform-version-macos.c --- a/clang/test/Driver/darwin-ld-platform-version-macos.c +++ b/clang/test/Driver/darwin-ld-platform-version-macos.c @@ -41,7 +41,7 @@ // ARM64_NEW_1: "-platform_version" "macos" "11.1.0" "10.14" // ARM64_OLD: "-macosx_version_min" "11.0.0" -// RUN: %clang -target x86_64-apple-macos10.13 -mlinker-version=520 \ +// RUN: %clang --no-detect-xcode -target x86_64-apple-macos10.13 -mlinker-version=520 \ // RUN: -### %t.o 2>&1 \ // RUN: | FileCheck --check-prefix=NOSDK %s // RUN: %clang -target x86_64-apple-darwin17 -mlinker-version=520 \ diff --git a/clang/test/Driver/darwin-sdk-detect.c b/clang/test/Driver/darwin-sdk-detect.c new file mode 100644 --- /dev/null +++ b/clang/test/Driver/darwin-sdk-detect.c @@ -0,0 +1,20 @@ +// REQUIRES: system-darwin + +// Check that we default to running `xcrun --show-sdk-path` if there is no +// SDKROOT defined in the environment. +// +// RUN: env -u SDKROOT %clang -target x86_64-apple-macos -c %s -### 2> %t.log +// RUN: FileCheck --check-prefix=CHECK-XC < %t.log %s +// +// CHECK-XC: clang +// CHECK-XC: "-cc1" +// CHECK-XC: "-isysroot" "{{.*MacOSX[0-9\.]*\.sdk}}" + +// Check once again that we default to running `xcrun`, this time with another target. +// +// RUN: env -u SDKROOT %clang -target arm64-apple-ios -c %s -### 2> %t.log +// RUN: FileCheck --check-prefix=CHECK-XC-IOS < %t.log %s +// +// CHECK-XC-IOS: clang +// CHECK-XC-IOS: "-cc1" +// CHECK-XC-IOS: "-isysroot" "{{.*iPhoneOS[0-9\.]*\.sdk}}"