https://reviews.llvm.org/D89001 Currently, Clang looks for libc++ headers alongside the installation directory of Clang, and it also adds a search path for headers in the -isysroot. This is problematic if headers are found in both the toolchain and in the sysroot, since #include_next will end up finding the libc++ headers in the sysroot instead of the intended system headers. This patch changes the logic such that if the toolchain contains libc++ headers, no C++ header paths are added in the sysroot. However, if the toolchain does *not* contain libc++ headers, the sysroot is searched as usual. This should not be a breaking change, since any code that previously relied on some libc++ headers being found in the sysroot suffered from the #include_next issue described above, which renders any libc++ header basically useless. --- a/tools/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/tools/clang/lib/Driver/ToolChains/Darwin.cpp @@ -2021,21 +2021,42 @@ void DarwinClang::AddClangCXXStdlibIncludeArgs( switch (GetCXXStdlibType(DriverArgs)) { case ToolChain::CST_Libcxx: { - // On Darwin, libc++ is installed alongside the compiler in - // include/c++/v1, so get from '/bin' to '/include/c++/v1'. - { - llvm::SmallString<128> P = llvm::StringRef(getDriver().getInstalledDir()); - // Note that P can be relative, so we have to '..' and not parent_path. - llvm::sys::path::append(P, "..", "include", "c++", "v1"); - addSystemInclude(DriverArgs, CC1Args, P); + // On Darwin, libc++ can be installed in one of the following two places: + // 1. Alongside the compiler in /include/c++/v1 + // 2. In a SDK (or a custom sysroot) in /usr/include/c++/v1 + // + // The precendence of paths is as listed above, i.e. we take the first path + // that exists. Also note that we never include libc++ twice -- we take the + // first path that exists and don't send the other paths to CC1 (otherwise + // include_next could break). + + // Check for (1) + // Get from '/bin' to '/include/c++/v1'. + // Note that InstallBin can be relative, so we use '..' instead of + // parent_path. + llvm::SmallString<128> InstallBin = + llvm::StringRef(getDriver().getInstalledDir()); // /bin + llvm::sys::path::append(InstallBin, "..", "include", "c++", "v1"); + if (getVFS().exists(InstallBin)) { + addSystemInclude(DriverArgs, CC1Args, InstallBin); + return; + } else if (DriverArgs.hasArg(options::OPT_v)) { + llvm::errs() << "ignoring nonexistent directory \"" << InstallBin + << "\"\n"; } - // Also add /usr/include/c++/v1 unless -nostdinc is used, - // to match the legacy behavior in CC1. - if (!DriverArgs.hasArg(options::OPT_nostdinc)) { - llvm::SmallString<128> P = Sysroot; - llvm::sys::path::append(P, "usr", "include", "c++", "v1"); - addSystemInclude(DriverArgs, CC1Args, P); + + // Otherwise, check for (2) + llvm::SmallString<128> SysrootUsr = Sysroot; + llvm::sys::path::append(SysrootUsr, "usr", "include", "c++", "v1"); + if (getVFS().exists(SysrootUsr)) { + addSystemInclude(DriverArgs, CC1Args, SysrootUsr); + return; + } else if (DriverArgs.hasArg(options::OPT_v)) { + llvm::errs() << "ignoring nonexistent directory \"" << SysrootUsr + << "\"\n"; } + + // Otherwise, don't add any path. break; } --- a/tools/clang/test/Driver/darwin-header-search-libcxx.cpp +++ b/tools/clang/test/Driver/darwin-header-search-libcxx.cpp @@ -13,39 +13,57 @@ // RUN: | FileCheck --check-prefix=CHECK-LIBCXX-NONE %s // CHECK-LIBCXX-NONE: "{{[^"]*}}clang{{[^"]*}}" "-cc1" -// Check with only headers alongside the installation (those should be used, -// but we should still add /usr/include/c++/v1 after to preserve legacy). +// Check with only headers alongside the installation (those should be used). // // RUN: %clang -no-canonical-prefixes %s -### -fsyntax-only 2>&1 \ // RUN: -target x86_64-apple-darwin \ // RUN: -stdlib=libc++ \ // RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain/usr/bin \ // RUN: --sysroot="" \ -// RUN: | FileCheck -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain --check-prefix=CHECK-LIBCXX-TOOLCHAIN-1 %s +// RUN: | FileCheck -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain \ +// RUN: --check-prefix=CHECK-LIBCXX-TOOLCHAIN-1 %s // CHECK-LIBCXX-TOOLCHAIN-1: "{{[^"]*}}clang{{[^"]*}}" "-cc1" // CHECK-LIBCXX-TOOLCHAIN-1: "-internal-isystem" "[[TOOLCHAIN]]/usr/bin/../include/c++/v1" -// CHECK-LIBCXX-TOOLCHAIN-1: "-internal-isystem" "/usr/include/c++/v1" +// CHECK-LIBCXX-TOOLCHAIN-1-NOT: "-internal-isystem" "/usr/include/c++/v1" // // RUN: %clang -no-canonical-prefixes %s -### -fsyntax-only 2>&1 \ // RUN: -target x86_64-apple-darwin \ // RUN: -stdlib=libc++ \ // RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain/usr/bin \ // RUN: -isysroot %S/Inputs/basic_darwin_sdk_no_libcxx \ -// RUN: | FileCheck -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain --check-prefix=CHECK-LIBCXX-TOOLCHAIN-2 %s +// RUN: | FileCheck -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain \ +// RUN: -DSYSROOT=%S/Inputs/basic_darwin_sdk_no_libcxx \ +// RUN: --check-prefix=CHECK-LIBCXX-TOOLCHAIN-2 %s // CHECK-LIBCXX-TOOLCHAIN-2: "{{[^"]*}}clang{{[^"]*}}" "-cc1" // CHECK-LIBCXX-TOOLCHAIN-2: "-internal-isystem" "[[TOOLCHAIN]]/usr/bin/../include/c++/v1" +// CHECK-LIBCXX-TOOLCHAIN-2-NOT: "-internal-isystem" "[[SYSROOT]]/usr/include/c++/v1" + +// Check with only headers in the sysroot (those should be used). +// +// RUN: %clang -no-canonical-prefixes %s -### -fsyntax-only 2>&1 \ +// RUN: -target x86_64-apple-darwin \ +// RUN: -stdlib=libc++ \ +// RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain_no_libcxx/usr/bin \ +// RUN: -isysroot %S/Inputs/basic_darwin_sdk_usr_cxx_v1 \ +// RUN: | FileCheck -DSYSROOT=%S/Inputs/basic_darwin_sdk_usr_cxx_v1 \ +// RUN: -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain_no_libcxx \ +// RUN: --check-prefix=CHECK-LIBCXX-SYSROOT-1 %s +// CHECK-LIBCXX-SYSROOT-1: "{{[^"]*}}clang{{[^"]*}}" "-cc1" +// CHECK-LIBCXX-SYSROOT-1: "-internal-isystem" "[[SYSROOT]]/usr/include/c++/v1" +// CHECK-LIBCXX-SYSROOT-1-NOT: "-internal-isystem" "[[TOOLCHAIN]]/usr/bin/../include/c++/v1" // Check with both headers in the sysroot and headers alongside the installation -// (the headers in should be added after the toolchain headers). -// Ensure that both -isysroot and --sysroot work, and that isysroot has precedence. +// (the headers in the toolchain should be preferred over the headers). +// Ensure that both -isysroot and --sysroot work, and that isysroot has precedence +// over --sysroot. // // RUN: %clang -no-canonical-prefixes %s -### -fsyntax-only 2>&1 \ // RUN: -target x86_64-apple-darwin \ // RUN: -stdlib=libc++ \ // RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain/usr/bin \ // RUN: -resource-dir=%S/Inputs/resource_dir \ -// RUN: -isysroot %S/Inputs/basic_darwin_sdk_usr \ -// RUN: | FileCheck -DSYSROOT=%S/Inputs/basic_darwin_sdk_usr \ +// RUN: -isysroot %S/Inputs/basic_darwin_sdk_usr_cxx_v1 \ +// RUN: | FileCheck -DSYSROOT=%S/Inputs/basic_darwin_sdk_usr_cxx_v1 \ // RUN: -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain \ // RUN: --check-prefix=CHECK-LIBCXX-SYSROOT_AND_TOOLCHAIN-1 %s // @@ -54,8 +72,8 @@ // RUN: -stdlib=libc++ \ // RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain/usr/bin \ // RUN: -resource-dir=%S/Inputs/resource_dir \ -// RUN: --sysroot %S/Inputs/basic_darwin_sdk_usr \ -// RUN: | FileCheck -DSYSROOT=%S/Inputs/basic_darwin_sdk_usr \ +// RUN: --sysroot %S/Inputs/basic_darwin_sdk_usr_cxx_v1 \ +// RUN: | FileCheck -DSYSROOT=%S/Inputs/basic_darwin_sdk_usr_cxx_v1 \ // RUN: -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain \ // RUN: --check-prefix=CHECK-LIBCXX-SYSROOT_AND_TOOLCHAIN-1 %s // @@ -64,32 +82,46 @@ // RUN: -stdlib=libc++ \ // RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain/usr/bin \ // RUN: -resource-dir=%S/Inputs/resource_dir \ -// RUN: -isysroot %S/Inputs/basic_darwin_sdk_usr \ +// RUN: -isysroot %S/Inputs/basic_darwin_sdk_usr_cxx_v1 \ // RUN: --sysroot %S/Inputs/basic_darwin_sdk_no_libcxx \ -// RUN: | FileCheck -DSYSROOT=%S/Inputs/basic_darwin_sdk_usr \ +// RUN: | FileCheck -DSYSROOT=%S/Inputs/basic_darwin_sdk_usr_cxx_v1 \ // RUN: -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain \ // RUN: --check-prefix=CHECK-LIBCXX-SYSROOT_AND_TOOLCHAIN-1 %s // // CHECK-LIBCXX-SYSROOT_AND_TOOLCHAIN-1: "{{[^"]*}}clang{{[^"]*}}" "-cc1" // CHECK-LIBCXX-SYSROOT_AND_TOOLCHAIN-1: "-internal-isystem" "[[TOOLCHAIN]]/usr/bin/../include/c++/v1" -// CHECK-LIBCXX-SYSROOT_AND_TOOLCHAIN-1: "-internal-isystem" "[[SYSROOT]]/usr/include/c++/v1" +// CHECK-LIBCXX-SYSROOT_AND_TOOLCHAIN-1-NOT: "-internal-isystem" "[[SYSROOT]]/usr/include/c++/v1" -// Make sure that using -nostdinc will drop the sysroot C++ library include -// path, but not the toolchain one. +// Make sure that using -nostdinc does not drop any C++ library include path. +// This behavior is strange, but it is compatible with the legacy CC1 behavior. // // RUN: %clang -no-canonical-prefixes %s -### -fsyntax-only 2>&1 \ // RUN: -target x86_64-apple-darwin16 \ // RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain/usr/bin \ // RUN: -resource-dir=%S/Inputs/resource_dir \ -// RUN: -isysroot %S/Inputs/basic_darwin_sdk_usr \ +// RUN: -isysroot %S/Inputs/basic_darwin_sdk_usr_cxx_v1 \ // RUN: -stdlib=platform \ // RUN: -nostdinc \ -// RUN: | FileCheck -DSYSROOT=%S/Inputs/basic_darwin_sdk_usr \ +// RUN: | FileCheck -DSYSROOT=%S/Inputs/basic_darwin_sdk_usr_cxx_v1 \ // RUN: -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain \ -// RUN: --check-prefix=CHECK-LIBCXX-NOSTDINC %s -// CHECK-LIBCXX-NOSTDINC: "{{[^"]*}}clang{{[^"]*}}" "-cc1" -// CHECK-LIBCXX-NOSTDINC: "-internal-isystem" "[[TOOLCHAIN]]/usr/bin/../include/c++/v1" -// CHECK-LIBCXX-NOSTDINC-NOT: "-internal-isystem" "[[SYSROOT]]/usr/include/c++/v1" +// RUN: --check-prefix=CHECK-LIBCXX-NOSTDINC-1 %s +// CHECK-LIBCXX-NOSTDINC-1: "{{[^"]*}}clang{{[^"]*}}" "-cc1" +// CHECK-LIBCXX-NOSTDINC-1-NOT: "-internal-isystem" "[[SYSROOT]]/usr/include/c++/v1" +// CHECK-LIBCXX-NOSTDINC-1: "-internal-isystem" "[[TOOLCHAIN]]/usr/bin/../include/c++/v1" +// +// RUN: %clang -no-canonical-prefixes %s -### -fsyntax-only 2>&1 \ +// RUN: -target x86_64-apple-darwin16 \ +// RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain/usr/bin \ +// RUN: -resource-dir=%S/Inputs/resource_dir \ +// RUN: -isysroot %S/Inputs/basic_darwin_sdk_no_libcxx \ +// RUN: -stdlib=platform \ +// RUN: -nostdinc \ +// RUN: | FileCheck -DSYSROOT=%S/Inputs/basic_darwin_sdk_no_libcxx \ +// RUN: -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain \ +// RUN: --check-prefix=CHECK-LIBCXX-NOSTDINC-2 %s +// CHECK-LIBCXX-NOSTDINC-2: "{{[^"]*}}clang{{[^"]*}}" "-cc1" +// CHECK-LIBCXX-NOSTDINC-2: "-internal-isystem" "[[TOOLCHAIN]]/usr/bin/../include/c++/v1" +// CHECK-LIBCXX-NOSTDINC-2-NOT: "-internal-isystem" "[[SYSROOT]]/usr/include/c++/v1" // Make sure that using -nostdinc++ or -nostdlib will drop both the toolchain // C++ include path and the sysroot one. @@ -98,7 +130,7 @@ // RUN: -target x86_64-apple-darwin16 \ // RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain/usr/bin \ // RUN: -resource-dir=%S/Inputs/resource_dir \ -// RUN: -isysroot %S/Inputs/basic_darwin_sdk_usr \ +// RUN: -isysroot %S/Inputs/basic_darwin_sdk_usr_cxx_v1 \ // RUN: -stdlib=platform \ // RUN: -nostdinc++ \ // RUN: | FileCheck -DSYSROOT=%S/Inputs/basic_darwin_sdk_usr \ @@ -121,3 +153,26 @@ // CHECK-LIBCXX-NOSTDLIBINC: "{{[^"]*}}clang{{[^"]*}}" "-cc1" // CHECK-LIBCXX-NOSTDLIBINC-NOT: "-internal-isystem" "[[TOOLCHAIN]]/usr/bin/../include/c++/v1" // CHECK-LIBCXX-NOSTDLIBINC-NOT: "-internal-isystem" "[[SYSROOT]]/usr/include/c++/v1" + +// Make sure we explain that we considered a path but didn't add it when it +// doesn't exist. +// +// RUN: %clang -no-canonical-prefixes %s -fsyntax-only -v 2>&1 \ +// RUN: -target x86_64-apple-darwin \ +// RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain_no_libcxx/usr/bin \ +// RUN: -isysroot %S/Inputs/basic_darwin_sdk \ +// RUN: -stdlib=libc++ \ +// RUN: | FileCheck -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain_no_libcxx \ +// RUN: --check-prefix=CHECK-LIBCXX-MISSING-TOOLCHAIN %s +// CHECK-LIBCXX-MISSING-TOOLCHAIN: ignoring nonexistent directory "[[TOOLCHAIN]]/usr/bin/../include/c++/v1" +// +// RUN: %clang -no-canonical-prefixes %s -fsyntax-only -v 2>&1 \ +// RUN: -target x86_64-apple-darwin \ +// RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain_no_libcxx/usr/bin \ +// RUN: -isysroot %S/Inputs/basic_darwin_sdk_no_libcxx \ +// RUN: -stdlib=libc++ \ +// RUN: | FileCheck -DSYSROOT=%S/Inputs/basic_darwin_sdk_no_libcxx \ +// RUN: -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain_no_libcxx \ +// RUN: --check-prefix=CHECK-LIBCXX-MISSING-BOTH %s +// CHECK-LIBCXX-MISSING-BOTH: ignoring nonexistent directory "[[TOOLCHAIN]]/usr/bin/../include/c++/v1" +// CHECK-LIBCXX-MISSING-BOTH: ignoring nonexistent directory "[[SYSROOT]]/usr/include/c++/v1"