17330f729Sjoerg //===- Multilib.cpp - Multilib Implementation -----------------------------===//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg
97330f729Sjoerg #include "clang/Driver/Multilib.h"
107330f729Sjoerg #include "clang/Basic/LLVM.h"
117330f729Sjoerg #include "llvm/ADT/SmallString.h"
127330f729Sjoerg #include "llvm/ADT/StringMap.h"
137330f729Sjoerg #include "llvm/ADT/StringRef.h"
147330f729Sjoerg #include "llvm/ADT/StringSet.h"
157330f729Sjoerg #include "llvm/Support/Compiler.h"
167330f729Sjoerg #include "llvm/Support/ErrorHandling.h"
177330f729Sjoerg #include "llvm/Support/Path.h"
187330f729Sjoerg #include "llvm/Support/Regex.h"
197330f729Sjoerg #include "llvm/Support/raw_ostream.h"
207330f729Sjoerg #include <algorithm>
217330f729Sjoerg #include <cassert>
227330f729Sjoerg #include <string>
237330f729Sjoerg
247330f729Sjoerg using namespace clang;
257330f729Sjoerg using namespace driver;
267330f729Sjoerg using namespace llvm::sys;
277330f729Sjoerg
287330f729Sjoerg /// normalize Segment to "/foo/bar" or "".
normalizePathSegment(std::string & Segment)297330f729Sjoerg static void normalizePathSegment(std::string &Segment) {
307330f729Sjoerg StringRef seg = Segment;
317330f729Sjoerg
327330f729Sjoerg // Prune trailing "/" or "./"
337330f729Sjoerg while (true) {
347330f729Sjoerg StringRef last = path::filename(seg);
357330f729Sjoerg if (last != ".")
367330f729Sjoerg break;
377330f729Sjoerg seg = path::parent_path(seg);
387330f729Sjoerg }
397330f729Sjoerg
407330f729Sjoerg if (seg.empty() || seg == "/") {
417330f729Sjoerg Segment.clear();
427330f729Sjoerg return;
437330f729Sjoerg }
447330f729Sjoerg
457330f729Sjoerg // Add leading '/'
467330f729Sjoerg if (seg.front() != '/') {
477330f729Sjoerg Segment = "/" + seg.str();
487330f729Sjoerg } else {
49*e038c9c4Sjoerg Segment = std::string(seg);
507330f729Sjoerg }
517330f729Sjoerg }
527330f729Sjoerg
Multilib(StringRef GCCSuffix,StringRef OSSuffix,StringRef IncludeSuffix,int Priority)537330f729Sjoerg Multilib::Multilib(StringRef GCCSuffix, StringRef OSSuffix,
547330f729Sjoerg StringRef IncludeSuffix, int Priority)
557330f729Sjoerg : GCCSuffix(GCCSuffix), OSSuffix(OSSuffix), IncludeSuffix(IncludeSuffix),
567330f729Sjoerg Priority(Priority) {
577330f729Sjoerg normalizePathSegment(this->GCCSuffix);
587330f729Sjoerg normalizePathSegment(this->OSSuffix);
597330f729Sjoerg normalizePathSegment(this->IncludeSuffix);
607330f729Sjoerg }
617330f729Sjoerg
gccSuffix(StringRef S)627330f729Sjoerg Multilib &Multilib::gccSuffix(StringRef S) {
63*e038c9c4Sjoerg GCCSuffix = std::string(S);
647330f729Sjoerg normalizePathSegment(GCCSuffix);
657330f729Sjoerg return *this;
667330f729Sjoerg }
677330f729Sjoerg
osSuffix(StringRef S)687330f729Sjoerg Multilib &Multilib::osSuffix(StringRef S) {
69*e038c9c4Sjoerg OSSuffix = std::string(S);
707330f729Sjoerg normalizePathSegment(OSSuffix);
717330f729Sjoerg return *this;
727330f729Sjoerg }
737330f729Sjoerg
includeSuffix(StringRef S)747330f729Sjoerg Multilib &Multilib::includeSuffix(StringRef S) {
75*e038c9c4Sjoerg IncludeSuffix = std::string(S);
767330f729Sjoerg normalizePathSegment(IncludeSuffix);
777330f729Sjoerg return *this;
787330f729Sjoerg }
797330f729Sjoerg
dump() const807330f729Sjoerg LLVM_DUMP_METHOD void Multilib::dump() const {
817330f729Sjoerg print(llvm::errs());
827330f729Sjoerg }
837330f729Sjoerg
print(raw_ostream & OS) const847330f729Sjoerg void Multilib::print(raw_ostream &OS) const {
857330f729Sjoerg assert(GCCSuffix.empty() || (StringRef(GCCSuffix).front() == '/'));
867330f729Sjoerg if (GCCSuffix.empty())
877330f729Sjoerg OS << ".";
887330f729Sjoerg else {
897330f729Sjoerg OS << StringRef(GCCSuffix).drop_front();
907330f729Sjoerg }
917330f729Sjoerg OS << ";";
927330f729Sjoerg for (StringRef Flag : Flags) {
937330f729Sjoerg if (Flag.front() == '+')
947330f729Sjoerg OS << "@" << Flag.substr(1);
957330f729Sjoerg }
967330f729Sjoerg }
977330f729Sjoerg
isValid() const987330f729Sjoerg bool Multilib::isValid() const {
997330f729Sjoerg llvm::StringMap<int> FlagSet;
1007330f729Sjoerg for (unsigned I = 0, N = Flags.size(); I != N; ++I) {
1017330f729Sjoerg StringRef Flag(Flags[I]);
1027330f729Sjoerg llvm::StringMap<int>::iterator SI = FlagSet.find(Flag.substr(1));
1037330f729Sjoerg
1047330f729Sjoerg assert(StringRef(Flag).front() == '+' || StringRef(Flag).front() == '-');
1057330f729Sjoerg
1067330f729Sjoerg if (SI == FlagSet.end())
1077330f729Sjoerg FlagSet[Flag.substr(1)] = I;
1087330f729Sjoerg else if (Flags[I] != Flags[SI->getValue()])
1097330f729Sjoerg return false;
1107330f729Sjoerg }
1117330f729Sjoerg return true;
1127330f729Sjoerg }
1137330f729Sjoerg
operator ==(const Multilib & Other) const1147330f729Sjoerg bool Multilib::operator==(const Multilib &Other) const {
1157330f729Sjoerg // Check whether the flags sets match
1167330f729Sjoerg // allowing for the match to be order invariant
1177330f729Sjoerg llvm::StringSet<> MyFlags;
1187330f729Sjoerg for (const auto &Flag : Flags)
1197330f729Sjoerg MyFlags.insert(Flag);
1207330f729Sjoerg
1217330f729Sjoerg for (const auto &Flag : Other.Flags)
1227330f729Sjoerg if (MyFlags.find(Flag) == MyFlags.end())
1237330f729Sjoerg return false;
1247330f729Sjoerg
1257330f729Sjoerg if (osSuffix() != Other.osSuffix())
1267330f729Sjoerg return false;
1277330f729Sjoerg
1287330f729Sjoerg if (gccSuffix() != Other.gccSuffix())
1297330f729Sjoerg return false;
1307330f729Sjoerg
1317330f729Sjoerg if (includeSuffix() != Other.includeSuffix())
1327330f729Sjoerg return false;
1337330f729Sjoerg
1347330f729Sjoerg return true;
1357330f729Sjoerg }
1367330f729Sjoerg
operator <<(raw_ostream & OS,const Multilib & M)1377330f729Sjoerg raw_ostream &clang::driver::operator<<(raw_ostream &OS, const Multilib &M) {
1387330f729Sjoerg M.print(OS);
1397330f729Sjoerg return OS;
1407330f729Sjoerg }
1417330f729Sjoerg
Maybe(const Multilib & M)1427330f729Sjoerg MultilibSet &MultilibSet::Maybe(const Multilib &M) {
1437330f729Sjoerg Multilib Opposite;
1447330f729Sjoerg // Negate any '+' flags
1457330f729Sjoerg for (StringRef Flag : M.flags()) {
1467330f729Sjoerg if (Flag.front() == '+')
1477330f729Sjoerg Opposite.flags().push_back(("-" + Flag.substr(1)).str());
1487330f729Sjoerg }
1497330f729Sjoerg return Either(M, Opposite);
1507330f729Sjoerg }
1517330f729Sjoerg
Either(const Multilib & M1,const Multilib & M2)1527330f729Sjoerg MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2) {
1537330f729Sjoerg return Either({M1, M2});
1547330f729Sjoerg }
1557330f729Sjoerg
Either(const Multilib & M1,const Multilib & M2,const Multilib & M3)1567330f729Sjoerg MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2,
1577330f729Sjoerg const Multilib &M3) {
1587330f729Sjoerg return Either({M1, M2, M3});
1597330f729Sjoerg }
1607330f729Sjoerg
Either(const Multilib & M1,const Multilib & M2,const Multilib & M3,const Multilib & M4)1617330f729Sjoerg MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2,
1627330f729Sjoerg const Multilib &M3, const Multilib &M4) {
1637330f729Sjoerg return Either({M1, M2, M3, M4});
1647330f729Sjoerg }
1657330f729Sjoerg
Either(const Multilib & M1,const Multilib & M2,const Multilib & M3,const Multilib & M4,const Multilib & M5)1667330f729Sjoerg MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2,
1677330f729Sjoerg const Multilib &M3, const Multilib &M4,
1687330f729Sjoerg const Multilib &M5) {
1697330f729Sjoerg return Either({M1, M2, M3, M4, M5});
1707330f729Sjoerg }
1717330f729Sjoerg
compose(const Multilib & Base,const Multilib & New)1727330f729Sjoerg static Multilib compose(const Multilib &Base, const Multilib &New) {
1737330f729Sjoerg SmallString<128> GCCSuffix;
1747330f729Sjoerg llvm::sys::path::append(GCCSuffix, "/", Base.gccSuffix(), New.gccSuffix());
1757330f729Sjoerg SmallString<128> OSSuffix;
1767330f729Sjoerg llvm::sys::path::append(OSSuffix, "/", Base.osSuffix(), New.osSuffix());
1777330f729Sjoerg SmallString<128> IncludeSuffix;
1787330f729Sjoerg llvm::sys::path::append(IncludeSuffix, "/", Base.includeSuffix(),
1797330f729Sjoerg New.includeSuffix());
1807330f729Sjoerg
1817330f729Sjoerg Multilib Composed(GCCSuffix, OSSuffix, IncludeSuffix);
1827330f729Sjoerg
1837330f729Sjoerg Multilib::flags_list &Flags = Composed.flags();
1847330f729Sjoerg
1857330f729Sjoerg Flags.insert(Flags.end(), Base.flags().begin(), Base.flags().end());
1867330f729Sjoerg Flags.insert(Flags.end(), New.flags().begin(), New.flags().end());
1877330f729Sjoerg
1887330f729Sjoerg return Composed;
1897330f729Sjoerg }
1907330f729Sjoerg
Either(ArrayRef<Multilib> MultilibSegments)1917330f729Sjoerg MultilibSet &MultilibSet::Either(ArrayRef<Multilib> MultilibSegments) {
1927330f729Sjoerg multilib_list Composed;
1937330f729Sjoerg
1947330f729Sjoerg if (Multilibs.empty())
1957330f729Sjoerg Multilibs.insert(Multilibs.end(), MultilibSegments.begin(),
1967330f729Sjoerg MultilibSegments.end());
1977330f729Sjoerg else {
1987330f729Sjoerg for (const auto &New : MultilibSegments) {
1997330f729Sjoerg for (const auto &Base : *this) {
2007330f729Sjoerg Multilib MO = compose(Base, New);
2017330f729Sjoerg if (MO.isValid())
2027330f729Sjoerg Composed.push_back(MO);
2037330f729Sjoerg }
2047330f729Sjoerg }
2057330f729Sjoerg
2067330f729Sjoerg Multilibs = Composed;
2077330f729Sjoerg }
2087330f729Sjoerg
2097330f729Sjoerg return *this;
2107330f729Sjoerg }
2117330f729Sjoerg
FilterOut(FilterCallback F)2127330f729Sjoerg MultilibSet &MultilibSet::FilterOut(FilterCallback F) {
2137330f729Sjoerg filterInPlace(F, Multilibs);
2147330f729Sjoerg return *this;
2157330f729Sjoerg }
2167330f729Sjoerg
FilterOut(const char * Regex)2177330f729Sjoerg MultilibSet &MultilibSet::FilterOut(const char *Regex) {
2187330f729Sjoerg llvm::Regex R(Regex);
2197330f729Sjoerg #ifndef NDEBUG
2207330f729Sjoerg std::string Error;
2217330f729Sjoerg if (!R.isValid(Error)) {
2227330f729Sjoerg llvm::errs() << Error;
2237330f729Sjoerg llvm_unreachable("Invalid regex!");
2247330f729Sjoerg }
2257330f729Sjoerg #endif
2267330f729Sjoerg
2277330f729Sjoerg filterInPlace([&R](const Multilib &M) { return R.match(M.gccSuffix()); },
2287330f729Sjoerg Multilibs);
2297330f729Sjoerg return *this;
2307330f729Sjoerg }
2317330f729Sjoerg
push_back(const Multilib & M)2327330f729Sjoerg void MultilibSet::push_back(const Multilib &M) { Multilibs.push_back(M); }
2337330f729Sjoerg
combineWith(const MultilibSet & Other)2347330f729Sjoerg void MultilibSet::combineWith(const MultilibSet &Other) {
2357330f729Sjoerg Multilibs.insert(Multilibs.end(), Other.begin(), Other.end());
2367330f729Sjoerg }
2377330f729Sjoerg
isFlagEnabled(StringRef Flag)2387330f729Sjoerg static bool isFlagEnabled(StringRef Flag) {
2397330f729Sjoerg char Indicator = Flag.front();
2407330f729Sjoerg assert(Indicator == '+' || Indicator == '-');
2417330f729Sjoerg return Indicator == '+';
2427330f729Sjoerg }
2437330f729Sjoerg
select(const Multilib::flags_list & Flags,Multilib & M) const2447330f729Sjoerg bool MultilibSet::select(const Multilib::flags_list &Flags, Multilib &M) const {
2457330f729Sjoerg llvm::StringMap<bool> FlagSet;
2467330f729Sjoerg
2477330f729Sjoerg // Stuff all of the flags into the FlagSet such that a true mappend indicates
2487330f729Sjoerg // the flag was enabled, and a false mappend indicates the flag was disabled.
2497330f729Sjoerg for (StringRef Flag : Flags)
2507330f729Sjoerg FlagSet[Flag.substr(1)] = isFlagEnabled(Flag);
2517330f729Sjoerg
2527330f729Sjoerg multilib_list Filtered = filterCopy([&FlagSet](const Multilib &M) {
2537330f729Sjoerg for (StringRef Flag : M.flags()) {
2547330f729Sjoerg llvm::StringMap<bool>::const_iterator SI = FlagSet.find(Flag.substr(1));
2557330f729Sjoerg if (SI != FlagSet.end())
2567330f729Sjoerg if (SI->getValue() != isFlagEnabled(Flag))
2577330f729Sjoerg return true;
2587330f729Sjoerg }
2597330f729Sjoerg return false;
2607330f729Sjoerg }, Multilibs);
2617330f729Sjoerg
2627330f729Sjoerg if (Filtered.empty())
2637330f729Sjoerg return false;
2647330f729Sjoerg if (Filtered.size() == 1) {
2657330f729Sjoerg M = Filtered[0];
2667330f729Sjoerg return true;
2677330f729Sjoerg }
2687330f729Sjoerg
2697330f729Sjoerg // Sort multilibs by priority and select the one with the highest priority.
2707330f729Sjoerg llvm::sort(Filtered.begin(), Filtered.end(),
2717330f729Sjoerg [](const Multilib &a, const Multilib &b) -> bool {
2727330f729Sjoerg return a.priority() > b.priority();
2737330f729Sjoerg });
2747330f729Sjoerg
2757330f729Sjoerg if (Filtered[0].priority() > Filtered[1].priority()) {
2767330f729Sjoerg M = Filtered[0];
2777330f729Sjoerg return true;
2787330f729Sjoerg }
2797330f729Sjoerg
2807330f729Sjoerg // TODO: We should consider returning llvm::Error rather than aborting.
2817330f729Sjoerg assert(false && "More than one multilib with the same priority");
2827330f729Sjoerg return false;
2837330f729Sjoerg }
2847330f729Sjoerg
dump() const2857330f729Sjoerg LLVM_DUMP_METHOD void MultilibSet::dump() const {
2867330f729Sjoerg print(llvm::errs());
2877330f729Sjoerg }
2887330f729Sjoerg
print(raw_ostream & OS) const2897330f729Sjoerg void MultilibSet::print(raw_ostream &OS) const {
2907330f729Sjoerg for (const auto &M : *this)
2917330f729Sjoerg OS << M << "\n";
2927330f729Sjoerg }
2937330f729Sjoerg
filterCopy(FilterCallback F,const multilib_list & Ms)2947330f729Sjoerg MultilibSet::multilib_list MultilibSet::filterCopy(FilterCallback F,
2957330f729Sjoerg const multilib_list &Ms) {
2967330f729Sjoerg multilib_list Copy(Ms);
2977330f729Sjoerg filterInPlace(F, Copy);
2987330f729Sjoerg return Copy;
2997330f729Sjoerg }
3007330f729Sjoerg
filterInPlace(FilterCallback F,multilib_list & Ms)3017330f729Sjoerg void MultilibSet::filterInPlace(FilterCallback F, multilib_list &Ms) {
3027330f729Sjoerg Ms.erase(std::remove_if(Ms.begin(), Ms.end(), F), Ms.end());
3037330f729Sjoerg }
3047330f729Sjoerg
operator <<(raw_ostream & OS,const MultilibSet & MS)3057330f729Sjoerg raw_ostream &clang::driver::operator<<(raw_ostream &OS, const MultilibSet &MS) {
3067330f729Sjoerg MS.print(OS);
3077330f729Sjoerg return OS;
3087330f729Sjoerg }
309