/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Driver/Multilib.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | /// normalize Segment to "/foo/bar" or "". |
29 | 381k | static void normalizePathSegment(std::string &Segment) { |
30 | 381k | StringRef seg = Segment; |
31 | | |
32 | | // Prune trailing "/" or "./" |
33 | 456k | while (true) { |
34 | 456k | StringRef last = path::filename(seg); |
35 | 456k | if (last != ".") |
36 | 381k | break; |
37 | 75.0k | seg = path::parent_path(seg); |
38 | 75.0k | } |
39 | | |
40 | 381k | if (seg.empty() || seg == "/"174k ) { |
41 | 209k | Segment.clear(); |
42 | 209k | return; |
43 | 209k | } |
44 | | |
45 | | // Add leading '/' |
46 | 171k | if (seg.front() != '/') { |
47 | 1.08k | Segment = "/" + seg.str(); |
48 | 170k | } else { |
49 | 170k | Segment = std::string(seg); |
50 | 170k | } |
51 | 171k | } |
52 | | |
53 | | Multilib::Multilib(StringRef GCCSuffix, StringRef OSSuffix, |
54 | | StringRef IncludeSuffix, int Priority) |
55 | | : GCCSuffix(GCCSuffix), OSSuffix(OSSuffix), IncludeSuffix(IncludeSuffix), |
56 | 126k | Priority(Priority) { |
57 | 126k | normalizePathSegment(this->GCCSuffix); |
58 | 126k | normalizePathSegment(this->OSSuffix); |
59 | 126k | normalizePathSegment(this->IncludeSuffix); |
60 | 126k | } |
61 | | |
62 | 973 | Multilib &Multilib::gccSuffix(StringRef S) { |
63 | 973 | GCCSuffix = std::string(S); |
64 | 973 | normalizePathSegment(GCCSuffix); |
65 | 973 | return *this; |
66 | 973 | } |
67 | | |
68 | 417 | Multilib &Multilib::osSuffix(StringRef S) { |
69 | 417 | OSSuffix = std::string(S); |
70 | 417 | normalizePathSegment(OSSuffix); |
71 | 417 | return *this; |
72 | 417 | } |
73 | | |
74 | 973 | Multilib &Multilib::includeSuffix(StringRef S) { |
75 | 973 | IncludeSuffix = std::string(S); |
76 | 973 | normalizePathSegment(IncludeSuffix); |
77 | 973 | return *this; |
78 | 973 | } |
79 | | |
80 | 0 | LLVM_DUMP_METHOD void Multilib::dump() const { |
81 | 0 | print(llvm::errs()); |
82 | 0 | } |
83 | | |
84 | 22 | void Multilib::print(raw_ostream &OS) const { |
85 | 22 | assert(GCCSuffix.empty() || (StringRef(GCCSuffix).front() == '/')); |
86 | 22 | if (GCCSuffix.empty()) |
87 | 19 | OS << "."; |
88 | 3 | else { |
89 | 3 | OS << StringRef(GCCSuffix).drop_front(); |
90 | 3 | } |
91 | 22 | OS << ";"; |
92 | 62 | for (StringRef Flag : Flags) { |
93 | 62 | if (Flag.front() == '+') |
94 | 22 | OS << "@" << Flag.substr(1); |
95 | 62 | } |
96 | 22 | } |
97 | | |
98 | 52.9k | bool Multilib::isValid() const { |
99 | 52.9k | llvm::StringMap<int> FlagSet; |
100 | 522k | for (unsigned I = 0, N = Flags.size(); I != N; ++I469k ) { |
101 | 471k | StringRef Flag(Flags[I]); |
102 | 471k | llvm::StringMap<int>::iterator SI = FlagSet.find(Flag.substr(1)); |
103 | | |
104 | 471k | assert(StringRef(Flag).front() == '+' || StringRef(Flag).front() == '-'); |
105 | | |
106 | 471k | if (SI == FlagSet.end()) |
107 | 461k | FlagSet[Flag.substr(1)] = I; |
108 | 9.63k | else if (Flags[I] != Flags[SI->getValue()]) |
109 | 2.14k | return false; |
110 | 471k | } |
111 | 50.7k | return true; |
112 | 52.9k | } |
113 | | |
114 | 821 | bool Multilib::operator==(const Multilib &Other) const { |
115 | | // Check whether the flags sets match |
116 | | // allowing for the match to be order invariant |
117 | 821 | llvm::StringSet<> MyFlags; |
118 | 821 | for (const auto &Flag : Flags) |
119 | 2.40k | MyFlags.insert(Flag); |
120 | | |
121 | 821 | for (const auto &Flag : Other.Flags) |
122 | 1.49k | if (MyFlags.find(Flag) == MyFlags.end()) |
123 | 527 | return false; |
124 | | |
125 | 294 | if (osSuffix() != Other.osSuffix()) |
126 | 2 | return false; |
127 | | |
128 | 292 | if (gccSuffix() != Other.gccSuffix()) |
129 | 251 | return false; |
130 | | |
131 | 41 | if (includeSuffix() != Other.includeSuffix()) |
132 | 2 | return false; |
133 | | |
134 | 39 | return true; |
135 | 41 | } |
136 | | |
137 | 22 | raw_ostream &clang::driver::operator<<(raw_ostream &OS, const Multilib &M) { |
138 | 22 | M.print(OS); |
139 | 22 | return OS; |
140 | 22 | } |
141 | | |
142 | 756 | MultilibSet &MultilibSet::Maybe(const Multilib &M) { |
143 | 756 | Multilib Opposite; |
144 | | // Negate any '+' flags |
145 | 1.14k | for (StringRef Flag : M.flags()) { |
146 | 1.14k | if (Flag.front() == '+') |
147 | 750 | Opposite.flags().push_back(("-" + Flag.substr(1)).str()); |
148 | 1.14k | } |
149 | 756 | return Either(M, Opposite); |
150 | 756 | } |
151 | | |
152 | 977 | MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2) { |
153 | 977 | return Either({M1, M2}); |
154 | 977 | } |
155 | | |
156 | | MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2, |
157 | 265 | const Multilib &M3) { |
158 | 265 | return Either({M1, M2, M3}); |
159 | 265 | } |
160 | | |
161 | | MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2, |
162 | 22 | const Multilib &M3, const Multilib &M4) { |
163 | 22 | return Either({M1, M2, M3, M4}); |
164 | 22 | } |
165 | | |
166 | | MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2, |
167 | | const Multilib &M3, const Multilib &M4, |
168 | 241 | const Multilib &M5) { |
169 | 241 | return Either({M1, M2, M3, M4, M5}); |
170 | 241 | } |
171 | | |
172 | 52.8k | static Multilib compose(const Multilib &Base, const Multilib &New) { |
173 | 52.8k | SmallString<128> GCCSuffix; |
174 | 52.8k | llvm::sys::path::append(GCCSuffix, "/", Base.gccSuffix(), New.gccSuffix()); |
175 | 52.8k | SmallString<128> OSSuffix; |
176 | 52.8k | llvm::sys::path::append(OSSuffix, "/", Base.osSuffix(), New.osSuffix()); |
177 | 52.8k | SmallString<128> IncludeSuffix; |
178 | 52.8k | llvm::sys::path::append(IncludeSuffix, "/", Base.includeSuffix(), |
179 | 52.8k | New.includeSuffix()); |
180 | | |
181 | 52.8k | Multilib Composed(GCCSuffix, OSSuffix, IncludeSuffix); |
182 | | |
183 | 52.8k | Multilib::flags_list &Flags = Composed.flags(); |
184 | | |
185 | 52.8k | Flags.insert(Flags.end(), Base.flags().begin(), Base.flags().end()); |
186 | 52.8k | Flags.insert(Flags.end(), New.flags().begin(), New.flags().end()); |
187 | | |
188 | 52.8k | return Composed; |
189 | 52.8k | } |
190 | | |
191 | 1.67k | MultilibSet &MultilibSet::Either(ArrayRef<Multilib> MultilibSegments) { |
192 | 1.67k | multilib_list Composed; |
193 | | |
194 | 1.67k | if (Multilibs.empty()) |
195 | 611 | Multilibs.insert(Multilibs.end(), MultilibSegments.begin(), |
196 | 611 | MultilibSegments.end()); |
197 | 1.06k | else { |
198 | 2.31k | for (const auto &New : MultilibSegments) { |
199 | 52.8k | for (const auto &Base : *this) { |
200 | 52.8k | Multilib MO = compose(Base, New); |
201 | 52.8k | if (MO.isValid()) |
202 | 50.7k | Composed.push_back(MO); |
203 | 52.8k | } |
204 | 2.31k | } |
205 | | |
206 | 1.06k | Multilibs = Composed; |
207 | 1.06k | } |
208 | | |
209 | 1.67k | return *this; |
210 | 1.67k | } |
211 | | |
212 | 774 | MultilibSet &MultilibSet::FilterOut(FilterCallback F) { |
213 | 774 | filterInPlace(F, Multilibs); |
214 | 774 | return *this; |
215 | 774 | } |
216 | | |
217 | 1.12k | MultilibSet &MultilibSet::FilterOut(const char *Regex) { |
218 | 1.12k | llvm::Regex R(Regex); |
219 | 1.12k | #ifndef NDEBUG |
220 | 1.12k | std::string Error; |
221 | 1.12k | if (!R.isValid(Error)) { |
222 | 0 | llvm::errs() << Error; |
223 | 0 | llvm_unreachable("Invalid regex!"); |
224 | 0 | } |
225 | 1.12k | #endif |
226 | | |
227 | 43.0k | filterInPlace([&R](const Multilib &M) 1.12k { return R.match(M.gccSuffix()); }, |
228 | 1.12k | Multilibs); |
229 | 1.12k | return *this; |
230 | 1.12k | } |
231 | | |
232 | 1.54k | void MultilibSet::push_back(const Multilib &M) { Multilibs.push_back(M); } |
233 | | |
234 | 2 | void MultilibSet::combineWith(const MultilibSet &Other) { |
235 | 2 | Multilibs.insert(Multilibs.end(), Other.begin(), Other.end()); |
236 | 2 | } |
237 | | |
238 | 30.7k | static bool isFlagEnabled(StringRef Flag) { |
239 | 30.7k | char Indicator = Flag.front(); |
240 | 30.7k | assert(Indicator == '+' || Indicator == '-'); |
241 | 0 | return Indicator == '+'; |
242 | 30.7k | } |
243 | | |
244 | 810 | bool MultilibSet::select(const Multilib::flags_list &Flags, Multilib &M) const { |
245 | 810 | llvm::StringMap<bool> FlagSet; |
246 | | |
247 | | // Stuff all of the flags into the FlagSet such that a true mappend indicates |
248 | | // the flag was enabled, and a false mappend indicates the flag was disabled. |
249 | 810 | for (StringRef Flag : Flags) |
250 | 6.56k | FlagSet[Flag.substr(1)] = isFlagEnabled(Flag); |
251 | | |
252 | 8.69k | multilib_list Filtered = filterCopy([&FlagSet](const Multilib &M) { |
253 | 24.1k | for (StringRef Flag : M.flags()) { |
254 | 24.1k | llvm::StringMap<bool>::const_iterator SI = FlagSet.find(Flag.substr(1)); |
255 | 24.1k | if (SI != FlagSet.end()) |
256 | 24.1k | if (SI->getValue() != isFlagEnabled(Flag)) |
257 | 8.07k | return true; |
258 | 24.1k | } |
259 | 616 | return false; |
260 | 8.69k | }, Multilibs); |
261 | | |
262 | 810 | if (Filtered.empty()) |
263 | 215 | return false; |
264 | 595 | if (Filtered.size() == 1) { |
265 | 586 | M = Filtered[0]; |
266 | 586 | return true; |
267 | 586 | } |
268 | | |
269 | | // Sort multilibs by priority and select the one with the highest priority. |
270 | 9 | llvm::sort(Filtered.begin(), Filtered.end(), |
271 | 37 | [](const Multilib &a, const Multilib &b) -> bool { |
272 | 37 | return a.priority() > b.priority(); |
273 | 37 | }); |
274 | | |
275 | 9 | if (Filtered[0].priority() > Filtered[1].priority()) { |
276 | 9 | M = Filtered[0]; |
277 | 9 | return true; |
278 | 9 | } |
279 | | |
280 | | // TODO: We should consider returning llvm::Error rather than aborting. |
281 | 0 | assert(false && "More than one multilib with the same priority"); |
282 | 0 | return false; |
283 | 9 | } |
284 | | |
285 | 0 | LLVM_DUMP_METHOD void MultilibSet::dump() const { |
286 | 0 | print(llvm::errs()); |
287 | 0 | } |
288 | | |
289 | 0 | void MultilibSet::print(raw_ostream &OS) const { |
290 | 0 | for (const auto &M : *this) |
291 | 0 | OS << M << "\n"; |
292 | 0 | } |
293 | | |
294 | | MultilibSet::multilib_list MultilibSet::filterCopy(FilterCallback F, |
295 | 810 | const multilib_list &Ms) { |
296 | 810 | multilib_list Copy(Ms); |
297 | 810 | filterInPlace(F, Copy); |
298 | 810 | return Copy; |
299 | 810 | } |
300 | | |
301 | 2.70k | void MultilibSet::filterInPlace(FilterCallback F, multilib_list &Ms) { |
302 | 2.70k | Ms.erase(std::remove_if(Ms.begin(), Ms.end(), F), Ms.end()); |
303 | 2.70k | } |
304 | | |
305 | 0 | raw_ostream &clang::driver::operator<<(raw_ostream &OS, const MultilibSet &MS) { |
306 | 0 | MS.print(OS); |
307 | 0 | return OS; |
308 | 0 | } |