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 @@ -23,6 +23,8 @@ class Distro { public: enum DistroType { + // Special value means that no detection was performed yet. + UninitializedDistro, // NB: Releases of a particular Linux distro should be kept together // in this enum, because some tests are done by integer comparison against // the first and last known member in the family, e.g. IsRedHat(). 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 @@ -15,76 +15,106 @@ #include "llvm/Support/ErrorOr.h" #include "llvm/Support/Host.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Threading.h" using namespace clang::driver; using namespace clang; -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()) +static Distro::DistroType DetectOsRelease(llvm::vfs::FileSystem &VFS) { + llvm::ErrorOr> File = + VFS.getBufferForFile("/etc/os-release"); + if (!File) + File = VFS.getBufferForFile("/usr/lib/os-release"); + if (!File) 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; + SmallVector Lines; + File.get()->getBuffer().split(Lines, "\n"); + Distro::DistroType Version = Distro::UnknownDistro; + + // Obviously this can be improved a lot. + for (StringRef Line : Lines) + if (Version == Distro::UnknownDistro && Line.startswith("ID=")) + Version = llvm::StringSwitch(Line.substr(3)) + .Case("fedora", Distro::Fedora) + .Case("gentoo", Distro::Gentoo) + .Case("arch", Distro::ArchLinux) + // On SLES, /etc/os-release was introduced in SLES 11. + .Case("sles", Distro::OpenSUSE) + .Case("opensuse", Distro::OpenSUSE) + .Case("exherbo", Distro::Exherbo) + .Default(Distro::UnknownDistro); + return Version; +} +static Distro::DistroType DetectLsbRelease(llvm::vfs::FileSystem &VFS) { llvm::ErrorOr> File = VFS.getBufferForFile("/etc/lsb-release"); - if (File) { - StringRef Data = File.get()->getBuffer(); - SmallVector Lines; - Data.split(Lines, "\n"); - Distro::DistroType Version = Distro::UnknownDistro; - for (StringRef Line : Lines) - if (Version == Distro::UnknownDistro && Line.startswith("DISTRIB_CODENAME=")) - Version = llvm::StringSwitch(Line.substr(17)) - .Case("hardy", Distro::UbuntuHardy) - .Case("intrepid", Distro::UbuntuIntrepid) - .Case("jaunty", Distro::UbuntuJaunty) - .Case("karmic", Distro::UbuntuKarmic) - .Case("lucid", Distro::UbuntuLucid) - .Case("maverick", Distro::UbuntuMaverick) - .Case("natty", Distro::UbuntuNatty) - .Case("oneiric", Distro::UbuntuOneiric) - .Case("precise", Distro::UbuntuPrecise) - .Case("quantal", Distro::UbuntuQuantal) - .Case("raring", Distro::UbuntuRaring) - .Case("saucy", Distro::UbuntuSaucy) - .Case("trusty", Distro::UbuntuTrusty) - .Case("utopic", Distro::UbuntuUtopic) - .Case("vivid", Distro::UbuntuVivid) - .Case("wily", Distro::UbuntuWily) - .Case("xenial", Distro::UbuntuXenial) - .Case("yakkety", Distro::UbuntuYakkety) - .Case("zesty", Distro::UbuntuZesty) - .Case("artful", Distro::UbuntuArtful) - .Case("bionic", Distro::UbuntuBionic) - .Case("cosmic", Distro::UbuntuCosmic) - .Case("disco", Distro::UbuntuDisco) - .Case("eoan", Distro::UbuntuEoan) - .Case("focal", Distro::UbuntuFocal) - .Case("groovy", Distro::UbuntuGroovy) - .Default(Distro::UnknownDistro); - if (Version != Distro::UnknownDistro) - return Version; - } + if (!File) + return Distro::UnknownDistro; + + SmallVector Lines; + File.get()->getBuffer().split(Lines, "\n"); + Distro::DistroType Version = Distro::UnknownDistro; + + for (StringRef Line : Lines) + if (Version == Distro::UnknownDistro && + Line.startswith("DISTRIB_CODENAME=")) + Version = llvm::StringSwitch(Line.substr(17)) + .Case("hardy", Distro::UbuntuHardy) + .Case("intrepid", Distro::UbuntuIntrepid) + .Case("jaunty", Distro::UbuntuJaunty) + .Case("karmic", Distro::UbuntuKarmic) + .Case("lucid", Distro::UbuntuLucid) + .Case("maverick", Distro::UbuntuMaverick) + .Case("natty", Distro::UbuntuNatty) + .Case("oneiric", Distro::UbuntuOneiric) + .Case("precise", Distro::UbuntuPrecise) + .Case("quantal", Distro::UbuntuQuantal) + .Case("raring", Distro::UbuntuRaring) + .Case("saucy", Distro::UbuntuSaucy) + .Case("trusty", Distro::UbuntuTrusty) + .Case("utopic", Distro::UbuntuUtopic) + .Case("vivid", Distro::UbuntuVivid) + .Case("wily", Distro::UbuntuWily) + .Case("xenial", Distro::UbuntuXenial) + .Case("yakkety", Distro::UbuntuYakkety) + .Case("zesty", Distro::UbuntuZesty) + .Case("artful", Distro::UbuntuArtful) + .Case("bionic", Distro::UbuntuBionic) + .Case("cosmic", Distro::UbuntuCosmic) + .Case("disco", Distro::UbuntuDisco) + .Case("eoan", Distro::UbuntuEoan) + .Case("focal", Distro::UbuntuFocal) + .Case("groovy", Distro::UbuntuGroovy) + .Default(Distro::UnknownDistro); + return Version; +} + +static Distro::DistroType DetectDistro(llvm::vfs::FileSystem &VFS) { + Distro::DistroType Version = Distro::UnknownDistro; + + // Newer freedesktop.org's compilant systemd-based systems + // should provide /etc/os-release or /usr/lib/os-release. + Version = DetectOsRelease(VFS); + if (Version != Distro::UnknownDistro) + return Version; + + // Older systems might provide /etc/lsb-release. + Version = DetectLsbRelease(VFS); + if (Version != Distro::UnknownDistro) + return Version; + + // Otherwise try some distro-specific quirks for RedHat... + llvm::ErrorOr> File = + VFS.getBufferForFile("/etc/redhat-release"); - File = VFS.getBufferForFile("/etc/redhat-release"); if (File) { StringRef Data = File.get()->getBuffer(); if (Data.startswith("Fedora release")) return Distro::Fedora; if (Data.startswith("Red Hat Enterprise Linux") || - Data.startswith("CentOS") || - Data.startswith("Scientific Linux")) { + Data.startswith("CentOS") || Data.startswith("Scientific Linux")) { if (Data.find("release 7") != StringRef::npos) return Distro::RHEL7; else if (Data.find("release 6") != StringRef::npos) @@ -95,6 +125,7 @@ return Distro::UnknownDistro; } + // ...for Debian File = VFS.getBufferForFile("/etc/debian_version"); if (File) { StringRef Data = File.get()->getBuffer(); @@ -130,18 +161,20 @@ .Default(Distro::UnknownDistro); } + // ...for SUSE File = VFS.getBufferForFile("/etc/SuSE-release"); if (File) { StringRef Data = File.get()->getBuffer(); SmallVector Lines; Data.split(Lines, "\n"); - for (const StringRef& Line : Lines) { + for (const StringRef &Line : Lines) { if (!Line.trim().startswith("VERSION")) continue; std::pair SplitLine = Line.split('='); // Old versions have split VERSION and PATCHLEVEL // Newer versions use VERSION = x.y - std::pair SplitVer = SplitLine.second.trim().split('.'); + std::pair SplitVer = + SplitLine.second.trim().split('.'); int Version; // OpenSUSE/SLES 10 and older are not supported and not compatible @@ -153,6 +186,7 @@ return Distro::UnknownDistro; } + // ...and others. if (VFS.exists("/etc/exherbo-release")) return Distro::Exherbo; @@ -168,5 +202,34 @@ return Distro::UnknownDistro; } +static Distro::DistroType GetDistro(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; + + // True if we're backed by a real file system. + const bool onRealFS = (llvm::vfs::getRealFileSystem() == &VFS); + + // 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. + llvm::Triple HostTriple(llvm::sys::getProcessTriple()); + if (!HostTriple.isOSLinux() && onRealFS) + return Distro::UnknownDistro; + + if (onRealFS) { + // If we're backed by a real file system, perform + // the detection only once and save the result. + static Distro::DistroType LinuxDistro = DetectDistro(VFS); + return LinuxDistro; + } + // This is mostly for passing tests which uses llvm::vfs::InMemoryFileSystem, + // which is not "real". + return DetectDistro(VFS); +} + Distro::Distro(llvm::vfs::FileSystem &VFS, const llvm::Triple &TargetOrHost) - : DistroVal(DetectDistro(VFS, TargetOrHost)) {} + : DistroVal(GetDistro(VFS, TargetOrHost)) {}