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/Driver/Driver.h"
12 #include "llvm/ADT/DenseSet.h"
13 #include "llvm/ADT/StringRef.h"
14 #include "llvm/Support/Compiler.h"
15 #include "llvm/Support/ErrorHandling.h"
16 #include "llvm/Support/Regex.h"
17 #include "llvm/Support/VersionTuple.h"
18 #include "llvm/Support/YAMLParser.h"
19 #include "llvm/Support/YAMLTraits.h"
20 #include "llvm/Support/raw_ostream.h"
25 using namespace clang
;
26 using namespace driver
;
27 using namespace llvm::sys
;
29 Multilib::Multilib(StringRef GCCSuffix
, StringRef OSSuffix
,
30 StringRef IncludeSuffix
, const flags_list
&Flags
,
31 StringRef ExclusiveGroup
, std::optional
<StringRef
> Error
)
32 : GCCSuffix(GCCSuffix
), OSSuffix(OSSuffix
), IncludeSuffix(IncludeSuffix
),
33 Flags(Flags
), ExclusiveGroup(ExclusiveGroup
), Error(Error
) {
34 assert(GCCSuffix
.empty() ||
35 (StringRef(GCCSuffix
).front() == '/' && GCCSuffix
.size() > 1));
36 assert(OSSuffix
.empty() ||
37 (StringRef(OSSuffix
).front() == '/' && OSSuffix
.size() > 1));
38 assert(IncludeSuffix
.empty() ||
39 (StringRef(IncludeSuffix
).front() == '/' && IncludeSuffix
.size() > 1));
42 LLVM_DUMP_METHOD
void Multilib::dump() const {
46 void Multilib::print(raw_ostream
&OS
) const {
47 if (GCCSuffix
.empty())
50 OS
<< StringRef(GCCSuffix
).drop_front();
53 for (StringRef Flag
: Flags
) {
54 if (Flag
.front() == '-')
55 OS
<< "@" << Flag
.substr(1);
59 bool Multilib::operator==(const Multilib
&Other
) const {
60 // Check whether the flags sets match
61 // allowing for the match to be order invariant
62 llvm::StringSet
<> MyFlags
;
63 for (const auto &Flag
: Flags
)
66 for (const auto &Flag
: Other
.Flags
)
67 if (!MyFlags
.contains(Flag
))
70 if (osSuffix() != Other
.osSuffix())
73 if (gccSuffix() != Other
.gccSuffix())
76 if (includeSuffix() != Other
.includeSuffix())
82 raw_ostream
&clang::driver::operator<<(raw_ostream
&OS
, const Multilib
&M
) {
87 MultilibSet
&MultilibSet::FilterOut(FilterCallback F
) {
88 llvm::erase_if(Multilibs
, F
);
92 void MultilibSet::push_back(const Multilib
&M
) { Multilibs
.push_back(M
); }
94 bool MultilibSet::select(const Driver
&D
, const Multilib::flags_list
&Flags
,
95 llvm::SmallVectorImpl
<Multilib
> &Selected
) const {
96 llvm::StringSet
<> FlagSet(expandFlags(Flags
));
98 bool AnyErrors
= false;
100 // Decide which multilibs we're going to select at all.
101 llvm::DenseSet
<StringRef
> ExclusiveGroupsSelected
;
102 for (const Multilib
&M
: llvm::reverse(Multilibs
)) {
103 // If this multilib doesn't match all our flags, don't select it.
104 if (!llvm::all_of(M
.flags(), [&FlagSet
](const std::string
&F
) {
105 return FlagSet
.contains(F
);
109 const std::string
&group
= M
.exclusiveGroup();
110 if (!group
.empty()) {
111 // If this multilib has the same ExclusiveGroup as one we've already
112 // selected, skip it. We're iterating in reverse order, so the group
113 // member we've selected already is preferred.
115 // Otherwise, add the group name to the set of groups we've already
116 // selected a member of.
117 auto [It
, Inserted
] = ExclusiveGroupsSelected
.insert(group
);
122 // If this multilib is actually a placeholder containing an error message
123 // written by the multilib.yaml author, then set a flag that will cause a
124 // failure return. Our caller will display the error message.
128 // Select this multilib.
129 Selected
.push_back(M
);
132 // We iterated in reverse order, so now put Selected back the right way
134 std::reverse(Selected
.begin(), Selected
.end());
136 return !AnyErrors
&& !Selected
.empty();
140 MultilibSet::expandFlags(const Multilib::flags_list
&InFlags
) const {
141 llvm::StringSet
<> Result
;
142 for (const auto &F
: InFlags
)
144 for (const FlagMatcher
&M
: FlagMatchers
) {
145 std::string
RegexString(M
.Match
);
147 // Make the regular expression match the whole string.
148 if (!StringRef(M
.Match
).starts_with("^"))
149 RegexString
.insert(RegexString
.begin(), '^');
150 if (!StringRef(M
.Match
).ends_with("$"))
151 RegexString
.push_back('$');
153 const llvm::Regex
Regex(RegexString
);
154 assert(Regex
.isValid());
155 if (llvm::any_of(InFlags
,
156 [&Regex
](StringRef F
) { return Regex
.match(F
); })) {
157 Result
.insert(M
.Flags
.begin(), M
.Flags
.end());
165 // When updating this also update MULTILIB_VERSION in MultilibTest.cpp
166 static const VersionTuple
MultilibVersionCurrent(1, 0);
168 struct MultilibSerialization
{
169 std::string Dir
; // if this record successfully selects a library dir
170 std::string Error
; // if this record reports a fatal error message
171 std::vector
<std::string
> Flags
;
175 enum class MultilibGroupType
{
177 * The only group type currently supported is 'Exclusive', which indicates a
178 * group of multilibs of which at most one may be selected.
183 * Future possibility: a second group type indicating a set of library
184 * directories that are mutually _dependent_ rather than mutually exclusive:
185 * if you include one you must include them all.
187 * It might also be useful to allow groups to be members of other groups, so
188 * that a mutually exclusive group could contain a mutually dependent set of
189 * library directories, or vice versa.
191 * These additional features would need changes in the implementation, but
192 * the YAML schema is set up so they can be added without requiring changes
193 * in existing users' multilib.yaml files.
197 struct MultilibGroupSerialization
{
199 MultilibGroupType Type
;
202 struct MultilibSetSerialization
{
203 llvm::VersionTuple MultilibVersion
;
204 std::vector
<MultilibGroupSerialization
> Groups
;
205 std::vector
<MultilibSerialization
> Multilibs
;
206 std::vector
<MultilibSet::FlagMatcher
> FlagMatchers
;
209 } // end anonymous namespace
211 template <> struct llvm::yaml::MappingTraits
<MultilibSerialization
> {
212 static void mapping(llvm::yaml::IO
&io
, MultilibSerialization
&V
) {
213 io
.mapOptional("Dir", V
.Dir
);
214 io
.mapOptional("Error", V
.Error
);
215 io
.mapRequired("Flags", V
.Flags
);
216 io
.mapOptional("Group", V
.Group
);
218 static std::string
validate(IO
&io
, MultilibSerialization
&V
) {
219 if (V
.Dir
.empty() && V
.Error
.empty())
220 return "one of the 'Dir' and 'Error' keys must be specified";
221 if (!V
.Dir
.empty() && !V
.Error
.empty())
222 return "the 'Dir' and 'Error' keys may not both be specified";
223 if (StringRef(V
.Dir
).starts_with("/"))
224 return "paths must be relative but \"" + V
.Dir
+ "\" starts with \"/\"";
225 return std::string
{};
229 template <> struct llvm::yaml::ScalarEnumerationTraits
<MultilibGroupType
> {
230 static void enumeration(IO
&io
, MultilibGroupType
&Val
) {
231 io
.enumCase(Val
, "Exclusive", MultilibGroupType::Exclusive
);
235 template <> struct llvm::yaml::MappingTraits
<MultilibGroupSerialization
> {
236 static void mapping(llvm::yaml::IO
&io
, MultilibGroupSerialization
&V
) {
237 io
.mapRequired("Name", V
.Name
);
238 io
.mapRequired("Type", V
.Type
);
242 template <> struct llvm::yaml::MappingTraits
<MultilibSet::FlagMatcher
> {
243 static void mapping(llvm::yaml::IO
&io
, MultilibSet::FlagMatcher
&M
) {
244 io
.mapRequired("Match", M
.Match
);
245 io
.mapRequired("Flags", M
.Flags
);
247 static std::string
validate(IO
&io
, MultilibSet::FlagMatcher
&M
) {
248 llvm::Regex
Regex(M
.Match
);
249 std::string RegexError
;
250 if (!Regex
.isValid(RegexError
))
253 return "value required for 'Flags'";
254 return std::string
{};
258 template <> struct llvm::yaml::MappingTraits
<MultilibSetSerialization
> {
259 static void mapping(llvm::yaml::IO
&io
, MultilibSetSerialization
&M
) {
260 io
.mapRequired("MultilibVersion", M
.MultilibVersion
);
261 io
.mapRequired("Variants", M
.Multilibs
);
262 io
.mapOptional("Groups", M
.Groups
);
263 io
.mapOptional("Mappings", M
.FlagMatchers
);
265 static std::string
validate(IO
&io
, MultilibSetSerialization
&M
) {
266 if (M
.MultilibVersion
.empty())
267 return "missing required key 'MultilibVersion'";
268 if (M
.MultilibVersion
.getMajor() != MultilibVersionCurrent
.getMajor())
269 return "multilib version " + M
.MultilibVersion
.getAsString() +
271 if (M
.MultilibVersion
.getMinor() > MultilibVersionCurrent
.getMinor())
272 return "multilib version " + M
.MultilibVersion
.getAsString() +
274 for (const MultilibSerialization
&Lib
: M
.Multilibs
) {
275 if (!Lib
.Group
.empty()) {
277 for (const MultilibGroupSerialization
&Group
: M
.Groups
)
278 if (Group
.Name
== Lib
.Group
) {
283 return "multilib \"" + Lib
.Dir
+
284 "\" specifies undefined group name \"" + Lib
.Group
+ "\"";
287 return std::string
{};
291 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSerialization
)
292 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibGroupSerialization
)
293 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSet::FlagMatcher
)
295 llvm::ErrorOr
<MultilibSet
>
296 MultilibSet::parseYaml(llvm::MemoryBufferRef Input
,
297 llvm::SourceMgr::DiagHandlerTy DiagHandler
,
298 void *DiagHandlerCtxt
) {
299 MultilibSetSerialization MS
;
300 llvm::yaml::Input
YamlInput(Input
, nullptr, DiagHandler
, DiagHandlerCtxt
);
302 if (YamlInput
.error())
303 return YamlInput
.error();
305 multilib_list Multilibs
;
306 Multilibs
.reserve(MS
.Multilibs
.size());
307 for (const auto &M
: MS
.Multilibs
) {
308 if (!M
.Error
.empty()) {
309 Multilibs
.emplace_back("", "", "", M
.Flags
, M
.Group
, M
.Error
);
314 // We transfer M.Group straight into the ExclusiveGroup parameter for the
315 // Multilib constructor. If we later support more than one type of group,
316 // we'll have to look up the group name in MS.Groups, check its type, and
317 // decide what to do here.
318 Multilibs
.emplace_back(Dir
, Dir
, Dir
, M
.Flags
, M
.Group
);
322 return MultilibSet(std::move(Multilibs
), std::move(MS
.FlagMatchers
));
325 LLVM_DUMP_METHOD
void MultilibSet::dump() const {
329 void MultilibSet::print(raw_ostream
&OS
) const {
330 for (const auto &M
: *this)
334 raw_ostream
&clang::driver::operator<<(raw_ostream
&OS
, const MultilibSet
&MS
) {