diff --git a/clang/include/clang/Driver/Distro.h b/clang/include/clang/Driver/Distro.h --- a/clang/include/clang/Driver/Distro.h +++ b/clang/include/clang/Driver/Distro.h @@ -9,6 +9,7 @@ #ifndef LLVM_CLANG_DRIVER_DISTRO_H #define LLVM_CLANG_DRIVER_DISTRO_H +#include "llvm/ADT/Triple.h" #include "llvm/Support/VirtualFileSystem.h" namespace clang { @@ -84,7 +85,7 @@ Distro(DistroType D) : DistroVal(D) {} /// Detects the distribution using specified VFS. - explicit Distro(llvm::vfs::FileSystem &VFS); + explicit Distro(llvm::vfs::FileSystem &VFS, const llvm::Triple &TargetOrHost); bool operator==(const Distro &Other) const { return DistroVal == Other.DistroVal; diff --git a/clang/lib/Driver/Distro.cpp b/clang/lib/Driver/Distro.cpp --- a/clang/lib/Driver/Distro.cpp +++ b/clang/lib/Driver/Distro.cpp @@ -13,11 +13,28 @@ #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/ADT/Triple.h" using namespace clang::driver; using namespace clang; -static Distro::DistroType DetectDistro(llvm::vfs::FileSystem &VFS) { +static Distro::DistroType DetectDistro(llvm::vfs::FileSystem &VFS, + const llvm::Triple &TargetOrHost) { + // If we don't target Linux, no need to check the distro. This saves a few + // OS calls. + if (!TargetOrHost.isOSLinux()) + return Distro::UnknownDistro; + + // If the host is not running Linux, and we're backed by a real file system, + // no need to check the distro. This is the case where someone is + // cross-compiling from BSD or Windows to Linux, and it would be meaningless + // to try to figure out the "distro" of the non-Linux host. + IntrusiveRefCntPtr RealFS = + llvm::vfs::getRealFileSystem(); + llvm::Triple HostTriple(llvm::sys::getProcessTriple()); + if (!HostTriple.isOSLinux() && &VFS == RealFS.get()) + return Distro::UnknownDistro; + llvm::ErrorOr> File = VFS.getBufferForFile("/etc/lsb-release"); if (File) { @@ -149,4 +166,5 @@ return Distro::UnknownDistro; } -Distro::Distro(llvm::vfs::FileSystem &VFS) : DistroVal(DetectDistro(VFS)) {} +Distro::Distro(llvm::vfs::FileSystem &VFS, const llvm::Triple &TargetOrHost) + : DistroVal(DetectDistro(VFS, TargetOrHost)) {} diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -5619,7 +5619,7 @@ TC.getTriple().isOSBinFormatCOFF()) && !TC.getTriple().isPS4() && !TC.getTriple().isOSNetBSD() && - !Distro(D.getVFS()).IsGentoo() && + !Distro(D.getVFS(), TC.getTriple()).IsGentoo() && !TC.getTriple().isAndroid() && TC.useIntegratedAs())) CmdArgs.push_back("-faddrsig"); diff --git a/clang/lib/Driver/ToolChains/Cuda.cpp b/clang/lib/Driver/ToolChains/Cuda.cpp --- a/clang/lib/Driver/ToolChains/Cuda.cpp +++ b/clang/lib/Driver/ToolChains/Cuda.cpp @@ -115,7 +115,8 @@ for (const char *Ver : Versions) Candidates.emplace_back(D.SysRoot + "/usr/local/cuda-" + Ver); - if (Distro(D.getVFS()).IsDebian() || Distro(D.getVFS()).IsUbuntu()) + Distro Dist(D.getVFS(), llvm::Triple(llvm::sys::getProcessTriple())); + if (Dist.IsDebian() || Dist.IsUbuntu()) // Special case for Debian to have nvidia-cuda-toolkit work // out of the box. More info on http://bugs.debian.org/882505 Candidates.emplace_back(D.SysRoot + "/usr/lib/cuda"); diff --git a/clang/lib/Driver/ToolChains/Linux.cpp b/clang/lib/Driver/ToolChains/Linux.cpp --- a/clang/lib/Driver/ToolChains/Linux.cpp +++ b/clang/lib/Driver/ToolChains/Linux.cpp @@ -240,7 +240,7 @@ .str()); } - Distro Distro(D.getVFS()); + Distro Distro(D.getVFS(), Triple); if (Distro.IsAlpineLinux() || Triple.isAndroid()) { ExtraOpts.push_back("-z"); @@ -511,7 +511,7 @@ const llvm::Triple::ArchType Arch = getArch(); const llvm::Triple &Triple = getTriple(); - const Distro Distro(getDriver().getVFS()); + const Distro Distro(getDriver().getVFS(), Triple); if (Triple.isAndroid()) return Triple.isArch64Bit() ? "/system/bin/linker64" : "/system/bin/linker"; diff --git a/clang/unittests/Driver/DistroTest.cpp b/clang/unittests/Driver/DistroTest.cpp --- a/clang/unittests/Driver/DistroTest.cpp +++ b/clang/unittests/Driver/DistroTest.cpp @@ -44,7 +44,7 @@ "SUPPORT_URL=\"http://help.ubuntu.com/\"\n" "BUG_REPORT_URL=\"http://bugs.launchpad.net/ubuntu/\"\n")); - Distro UbuntuTrusty{UbuntuTrustyFileSystem}; + Distro UbuntuTrusty{UbuntuTrustyFileSystem, llvm::Triple("unknown-pc-linux")}; ASSERT_EQ(Distro(Distro::UbuntuTrusty), UbuntuTrusty); ASSERT_TRUE(UbuntuTrusty.IsUbuntu()); ASSERT_FALSE(UbuntuTrusty.IsRedhat()); @@ -52,6 +52,9 @@ ASSERT_FALSE(UbuntuTrusty.IsDebian()); ASSERT_FALSE(UbuntuTrusty.IsGentoo()); + Distro UbuntuTrusty2{UbuntuTrustyFileSystem, llvm::Triple("unknown-pc-windows")}; + ASSERT_EQ(Distro(Distro::UnknownDistro), UbuntuTrusty2); + llvm::vfs::InMemoryFileSystem UbuntuYakketyFileSystem; UbuntuYakketyFileSystem.addFile("/etc/debian_version", 0, llvm::MemoryBuffer::getMemBuffer("stretch/sid\n")); @@ -74,7 +77,7 @@ "VERSION_CODENAME=yakkety\n" "UBUNTU_CODENAME=yakkety\n")); - Distro UbuntuYakkety{UbuntuYakketyFileSystem}; + Distro UbuntuYakkety{UbuntuYakketyFileSystem, llvm::Triple("unknown-pc-linux")}; ASSERT_EQ(Distro(Distro::UbuntuYakkety), UbuntuYakkety); ASSERT_TRUE(UbuntuYakkety.IsUbuntu()); ASSERT_FALSE(UbuntuYakkety.IsRedhat()); @@ -109,7 +112,7 @@ "REDHAT_SUPPORT_PRODUCT=\"Fedora\"\n" "REDHAT_SUPPORT_PRODUCT_VERSION=25\n" "PRIVACY_POLICY_URL=https://fedoraproject.org/wiki/Legal:PrivacyPolicy\n")); - Distro Fedora25{Fedora25FileSystem}; + Distro Fedora25{Fedora25FileSystem, llvm::Triple("unknown-pc-linux")}; ASSERT_EQ(Distro(Distro::Fedora), Fedora25); ASSERT_FALSE(Fedora25.IsUbuntu()); ASSERT_TRUE(Fedora25.IsRedhat()); @@ -146,7 +149,7 @@ "REDHAT_SUPPORT_PRODUCT=\"centos\"\n" "REDHAT_SUPPORT_PRODUCT_VERSION=\"7\"\n")); - Distro CentOS7{CentOS7FileSystem}; + Distro CentOS7{CentOS7FileSystem, llvm::Triple("unknown-pc-linux")}; ASSERT_EQ(Distro(Distro::RHEL7), CentOS7); ASSERT_FALSE(CentOS7.IsUbuntu()); ASSERT_TRUE(CentOS7.IsRedhat()); @@ -174,7 +177,7 @@ "HOME_URL=\"https://opensuse.org/\"\n" "ID_LIKE=\"suse\"\n")); - Distro OpenSUSELeap421{OpenSUSELeap421FileSystem}; + Distro OpenSUSELeap421{OpenSUSELeap421FileSystem, llvm::Triple("unknown-pc-linux")}; ASSERT_EQ(Distro(Distro::OpenSUSE), OpenSUSELeap421); ASSERT_FALSE(OpenSUSELeap421.IsUbuntu()); ASSERT_FALSE(OpenSUSELeap421.IsRedhat()); @@ -200,7 +203,7 @@ "HOME_URL=\"https://opensuse.org/\"\n" "ID_LIKE=\"suse\"\n")); - Distro OpenSUSE132{OpenSUSE132FileSystem}; + Distro OpenSUSE132{OpenSUSE132FileSystem, llvm::Triple("unknown-pc-linux")}; ASSERT_EQ(Distro(Distro::OpenSUSE), OpenSUSE132); ASSERT_FALSE(OpenSUSE132.IsUbuntu()); ASSERT_FALSE(OpenSUSE132.IsRedhat()); @@ -217,7 +220,7 @@ llvm::MemoryBuffer::getMemBuffer("LSB_VERSION=\"core-2.0-noarch:core-3.0-noarch:core-2.0-x86_64:core-3.0-x86_64\"\n")); // SLES10 is unsupported and therefore evaluates to unknown - Distro SLES10{SLES10FileSystem}; + Distro SLES10{SLES10FileSystem, llvm::Triple("unknown-pc-linux")}; ASSERT_EQ(Distro(Distro::UnknownDistro), SLES10); ASSERT_FALSE(SLES10.IsUbuntu()); ASSERT_FALSE(SLES10.IsRedhat()); @@ -240,7 +243,7 @@ "SUPPORT_URL=\"http://www.debian.org/support\"\n" "BUG_REPORT_URL=\"https://bugs.debian.org/\"\n")); - Distro DebianJessie{DebianJessieFileSystem}; + Distro DebianJessie{DebianJessieFileSystem, llvm::Triple("unknown-pc-linux")}; ASSERT_EQ(Distro(Distro::DebianJessie), DebianJessie); ASSERT_FALSE(DebianJessie.IsUbuntu()); ASSERT_FALSE(DebianJessie.IsRedhat()); @@ -259,7 +262,7 @@ "SUPPORT_URL=\"http://www.debian.org/support\"\n" "BUG_REPORT_URL=\"https://bugs.debian.org/\"\n")); - Distro DebianStretchSid{DebianStretchSidFileSystem}; + Distro DebianStretchSid{DebianStretchSidFileSystem, llvm::Triple("unknown-pc-linux")}; ASSERT_EQ(Distro(Distro::DebianStretch), DebianStretchSid); ASSERT_FALSE(DebianStretchSid.IsUbuntu()); ASSERT_FALSE(DebianStretchSid.IsRedhat()); @@ -281,7 +284,7 @@ "SUPPORT_URL=\"irc://irc.freenode.net/#exherbo\"\n" "BUG_REPORT_URL=\"https://bugs.exherbo.org/\"\n")); - Distro Exherbo{ExherboFileSystem}; + Distro Exherbo{ExherboFileSystem, llvm::Triple("unknown-pc-linux")}; ASSERT_EQ(Distro(Distro::Exherbo), Exherbo); ASSERT_FALSE(Exherbo.IsUbuntu()); ASSERT_FALSE(Exherbo.IsRedhat()); @@ -303,7 +306,7 @@ "SUPPORT_URL=\"https://bbs.archlinux.org/\"\n" "BUG_REPORT_URL=\"https://bugs.archlinux.org/\"\n")); - Distro ArchLinux{ArchLinuxFileSystem}; + Distro ArchLinux{ArchLinuxFileSystem, llvm::Triple("unknown-pc-linux")}; ASSERT_EQ(Distro(Distro::ArchLinux), ArchLinux); ASSERT_FALSE(ArchLinux.IsUbuntu()); ASSERT_FALSE(ArchLinux.IsRedhat()); @@ -328,7 +331,7 @@ "SUPPORT_URL=\"https://www.gentoo.org/support/\"\n" "BUG_REPORT_URL=\"https://bugs.gentoo.org/\"\n")); - Distro Gentoo{GentooFileSystem}; + Distro Gentoo{GentooFileSystem, llvm::Triple("unknown-pc-linux")}; ASSERT_EQ(Distro(Distro::Gentoo), Gentoo); ASSERT_FALSE(Gentoo.IsUbuntu()); ASSERT_FALSE(Gentoo.IsRedhat()); @@ -337,4 +340,57 @@ ASSERT_TRUE(Gentoo.IsGentoo()); } +TEST(DistroTest, DetectWindowsAndCrossCompile) { + + class CountingFileSystem : public llvm::vfs::ProxyFileSystem { + public: + CountingFileSystem() : ProxyFileSystem(llvm::vfs::getRealFileSystem()) {} + + llvm::ErrorOr status(const llvm::Twine &Path) override { + ++Count; + return llvm::vfs::ProxyFileSystem::status(Path); + } + + llvm::ErrorOr> + openFileForRead(const llvm::Twine &Path) override { + ++Count; + return llvm::vfs::ProxyFileSystem::openFileForRead(Path); + } + + unsigned Count{}; + }; + + llvm::IntrusiveRefCntPtr RFS = + llvm::vfs::getRealFileSystem(); + llvm::Triple Host(llvm::sys::getProcessTriple()); + + CountingFileSystem CFileSystem; + Distro LinuxDistro{CFileSystem, llvm::Triple("unknown-pc-linux")}; + if (Host.isOSWindows()) { + ASSERT_EQ(Distro(Distro::UnknownDistro), LinuxDistro); + ASSERT_GT(CFileSystem.Count, 0U); + } + + Distro WinDistro{CFileSystem, llvm::Triple("unknown-pc-windows")}; + ASSERT_EQ(Distro(Distro::UnknownDistro), WinDistro); + ASSERT_GT(CFileSystem.Count, 0U); + + // When running on Windows along with a real file system, ensure that no + // distro is returned if targeting Linux + if (Host.isOSWindows()) { + Distro LinuxRealDistro{*RFS, llvm::Triple("unknown-pc-linux")}; + ASSERT_EQ(Distro(Distro::UnknownDistro), LinuxRealDistro); + } + // When running on Linux, check if the distro is the same as the host when + // targeting Linux + if (Host.isOSLinux()) { + Distro HostDistro{*RFS, Host}; + Distro LinuxRealDistro{*RFS, llvm::Triple("unknown-pc-linux")}; + ASSERT_EQ(HostDistro, LinuxRealDistro); + } + + Distro WinRealDistro{*RFS, llvm::Triple("unknown-pc-windows")}; + ASSERT_EQ(Distro(Distro::UnknownDistro), WinRealDistro); +} + } // end anonymous namespace