1 //===--- Multilib.cpp - Multilib Implementation ---------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "clang/Driver/Multilib.h" 11 #include "ToolChains/CommonArgs.h" 12 #include "clang/Driver/Options.h" 13 #include "llvm/ADT/StringMap.h" 14 #include "llvm/ADT/StringRef.h" 15 #include "llvm/ADT/StringSet.h" 16 #include "llvm/Option/Arg.h" 17 #include "llvm/Option/ArgList.h" 18 #include "llvm/Option/OptTable.h" 19 #include "llvm/Option/Option.h" 20 #include "llvm/Support/Path.h" 21 #include "llvm/Support/Regex.h" 22 #include "llvm/Support/YAMLParser.h" 23 #include "llvm/Support/YAMLTraits.h" 24 #include "llvm/Support/raw_ostream.h" 25 #include <algorithm> 26 27 using namespace clang::driver; 28 using namespace clang; 29 using namespace llvm::opt; 30 using namespace llvm::sys; 31 32 /// normalize Segment to "/foo/bar" or "". 33 static void normalizePathSegment(std::string &Segment) { 34 StringRef seg = Segment; 35 36 // Prune trailing "/" or "./" 37 while (1) { 38 StringRef last = path::filename(seg); 39 if (last != ".") 40 break; 41 seg = path::parent_path(seg); 42 } 43 44 if (seg.empty() || seg == "/") { 45 Segment = ""; 46 return; 47 } 48 49 // Add leading '/' 50 if (seg.front() != '/') { 51 Segment = "/" + seg.str(); 52 } else { 53 Segment = seg; 54 } 55 } 56 57 Multilib::Multilib(StringRef GCCSuffix, StringRef OSSuffix, 58 StringRef IncludeSuffix) 59 : GCCSuffix(GCCSuffix), OSSuffix(OSSuffix), IncludeSuffix(IncludeSuffix) { 60 normalizePathSegment(this->GCCSuffix); 61 normalizePathSegment(this->OSSuffix); 62 normalizePathSegment(this->IncludeSuffix); 63 } 64 65 Multilib &Multilib::gccSuffix(StringRef S) { 66 GCCSuffix = S; 67 normalizePathSegment(GCCSuffix); 68 return *this; 69 } 70 71 Multilib &Multilib::osSuffix(StringRef S) { 72 OSSuffix = S; 73 normalizePathSegment(OSSuffix); 74 return *this; 75 } 76 77 Multilib &Multilib::includeSuffix(StringRef S) { 78 IncludeSuffix = S; 79 normalizePathSegment(IncludeSuffix); 80 return *this; 81 } 82 83 LLVM_DUMP_METHOD void Multilib::dump() const { 84 print(llvm::errs()); 85 } 86 87 void Multilib::print(raw_ostream &OS) const { 88 assert(GCCSuffix.empty() || (StringRef(GCCSuffix).front() == '/')); 89 if (GCCSuffix.empty()) 90 OS << "."; 91 else { 92 OS << StringRef(GCCSuffix).drop_front(); 93 } 94 OS << ";"; 95 for (StringRef Flag : Flags) { 96 if (Flag.front() == '+') 97 OS << "@" << Flag.substr(1); 98 } 99 } 100 101 bool Multilib::isValid() const { 102 llvm::StringMap<int> FlagSet; 103 for (unsigned I = 0, N = Flags.size(); I != N; ++I) { 104 StringRef Flag(Flags[I]); 105 llvm::StringMap<int>::iterator SI = FlagSet.find(Flag.substr(1)); 106 107 assert(StringRef(Flag).front() == '+' || StringRef(Flag).front() == '-'); 108 109 if (SI == FlagSet.end()) 110 FlagSet[Flag.substr(1)] = I; 111 else if (Flags[I] != Flags[SI->getValue()]) 112 return false; 113 } 114 return true; 115 } 116 117 bool Multilib::operator==(const Multilib &Other) const { 118 // Check whether the flags sets match 119 // allowing for the match to be order invariant 120 llvm::StringSet<> MyFlags; 121 for (const auto &Flag : Flags) 122 MyFlags.insert(Flag); 123 124 for (const auto &Flag : Other.Flags) 125 if (MyFlags.find(Flag) == MyFlags.end()) 126 return false; 127 128 if (osSuffix() != Other.osSuffix()) 129 return false; 130 131 if (gccSuffix() != Other.gccSuffix()) 132 return false; 133 134 if (includeSuffix() != Other.includeSuffix()) 135 return false; 136 137 return true; 138 } 139 140 raw_ostream &clang::driver::operator<<(raw_ostream &OS, const Multilib &M) { 141 M.print(OS); 142 return OS; 143 } 144 145 MultilibSet &MultilibSet::Maybe(const Multilib &M) { 146 Multilib Opposite; 147 // Negate any '+' flags 148 for (StringRef Flag : M.flags()) { 149 if (Flag.front() == '+') 150 Opposite.flags().push_back(("-" + Flag.substr(1)).str()); 151 } 152 return Either(M, Opposite); 153 } 154 155 MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2) { 156 return Either({M1, M2}); 157 } 158 159 MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2, 160 const Multilib &M3) { 161 return Either({M1, M2, M3}); 162 } 163 164 MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2, 165 const Multilib &M3, const Multilib &M4) { 166 return Either({M1, M2, M3, M4}); 167 } 168 169 MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2, 170 const Multilib &M3, const Multilib &M4, 171 const Multilib &M5) { 172 return Either({M1, M2, M3, M4, M5}); 173 } 174 175 static Multilib compose(const Multilib &Base, const Multilib &New) { 176 SmallString<128> GCCSuffix; 177 llvm::sys::path::append(GCCSuffix, "/", Base.gccSuffix(), New.gccSuffix()); 178 SmallString<128> OSSuffix; 179 llvm::sys::path::append(OSSuffix, "/", Base.osSuffix(), New.osSuffix()); 180 SmallString<128> IncludeSuffix; 181 llvm::sys::path::append(IncludeSuffix, "/", Base.includeSuffix(), 182 New.includeSuffix()); 183 184 Multilib Composed(GCCSuffix, OSSuffix, IncludeSuffix); 185 186 Multilib::flags_list &Flags = Composed.flags(); 187 188 Flags.insert(Flags.end(), Base.flags().begin(), Base.flags().end()); 189 Flags.insert(Flags.end(), New.flags().begin(), New.flags().end()); 190 191 return Composed; 192 } 193 194 MultilibSet &MultilibSet::Either(ArrayRef<Multilib> MultilibSegments) { 195 multilib_list Composed; 196 197 if (Multilibs.empty()) 198 Multilibs.insert(Multilibs.end(), MultilibSegments.begin(), 199 MultilibSegments.end()); 200 else { 201 for (const Multilib &New : MultilibSegments) { 202 for (const Multilib &Base : *this) { 203 Multilib MO = compose(Base, New); 204 if (MO.isValid()) 205 Composed.push_back(MO); 206 } 207 } 208 209 Multilibs = Composed; 210 } 211 212 return *this; 213 } 214 215 MultilibSet &MultilibSet::FilterOut(FilterCallback F) { 216 filterInPlace(F, Multilibs); 217 return *this; 218 } 219 220 MultilibSet &MultilibSet::FilterOut(const char *Regex) { 221 llvm::Regex R(Regex); 222 #ifndef NDEBUG 223 std::string Error; 224 if (!R.isValid(Error)) { 225 llvm::errs() << Error; 226 llvm_unreachable("Invalid regex!"); 227 } 228 #endif 229 230 filterInPlace([&R](const Multilib &M) { return R.match(M.gccSuffix()); }, 231 Multilibs); 232 return *this; 233 } 234 235 void MultilibSet::push_back(const Multilib &M) { Multilibs.push_back(M); } 236 237 void MultilibSet::combineWith(const MultilibSet &Other) { 238 Multilibs.insert(Multilibs.end(), Other.begin(), Other.end()); 239 } 240 241 static bool isFlagEnabled(StringRef Flag) { 242 char Indicator = Flag.front(); 243 assert(Indicator == '+' || Indicator == '-'); 244 return Indicator == '+'; 245 } 246 247 bool MultilibSet::select(const Multilib::flags_list &Flags, Multilib &M) const { 248 llvm::StringMap<bool> FlagSet; 249 250 // Stuff all of the flags into the FlagSet such that a true mappend indicates 251 // the flag was enabled, and a false mappend indicates the flag was disabled. 252 for (StringRef Flag : Flags) 253 FlagSet[Flag.substr(1)] = isFlagEnabled(Flag); 254 255 multilib_list Filtered = filterCopy([&FlagSet](const Multilib &M) { 256 for (StringRef Flag : M.flags()) { 257 llvm::StringMap<bool>::const_iterator SI = FlagSet.find(Flag.substr(1)); 258 if (SI != FlagSet.end()) 259 if (SI->getValue() != isFlagEnabled(Flag)) 260 return true; 261 } 262 return false; 263 }, Multilibs); 264 265 if (Filtered.size() == 0) 266 return false; 267 if (Filtered.size() == 1) { 268 M = Filtered[0]; 269 return true; 270 } 271 272 // TODO: pick the "best" multlib when more than one is suitable 273 assert(false); 274 return false; 275 } 276 277 LLVM_DUMP_METHOD void MultilibSet::dump() const { 278 print(llvm::errs()); 279 } 280 281 void MultilibSet::print(raw_ostream &OS) const { 282 for (const Multilib &M : *this) 283 OS << M << "\n"; 284 } 285 286 MultilibSet::multilib_list MultilibSet::filterCopy(FilterCallback F, 287 const multilib_list &Ms) { 288 multilib_list Copy(Ms); 289 filterInPlace(F, Copy); 290 return Copy; 291 } 292 293 void MultilibSet::filterInPlace(FilterCallback F, multilib_list &Ms) { 294 Ms.erase(std::remove_if(Ms.begin(), Ms.end(), F), Ms.end()); 295 } 296 297 raw_ostream &clang::driver::operator<<(raw_ostream &OS, const MultilibSet &MS) { 298 MS.print(OS); 299 return OS; 300 } 301