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 "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"
28 using namespace clang
;
29 using namespace driver
;
30 using namespace llvm::sys
;
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));
45 LLVM_DUMP_METHOD
void Multilib::dump() const {
49 void Multilib::print(raw_ostream
&OS
) const {
50 if (GCCSuffix
.empty())
53 OS
<< StringRef(GCCSuffix
).drop_front();
56 for (StringRef Flag
: Flags
) {
57 if (Flag
.front() == '-')
58 OS
<< "@" << Flag
.substr(1);
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
)
69 for (const auto &Flag
: Other
.Flags
)
70 if (!MyFlags
.contains(Flag
))
73 if (osSuffix() != Other
.osSuffix())
76 if (gccSuffix() != Other
.gccSuffix())
79 if (includeSuffix() != Other
.includeSuffix())
85 raw_ostream
&clang::driver::operator<<(raw_ostream
&OS
, const Multilib
&M
) {
90 MultilibSet
&MultilibSet::FilterOut(FilterCallback F
) {
91 llvm::erase_if(Multilibs
, F
);
95 void MultilibSet::push_back(const Multilib
&M
) { Multilibs
.push_back(M
); }
97 bool MultilibSet::select(const Multilib::flags_list
&Flags
,
98 llvm::SmallVectorImpl
<Multilib
> &Selected
) const {
99 llvm::StringSet
<> FlagSet(expandFlags(Flags
));
102 // Decide which multilibs we're going to select at all.
103 llvm::DenseSet
<StringRef
> ExclusiveGroupsSelected
;
104 for (const Multilib
&M
: llvm::reverse(Multilibs
)) {
105 // If this multilib doesn't match all our flags, don't select it.
106 if (!llvm::all_of(M
.flags(), [&FlagSet
](const std::string
&F
) {
107 return FlagSet
.contains(F
);
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.
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(group
);
124 // Select this multilib.
125 Selected
.push_back(M
);
128 // We iterated in reverse order, so now put Selected back the right way
130 std::reverse(Selected
.begin(), Selected
.end());
132 return !Selected
.empty();
136 MultilibSet::expandFlags(const Multilib::flags_list
&InFlags
) const {
137 llvm::StringSet
<> Result
;
138 for (const auto &F
: InFlags
)
140 for (const FlagMatcher
&M
: FlagMatchers
) {
141 std::string
RegexString(M
.Match
);
143 // Make the regular expression match the whole string.
144 if (!StringRef(M
.Match
).starts_with("^"))
145 RegexString
.insert(RegexString
.begin(), '^');
146 if (!StringRef(M
.Match
).ends_with("$"))
147 RegexString
.push_back('$');
149 const llvm::Regex
Regex(RegexString
);
150 assert(Regex
.isValid());
151 if (llvm::any_of(InFlags
,
152 [&Regex
](StringRef F
) { return Regex
.match(F
); })) {
153 Result
.insert(M
.Flags
.begin(), M
.Flags
.end());
161 // When updating this also update MULTILIB_VERSION in MultilibTest.cpp
162 static const VersionTuple
MultilibVersionCurrent(1, 0);
164 struct MultilibSerialization
{
166 std::vector
<std::string
> Flags
;
170 enum class MultilibGroupType
{
172 * The only group type currently supported is 'Exclusive', which indicates a
173 * group of multilibs of which at most one may be selected.
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.
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.
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.
192 struct MultilibGroupSerialization
{
194 MultilibGroupType Type
;
197 struct MultilibSetSerialization
{
198 llvm::VersionTuple MultilibVersion
;
199 std::vector
<MultilibGroupSerialization
> Groups
;
200 std::vector
<MultilibSerialization
> Multilibs
;
201 std::vector
<MultilibSet::FlagMatcher
> FlagMatchers
;
204 } // end anonymous namespace
206 template <> struct llvm::yaml::MappingTraits
<MultilibSerialization
> {
207 static void mapping(llvm::yaml::IO
&io
, MultilibSerialization
&V
) {
208 io
.mapRequired("Dir", V
.Dir
);
209 io
.mapRequired("Flags", V
.Flags
);
210 io
.mapOptional("Group", V
.Group
);
212 static std::string
validate(IO
&io
, MultilibSerialization
&V
) {
213 if (StringRef(V
.Dir
).starts_with("/"))
214 return "paths must be relative but \"" + V
.Dir
+ "\" starts with \"/\"";
215 return std::string
{};
219 template <> struct llvm::yaml::ScalarEnumerationTraits
<MultilibGroupType
> {
220 static void enumeration(IO
&io
, MultilibGroupType
&Val
) {
221 io
.enumCase(Val
, "Exclusive", MultilibGroupType::Exclusive
);
225 template <> struct llvm::yaml::MappingTraits
<MultilibGroupSerialization
> {
226 static void mapping(llvm::yaml::IO
&io
, MultilibGroupSerialization
&V
) {
227 io
.mapRequired("Name", V
.Name
);
228 io
.mapRequired("Type", V
.Type
);
232 template <> struct llvm::yaml::MappingTraits
<MultilibSet::FlagMatcher
> {
233 static void mapping(llvm::yaml::IO
&io
, MultilibSet::FlagMatcher
&M
) {
234 io
.mapRequired("Match", M
.Match
);
235 io
.mapRequired("Flags", M
.Flags
);
237 static std::string
validate(IO
&io
, MultilibSet::FlagMatcher
&M
) {
238 llvm::Regex
Regex(M
.Match
);
239 std::string RegexError
;
240 if (!Regex
.isValid(RegexError
))
243 return "value required for 'Flags'";
244 return std::string
{};
248 template <> struct llvm::yaml::MappingTraits
<MultilibSetSerialization
> {
249 static void mapping(llvm::yaml::IO
&io
, MultilibSetSerialization
&M
) {
250 io
.mapRequired("MultilibVersion", M
.MultilibVersion
);
251 io
.mapRequired("Variants", M
.Multilibs
);
252 io
.mapOptional("Groups", M
.Groups
);
253 io
.mapOptional("Mappings", M
.FlagMatchers
);
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() +
261 if (M
.MultilibVersion
.getMinor() > MultilibVersionCurrent
.getMinor())
262 return "multilib version " + M
.MultilibVersion
.getAsString() +
264 for (const MultilibSerialization
&Lib
: M
.Multilibs
) {
265 if (!Lib
.Group
.empty()) {
267 for (const MultilibGroupSerialization
&Group
: M
.Groups
)
268 if (Group
.Name
== Lib
.Group
) {
273 return "multilib \"" + Lib
.Dir
+
274 "\" specifies undefined group name \"" + Lib
.Group
+ "\"";
277 return std::string
{};
281 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSerialization
)
282 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibGroupSerialization
)
283 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSet::FlagMatcher
)
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
);
292 if (YamlInput
.error())
293 return YamlInput
.error();
295 multilib_list Multilibs
;
296 Multilibs
.reserve(MS
.Multilibs
.size());
297 for (const auto &M
: MS
.Multilibs
) {
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(Dir
, Dir
, Dir
, M
.Flags
, M
.Group
);
308 return MultilibSet(std::move(Multilibs
), std::move(MS
.FlagMatchers
));
311 LLVM_DUMP_METHOD
void MultilibSet::dump() const {
315 void MultilibSet::print(raw_ostream
&OS
) const {
316 for (const auto &M
: *this)
320 raw_ostream
&clang::driver::operator<<(raw_ostream
&OS
, const MultilibSet
&MS
) {