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/SmallString.h"
13 #include "llvm/ADT/StringMap.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 : GCCSuffix(GCCSuffix
), OSSuffix(OSSuffix
), IncludeSuffix(IncludeSuffix
),
36 assert(GCCSuffix
.empty() ||
37 (StringRef(GCCSuffix
).front() == '/' && GCCSuffix
.size() > 1));
38 assert(OSSuffix
.empty() ||
39 (StringRef(OSSuffix
).front() == '/' && OSSuffix
.size() > 1));
40 assert(IncludeSuffix
.empty() ||
41 (StringRef(IncludeSuffix
).front() == '/' && IncludeSuffix
.size() > 1));
44 LLVM_DUMP_METHOD
void Multilib::dump() const {
48 void Multilib::print(raw_ostream
&OS
) const {
49 if (GCCSuffix
.empty())
52 OS
<< StringRef(GCCSuffix
).drop_front();
55 for (StringRef Flag
: Flags
) {
56 if (Flag
.front() == '-')
57 OS
<< "@" << Flag
.substr(1);
61 bool Multilib::operator==(const Multilib
&Other
) const {
62 // Check whether the flags sets match
63 // allowing for the match to be order invariant
64 llvm::StringSet
<> MyFlags
;
65 for (const auto &Flag
: Flags
)
68 for (const auto &Flag
: Other
.Flags
)
69 if (!MyFlags
.contains(Flag
))
72 if (osSuffix() != Other
.osSuffix())
75 if (gccSuffix() != Other
.gccSuffix())
78 if (includeSuffix() != Other
.includeSuffix())
84 raw_ostream
&clang::driver::operator<<(raw_ostream
&OS
, const Multilib
&M
) {
89 MultilibSet
&MultilibSet::FilterOut(FilterCallback F
) {
90 llvm::erase_if(Multilibs
, F
);
94 void MultilibSet::push_back(const Multilib
&M
) { Multilibs
.push_back(M
); }
96 bool MultilibSet::select(const Multilib::flags_list
&Flags
,
97 llvm::SmallVector
<Multilib
> &Selected
) const {
98 llvm::StringSet
<> FlagSet(expandFlags(Flags
));
100 llvm::copy_if(Multilibs
, std::back_inserter(Selected
),
101 [&FlagSet
](const Multilib
&M
) {
102 for (const std::string
&F
: M
.flags())
103 if (!FlagSet
.contains(F
))
107 return !Selected
.empty();
111 MultilibSet::expandFlags(const Multilib::flags_list
&InFlags
) const {
112 llvm::StringSet
<> Result
;
113 for (const auto &F
: InFlags
)
115 for (const FlagMatcher
&M
: FlagMatchers
) {
116 std::string
RegexString(M
.Match
);
118 // Make the regular expression match the whole string.
119 if (!StringRef(M
.Match
).starts_with("^"))
120 RegexString
.insert(RegexString
.begin(), '^');
121 if (!StringRef(M
.Match
).ends_with("$"))
122 RegexString
.push_back('$');
124 const llvm::Regex
Regex(RegexString
);
125 assert(Regex
.isValid());
126 if (llvm::find_if(InFlags
, [&Regex
](StringRef F
) {
127 return Regex
.match(F
);
128 }) != InFlags
.end()) {
129 Result
.insert(M
.Flags
.begin(), M
.Flags
.end());
137 // When updating this also update MULTILIB_VERSION in MultilibTest.cpp
138 static const VersionTuple
MultilibVersionCurrent(1, 0);
140 struct MultilibSerialization
{
142 std::vector
<std::string
> Flags
;
145 struct MultilibSetSerialization
{
146 llvm::VersionTuple MultilibVersion
;
147 std::vector
<MultilibSerialization
> Multilibs
;
148 std::vector
<MultilibSet::FlagMatcher
> FlagMatchers
;
151 } // end anonymous namespace
153 template <> struct llvm::yaml::MappingTraits
<MultilibSerialization
> {
154 static void mapping(llvm::yaml::IO
&io
, MultilibSerialization
&V
) {
155 io
.mapRequired("Dir", V
.Dir
);
156 io
.mapRequired("Flags", V
.Flags
);
158 static std::string
validate(IO
&io
, MultilibSerialization
&V
) {
159 if (StringRef(V
.Dir
).starts_with("/"))
160 return "paths must be relative but \"" + V
.Dir
+ "\" starts with \"/\"";
161 return std::string
{};
165 template <> struct llvm::yaml::MappingTraits
<MultilibSet::FlagMatcher
> {
166 static void mapping(llvm::yaml::IO
&io
, MultilibSet::FlagMatcher
&M
) {
167 io
.mapRequired("Match", M
.Match
);
168 io
.mapRequired("Flags", M
.Flags
);
170 static std::string
validate(IO
&io
, MultilibSet::FlagMatcher
&M
) {
171 llvm::Regex
Regex(M
.Match
);
172 std::string RegexError
;
173 if (!Regex
.isValid(RegexError
))
176 return "value required for 'Flags'";
177 return std::string
{};
181 template <> struct llvm::yaml::MappingTraits
<MultilibSetSerialization
> {
182 static void mapping(llvm::yaml::IO
&io
, MultilibSetSerialization
&M
) {
183 io
.mapRequired("MultilibVersion", M
.MultilibVersion
);
184 io
.mapRequired("Variants", M
.Multilibs
);
185 io
.mapOptional("Mappings", M
.FlagMatchers
);
187 static std::string
validate(IO
&io
, MultilibSetSerialization
&M
) {
188 if (M
.MultilibVersion
.empty())
189 return "missing required key 'MultilibVersion'";
190 if (M
.MultilibVersion
.getMajor() != MultilibVersionCurrent
.getMajor())
191 return "multilib version " + M
.MultilibVersion
.getAsString() +
193 if (M
.MultilibVersion
.getMinor() > MultilibVersionCurrent
.getMinor())
194 return "multilib version " + M
.MultilibVersion
.getAsString() +
196 return std::string
{};
200 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSerialization
)
201 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSet::FlagMatcher
)
203 llvm::ErrorOr
<MultilibSet
>
204 MultilibSet::parseYaml(llvm::MemoryBufferRef Input
,
205 llvm::SourceMgr::DiagHandlerTy DiagHandler
,
206 void *DiagHandlerCtxt
) {
207 MultilibSetSerialization MS
;
208 llvm::yaml::Input
YamlInput(Input
, nullptr, DiagHandler
, DiagHandlerCtxt
);
210 if (YamlInput
.error())
211 return YamlInput
.error();
213 multilib_list Multilibs
;
214 Multilibs
.reserve(MS
.Multilibs
.size());
215 for (const auto &M
: MS
.Multilibs
) {
219 Multilibs
.emplace_back(Dir
, Dir
, Dir
, M
.Flags
);
222 return MultilibSet(std::move(Multilibs
), std::move(MS
.FlagMatchers
));
225 LLVM_DUMP_METHOD
void MultilibSet::dump() const {
229 void MultilibSet::print(raw_ostream
&OS
) const {
230 for (const auto &M
: *this)
234 raw_ostream
&clang::driver::operator<<(raw_ostream
&OS
, const MultilibSet
&MS
) {