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