| //===-- GccUtils.cpp ------------------------------------------------------===// |
| // |
| // Copyright 2018 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Gcc installation detector and related code. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include <sstream> |
| |
| #include "GccUtils.h" |
| |
| #include "llvm/Support/FileSystem.h" |
| |
| namespace gnutools { |
| |
| namespace gccdetect { |
| |
| stringvec InspectRealFS::scanDir(const std::string &dir) |
| { |
| stringvec result; |
| std::error_code ec; |
| llvm::sys::fs::directory_iterator dend, dit(dir, ec); |
| if (ec) |
| return stringvec(); |
| while (dit != dend) { |
| result.push_back(dit->path()); |
| dit.increment(ec); |
| if (ec) |
| return stringvec(); |
| } |
| return result; |
| } |
| |
| bool InspectRealFS::is_directory(const std::string &path) |
| { |
| return llvm::sys::fs::is_directory(path); |
| } |
| |
| bool InspectRealFS::exists(const std::string &path) |
| { |
| return llvm::sys::fs::exists(path); |
| } |
| |
| //........................................................................ |
| |
| GCCVersion::GCCVersion() |
| : maj_(-1), min_(-1) |
| { |
| } |
| |
| // Break up a string into (up to) three chunks/tokens based on a specific |
| // delimitor. |
| |
| static llvm::SmallVector<std::string, 4> tokenize3(const std::string &s, |
| char delim) |
| { |
| llvm::SmallVector<std::string, 4> result; |
| size_t cur; |
| size_t nxt = -1; |
| for (unsigned chunks = 0; chunks < 2; ++ chunks) { |
| cur = nxt + 1; |
| nxt = s.find_first_of(delim, cur); |
| size_t rem = (nxt == std::string::npos ? nxt : nxt - cur); |
| result.push_back(s.substr(cur, rem)); |
| if (nxt == std::string::npos) |
| break; |
| } |
| if (nxt != std::string::npos) |
| result.push_back(s.substr(nxt+1, std::string::npos)); |
| return result; |
| } |
| |
| GCCVersion GCCVersion::parse(llvm::StringRef vtext) |
| { |
| const GCCVersion bad; |
| GCCVersion result; |
| std::string vts(vtext.str()); |
| auto tokens = tokenize3(vts, '.'); |
| |
| // Major version. |
| if (tokens.empty() || |
| llvm::StringRef(tokens[0]).getAsInteger(10, result.maj_) || |
| result.maj_ < 4) |
| return bad; |
| result.text_ = vtext.str(); |
| if (tokens.size() == 1) |
| return result; |
| |
| // Minor version |
| if (llvm::StringRef(tokens[1]).getAsInteger(10, result.min_) || |
| result.min_ < 0) |
| return bad; |
| if (tokens.size() == 2) |
| return result; |
| |
| // Store away remainder (patch or equivalent). |
| result.remainder_ = tokens[2]; |
| return result; |
| } |
| |
| bool GCCVersion::operator<(const GCCVersion &rhs) const |
| { |
| if (maj_ != rhs.maj_) |
| return maj_ < rhs.maj_; |
| if (min_ != rhs.min_) |
| return min_ < rhs.min_; |
| return remainder_.compare(rhs.remainder_) < 0; |
| } |
| |
| std::string GCCVersion::toString() const |
| { |
| std::stringstream ss; |
| ss << "maj=" << maj_ << " min=" << min_ << " rem=" << remainder_; |
| return ss.str(); |
| } |
| |
| //........................................................................ |
| |
| GCCInstallationDetector:: |
| GCCInstallationDetector(const llvm::Triple &targetTriple, |
| const std::string &gccToolchainDir, |
| const std::string &sysroot, |
| InspectFS &inspector) |
| : triple_(targetTriple), |
| gccToolchainDir_(gccToolchainDir), |
| sysroot_(sysroot), |
| inspector_(inspector), |
| version_(new GCCVersion()) |
| { |
| } |
| |
| struct GCCInstallationDetector::state { |
| // requested triple |
| llvm::Triple triple; |
| // list of triple aliases to use when searching |
| std::vector<std::string> tripleAliases; |
| // libdirs to look for within prefix dir |
| stringvec libdirs; |
| // biarch suffixes (or empty string) |
| std::vector<std::string> suffixes; |
| // best version so far |
| GCCVersion version; |
| // path to best verison |
| std::string candidate; |
| // found suffix |
| std::string suffix; |
| // found triple |
| llvm::Triple candTriple; |
| }; |
| |
| bool GCCInstallationDetector::selectLibDirs(state &s) |
| { |
| // TODO: add more cases as additional architectures brought on. |
| |
| switch (triple_.getArch()) { |
| case llvm::Triple::x86: |
| case llvm::Triple::x86_64: |
| s.tripleAliases = { |
| triple_.str(), |
| "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" |
| }; |
| s.libdirs.push_back("lib"); |
| s.libdirs.push_back("lib64"); |
| if (triple_.getArch() == llvm::Triple::x86) { |
| s.triple.setTriple(triple_.get64BitArchVariant().str()); |
| s.suffixes = {"/32", "/lib32"}; |
| } else { |
| s.triple.setTriple(triple_.str()); |
| s.suffixes = {""}; |
| } |
| break; |
| case llvm::Triple::aarch64: |
| // more triples to be identified and added |
| s.tripleAliases = { |
| triple_.str(), |
| "aarch64-linux-gnu", "aarch64-unknown-linux-gnu", |
| "aarch64-pc-linux-gnu", "aarch64-redhat-linux", |
| "aarch64-suse-linux" |
| }; |
| // multilib is not supported on major aarch64/arm64 linux distributions |
| // subject to change when more scenarios to be taken into account |
| s.libdirs.push_back("lib"); |
| s.triple.setTriple(triple_.str()); |
| s.suffixes = {""}; |
| break; |
| default: |
| llvm::errs() << "error: unsupported triple " |
| << triple_.str() << " in " << __FUNCTION__ << "\n"; |
| assert(false); |
| return false; |
| } |
| return true; |
| } |
| |
| // From comments in Clang it appears that there are some Linux systems |
| // that do not use crtbegin.o; if need be this could be switched to libgcc.a. |
| |
| bool GCCInstallationDetector::validCandidate(const std::string &cand, |
| const std::string &suffix) |
| { |
| // is there an actual installation? |
| std::stringstream ss; |
| ss << cand << suffix << "/crtbegin.o"; |
| std::string crtb = ss.str(); |
| if (!inspector_.exists(crtb)) |
| return false; |
| return true; |
| } |
| |
| void GCCInstallationDetector::scanCandidate(state &s, |
| const std::string &cand, |
| const std::string &alias, |
| const std::string &suffix) |
| { |
| stringvec dirents = inspector_.scanDir(cand); |
| for (auto &ent : dirents) { |
| std::size_t pos = ent.find_last_of('/'); |
| assert(pos != std::string::npos); |
| std::string base(ent.substr(pos+1)); |
| // gcc version valid? |
| GCCVersion v = GCCVersion::parse(base); |
| if (!v.valid()) |
| continue; |
| if (!validCandidate(ent, suffix)) |
| continue; |
| // more recent than previously found version? |
| if (s.version < v) { |
| s.version = v; |
| s.candidate = ent; |
| s.suffix = suffix; |
| s.candTriple.setTriple(alias); |
| } |
| } |
| } |
| |
| void GCCInstallationDetector::scanPrefix(state &s, const std::string &pref) |
| { |
| const char *infixes[] = { "/gcc/", "/gcc-cross/" }; |
| for (auto &lib : s.libdirs) { |
| // Form candidate dir |
| for (auto &infix : infixes) { |
| for (auto &tripleAlias : s.tripleAliases) { |
| std::stringstream ss; |
| ss << pref << "/" << lib << infix << tripleAlias; |
| for (auto &suffix : s.suffixes) |
| scanCandidate(s, ss.str(), tripleAlias, suffix); |
| } |
| } |
| } |
| } |
| |
| void GCCInstallationDetector::init() |
| { |
| state s; |
| |
| // Setup |
| if (!selectLibDirs(s)) |
| return; |
| |
| if (!gccToolchainDir_.empty()) |
| scanPrefix(s, gccToolchainDir_); |
| |
| if (!s.version.valid() && !sysroot_.empty()) { |
| // If sysroot is non-empty, then perform a search there first. |
| std::string srp(sysroot_); |
| srp += "/usr"; |
| scanPrefix(s, srp); |
| } |
| |
| // If we found something viable in the sysroot, then don't look any |
| // farther (this differs from clang AFAICT). The intent here is that |
| // if the sysroot provides GCC X but the system has GCC X+1, we |
| // still want to use the version in the sysroot. |
| if (!s.version.valid()) { |
| // Examine the host system. |
| scanPrefix(s, "/usr"); |
| } |
| |
| // Incorporate results. |
| if (s.version.valid()) { |
| *version_ = s.version; |
| foundTriple_.setTriple(s.candTriple.str()); |
| installPath_ = s.candidate; |
| libPath_ = s.candidate; |
| libPath_ += s.suffix; |
| parentLibPath_ = installPath_ + "/../.." + |
| (s.suffix.empty() ? "" : "/.."); |
| } |
| } |
| |
| std::string GCCInstallationDetector::toString() |
| { |
| std::stringstream ss; |
| ss << "version: " << version_->text() << "\n"; |
| ss << "foundTriple: " << foundTriple_.str() << "\n"; |
| ss << "libPath: " << libPath_ << "\n"; |
| ss << "parentLibPath: " << parentLibPath_ << "\n"; |
| ss << "installPath: " << installPath_ << "\n"; |
| return ss.str(); |
| } |
| |
| } // end namespace gccdetect |
| |
| } // end namespace gnutools |