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 "llvm/ADT/SmallString.h" 13 #include "llvm/ADT/StringMap.h" 14 #include "llvm/ADT/StringRef.h" 15 #include "llvm/Support/Compiler.h" 16 #include "llvm/Support/Error.h" 17 #include "llvm/Support/ErrorHandling.h" 18 #include "llvm/Support/Path.h" 19 #include "llvm/Support/Regex.h" 20 #include "llvm/Support/VersionTuple.h" 21 #include "llvm/Support/YAMLParser.h" 22 #include "llvm/Support/YAMLTraits.h" 23 #include "llvm/Support/raw_ostream.h" 24 #include <algorithm> 25 #include <cassert> 26 #include <string> 27 28 using namespace clang; 29 using namespace driver; 30 using namespace llvm::sys; 31 32 Multilib::Multilib(StringRef GCCSuffix, StringRef OSSuffix, 33 StringRef IncludeSuffix, const flags_list &Flags) 34 : GCCSuffix(GCCSuffix), OSSuffix(OSSuffix), IncludeSuffix(IncludeSuffix), 35 Flags(Flags) { 36 assert(GCCSuffix.empty() || 37 (StringRef(GCCSuffix).front() == '/' && GCCSuffix.size() > 1)); 38 assert(OSSuffix.empty() || 39 (StringRef(OSSuffix).front() == '/' && OSSuffix.size() > 1)); 40 assert(IncludeSuffix.empty() || 41 (StringRef(IncludeSuffix).front() == '/' && IncludeSuffix.size() > 1)); 42 } 43 44 LLVM_DUMP_METHOD void Multilib::dump() const { 45 print(llvm::errs()); 46 } 47 48 void Multilib::print(raw_ostream &OS) const { 49 if (GCCSuffix.empty()) 50 OS << "."; 51 else { 52 OS << StringRef(GCCSuffix).drop_front(); 53 } 54 OS << ";"; 55 for (StringRef Flag : Flags) { 56 if (Flag.front() == '-') 57 OS << "@" << Flag.substr(1); 58 } 59 } 60 61 bool Multilib::operator==(const Multilib &Other) const { 62 // Check whether the flags sets match 63 // allowing for the match to be order invariant 64 llvm::StringSet<> MyFlags; 65 for (const auto &Flag : Flags) 66 MyFlags.insert(Flag); 67 68 for (const auto &Flag : Other.Flags) 69 if (!MyFlags.contains(Flag)) 70 return false; 71 72 if (osSuffix() != Other.osSuffix()) 73 return false; 74 75 if (gccSuffix() != Other.gccSuffix()) 76 return false; 77 78 if (includeSuffix() != Other.includeSuffix()) 79 return false; 80 81 return true; 82 } 83 84 raw_ostream &clang::driver::operator<<(raw_ostream &OS, const Multilib &M) { 85 M.print(OS); 86 return OS; 87 } 88 89 MultilibSet &MultilibSet::FilterOut(FilterCallback F) { 90 llvm::erase_if(Multilibs, F); 91 return *this; 92 } 93 94 void MultilibSet::push_back(const Multilib &M) { Multilibs.push_back(M); } 95 96 bool MultilibSet::select(const Multilib::flags_list &Flags, 97 llvm::SmallVector<Multilib> &Selected) const { 98 llvm::StringSet<> FlagSet(expandFlags(Flags)); 99 Selected.clear(); 100 llvm::copy_if(Multilibs, std::back_inserter(Selected), 101 [&FlagSet](const Multilib &M) { 102 for (const std::string &F : M.flags()) 103 if (!FlagSet.contains(F)) 104 return false; 105 return true; 106 }); 107 return !Selected.empty(); 108 } 109 110 llvm::StringSet<> 111 MultilibSet::expandFlags(const Multilib::flags_list &InFlags) const { 112 llvm::StringSet<> Result; 113 for (const auto &F : InFlags) 114 Result.insert(F); 115 for (const FlagMatcher &M : FlagMatchers) { 116 std::string RegexString(M.Match); 117 118 // Make the regular expression match the whole string. 119 if (!StringRef(M.Match).starts_with("^")) 120 RegexString.insert(RegexString.begin(), '^'); 121 if (!StringRef(M.Match).ends_with("$")) 122 RegexString.push_back('$'); 123 124 const llvm::Regex Regex(RegexString); 125 assert(Regex.isValid()); 126 if (llvm::find_if(InFlags, [&Regex](StringRef F) { 127 return Regex.match(F); 128 }) != InFlags.end()) { 129 Result.insert(M.Flags.begin(), M.Flags.end()); 130 } 131 } 132 return Result; 133 } 134 135 namespace { 136 137 // When updating this also update MULTILIB_VERSION in MultilibTest.cpp 138 static const VersionTuple MultilibVersionCurrent(1, 0); 139 140 struct MultilibSerialization { 141 std::string Dir; 142 std::vector<std::string> Flags; 143 }; 144 145 struct MultilibSetSerialization { 146 llvm::VersionTuple MultilibVersion; 147 std::vector<MultilibSerialization> Multilibs; 148 std::vector<MultilibSet::FlagMatcher> FlagMatchers; 149 }; 150 151 } // end anonymous namespace 152 153 template <> struct llvm::yaml::MappingTraits<MultilibSerialization> { 154 static void mapping(llvm::yaml::IO &io, MultilibSerialization &V) { 155 io.mapRequired("Dir", V.Dir); 156 io.mapRequired("Flags", V.Flags); 157 } 158 static std::string validate(IO &io, MultilibSerialization &V) { 159 if (StringRef(V.Dir).starts_with("/")) 160 return "paths must be relative but \"" + V.Dir + "\" starts with \"/\""; 161 return std::string{}; 162 } 163 }; 164 165 template <> struct llvm::yaml::MappingTraits<MultilibSet::FlagMatcher> { 166 static void mapping(llvm::yaml::IO &io, MultilibSet::FlagMatcher &M) { 167 io.mapRequired("Match", M.Match); 168 io.mapRequired("Flags", M.Flags); 169 } 170 static std::string validate(IO &io, MultilibSet::FlagMatcher &M) { 171 llvm::Regex Regex(M.Match); 172 std::string RegexError; 173 if (!Regex.isValid(RegexError)) 174 return RegexError; 175 if (M.Flags.empty()) 176 return "value required for 'Flags'"; 177 return std::string{}; 178 } 179 }; 180 181 template <> struct llvm::yaml::MappingTraits<MultilibSetSerialization> { 182 static void mapping(llvm::yaml::IO &io, MultilibSetSerialization &M) { 183 io.mapRequired("MultilibVersion", M.MultilibVersion); 184 io.mapRequired("Variants", M.Multilibs); 185 io.mapOptional("Mappings", M.FlagMatchers); 186 } 187 static std::string validate(IO &io, MultilibSetSerialization &M) { 188 if (M.MultilibVersion.empty()) 189 return "missing required key 'MultilibVersion'"; 190 if (M.MultilibVersion.getMajor() != MultilibVersionCurrent.getMajor()) 191 return "multilib version " + M.MultilibVersion.getAsString() + 192 " is unsupported"; 193 if (M.MultilibVersion.getMinor() > MultilibVersionCurrent.getMinor()) 194 return "multilib version " + M.MultilibVersion.getAsString() + 195 " is unsupported"; 196 return std::string{}; 197 } 198 }; 199 200 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSerialization) 201 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSet::FlagMatcher) 202 203 llvm::ErrorOr<MultilibSet> 204 MultilibSet::parseYaml(llvm::MemoryBufferRef Input, 205 llvm::SourceMgr::DiagHandlerTy DiagHandler, 206 void *DiagHandlerCtxt) { 207 MultilibSetSerialization MS; 208 llvm::yaml::Input YamlInput(Input, nullptr, DiagHandler, DiagHandlerCtxt); 209 YamlInput >> MS; 210 if (YamlInput.error()) 211 return YamlInput.error(); 212 213 multilib_list Multilibs; 214 Multilibs.reserve(MS.Multilibs.size()); 215 for (const auto &M : MS.Multilibs) { 216 std::string Dir; 217 if (M.Dir != ".") 218 Dir = "/" + M.Dir; 219 Multilibs.emplace_back(Dir, Dir, Dir, M.Flags); 220 } 221 222 return MultilibSet(std::move(Multilibs), std::move(MS.FlagMatchers)); 223 } 224 225 LLVM_DUMP_METHOD void MultilibSet::dump() const { 226 print(llvm::errs()); 227 } 228 229 void MultilibSet::print(raw_ostream &OS) const { 230 for (const auto &M : *this) 231 OS << M << "\n"; 232 } 233 234 raw_ostream &clang::driver::operator<<(raw_ostream &OS, const MultilibSet &MS) { 235 MS.print(OS); 236 return OS; 237 } 238