15e4511cfSEugene Zelenko //===- Multilib.cpp - Multilib Implementation -----------------------------===//
22cea1beaSJonathan Roelofs //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
62cea1beaSJonathan Roelofs //
72cea1beaSJonathan Roelofs //===----------------------------------------------------------------------===//
82cea1beaSJonathan Roelofs 
92cea1beaSJonathan Roelofs #include "clang/Driver/Multilib.h"
105e4511cfSEugene Zelenko #include "clang/Basic/LLVM.h"
115e4511cfSEugene Zelenko #include "llvm/ADT/SmallString.h"
122cea1beaSJonathan Roelofs #include "llvm/ADT/StringMap.h"
132cea1beaSJonathan Roelofs #include "llvm/ADT/StringRef.h"
142cea1beaSJonathan Roelofs #include "llvm/ADT/StringSet.h"
155e4511cfSEugene Zelenko #include "llvm/Support/Compiler.h"
165e4511cfSEugene Zelenko #include "llvm/Support/ErrorHandling.h"
172cea1beaSJonathan Roelofs #include "llvm/Support/Path.h"
182cea1beaSJonathan Roelofs #include "llvm/Support/Regex.h"
19757fcd6dSChandler Carruth #include "llvm/Support/raw_ostream.h"
202cea1beaSJonathan Roelofs #include <algorithm>
215e4511cfSEugene Zelenko #include <cassert>
225e4511cfSEugene Zelenko #include <string>
232cea1beaSJonathan Roelofs 
242cea1beaSJonathan Roelofs using namespace clang;
255e4511cfSEugene Zelenko using namespace driver;
26691e6ef4SNAKAMURA Takumi using namespace llvm::sys;
272cea1beaSJonathan Roelofs 
28691e6ef4SNAKAMURA Takumi /// normalize Segment to "/foo/bar" or "".
normalizePathSegment(std::string & Segment)292cea1beaSJonathan Roelofs static void normalizePathSegment(std::string &Segment) {
30691e6ef4SNAKAMURA Takumi   StringRef seg = Segment;
31691e6ef4SNAKAMURA Takumi 
32691e6ef4SNAKAMURA Takumi   // Prune trailing "/" or "./"
335e4511cfSEugene Zelenko   while (true) {
346bcf724fSJustin Bogner     StringRef last = path::filename(seg);
35691e6ef4SNAKAMURA Takumi     if (last != ".")
36691e6ef4SNAKAMURA Takumi       break;
37691e6ef4SNAKAMURA Takumi     seg = path::parent_path(seg);
38691e6ef4SNAKAMURA Takumi   }
39691e6ef4SNAKAMURA Takumi 
40691e6ef4SNAKAMURA Takumi   if (seg.empty() || seg == "/") {
415e4511cfSEugene Zelenko     Segment.clear();
42691e6ef4SNAKAMURA Takumi     return;
43691e6ef4SNAKAMURA Takumi   }
44691e6ef4SNAKAMURA Takumi 
45691e6ef4SNAKAMURA Takumi   // Add leading '/'
46691e6ef4SNAKAMURA Takumi   if (seg.front() != '/') {
47691e6ef4SNAKAMURA Takumi     Segment = "/" + seg.str();
482cea1beaSJonathan Roelofs   } else {
49adcd0268SBenjamin Kramer     Segment = std::string(seg);
502cea1beaSJonathan Roelofs   }
512cea1beaSJonathan Roelofs }
522cea1beaSJonathan Roelofs 
Multilib(StringRef GCCSuffix,StringRef OSSuffix,StringRef IncludeSuffix,int Priority)532cea1beaSJonathan Roelofs Multilib::Multilib(StringRef GCCSuffix, StringRef OSSuffix,
540f9f021dSPetr Hosek                    StringRef IncludeSuffix, int Priority)
550f9f021dSPetr Hosek     : GCCSuffix(GCCSuffix), OSSuffix(OSSuffix), IncludeSuffix(IncludeSuffix),
560f9f021dSPetr Hosek       Priority(Priority) {
572cea1beaSJonathan Roelofs   normalizePathSegment(this->GCCSuffix);
582cea1beaSJonathan Roelofs   normalizePathSegment(this->OSSuffix);
592cea1beaSJonathan Roelofs   normalizePathSegment(this->IncludeSuffix);
602cea1beaSJonathan Roelofs }
612cea1beaSJonathan Roelofs 
gccSuffix(StringRef S)622cea1beaSJonathan Roelofs Multilib &Multilib::gccSuffix(StringRef S) {
63adcd0268SBenjamin Kramer   GCCSuffix = std::string(S);
642cea1beaSJonathan Roelofs   normalizePathSegment(GCCSuffix);
652cea1beaSJonathan Roelofs   return *this;
662cea1beaSJonathan Roelofs }
672cea1beaSJonathan Roelofs 
osSuffix(StringRef S)682cea1beaSJonathan Roelofs Multilib &Multilib::osSuffix(StringRef S) {
69adcd0268SBenjamin Kramer   OSSuffix = std::string(S);
702cea1beaSJonathan Roelofs   normalizePathSegment(OSSuffix);
712cea1beaSJonathan Roelofs   return *this;
722cea1beaSJonathan Roelofs }
732cea1beaSJonathan Roelofs 
includeSuffix(StringRef S)742cea1beaSJonathan Roelofs Multilib &Multilib::includeSuffix(StringRef S) {
75adcd0268SBenjamin Kramer   IncludeSuffix = std::string(S);
762cea1beaSJonathan Roelofs   normalizePathSegment(IncludeSuffix);
772cea1beaSJonathan Roelofs   return *this;
782cea1beaSJonathan Roelofs }
792cea1beaSJonathan Roelofs 
dump() const80c8e377c3SJonathan Roelofs LLVM_DUMP_METHOD void Multilib::dump() const {
81c8e377c3SJonathan Roelofs   print(llvm::errs());
82c8e377c3SJonathan Roelofs }
83c8e377c3SJonathan Roelofs 
print(raw_ostream & OS) const842cea1beaSJonathan Roelofs void Multilib::print(raw_ostream &OS) const {
852cea1beaSJonathan Roelofs   assert(GCCSuffix.empty() || (StringRef(GCCSuffix).front() == '/'));
862cea1beaSJonathan Roelofs   if (GCCSuffix.empty())
872cea1beaSJonathan Roelofs     OS << ".";
882cea1beaSJonathan Roelofs   else {
892cea1beaSJonathan Roelofs     OS << StringRef(GCCSuffix).drop_front();
902cea1beaSJonathan Roelofs   }
912cea1beaSJonathan Roelofs   OS << ";";
926f657c46SSimon Atanasyan   for (StringRef Flag : Flags) {
936f657c46SSimon Atanasyan     if (Flag.front() == '+')
946f657c46SSimon Atanasyan       OS << "@" << Flag.substr(1);
952cea1beaSJonathan Roelofs   }
962cea1beaSJonathan Roelofs }
972cea1beaSJonathan Roelofs 
isValid() const982cea1beaSJonathan Roelofs bool Multilib::isValid() const {
992cea1beaSJonathan Roelofs   llvm::StringMap<int> FlagSet;
1002cea1beaSJonathan Roelofs   for (unsigned I = 0, N = Flags.size(); I != N; ++I) {
1012cea1beaSJonathan Roelofs     StringRef Flag(Flags[I]);
1022cea1beaSJonathan Roelofs     llvm::StringMap<int>::iterator SI = FlagSet.find(Flag.substr(1));
1032cea1beaSJonathan Roelofs 
1042cea1beaSJonathan Roelofs     assert(StringRef(Flag).front() == '+' || StringRef(Flag).front() == '-');
1052cea1beaSJonathan Roelofs 
1062cea1beaSJonathan Roelofs     if (SI == FlagSet.end())
1072cea1beaSJonathan Roelofs       FlagSet[Flag.substr(1)] = I;
1082cea1beaSJonathan Roelofs     else if (Flags[I] != Flags[SI->getValue()])
1092cea1beaSJonathan Roelofs       return false;
1102cea1beaSJonathan Roelofs   }
1112cea1beaSJonathan Roelofs   return true;
1122cea1beaSJonathan Roelofs }
1132cea1beaSJonathan Roelofs 
operator ==(const Multilib & Other) const1142cea1beaSJonathan Roelofs bool Multilib::operator==(const Multilib &Other) const {
1152cea1beaSJonathan Roelofs   // Check whether the flags sets match
1162cea1beaSJonathan Roelofs   // allowing for the match to be order invariant
1172cea1beaSJonathan Roelofs   llvm::StringSet<> MyFlags;
1186f657c46SSimon Atanasyan   for (const auto &Flag : Flags)
1196f657c46SSimon Atanasyan     MyFlags.insert(Flag);
1206f657c46SSimon Atanasyan 
1216f657c46SSimon Atanasyan   for (const auto &Flag : Other.Flags)
1226f657c46SSimon Atanasyan     if (MyFlags.find(Flag) == MyFlags.end())
1232cea1beaSJonathan Roelofs       return false;
1242cea1beaSJonathan Roelofs 
1252cea1beaSJonathan Roelofs   if (osSuffix() != Other.osSuffix())
1262cea1beaSJonathan Roelofs     return false;
1272cea1beaSJonathan Roelofs 
1282cea1beaSJonathan Roelofs   if (gccSuffix() != Other.gccSuffix())
1292cea1beaSJonathan Roelofs     return false;
1302cea1beaSJonathan Roelofs 
1312cea1beaSJonathan Roelofs   if (includeSuffix() != Other.includeSuffix())
1322cea1beaSJonathan Roelofs     return false;
1332cea1beaSJonathan Roelofs 
1342cea1beaSJonathan Roelofs   return true;
1352cea1beaSJonathan Roelofs }
1362cea1beaSJonathan Roelofs 
operator <<(raw_ostream & OS,const Multilib & M)1372cea1beaSJonathan Roelofs raw_ostream &clang::driver::operator<<(raw_ostream &OS, const Multilib &M) {
1382cea1beaSJonathan Roelofs   M.print(OS);
1392cea1beaSJonathan Roelofs   return OS;
1402cea1beaSJonathan Roelofs }
1412cea1beaSJonathan Roelofs 
Maybe(const Multilib & M)1422cea1beaSJonathan Roelofs MultilibSet &MultilibSet::Maybe(const Multilib &M) {
1432cea1beaSJonathan Roelofs   Multilib Opposite;
1442cea1beaSJonathan Roelofs   // Negate any '+' flags
1456f657c46SSimon Atanasyan   for (StringRef Flag : M.flags()) {
1462cea1beaSJonathan Roelofs     if (Flag.front() == '+')
1472cea1beaSJonathan Roelofs       Opposite.flags().push_back(("-" + Flag.substr(1)).str());
1482cea1beaSJonathan Roelofs   }
1492cea1beaSJonathan Roelofs   return Either(M, Opposite);
1502cea1beaSJonathan Roelofs }
1512cea1beaSJonathan Roelofs 
Either(const Multilib & M1,const Multilib & M2)1522cea1beaSJonathan Roelofs MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2) {
153ac75baaaSBenjamin Kramer   return Either({M1, M2});
1542cea1beaSJonathan Roelofs }
1552cea1beaSJonathan Roelofs 
Either(const Multilib & M1,const Multilib & M2,const Multilib & M3)1562cea1beaSJonathan Roelofs MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2,
1572cea1beaSJonathan Roelofs                                  const Multilib &M3) {
158ac75baaaSBenjamin Kramer   return Either({M1, M2, M3});
1592cea1beaSJonathan Roelofs }
1602cea1beaSJonathan Roelofs 
Either(const Multilib & M1,const Multilib & M2,const Multilib & M3,const Multilib & M4)1612cea1beaSJonathan Roelofs MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2,
1622cea1beaSJonathan Roelofs                                  const Multilib &M3, const Multilib &M4) {
163ac75baaaSBenjamin Kramer   return Either({M1, M2, M3, M4});
1642cea1beaSJonathan Roelofs }
1652cea1beaSJonathan Roelofs 
Either(const Multilib & M1,const Multilib & M2,const Multilib & M3,const Multilib & M4,const Multilib & M5)1662cea1beaSJonathan Roelofs MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2,
1672cea1beaSJonathan Roelofs                                  const Multilib &M3, const Multilib &M4,
1682cea1beaSJonathan Roelofs                                  const Multilib &M5) {
169ac75baaaSBenjamin Kramer   return Either({M1, M2, M3, M4, M5});
1702cea1beaSJonathan Roelofs }
1712cea1beaSJonathan Roelofs 
compose(const Multilib & Base,const Multilib & New)1722cea1beaSJonathan Roelofs static Multilib compose(const Multilib &Base, const Multilib &New) {
1732cea1beaSJonathan Roelofs   SmallString<128> GCCSuffix;
1742cea1beaSJonathan Roelofs   llvm::sys::path::append(GCCSuffix, "/", Base.gccSuffix(), New.gccSuffix());
1752cea1beaSJonathan Roelofs   SmallString<128> OSSuffix;
1762cea1beaSJonathan Roelofs   llvm::sys::path::append(OSSuffix, "/", Base.osSuffix(), New.osSuffix());
1772cea1beaSJonathan Roelofs   SmallString<128> IncludeSuffix;
1782cea1beaSJonathan Roelofs   llvm::sys::path::append(IncludeSuffix, "/", Base.includeSuffix(),
1792cea1beaSJonathan Roelofs                           New.includeSuffix());
1802cea1beaSJonathan Roelofs 
18192e1b62dSYaron Keren   Multilib Composed(GCCSuffix, OSSuffix, IncludeSuffix);
1822cea1beaSJonathan Roelofs 
1832cea1beaSJonathan Roelofs   Multilib::flags_list &Flags = Composed.flags();
1842cea1beaSJonathan Roelofs 
1852cea1beaSJonathan Roelofs   Flags.insert(Flags.end(), Base.flags().begin(), Base.flags().end());
1862cea1beaSJonathan Roelofs   Flags.insert(Flags.end(), New.flags().begin(), New.flags().end());
1872cea1beaSJonathan Roelofs 
1882cea1beaSJonathan Roelofs   return Composed;
1892cea1beaSJonathan Roelofs }
1902cea1beaSJonathan Roelofs 
Either(ArrayRef<Multilib> MultilibSegments)191ac75baaaSBenjamin Kramer MultilibSet &MultilibSet::Either(ArrayRef<Multilib> MultilibSegments) {
1922cea1beaSJonathan Roelofs   multilib_list Composed;
1932cea1beaSJonathan Roelofs 
1942cea1beaSJonathan Roelofs   if (Multilibs.empty())
1952cea1beaSJonathan Roelofs     Multilibs.insert(Multilibs.end(), MultilibSegments.begin(),
1962cea1beaSJonathan Roelofs                      MultilibSegments.end());
1972cea1beaSJonathan Roelofs   else {
1985e4511cfSEugene Zelenko     for (const auto &New : MultilibSegments) {
1995e4511cfSEugene Zelenko       for (const auto &Base : *this) {
2006f657c46SSimon Atanasyan         Multilib MO = compose(Base, New);
2012cea1beaSJonathan Roelofs         if (MO.isValid())
2022cea1beaSJonathan Roelofs           Composed.push_back(MO);
2032cea1beaSJonathan Roelofs       }
2042cea1beaSJonathan Roelofs     }
2052cea1beaSJonathan Roelofs 
2062cea1beaSJonathan Roelofs     Multilibs = Composed;
2072cea1beaSJonathan Roelofs   }
2082cea1beaSJonathan Roelofs 
2092cea1beaSJonathan Roelofs   return *this;
2102cea1beaSJonathan Roelofs }
2112cea1beaSJonathan Roelofs 
FilterOut(FilterCallback F)212ac75baaaSBenjamin Kramer MultilibSet &MultilibSet::FilterOut(FilterCallback F) {
2132cea1beaSJonathan Roelofs   filterInPlace(F, Multilibs);
2142cea1beaSJonathan Roelofs   return *this;
2152cea1beaSJonathan Roelofs }
2162cea1beaSJonathan Roelofs 
FilterOut(const char * Regex)217ac75baaaSBenjamin Kramer MultilibSet &MultilibSet::FilterOut(const char *Regex) {
218ac75baaaSBenjamin Kramer   llvm::Regex R(Regex);
219ac75baaaSBenjamin Kramer #ifndef NDEBUG
2202cea1beaSJonathan Roelofs   std::string Error;
2212cea1beaSJonathan Roelofs   if (!R.isValid(Error)) {
2222cea1beaSJonathan Roelofs     llvm::errs() << Error;
223ac75baaaSBenjamin Kramer     llvm_unreachable("Invalid regex!");
2242cea1beaSJonathan Roelofs   }
225ac75baaaSBenjamin Kramer #endif
2262cea1beaSJonathan Roelofs 
227ac75baaaSBenjamin Kramer   filterInPlace([&R](const Multilib &M) { return R.match(M.gccSuffix()); },
228ac75baaaSBenjamin Kramer                 Multilibs);
2292cea1beaSJonathan Roelofs   return *this;
2302cea1beaSJonathan Roelofs }
2312cea1beaSJonathan Roelofs 
push_back(const Multilib & M)2322cea1beaSJonathan Roelofs void MultilibSet::push_back(const Multilib &M) { Multilibs.push_back(M); }
2332cea1beaSJonathan Roelofs 
combineWith(const MultilibSet & Other)2342cea1beaSJonathan Roelofs void MultilibSet::combineWith(const MultilibSet &Other) {
2352cea1beaSJonathan Roelofs   Multilibs.insert(Multilibs.end(), Other.begin(), Other.end());
2362cea1beaSJonathan Roelofs }
2372cea1beaSJonathan Roelofs 
isFlagEnabled(StringRef Flag)238ac75baaaSBenjamin Kramer static bool isFlagEnabled(StringRef Flag) {
239ac75baaaSBenjamin Kramer   char Indicator = Flag.front();
240ac75baaaSBenjamin Kramer   assert(Indicator == '+' || Indicator == '-');
241ac75baaaSBenjamin Kramer   return Indicator == '+';
242ac75baaaSBenjamin Kramer }
243ac75baaaSBenjamin Kramer 
select(const Multilib::flags_list & Flags,Multilib & M) const2442cea1beaSJonathan Roelofs bool MultilibSet::select(const Multilib::flags_list &Flags, Multilib &M) const {
2452cea1beaSJonathan Roelofs   llvm::StringMap<bool> FlagSet;
2462cea1beaSJonathan Roelofs 
247ac75baaaSBenjamin Kramer   // Stuff all of the flags into the FlagSet such that a true mappend indicates
248ac75baaaSBenjamin Kramer   // the flag was enabled, and a false mappend indicates the flag was disabled.
2496f657c46SSimon Atanasyan   for (StringRef Flag : Flags)
2506f657c46SSimon Atanasyan     FlagSet[Flag.substr(1)] = isFlagEnabled(Flag);
251ac75baaaSBenjamin Kramer 
252ac75baaaSBenjamin Kramer   multilib_list Filtered = filterCopy([&FlagSet](const Multilib &M) {
2536f657c46SSimon Atanasyan     for (StringRef Flag : M.flags()) {
2542cea1beaSJonathan Roelofs       llvm::StringMap<bool>::const_iterator SI = FlagSet.find(Flag.substr(1));
2552cea1beaSJonathan Roelofs       if (SI != FlagSet.end())
2566f657c46SSimon Atanasyan         if (SI->getValue() != isFlagEnabled(Flag))
2572cea1beaSJonathan Roelofs           return true;
2582cea1beaSJonathan Roelofs     }
2592cea1beaSJonathan Roelofs     return false;
260ac75baaaSBenjamin Kramer   }, Multilibs);
2612cea1beaSJonathan Roelofs 
2625e4511cfSEugene Zelenko   if (Filtered.empty())
2632cea1beaSJonathan Roelofs     return false;
264ecce7e1cSSimon Atanasyan   if (Filtered.size() == 1) {
2652cea1beaSJonathan Roelofs     M = Filtered[0];
2662cea1beaSJonathan Roelofs     return true;
2672cea1beaSJonathan Roelofs   }
2682cea1beaSJonathan Roelofs 
2690f9f021dSPetr Hosek   // Sort multilibs by priority and select the one with the highest priority.
270*cd9a5cfdSDmitri Gribenko   llvm::sort(Filtered, [](const Multilib &a, const Multilib &b) -> bool {
2710f9f021dSPetr Hosek     return a.priority() > b.priority();
2720f9f021dSPetr Hosek   });
2730f9f021dSPetr Hosek 
2740f9f021dSPetr Hosek   if (Filtered[0].priority() > Filtered[1].priority()) {
2750f9f021dSPetr Hosek     M = Filtered[0];
2760f9f021dSPetr Hosek     return true;
2770f9f021dSPetr Hosek   }
2780f9f021dSPetr Hosek 
2790f9f021dSPetr Hosek   // TODO: We should consider returning llvm::Error rather than aborting.
2800f9f021dSPetr Hosek   assert(false && "More than one multilib with the same priority");
2812cea1beaSJonathan Roelofs   return false;
2822cea1beaSJonathan Roelofs }
2832cea1beaSJonathan Roelofs 
dump() const284c8e377c3SJonathan Roelofs LLVM_DUMP_METHOD void MultilibSet::dump() const {
285c8e377c3SJonathan Roelofs   print(llvm::errs());
286c8e377c3SJonathan Roelofs }
287c8e377c3SJonathan Roelofs 
print(raw_ostream & OS) const2882cea1beaSJonathan Roelofs void MultilibSet::print(raw_ostream &OS) const {
2895e4511cfSEugene Zelenko   for (const auto &M : *this)
2906f657c46SSimon Atanasyan     OS << M << "\n";
2912cea1beaSJonathan Roelofs }
2922cea1beaSJonathan Roelofs 
filterCopy(FilterCallback F,const multilib_list & Ms)293ac75baaaSBenjamin Kramer MultilibSet::multilib_list MultilibSet::filterCopy(FilterCallback F,
2942cea1beaSJonathan Roelofs                                                    const multilib_list &Ms) {
2952cea1beaSJonathan Roelofs   multilib_list Copy(Ms);
2962cea1beaSJonathan Roelofs   filterInPlace(F, Copy);
2972cea1beaSJonathan Roelofs   return Copy;
2982cea1beaSJonathan Roelofs }
2992cea1beaSJonathan Roelofs 
filterInPlace(FilterCallback F,multilib_list & Ms)300ac75baaaSBenjamin Kramer void MultilibSet::filterInPlace(FilterCallback F, multilib_list &Ms) {
301d245f2e8SKazu Hirata   llvm::erase_if(Ms, F);
3022cea1beaSJonathan Roelofs }
3032cea1beaSJonathan Roelofs 
operator <<(raw_ostream & OS,const MultilibSet & MS)3042cea1beaSJonathan Roelofs raw_ostream &clang::driver::operator<<(raw_ostream &OS, const MultilibSet &MS) {
3052cea1beaSJonathan Roelofs   MS.print(OS);
3062cea1beaSJonathan Roelofs   return OS;
3072cea1beaSJonathan Roelofs }
308