1 //===- Multilib.cpp - Multilib Implementation -----------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
24 using namespace clang
;
25 using namespace driver
;
26 using namespace llvm::sys
;
28 /// normalize Segment to "/foo/bar" or "".
29 static void normalizePathSegment(std::string
&Segment
) {
30 StringRef seg
= Segment
;
32 // Prune trailing "/" or "./"
34 StringRef last
= path::filename(seg
);
37 seg
= path::parent_path(seg
);
40 if (seg
.empty() || seg
== "/") {
46 if (seg
.front() != '/') {
47 Segment
= "/" + seg
.str();
49 Segment
= std::string(seg
);
53 Multilib::Multilib(StringRef GCCSuffix
, StringRef OSSuffix
,
54 StringRef IncludeSuffix
, int Priority
)
55 : GCCSuffix(GCCSuffix
), OSSuffix(OSSuffix
), IncludeSuffix(IncludeSuffix
),
57 normalizePathSegment(this->GCCSuffix
);
58 normalizePathSegment(this->OSSuffix
);
59 normalizePathSegment(this->IncludeSuffix
);
62 Multilib
&Multilib::gccSuffix(StringRef S
) {
63 GCCSuffix
= std::string(S
);
64 normalizePathSegment(GCCSuffix
);
68 Multilib
&Multilib::osSuffix(StringRef S
) {
69 OSSuffix
= std::string(S
);
70 normalizePathSegment(OSSuffix
);
74 Multilib
&Multilib::includeSuffix(StringRef S
) {
75 IncludeSuffix
= std::string(S
);
76 normalizePathSegment(IncludeSuffix
);
80 LLVM_DUMP_METHOD
void Multilib::dump() const {
84 void Multilib::print(raw_ostream
&OS
) const {
85 assert(GCCSuffix
.empty() || (StringRef(GCCSuffix
).front() == '/'));
86 if (GCCSuffix
.empty())
89 OS
<< StringRef(GCCSuffix
).drop_front();
92 for (StringRef Flag
: Flags
) {
93 if (Flag
.front() == '+')
94 OS
<< "@" << Flag
.substr(1);
98 bool Multilib::isValid() const {
99 llvm::StringMap
<int> FlagSet
;
100 for (unsigned I
= 0, N
= Flags
.size(); I
!= N
; ++I
) {
101 StringRef
Flag(Flags
[I
]);
102 llvm::StringMap
<int>::iterator SI
= FlagSet
.find(Flag
.substr(1));
104 assert(StringRef(Flag
).front() == '+' || StringRef(Flag
).front() == '-');
106 if (SI
== FlagSet
.end())
107 FlagSet
[Flag
.substr(1)] = I
;
108 else if (Flags
[I
] != Flags
[SI
->getValue()])
114 bool Multilib::operator==(const Multilib
&Other
) const {
115 // Check whether the flags sets match
116 // allowing for the match to be order invariant
117 llvm::StringSet
<> MyFlags
;
118 for (const auto &Flag
: Flags
)
119 MyFlags
.insert(Flag
);
121 for (const auto &Flag
: Other
.Flags
)
122 if (MyFlags
.find(Flag
) == MyFlags
.end())
125 if (osSuffix() != Other
.osSuffix())
128 if (gccSuffix() != Other
.gccSuffix())
131 if (includeSuffix() != Other
.includeSuffix())
137 raw_ostream
&clang::driver::operator<<(raw_ostream
&OS
, const Multilib
&M
) {
142 MultilibSet
&MultilibSet::Maybe(const Multilib
&M
) {
144 // Negate any '+' flags
145 for (StringRef Flag
: M
.flags()) {
146 if (Flag
.front() == '+')
147 Opposite
.flags().push_back(("-" + Flag
.substr(1)).str());
149 return Either(M
, Opposite
);
152 MultilibSet
&MultilibSet::Either(const Multilib
&M1
, const Multilib
&M2
) {
153 return Either({M1
, M2
});
156 MultilibSet
&MultilibSet::Either(const Multilib
&M1
, const Multilib
&M2
,
157 const Multilib
&M3
) {
158 return Either({M1
, M2
, M3
});
161 MultilibSet
&MultilibSet::Either(const Multilib
&M1
, const Multilib
&M2
,
162 const Multilib
&M3
, const Multilib
&M4
) {
163 return Either({M1
, M2
, M3
, M4
});
166 MultilibSet
&MultilibSet::Either(const Multilib
&M1
, const Multilib
&M2
,
167 const Multilib
&M3
, const Multilib
&M4
,
168 const Multilib
&M5
) {
169 return Either({M1
, M2
, M3
, M4
, M5
});
172 static Multilib
compose(const Multilib
&Base
, const Multilib
&New
) {
173 SmallString
<128> GCCSuffix
;
174 llvm::sys::path::append(GCCSuffix
, "/", Base
.gccSuffix(), New
.gccSuffix());
175 SmallString
<128> OSSuffix
;
176 llvm::sys::path::append(OSSuffix
, "/", Base
.osSuffix(), New
.osSuffix());
177 SmallString
<128> IncludeSuffix
;
178 llvm::sys::path::append(IncludeSuffix
, "/", Base
.includeSuffix(),
179 New
.includeSuffix());
181 Multilib
Composed(GCCSuffix
, OSSuffix
, IncludeSuffix
);
183 Multilib::flags_list
&Flags
= Composed
.flags();
185 Flags
.insert(Flags
.end(), Base
.flags().begin(), Base
.flags().end());
186 Flags
.insert(Flags
.end(), New
.flags().begin(), New
.flags().end());
191 MultilibSet
&MultilibSet::Either(ArrayRef
<Multilib
> MultilibSegments
) {
192 multilib_list Composed
;
194 if (Multilibs
.empty())
195 Multilibs
.insert(Multilibs
.end(), MultilibSegments
.begin(),
196 MultilibSegments
.end());
198 for (const auto &New
: MultilibSegments
) {
199 for (const auto &Base
: *this) {
200 Multilib MO
= compose(Base
, New
);
202 Composed
.push_back(MO
);
206 Multilibs
= Composed
;
212 MultilibSet
&MultilibSet::FilterOut(FilterCallback F
) {
213 filterInPlace(F
, Multilibs
);
217 MultilibSet
&MultilibSet::FilterOut(const char *Regex
) {
218 llvm::Regex
R(Regex
);
221 if (!R
.isValid(Error
)) {
222 llvm::errs() << Error
;
223 llvm_unreachable("Invalid regex!");
227 filterInPlace([&R
](const Multilib
&M
) { return R
.match(M
.gccSuffix()); },
232 void MultilibSet::push_back(const Multilib
&M
) { Multilibs
.push_back(M
); }
234 void MultilibSet::combineWith(const MultilibSet
&Other
) {
235 Multilibs
.insert(Multilibs
.end(), Other
.begin(), Other
.end());
238 static bool isFlagEnabled(StringRef Flag
) {
239 char Indicator
= Flag
.front();
240 assert(Indicator
== '+' || Indicator
== '-');
241 return Indicator
== '+';
244 bool MultilibSet::select(const Multilib::flags_list
&Flags
, Multilib
&M
) const {
245 llvm::StringMap
<bool> FlagSet
;
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 for (StringRef Flag
: Flags
)
250 FlagSet
[Flag
.substr(1)] = isFlagEnabled(Flag
);
252 multilib_list Filtered
= filterCopy([&FlagSet
](const Multilib
&M
) {
253 for (StringRef Flag
: M
.flags()) {
254 llvm::StringMap
<bool>::const_iterator SI
= FlagSet
.find(Flag
.substr(1));
255 if (SI
!= FlagSet
.end())
256 if (SI
->getValue() != isFlagEnabled(Flag
))
262 if (Filtered
.empty())
264 if (Filtered
.size() == 1) {
269 // Sort multilibs by priority and select the one with the highest priority.
270 llvm::sort(Filtered
, [](const Multilib
&a
, const Multilib
&b
) -> bool {
271 return a
.priority() > b
.priority();
274 if (Filtered
[0].priority() > Filtered
[1].priority()) {
279 // TODO: We should consider returning llvm::Error rather than aborting.
280 assert(false && "More than one multilib with the same priority");
284 LLVM_DUMP_METHOD
void MultilibSet::dump() const {
288 void MultilibSet::print(raw_ostream
&OS
) const {
289 for (const auto &M
: *this)
293 MultilibSet::multilib_list
MultilibSet::filterCopy(FilterCallback F
,
294 const multilib_list
&Ms
) {
295 multilib_list
Copy(Ms
);
296 filterInPlace(F
, Copy
);
300 void MultilibSet::filterInPlace(FilterCallback F
, multilib_list
&Ms
) {
301 llvm::erase_if(Ms
, F
);
304 raw_ostream
&clang::driver::operator<<(raw_ostream
&OS
, const MultilibSet
&MS
) {