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 "clang/Basic/Version.h" 12 #include "clang/Driver/Driver.h" 13 #include "llvm/ADT/DenseSet.h" 14 #include "llvm/ADT/SmallString.h" 15 #include "llvm/ADT/StringRef.h" 16 #include "llvm/Support/Compiler.h" 17 #include "llvm/Support/Error.h" 18 #include "llvm/Support/ErrorHandling.h" 19 #include "llvm/Support/Path.h" 20 #include "llvm/Support/Regex.h" 21 #include "llvm/Support/VersionTuple.h" 22 #include "llvm/Support/YAMLParser.h" 23 #include "llvm/Support/YAMLTraits.h" 24 #include "llvm/Support/raw_ostream.h" 25 #include <algorithm> 26 #include <cassert> 27 #include <string> 28 29 using namespace clang; 30 using namespace driver; 31 using namespace llvm::sys; 32 33 Multilib::Multilib(StringRef GCCSuffix, StringRef OSSuffix, 34 StringRef IncludeSuffix, const flags_list &Flags, 35 StringRef ExclusiveGroup, 36 std::optional<StringRef> FatalError) 37 : GCCSuffix(GCCSuffix), OSSuffix(OSSuffix), IncludeSuffix(IncludeSuffix), 38 Flags(Flags), ExclusiveGroup(ExclusiveGroup), FatalError(FatalError) { 39 assert(GCCSuffix.empty() || 40 (StringRef(GCCSuffix).front() == '/' && GCCSuffix.size() > 1)); 41 assert(OSSuffix.empty() || 42 (StringRef(OSSuffix).front() == '/' && OSSuffix.size() > 1)); 43 assert(IncludeSuffix.empty() || 44 (StringRef(IncludeSuffix).front() == '/' && IncludeSuffix.size() > 1)); 45 } 46 47 LLVM_DUMP_METHOD void Multilib::dump() const { 48 print(llvm::errs()); 49 } 50 51 void Multilib::print(raw_ostream &OS) const { 52 if (GCCSuffix.empty()) 53 OS << "."; 54 else { 55 OS << StringRef(GCCSuffix).drop_front(); 56 } 57 OS << ";"; 58 for (StringRef Flag : Flags) { 59 if (Flag.front() == '-') 60 OS << "@" << Flag.substr(1); 61 } 62 } 63 64 bool Multilib::operator==(const Multilib &Other) const { 65 // Check whether the flags sets match 66 // allowing for the match to be order invariant 67 llvm::StringSet<> MyFlags; 68 for (const auto &Flag : Flags) 69 MyFlags.insert(Flag); 70 71 for (const auto &Flag : Other.Flags) 72 if (!MyFlags.contains(Flag)) 73 return false; 74 75 if (osSuffix() != Other.osSuffix()) 76 return false; 77 78 if (gccSuffix() != Other.gccSuffix()) 79 return false; 80 81 if (includeSuffix() != Other.includeSuffix()) 82 return false; 83 84 return true; 85 } 86 87 raw_ostream &clang::driver::operator<<(raw_ostream &OS, const Multilib &M) { 88 M.print(OS); 89 return OS; 90 } 91 92 MultilibSet &MultilibSet::FilterOut(FilterCallback F) { 93 llvm::erase_if(Multilibs, F); 94 return *this; 95 } 96 97 void MultilibSet::push_back(const Multilib &M) { Multilibs.push_back(M); } 98 99 bool MultilibSet::select(const Driver &D, const Multilib::flags_list &Flags, 100 llvm::SmallVectorImpl<Multilib> &Selected) const { 101 llvm::StringSet<> FlagSet(expandFlags(Flags)); 102 Selected.clear(); 103 104 // Decide which multilibs we're going to select at all. 105 llvm::DenseSet<StringRef> ExclusiveGroupsSelected; 106 for (const Multilib &M : llvm::reverse(Multilibs)) { 107 // If this multilib doesn't match all our flags, don't select it. 108 if (!llvm::all_of(M.flags(), [&FlagSet](const std::string &F) { 109 return FlagSet.contains(F); 110 })) 111 continue; 112 113 const std::string &group = M.exclusiveGroup(); 114 if (!group.empty()) { 115 // If this multilib has the same ExclusiveGroup as one we've already 116 // selected, skip it. We're iterating in reverse order, so the group 117 // member we've selected already is preferred. 118 // 119 // Otherwise, add the group name to the set of groups we've already 120 // selected a member of. 121 auto [It, Inserted] = ExclusiveGroupsSelected.insert(group); 122 if (!Inserted) 123 continue; 124 } 125 126 // If this multilib is actually a placeholder containing a fatal 127 // error message written by the multilib.yaml author, display that 128 // error message, and return failure. 129 if (M.isFatalError()) { 130 D.Diag(clang::diag::err_drv_multilib_custom_error) << M.getFatalError(); 131 return false; 132 } 133 134 // Select this multilib. 135 Selected.push_back(M); 136 } 137 138 // We iterated in reverse order, so now put Selected back the right way 139 // round. 140 std::reverse(Selected.begin(), Selected.end()); 141 142 return !Selected.empty(); 143 } 144 145 llvm::StringSet<> 146 MultilibSet::expandFlags(const Multilib::flags_list &InFlags) const { 147 llvm::StringSet<> Result; 148 for (const auto &F : InFlags) 149 Result.insert(F); 150 for (const FlagMatcher &M : FlagMatchers) { 151 std::string RegexString(M.Match); 152 153 // Make the regular expression match the whole string. 154 if (!StringRef(M.Match).starts_with("^")) 155 RegexString.insert(RegexString.begin(), '^'); 156 if (!StringRef(M.Match).ends_with("$")) 157 RegexString.push_back('$'); 158 159 const llvm::Regex Regex(RegexString); 160 assert(Regex.isValid()); 161 if (llvm::any_of(InFlags, 162 [&Regex](StringRef F) { return Regex.match(F); })) { 163 Result.insert(M.Flags.begin(), M.Flags.end()); 164 } 165 } 166 return Result; 167 } 168 169 namespace { 170 171 // When updating this also update MULTILIB_VERSION in MultilibTest.cpp 172 static const VersionTuple MultilibVersionCurrent(1, 0); 173 174 struct MultilibSerialization { 175 std::string Dir; // if this record successfully selects a library dir 176 std::string FatalError; // if this record reports a fatal error message 177 std::vector<std::string> Flags; 178 std::string Group; 179 }; 180 181 enum class MultilibGroupType { 182 /* 183 * The only group type currently supported is 'Exclusive', which indicates a 184 * group of multilibs of which at most one may be selected. 185 */ 186 Exclusive, 187 188 /* 189 * Future possibility: a second group type indicating a set of library 190 * directories that are mutually _dependent_ rather than mutually exclusive: 191 * if you include one you must include them all. 192 * 193 * It might also be useful to allow groups to be members of other groups, so 194 * that a mutually exclusive group could contain a mutually dependent set of 195 * library directories, or vice versa. 196 * 197 * These additional features would need changes in the implementation, but 198 * the YAML schema is set up so they can be added without requiring changes 199 * in existing users' multilib.yaml files. 200 */ 201 }; 202 203 struct MultilibGroupSerialization { 204 std::string Name; 205 MultilibGroupType Type; 206 }; 207 208 struct MultilibSetSerialization { 209 llvm::VersionTuple MultilibVersion; 210 std::vector<MultilibGroupSerialization> Groups; 211 std::vector<MultilibSerialization> Multilibs; 212 std::vector<MultilibSet::FlagMatcher> FlagMatchers; 213 }; 214 215 } // end anonymous namespace 216 217 template <> struct llvm::yaml::MappingTraits<MultilibSerialization> { 218 static void mapping(llvm::yaml::IO &io, MultilibSerialization &V) { 219 io.mapOptional("Dir", V.Dir); 220 io.mapOptional("FatalError", V.FatalError); 221 io.mapRequired("Flags", V.Flags); 222 io.mapOptional("Group", V.Group); 223 } 224 static std::string validate(IO &io, MultilibSerialization &V) { 225 if (V.Dir.empty() && V.FatalError.empty()) 226 return "one of the 'Dir' and 'FatalError' keys must be specified"; 227 if (!V.Dir.empty() && !V.FatalError.empty()) 228 return "the 'Dir' and 'FatalError' keys may not both be specified"; 229 if (StringRef(V.Dir).starts_with("/")) 230 return "paths must be relative but \"" + V.Dir + "\" starts with \"/\""; 231 return std::string{}; 232 } 233 }; 234 235 template <> struct llvm::yaml::ScalarEnumerationTraits<MultilibGroupType> { 236 static void enumeration(IO &io, MultilibGroupType &Val) { 237 io.enumCase(Val, "Exclusive", MultilibGroupType::Exclusive); 238 } 239 }; 240 241 template <> struct llvm::yaml::MappingTraits<MultilibGroupSerialization> { 242 static void mapping(llvm::yaml::IO &io, MultilibGroupSerialization &V) { 243 io.mapRequired("Name", V.Name); 244 io.mapRequired("Type", V.Type); 245 } 246 }; 247 248 template <> struct llvm::yaml::MappingTraits<MultilibSet::FlagMatcher> { 249 static void mapping(llvm::yaml::IO &io, MultilibSet::FlagMatcher &M) { 250 io.mapRequired("Match", M.Match); 251 io.mapRequired("Flags", M.Flags); 252 } 253 static std::string validate(IO &io, MultilibSet::FlagMatcher &M) { 254 llvm::Regex Regex(M.Match); 255 std::string RegexError; 256 if (!Regex.isValid(RegexError)) 257 return RegexError; 258 if (M.Flags.empty()) 259 return "value required for 'Flags'"; 260 return std::string{}; 261 } 262 }; 263 264 template <> struct llvm::yaml::MappingTraits<MultilibSetSerialization> { 265 static void mapping(llvm::yaml::IO &io, MultilibSetSerialization &M) { 266 io.mapRequired("MultilibVersion", M.MultilibVersion); 267 io.mapRequired("Variants", M.Multilibs); 268 io.mapOptional("Groups", M.Groups); 269 io.mapOptional("Mappings", M.FlagMatchers); 270 } 271 static std::string validate(IO &io, MultilibSetSerialization &M) { 272 if (M.MultilibVersion.empty()) 273 return "missing required key 'MultilibVersion'"; 274 if (M.MultilibVersion.getMajor() != MultilibVersionCurrent.getMajor()) 275 return "multilib version " + M.MultilibVersion.getAsString() + 276 " is unsupported"; 277 if (M.MultilibVersion.getMinor() > MultilibVersionCurrent.getMinor()) 278 return "multilib version " + M.MultilibVersion.getAsString() + 279 " is unsupported"; 280 for (const MultilibSerialization &Lib : M.Multilibs) { 281 if (!Lib.Group.empty()) { 282 bool Found = false; 283 for (const MultilibGroupSerialization &Group : M.Groups) 284 if (Group.Name == Lib.Group) { 285 Found = true; 286 break; 287 } 288 if (!Found) 289 return "multilib \"" + Lib.Dir + 290 "\" specifies undefined group name \"" + Lib.Group + "\""; 291 } 292 } 293 return std::string{}; 294 } 295 }; 296 297 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSerialization) 298 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibGroupSerialization) 299 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSet::FlagMatcher) 300 301 llvm::ErrorOr<MultilibSet> 302 MultilibSet::parseYaml(llvm::MemoryBufferRef Input, 303 llvm::SourceMgr::DiagHandlerTy DiagHandler, 304 void *DiagHandlerCtxt) { 305 MultilibSetSerialization MS; 306 llvm::yaml::Input YamlInput(Input, nullptr, DiagHandler, DiagHandlerCtxt); 307 YamlInput >> MS; 308 if (YamlInput.error()) 309 return YamlInput.error(); 310 311 multilib_list Multilibs; 312 Multilibs.reserve(MS.Multilibs.size()); 313 for (const auto &M : MS.Multilibs) { 314 if (!M.FatalError.empty()) { 315 Multilibs.emplace_back("", "", "", M.Flags, M.Group, M.FatalError); 316 } else { 317 std::string Dir; 318 if (M.Dir != ".") 319 Dir = "/" + M.Dir; 320 // We transfer M.Group straight into the ExclusiveGroup parameter for the 321 // Multilib constructor. If we later support more than one type of group, 322 // we'll have to look up the group name in MS.Groups, check its type, and 323 // decide what to do here. 324 Multilibs.emplace_back(Dir, Dir, Dir, M.Flags, M.Group); 325 } 326 } 327 328 return MultilibSet(std::move(Multilibs), std::move(MS.FlagMatchers)); 329 } 330 331 LLVM_DUMP_METHOD void MultilibSet::dump() const { 332 print(llvm::errs()); 333 } 334 335 void MultilibSet::print(raw_ostream &OS) const { 336 for (const auto &M : *this) 337 OS << M << "\n"; 338 } 339 340 raw_ostream &clang::driver::operator<<(raw_ostream &OS, const MultilibSet &MS) { 341 MS.print(OS); 342 return OS; 343 } 344