From 6d9694cf46af57a28b371bd964b7f6bcafd7d961 Mon Sep 17 00:00:00 2001 From: Chandler Carruth Date: Tue, 4 Oct 2011 21:22:33 +0000 Subject: [PATCH] Rework the search for a GCC installation still further. This combines two fundamental changes, as they ended up being interrelated. The first is to walk from the root down through the filesystem so that we prune subtrees which do not exist early. This greatly reduces the filesystem traffic of this routine. We store the "best" GCC version we encounter, and look at all of the GCC installations available. Also, we look through GCC versions by scanning the directory rather than using a hard-coded list of versions. This has several benefits. It makes it much more efficient to locate a GCC installation even in the presence of a large number of different options by simply reading the directory once. It also future-proofs us as new GCC versions are released and installed. We no longer have a hard coded list of version numbers, and won't need to manually updated it. We can still filter out known-bad versions as needed. Currently I've left in filtering for all GCC installations prior to 4.1.1, as that was the first one supported previously. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@141120 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Driver/ToolChains.cpp | 135 ++++++++++++++++++++++++-------------- 1 file changed, 85 insertions(+), 50 deletions(-) diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index b67f001e78..ec11e73a18 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -1490,6 +1490,42 @@ namespace { /// information about it. It starts from the host information provided to the /// Driver, and has logic for fuzzing that where appropriate. class GCCInstallationDetector { + /// \brief Struct to store and manipulate GCC versions. + /// + /// We rely on assumptions about the form and structure of GCC version + /// numbers: they consist of at most three '.'-separated components, and each + /// component is a non-negative integer. + struct GCCVersion { + unsigned Major, Minor, Patch; + + static GCCVersion Parse(StringRef VersionText) { + const GCCVersion BadVersion = {}; + std::pair First = VersionText.split('.'); + std::pair Second = First.second.split('.'); + + GCCVersion GoodVersion = {}; + if (First.first.getAsInteger(10, GoodVersion.Major)) + return BadVersion; + if (Second.first.getAsInteger(10, GoodVersion.Minor)) + return BadVersion; + if (!Second.first.empty()) + if (Second.first.getAsInteger(10, GoodVersion.Patch)) + return BadVersion; + return GoodVersion; + } + + bool operator<(const GCCVersion &RHS) const { + if (Major < RHS.Major) return true; + if (Major > RHS.Major) return false; + if (Minor < RHS.Minor) return true; + if (Minor > RHS.Minor) return false; + return Patch < RHS.Patch; + } + bool operator>(const GCCVersion &RHS) const { return RHS < *this; } + bool operator<=(const GCCVersion &RHS) const { return !(*this > RHS); } + bool operator>=(const GCCVersion &RHS) const { return !(*this < RHS); } + }; + bool IsValid; std::string GccTriple; @@ -1600,63 +1636,62 @@ public: // specific triple is detected. CandidateTriples.push_back(D.DefaultHostTriple); - // Loop over the various components which exist and select the best GCC - // installation available. GCC installs are ranked based on age, triple - // accuracy, and architecture specificity in that order. The inverted walk - // requires testing the filesystem more times than is ideal, but shouldn't - // matter in practice as this is once on startup. - // FIXME: Instead of this, we should walk from the root down through each - // layer, and if it is "better" than prior installations found, use it. - static const char* GccVersions[] = { - "4.6.1", "4.6.0", "4.6", - "4.5.3", "4.5.2", "4.5.1", "4.5", - "4.4.6", "4.4.5", "4.4.4", "4.4.3", "4.4", - "4.3.4", "4.3.3", "4.3.2", "4.3", - "4.2.4", "4.2.3", "4.2.2", "4.2.1", "4.2", - "4.1.1"}; + // Compute the set of prefixes for our search. SmallVector Prefixes(D.PrefixDirs.begin(), D.PrefixDirs.end()); Prefixes.push_back(D.SysRoot + "/usr"); - IsValid = true; - for (unsigned i = 0; i < llvm::array_lengthof(GccVersions); ++i) { - for (unsigned j = 0, je = CandidateTriples.size(); j < je; ++j) { - GccTriple = CandidateTriples[j]; - for (unsigned k = 0, ke = CandidateLibDirs.size(); k < ke; ++k) { - const std::string LibDir = CandidateLibDirs[k].str() + "/"; - for (unsigned l = 0, le = Prefixes.size(); l < le; ++l) { - if (!PathExists(Prefixes[l])) - continue; - - const std::string TripleDir = Prefixes[l] + LibDir + GccTriple; - GccInstallPath = TripleDir + "/" + GccVersions[i]; - GccParentLibPath = GccInstallPath + "/../../.."; - if (PathExists(GccInstallPath + "/crtbegin.o")) - return; - - // Try an install directory with an extra triple in it. - GccInstallPath = - TripleDir + "/gcc/" + GccTriple + "/" + GccVersions[i]; - GccParentLibPath = GccInstallPath + "/../../../.."; - if (PathExists(GccInstallPath + "/crtbegin.o")) - return; - - if (GccTriple != "i386-linux-gnu") - continue; - - // Ubuntu 11.04 uses an unusual path. - GccInstallPath = - TripleDir + "/gcc/i686-linux-gnu/" + GccVersions[i]; - GccParentLibPath = GccInstallPath + "/../../../.."; - if (PathExists(GccInstallPath + "/crtbegin.o")) - return; + + // Loop over the various components which exist and select the best GCC + // installation available. GCC installs are ranked by version number. + static const GCCVersion MinVersion = { 4, 1, 1 }; + GCCVersion BestVersion = {}; + for (unsigned i = 0, ie = Prefixes.size(); i < ie; ++i) { + if (!PathExists(Prefixes[i])) + continue; + for (unsigned j = 0, je = CandidateLibDirs.size(); j < je; ++j) { + const std::string LibDir = Prefixes[i] + CandidateLibDirs[j].str(); + if (!PathExists(LibDir)) + continue; + for (unsigned k = 0, ke = CandidateTriples.size(); k < ke; ++k) { + StringRef CandidateTriple = CandidateTriples[k]; + const std::string TripleDir = LibDir + "/" + CandidateTriple.str(); + if (!PathExists(TripleDir)) + continue; + + // There are various different suffixes on the triple directory we + // check for. We also record what is necessary to walk from each back + // up to the lib directory. + const std::string Suffixes[] = { "", "/gcc/" + CandidateTriple.str(), + "/gcc/i686-linux-gnu" }; + const std::string InstallSuffixes[] = { "/../../..", "/../../../..", + "/../../../.." }; + const unsigned NumSuffixes = (llvm::array_lengthof(Suffixes) - + (CandidateTriple != "i386-linux-gnu")); + for (unsigned l = 0; l < NumSuffixes; ++l) { + StringRef Suffix = Suffixes[l]; + llvm::error_code EC; + for (llvm::sys::fs::directory_iterator LI(TripleDir + Suffix, EC), + LE; + !EC && LI != LE; LI = LI.increment(EC)) { + StringRef VersionText = llvm::sys::path::filename(LI->path()); + GCCVersion CandidateVersion = GCCVersion::Parse(VersionText); + if (CandidateVersion < MinVersion) + continue; + if (CandidateVersion <= BestVersion) + continue; + if (!PathExists(LI->path() + "/crtbegin.o")) + continue; + + BestVersion = CandidateVersion; + GccTriple = CandidateTriple.str(); + GccInstallPath = LI->path(); + GccParentLibPath = GccInstallPath + InstallSuffixes[l]; + IsValid = true; + } } } } } - GccTriple.clear(); - GccInstallPath.clear(); - GccParentLibPath.clear(); - IsValid = false; } /// \brief Check whether we detected a valid GCC install. -- 2.40.0