From: Chandler Carruth Date: Wed, 25 Jan 2012 07:21:38 +0000 (+0000) Subject: Make a major refactoring to how the GCC installation detection works. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=1c6f04a28d8a8e3eca966385815305b13e0a6a05;p=clang Make a major refactoring to how the GCC installation detection works. The fundamental shift here is to stop making *any* assumptions about the *host* triple. Where these assumptions you ask? Why, they were in one of the two target triples referenced of course. This was the single biggest place where the previously named "host triple" was actually used as such. ;] The reason we were reasoning about the host is in order to detect the use of '-m32' or '-m64' flags to change the target. These flags shift the default target only slightly, which typically means a slight deviation from the host. When using these flags, the GCC installation is under a different triple from the one actually targeted in the compilation, and we used the host triple to find it. Too bad that wasn't even correct. Consider an x86 Linux host which has a PPC64 cross-compiling GCC toolchain installed. This toolchain is also configured for multiarch compiling and can target PPC32 with eth '-m32' flag. When targeting 'powerpc-linux-gnu' or some other PPC32 triple, we have to look for the PPC64 variant of the triple to find the GCC install, and that triple is neither the host nor target. The new logic computes the multiarch's alternate triple from the target triple, and looks under both sides. It also looks more aggressively for the correct subdirectory of the GCC installation, and exposes the subdirectory in a nice programmatic way. This '/32' or '/64' suffix is something we can reuse in many other parts of the toolchain. An important note -- while this likely fixes a large category of cross-compile use cases, that's not my primary goal, and I've not done testing (or added test cases) for scenarios that may now work. If someone else wants to try more interesting PPC cross compiles, I'd love to have reports. But my focus is on factoring away the references to the "host" triple. The refactoring is my goal, and so I'm mostly relying on the existing (pretty good) test coverage we have here. Future patches will leverage this new functionality to factor out more and more of the toolchain's triple manipulation. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@148935 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index b62be8bfdb..428f37cab7 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -1093,15 +1093,31 @@ bool Generic_GCC::GCCVersion::operator<(const GCCVersion &RHS) const { return false; } +// FIXME: Factor this helper into llvm::Triple itself. +static llvm::Triple getMultiarchAlternateTriple(llvm::Triple Triple) { + switch (Triple.getArch()) { + default: break; + case llvm::Triple::x86: Triple.setArchName("x86_64"); break; + case llvm::Triple::x86_64: Triple.setArchName("i386"); break; + case llvm::Triple::ppc: Triple.setArchName("powerpc64"); break; + case llvm::Triple::ppc64: Triple.setArchName("powerpc"); break; + } + return Triple; +} + /// \brief Construct a GCCInstallationDetector from the driver. /// /// This performs all of the autodetection and sets up the various paths. /// Once constructed, a GCCInstallation is esentially immutable. -Generic_GCC::GCCInstallationDetector::GCCInstallationDetector(const Driver &D) - : IsValid(false), - // FIXME: GCCTriple is using the target triple as both the target and host - // triple here. - GCCTriple(D.TargetTriple) { +/// +/// FIXME: We shouldn't need an explicit TargetTriple parameter here, and +/// should instead pull the target out of the driver. This is currently +/// necessary because the driver doesn't store the final version of the target +/// triple. +Generic_GCC::GCCInstallationDetector::GCCInstallationDetector( + const Driver &D, + const llvm::Triple &TargetTriple) + : IsValid(false) { // FIXME: Using CXX_INCLUDE_ROOT is here is a bit of a hack, but // avoids adding yet another option to configure/cmake. // It would probably be cleaner to break it in two variables @@ -1130,17 +1146,17 @@ Generic_GCC::GCCInstallationDetector::GCCInstallationDetector(const Driver &D) return; } - llvm::Triple::ArchType HostArch = GCCTriple.getArch(); + llvm::Triple MultiarchTriple = getMultiarchAlternateTriple(TargetTriple); + llvm::Triple::ArchType TargetArch = TargetTriple.getArch(); // The library directories which may contain GCC installations. - SmallVector CandidateLibDirs; + SmallVector CandidateLibDirs, CandidateMultiarchLibDirs; // The compatible GCC triples for this particular architecture. - SmallVector CandidateTriples; - CollectLibDirsAndTriples(HostArch, CandidateLibDirs, CandidateTriples); - - // Always include the default host triple as the final fallback if no - // specific triple is detected. - // FIXME: This is using the Driver's target triple as the host triple! - CandidateTriples.push_back(D.TargetTriple.str()); + SmallVector CandidateTripleAliases; + SmallVector CandidateMultiarchTripleAliases; + CollectLibDirsAndTriples(TargetTriple, MultiarchTriple, CandidateLibDirs, + CandidateTripleAliases, + CandidateMultiarchLibDirs, + CandidateMultiarchTripleAliases); // Compute the set of prefixes for our search. SmallVector Prefixes(D.PrefixDirs.begin(), @@ -1159,99 +1175,162 @@ Generic_GCC::GCCInstallationDetector::GCCInstallationDetector(const Driver &D) const std::string LibDir = Prefixes[i] + CandidateLibDirs[j].str(); if (!llvm::sys::fs::exists(LibDir)) continue; - for (unsigned k = 0, ke = CandidateTriples.size(); k < ke; ++k) - ScanLibDirForGCCTriple(HostArch, LibDir, CandidateTriples[k]); + for (unsigned k = 0, ke = CandidateTripleAliases.size(); k < ke; ++k) + ScanLibDirForGCCTriple(TargetArch, LibDir, CandidateTripleAliases[k]); + } + for (unsigned j = 0, je = CandidateMultiarchLibDirs.size(); j < je; ++j) { + const std::string LibDir + = Prefixes[i] + CandidateMultiarchLibDirs[j].str(); + if (!llvm::sys::fs::exists(LibDir)) + continue; + for (unsigned k = 0, ke = CandidateMultiarchTripleAliases.size(); k < ke; + ++k) + ScanLibDirForGCCTriple(TargetArch, LibDir, + CandidateMultiarchTripleAliases[k], + /*NeedsMultiarchSuffix=*/true); } } } /*static*/ void Generic_GCC::GCCInstallationDetector::CollectLibDirsAndTriples( - llvm::Triple::ArchType HostArch, SmallVectorImpl &LibDirs, - SmallVectorImpl &Triples) { - if (HostArch == llvm::Triple::arm || HostArch == llvm::Triple::thumb) { - static const char *const ARMLibDirs[] = { "/lib" }; - static const char *const ARMTriples[] = { - "arm-linux-gnueabi", - "arm-linux-androideabi" - }; + const llvm::Triple &TargetTriple, + const llvm::Triple &MultiarchTriple, + SmallVectorImpl &LibDirs, + SmallVectorImpl &TripleAliases, + SmallVectorImpl &MultiarchLibDirs, + SmallVectorImpl &MultiarchTripleAliases) { + // Declare a bunch of static data sets that we'll select between below. These + // are specifically designed to always refer to string literals to avoid any + // lifetime or initialization issues. + static const char *const ARMLibDirs[] = { "/lib" }; + static const char *const ARMTriples[] = { + "arm-linux-gnueabi", + "arm-linux-androideabi" + }; + + static const char *const X86_64LibDirs[] = { "/lib64", "/lib" }; + static const char *const X86_64Triples[] = { + "x86_64-linux-gnu", + "x86_64-unknown-linux-gnu", + "x86_64-pc-linux-gnu", + "x86_64-redhat-linux6E", + "x86_64-redhat-linux", + "x86_64-suse-linux", + "x86_64-manbo-linux-gnu", + "x86_64-linux-gnu", + "x86_64-slackware-linux" + }; + static const char *const X86LibDirs[] = { "/lib32", "/lib" }; + static const char *const X86Triples[] = { + "i686-linux-gnu", + "i686-pc-linux-gnu", + "i486-linux-gnu", + "i386-linux-gnu", + "i686-redhat-linux", + "i586-redhat-linux", + "i386-redhat-linux", + "i586-suse-linux", + "i486-slackware-linux" + }; + + static const char *const MIPSLibDirs[] = { "/lib" }; + static const char *const MIPSTriples[] = { "mips-linux-gnu" }; + static const char *const MIPSELLibDirs[] = { "/lib" }; + static const char *const MIPSELTriples[] = { "mipsel-linux-gnu" }; + + static const char *const PPCLibDirs[] = { "/lib32", "/lib" }; + static const char *const PPCTriples[] = { + "powerpc-linux-gnu", + "powerpc-unknown-linux-gnu", + "powerpc-suse-linux" + }; + static const char *const PPC64LibDirs[] = { "/lib64", "/lib" }; + static const char *const PPC64Triples[] = { + "powerpc64-unknown-linux-gnu", + "powerpc64-suse-linux", + "ppc64-redhat-linux" + }; + + switch (TargetTriple.getArch()) { + case llvm::Triple::arm: + case llvm::Triple::thumb: LibDirs.append(ARMLibDirs, ARMLibDirs + llvm::array_lengthof(ARMLibDirs)); - Triples.append(ARMTriples, ARMTriples + llvm::array_lengthof(ARMTriples)); - } else if (HostArch == llvm::Triple::x86_64) { - static const char *const X86_64LibDirs[] = { "/lib64", "/lib" }; - static const char *const X86_64Triples[] = { - "x86_64-linux-gnu", - "x86_64-unknown-linux-gnu", - "x86_64-pc-linux-gnu", - "x86_64-redhat-linux6E", - "x86_64-redhat-linux", - "x86_64-suse-linux", - "x86_64-manbo-linux-gnu", - "x86_64-linux-gnu", - "x86_64-slackware-linux" - }; - LibDirs.append(X86_64LibDirs, - X86_64LibDirs + llvm::array_lengthof(X86_64LibDirs)); - Triples.append(X86_64Triples, - X86_64Triples + llvm::array_lengthof(X86_64Triples)); - } else if (HostArch == llvm::Triple::x86) { - static const char *const X86LibDirs[] = { "/lib32", "/lib" }; - static const char *const X86Triples[] = { - "i686-linux-gnu", - "i686-pc-linux-gnu", - "i486-linux-gnu", - "i386-linux-gnu", - "i686-redhat-linux", - "i586-redhat-linux", - "i386-redhat-linux", - "i586-suse-linux", - "i486-slackware-linux" - }; + TripleAliases.append( + ARMTriples, ARMTriples + llvm::array_lengthof(ARMTriples)); + break; + case llvm::Triple::x86_64: + LibDirs.append( + X86_64LibDirs, X86_64LibDirs + llvm::array_lengthof(X86_64LibDirs)); + TripleAliases.append( + X86_64Triples, X86_64Triples + llvm::array_lengthof(X86_64Triples)); + MultiarchLibDirs.append( + X86LibDirs, X86LibDirs + llvm::array_lengthof(X86LibDirs)); + MultiarchTripleAliases.append( + X86Triples, X86Triples + llvm::array_lengthof(X86Triples)); + break; + case llvm::Triple::x86: LibDirs.append(X86LibDirs, X86LibDirs + llvm::array_lengthof(X86LibDirs)); - Triples.append(X86Triples, X86Triples + llvm::array_lengthof(X86Triples)); - } else if (HostArch == llvm::Triple::mips) { - static const char *const MIPSLibDirs[] = { "/lib" }; - static const char *const MIPSTriples[] = { "mips-linux-gnu" }; - LibDirs.append(MIPSLibDirs, - MIPSLibDirs + llvm::array_lengthof(MIPSLibDirs)); - Triples.append(MIPSTriples, - MIPSTriples + llvm::array_lengthof(MIPSTriples)); - } else if (HostArch == llvm::Triple::mipsel) { - static const char *const MIPSELLibDirs[] = { "/lib" }; - static const char *const MIPSELTriples[] = { "mipsel-linux-gnu" }; - LibDirs.append(MIPSELLibDirs, - MIPSELLibDirs + llvm::array_lengthof(MIPSELLibDirs)); - Triples.append(MIPSELTriples, - MIPSELTriples + llvm::array_lengthof(MIPSELTriples)); - } else if (HostArch == llvm::Triple::ppc) { - static const char *const PPCLibDirs[] = { "/lib32", "/lib" }; - static const char *const PPCTriples[] = { - "powerpc-linux-gnu", - "powerpc-unknown-linux-gnu", - "powerpc-suse-linux" - }; + TripleAliases.append( + X86Triples, X86Triples + llvm::array_lengthof(X86Triples)); + MultiarchLibDirs.append( + X86_64LibDirs, X86_64LibDirs + llvm::array_lengthof(X86_64LibDirs)); + MultiarchTripleAliases.append( + X86_64Triples, X86_64Triples + llvm::array_lengthof(X86_64Triples)); + break; + case llvm::Triple::mips: + LibDirs.append( + MIPSLibDirs, MIPSLibDirs + llvm::array_lengthof(MIPSLibDirs)); + TripleAliases.append( + MIPSTriples, MIPSTriples + llvm::array_lengthof(MIPSTriples)); + break; + case llvm::Triple::mipsel: + LibDirs.append( + MIPSELLibDirs, MIPSELLibDirs + llvm::array_lengthof(MIPSELLibDirs)); + TripleAliases.append( + MIPSELTriples, MIPSELTriples + llvm::array_lengthof(MIPSELTriples)); + break; + case llvm::Triple::ppc: LibDirs.append(PPCLibDirs, PPCLibDirs + llvm::array_lengthof(PPCLibDirs)); - Triples.append(PPCTriples, PPCTriples + llvm::array_lengthof(PPCTriples)); - } else if (HostArch == llvm::Triple::ppc64) { - static const char *const PPC64LibDirs[] = { "/lib64", "/lib" }; - static const char *const PPC64Triples[] = { - "powerpc64-unknown-linux-gnu", - "powerpc64-suse-linux", - "ppc64-redhat-linux" - }; - LibDirs.append(PPC64LibDirs, - PPC64LibDirs + llvm::array_lengthof(PPC64LibDirs)); - Triples.append(PPC64Triples, - PPC64Triples + llvm::array_lengthof(PPC64Triples)); + TripleAliases.append( + PPCTriples, PPCTriples + llvm::array_lengthof(PPCTriples)); + MultiarchLibDirs.append( + PPC64LibDirs, PPC64LibDirs + llvm::array_lengthof(PPC64LibDirs)); + MultiarchTripleAliases.append( + PPC64Triples, PPC64Triples + llvm::array_lengthof(PPC64Triples)); + break; + case llvm::Triple::ppc64: + LibDirs.append( + PPC64LibDirs, PPC64LibDirs + llvm::array_lengthof(PPC64LibDirs)); + TripleAliases.append( + PPC64Triples, PPC64Triples + llvm::array_lengthof(PPC64Triples)); + MultiarchLibDirs.append( + PPCLibDirs, PPCLibDirs + llvm::array_lengthof(PPCLibDirs)); + MultiarchTripleAliases.append( + PPCTriples, PPCTriples + llvm::array_lengthof(PPCTriples)); + break; + + default: + // By default, just rely on the standard lib directories and the original + // triple. + break; } + + // Always append the drivers target triple to the end, in case it doesn't + // match any of our aliases. + TripleAliases.push_back(TargetTriple.str()); + + // Also include the multiarch variant if it's different. + if (TargetTriple.str() != MultiarchTriple.str()) + MultiarchTripleAliases.push_back(MultiarchTriple.str()); } void Generic_GCC::GCCInstallationDetector::ScanLibDirForGCCTriple( - llvm::Triple::ArchType HostArch, const std::string &LibDir, - StringRef CandidateTriple) { + llvm::Triple::ArchType TargetArch, const std::string &LibDir, + StringRef CandidateTriple, bool NeedsMultiarchSuffix) { // There are various different suffixes involving the triple we // check for. We also record what is necessary to walk from each back // up to the lib directory. - const std::string Suffixes[] = { + const std::string LibSuffixes[] = { "/gcc/" + CandidateTriple.str(), "/" + CandidateTriple.str() + "/gcc/" + CandidateTriple.str(), @@ -1267,12 +1346,12 @@ void Generic_GCC::GCCInstallationDetector::ScanLibDirForGCCTriple( "/../../../.." }; // Only look at the final, weird Ubuntu suffix for i386-linux-gnu. - const unsigned NumSuffixes = (llvm::array_lengthof(Suffixes) - - (HostArch != llvm::Triple::x86)); - for (unsigned i = 0; i < NumSuffixes; ++i) { - StringRef Suffix = Suffixes[i]; + const unsigned NumLibSuffixes = (llvm::array_lengthof(LibSuffixes) - + (TargetArch != llvm::Triple::x86)); + for (unsigned i = 0; i < NumLibSuffixes; ++i) { + StringRef LibSuffix = LibSuffixes[i]; llvm::error_code EC; - for (llvm::sys::fs::directory_iterator LI(LibDir + Suffix, EC), LE; + for (llvm::sys::fs::directory_iterator LI(LibDir + LibSuffix, EC), LE; !EC && LI != LE; LI = LI.increment(EC)) { StringRef VersionText = llvm::sys::path::filename(LI->path()); GCCVersion CandidateVersion = GCCVersion::Parse(VersionText); @@ -1284,31 +1363,36 @@ void Generic_GCC::GCCInstallationDetector::ScanLibDirForGCCTriple( // Some versions of SUSE and Fedora on ppc64 put 32-bit libs // in what would normally be GCCInstallPath and put the 64-bit - // libs in a subdirectory named 64. We need the 64-bit libs - // for linking. - bool UseSlash64 = false; - if (HostArch == llvm::Triple::ppc64 && - llvm::sys::fs::exists(LI->path() + "/64/crtbegin.o")) - UseSlash64 = true; - - if (!llvm::sys::fs::exists(LI->path() + "/crtbegin.o")) - continue; + // libs in a subdirectory named 64. The simple logic we follow is that + // *if* there is a subdirectory of the right name with crtbegin.o in it, + // we use that. If not, and if not a multiarch triple, we look for + // crtbegin.o without the subdirectory. + StringRef MultiarchSuffix + = (TargetArch == llvm::Triple::x86_64 || + TargetArch == llvm::Triple::ppc64) ? "/64" : "/32"; + if (llvm::sys::fs::exists(LI->path() + MultiarchSuffix + "/crtbegin.o")) { + GCCMultiarchSuffix = MultiarchSuffix.str(); + } else { + if (NeedsMultiarchSuffix || + !llvm::sys::fs::exists(LI->path() + "/crtbegin.o")) + continue; + GCCMultiarchSuffix.clear(); + } Version = CandidateVersion; GCCTriple.setTriple(CandidateTriple); // FIXME: We hack together the directory name here instead of // using LI to ensure stable path separators across Windows and // Linux. - GCCInstallPath = LibDir + Suffixes[i] + "/" + VersionText.str(); + GCCInstallPath = LibDir + LibSuffixes[i] + "/" + VersionText.str(); GCCParentLibPath = GCCInstallPath + InstallSuffixes[i]; - if (UseSlash64) GCCInstallPath = GCCInstallPath + "/64"; IsValid = true; } } } Generic_GCC::Generic_GCC(const HostInfo &Host, const llvm::Triple& Triple) - : ToolChain(Host, Triple), GCCInstallation(getDriver()) { + : ToolChain(Host, Triple), GCCInstallation(getDriver(), Triple) { getProgramPaths().push_back(getDriver().getInstalledDir()); if (getDriver().getInstalledDir() != getDriver().Dir) getProgramPaths().push_back(getDriver().Dir); @@ -1970,24 +2054,12 @@ Linux::Linux(const HostInfo &Host, const llvm::Triple &Triple) const std::string MultiarchTriple = getMultiarchTriple(Triple, SysRoot); // Add the multilib suffixed paths where they are available. - bool SuffixedGCCInstallation = false; if (GCCInstallation.isValid()) { - StringRef Suffix32; - StringRef Suffix64; const llvm::Triple &GCCTriple = GCCInstallation.getTriple(); - if (GCCTriple.getArch() == llvm::Triple::x86_64 || - GCCTriple.getArch() == llvm::Triple::ppc64) { - Suffix32 = "/32"; - Suffix64 = ""; - } else { - Suffix32 = ""; - Suffix64 = "/64"; - } - const std::string Suffix = Is32Bits ? Suffix32 : Suffix64; - SuffixedGCCInstallation = !Suffix.empty(); - const std::string &LibPath = GCCInstallation.getParentLibPath(); - addPathIfExists(GCCInstallation.getInstallPath() + Suffix, Paths); + addPathIfExists((GCCInstallation.getInstallPath() + + GCCInstallation.getMultiarchSuffix()), + Paths); addPathIfExists(LibPath + "/../" + GCCTriple.str() + "/lib/../" + Multilib, Paths); addPathIfExists(LibPath + "/" + MultiarchTriple, Paths); @@ -2008,7 +2080,7 @@ Linux::Linux(const HostInfo &Host, const llvm::Triple &Triple) if (GCCInstallation.isValid()) { const std::string &LibPath = GCCInstallation.getParentLibPath(); const llvm::Triple &GCCTriple = GCCInstallation.getTriple(); - if (SuffixedGCCInstallation) + if (!GCCInstallation.getMultiarchSuffix().empty()) addPathIfExists(GCCInstallation.getInstallPath(), Paths); addPathIfExists(LibPath + "/../" + GCCTriple.str() + "/lib", Paths); addPathIfExists(LibPath, Paths); diff --git a/lib/Driver/ToolChains.h b/lib/Driver/ToolChains.h index 6b152f442e..2b7c432e1a 100644 --- a/lib/Driver/ToolChains.h +++ b/lib/Driver/ToolChains.h @@ -73,12 +73,13 @@ protected: // FIXME: These might be better as path objects. std::string GCCInstallPath; + std::string GCCMultiarchSuffix; std::string GCCParentLibPath; GCCVersion Version; public: - GCCInstallationDetector(const Driver &D); + GCCInstallationDetector(const Driver &D, const llvm::Triple &TargetTriple); /// \brief Check whether we detected a valid GCC install. bool isValid() const { return IsValid; } @@ -89,6 +90,9 @@ protected: /// \brief Get the detected GCC installation path. StringRef getInstallPath() const { return GCCInstallPath; } + /// \brief Get the detected GCC installation path suffix for multiarch GCCs. + StringRef getMultiarchSuffix() const { return GCCMultiarchSuffix; } + /// \brief Get the detected GCC parent lib path. StringRef getParentLibPath() const { return GCCParentLibPath; } @@ -96,13 +100,18 @@ protected: StringRef getVersion() const { return Version.Text; } private: - static void CollectLibDirsAndTriples(llvm::Triple::ArchType HostArch, - SmallVectorImpl &LibDirs, - SmallVectorImpl &Triples); - - void ScanLibDirForGCCTriple(llvm::Triple::ArchType HostArch, + static void CollectLibDirsAndTriples( + const llvm::Triple &TargetTriple, + const llvm::Triple &MultiarchTriple, + SmallVectorImpl &LibDirs, + SmallVectorImpl &TripleAliases, + SmallVectorImpl &MultiarchLibDirs, + SmallVectorImpl &MultiarchTripleAliases); + + void ScanLibDirForGCCTriple(llvm::Triple::ArchType TargetArch, const std::string &LibDir, - StringRef CandidateTriple); + StringRef CandidateTriple, + bool NeedsMultiarchSuffix = false); }; GCCInstallationDetector GCCInstallation;