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