1 //===- Multilib.cpp - Multilib Implementation -----------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "clang/Driver/Multilib.h" 10 #include "clang/Basic/LLVM.h" 11 #include "llvm/ADT/SmallString.h" 12 #include "llvm/ADT/StringMap.h" 13 #include "llvm/ADT/StringRef.h" 14 #include "llvm/ADT/StringSet.h" 15 #include "llvm/Support/Compiler.h" 16 #include "llvm/Support/ErrorHandling.h" 17 #include "llvm/Support/Path.h" 18 #include "llvm/Support/Regex.h" 19 #include "llvm/Support/raw_ostream.h" 20 #include <algorithm> 21 #include <cassert> 22 #include <string> 23 24 using namespace clang; 25 using namespace driver; 26 using namespace llvm::sys; 27 28 Multilib::Multilib(StringRef GCCSuffix, StringRef OSSuffix, 29 StringRef IncludeSuffix, int Priority, 30 const flags_list &Flags) 31 : GCCSuffix(GCCSuffix), OSSuffix(OSSuffix), IncludeSuffix(IncludeSuffix), 32 Flags(Flags), Priority(Priority) { 33 assert(GCCSuffix.empty() || 34 (StringRef(GCCSuffix).front() == '/' && GCCSuffix.size() > 1)); 35 assert(OSSuffix.empty() || 36 (StringRef(OSSuffix).front() == '/' && OSSuffix.size() > 1)); 37 assert(IncludeSuffix.empty() || 38 (StringRef(IncludeSuffix).front() == '/' && IncludeSuffix.size() > 1)); 39 } 40 41 LLVM_DUMP_METHOD void Multilib::dump() const { 42 print(llvm::errs()); 43 } 44 45 void Multilib::print(raw_ostream &OS) const { 46 if (GCCSuffix.empty()) 47 OS << "."; 48 else { 49 OS << StringRef(GCCSuffix).drop_front(); 50 } 51 OS << ";"; 52 for (StringRef Flag : Flags) { 53 if (Flag.front() == '+') 54 OS << "@" << Flag.substr(1); 55 } 56 } 57 58 bool Multilib::operator==(const Multilib &Other) const { 59 // Check whether the flags sets match 60 // allowing for the match to be order invariant 61 llvm::StringSet<> MyFlags; 62 for (const auto &Flag : Flags) 63 MyFlags.insert(Flag); 64 65 for (const auto &Flag : Other.Flags) 66 if (!MyFlags.contains(Flag)) 67 return false; 68 69 if (osSuffix() != Other.osSuffix()) 70 return false; 71 72 if (gccSuffix() != Other.gccSuffix()) 73 return false; 74 75 if (includeSuffix() != Other.includeSuffix()) 76 return false; 77 78 return true; 79 } 80 81 raw_ostream &clang::driver::operator<<(raw_ostream &OS, const Multilib &M) { 82 M.print(OS); 83 return OS; 84 } 85 86 MultilibSet &MultilibSet::FilterOut(FilterCallback F) { 87 filterInPlace(F, Multilibs); 88 return *this; 89 } 90 91 void MultilibSet::push_back(const Multilib &M) { Multilibs.push_back(M); } 92 93 static bool isFlagEnabled(StringRef Flag) { 94 char Indicator = Flag.front(); 95 assert(Indicator == '+' || Indicator == '-'); 96 return Indicator == '+'; 97 } 98 99 bool MultilibSet::select(const Multilib::flags_list &Flags, Multilib &M) const { 100 llvm::StringMap<bool> FlagSet; 101 102 // Stuff all of the flags into the FlagSet such that a true mappend indicates 103 // the flag was enabled, and a false mappend indicates the flag was disabled. 104 for (StringRef Flag : Flags) 105 FlagSet[Flag.substr(1)] = isFlagEnabled(Flag); 106 107 multilib_list Filtered = filterCopy([&FlagSet](const Multilib &M) { 108 for (StringRef Flag : M.flags()) { 109 llvm::StringMap<bool>::const_iterator SI = FlagSet.find(Flag.substr(1)); 110 if (SI != FlagSet.end()) 111 if (SI->getValue() != isFlagEnabled(Flag)) 112 return true; 113 } 114 return false; 115 }, Multilibs); 116 117 if (Filtered.empty()) 118 return false; 119 if (Filtered.size() == 1) { 120 M = Filtered[0]; 121 return true; 122 } 123 124 // Sort multilibs by priority and select the one with the highest priority. 125 llvm::sort(Filtered, [](const Multilib &a, const Multilib &b) -> bool { 126 return a.priority() > b.priority(); 127 }); 128 129 if (Filtered[0].priority() > Filtered[1].priority()) { 130 M = Filtered[0]; 131 return true; 132 } 133 134 // TODO: We should consider returning llvm::Error rather than aborting. 135 assert(false && "More than one multilib with the same priority"); 136 return false; 137 } 138 139 LLVM_DUMP_METHOD void MultilibSet::dump() const { 140 print(llvm::errs()); 141 } 142 143 void MultilibSet::print(raw_ostream &OS) const { 144 for (const auto &M : *this) 145 OS << M << "\n"; 146 } 147 148 MultilibSet::multilib_list MultilibSet::filterCopy(FilterCallback F, 149 const multilib_list &Ms) { 150 multilib_list Copy(Ms); 151 filterInPlace(F, Copy); 152 return Copy; 153 } 154 155 void MultilibSet::filterInPlace(FilterCallback F, multilib_list &Ms) { 156 llvm::erase_if(Ms, F); 157 } 158 159 raw_ostream &clang::driver::operator<<(raw_ostream &OS, const MultilibSet &MS) { 160 MS.print(OS); 161 return OS; 162 } 163