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/StringRef.h"
14 #include "llvm/Support/Compiler.h"
15 #include "llvm/Support/Error.h"
16 #include "llvm/Support/ErrorHandling.h"
17 #include "llvm/Support/Path.h"
18 #include "llvm/Support/Regex.h"
19 #include "llvm/Support/VersionTuple.h"
20 #include "llvm/Support/YAMLParser.h"
21 #include "llvm/Support/YAMLTraits.h"
22 #include "llvm/Support/raw_ostream.h"
27 using namespace clang
;
28 using namespace driver
;
29 using namespace llvm::sys
;
31 Multilib::Multilib(StringRef GCCSuffix
, StringRef OSSuffix
,
32 StringRef IncludeSuffix
, const flags_list
&Flags
)
33 : GCCSuffix(GCCSuffix
), OSSuffix(OSSuffix
), IncludeSuffix(IncludeSuffix
),
35 assert(GCCSuffix
.empty() ||
36 (StringRef(GCCSuffix
).front() == '/' && GCCSuffix
.size() > 1));
37 assert(OSSuffix
.empty() ||
38 (StringRef(OSSuffix
).front() == '/' && OSSuffix
.size() > 1));
39 assert(IncludeSuffix
.empty() ||
40 (StringRef(IncludeSuffix
).front() == '/' && IncludeSuffix
.size() > 1));
43 LLVM_DUMP_METHOD
void Multilib::dump() const {
47 void Multilib::print(raw_ostream
&OS
) const {
48 if (GCCSuffix
.empty())
51 OS
<< StringRef(GCCSuffix
).drop_front();
54 for (StringRef Flag
: Flags
) {
55 if (Flag
.front() == '-')
56 OS
<< "@" << Flag
.substr(1);
60 bool Multilib::operator==(const Multilib
&Other
) const {
61 // Check whether the flags sets match
62 // allowing for the match to be order invariant
63 llvm::StringSet
<> MyFlags
;
64 for (const auto &Flag
: Flags
)
67 for (const auto &Flag
: Other
.Flags
)
68 if (!MyFlags
.contains(Flag
))
71 if (osSuffix() != Other
.osSuffix())
74 if (gccSuffix() != Other
.gccSuffix())
77 if (includeSuffix() != Other
.includeSuffix())
83 raw_ostream
&clang::driver::operator<<(raw_ostream
&OS
, const Multilib
&M
) {
88 MultilibSet
&MultilibSet::FilterOut(FilterCallback F
) {
89 llvm::erase_if(Multilibs
, F
);
93 void MultilibSet::push_back(const Multilib
&M
) { Multilibs
.push_back(M
); }
95 bool MultilibSet::select(const Multilib::flags_list
&Flags
,
96 llvm::SmallVector
<Multilib
> &Selected
) const {
97 llvm::StringSet
<> FlagSet(expandFlags(Flags
));
99 llvm::copy_if(Multilibs
, std::back_inserter(Selected
),
100 [&FlagSet
](const Multilib
&M
) {
101 for (const std::string
&F
: M
.flags())
102 if (!FlagSet
.contains(F
))
106 return !Selected
.empty();
110 MultilibSet::expandFlags(const Multilib::flags_list
&InFlags
) const {
111 llvm::StringSet
<> Result
;
112 for (const auto &F
: InFlags
)
114 for (const FlagMatcher
&M
: FlagMatchers
) {
115 std::string
RegexString(M
.Match
);
117 // Make the regular expression match the whole string.
118 if (!StringRef(M
.Match
).starts_with("^"))
119 RegexString
.insert(RegexString
.begin(), '^');
120 if (!StringRef(M
.Match
).ends_with("$"))
121 RegexString
.push_back('$');
123 const llvm::Regex
Regex(RegexString
);
124 assert(Regex
.isValid());
125 if (llvm::any_of(InFlags
,
126 [&Regex
](StringRef F
) { return Regex
.match(F
); })) {
127 Result
.insert(M
.Flags
.begin(), M
.Flags
.end());
135 // When updating this also update MULTILIB_VERSION in MultilibTest.cpp
136 static const VersionTuple
MultilibVersionCurrent(1, 0);
138 struct MultilibSerialization
{
140 std::vector
<std::string
> Flags
;
143 struct MultilibSetSerialization
{
144 llvm::VersionTuple MultilibVersion
;
145 std::vector
<MultilibSerialization
> Multilibs
;
146 std::vector
<MultilibSet::FlagMatcher
> FlagMatchers
;
149 } // end anonymous namespace
151 template <> struct llvm::yaml::MappingTraits
<MultilibSerialization
> {
152 static void mapping(llvm::yaml::IO
&io
, MultilibSerialization
&V
) {
153 io
.mapRequired("Dir", V
.Dir
);
154 io
.mapRequired("Flags", V
.Flags
);
156 static std::string
validate(IO
&io
, MultilibSerialization
&V
) {
157 if (StringRef(V
.Dir
).starts_with("/"))
158 return "paths must be relative but \"" + V
.Dir
+ "\" starts with \"/\"";
159 return std::string
{};
163 template <> struct llvm::yaml::MappingTraits
<MultilibSet::FlagMatcher
> {
164 static void mapping(llvm::yaml::IO
&io
, MultilibSet::FlagMatcher
&M
) {
165 io
.mapRequired("Match", M
.Match
);
166 io
.mapRequired("Flags", M
.Flags
);
168 static std::string
validate(IO
&io
, MultilibSet::FlagMatcher
&M
) {
169 llvm::Regex
Regex(M
.Match
);
170 std::string RegexError
;
171 if (!Regex
.isValid(RegexError
))
174 return "value required for 'Flags'";
175 return std::string
{};
179 template <> struct llvm::yaml::MappingTraits
<MultilibSetSerialization
> {
180 static void mapping(llvm::yaml::IO
&io
, MultilibSetSerialization
&M
) {
181 io
.mapRequired("MultilibVersion", M
.MultilibVersion
);
182 io
.mapRequired("Variants", M
.Multilibs
);
183 io
.mapOptional("Mappings", M
.FlagMatchers
);
185 static std::string
validate(IO
&io
, MultilibSetSerialization
&M
) {
186 if (M
.MultilibVersion
.empty())
187 return "missing required key 'MultilibVersion'";
188 if (M
.MultilibVersion
.getMajor() != MultilibVersionCurrent
.getMajor())
189 return "multilib version " + M
.MultilibVersion
.getAsString() +
191 if (M
.MultilibVersion
.getMinor() > MultilibVersionCurrent
.getMinor())
192 return "multilib version " + M
.MultilibVersion
.getAsString() +
194 return std::string
{};
198 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSerialization
)
199 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSet::FlagMatcher
)
201 llvm::ErrorOr
<MultilibSet
>
202 MultilibSet::parseYaml(llvm::MemoryBufferRef Input
,
203 llvm::SourceMgr::DiagHandlerTy DiagHandler
,
204 void *DiagHandlerCtxt
) {
205 MultilibSetSerialization MS
;
206 llvm::yaml::Input
YamlInput(Input
, nullptr, DiagHandler
, DiagHandlerCtxt
);
208 if (YamlInput
.error())
209 return YamlInput
.error();
211 multilib_list Multilibs
;
212 Multilibs
.reserve(MS
.Multilibs
.size());
213 for (const auto &M
: MS
.Multilibs
) {
217 Multilibs
.emplace_back(Dir
, Dir
, Dir
, M
.Flags
);
220 return MultilibSet(std::move(Multilibs
), std::move(MS
.FlagMatchers
));
223 LLVM_DUMP_METHOD
void MultilibSet::dump() const {
227 void MultilibSet::print(raw_ostream
&OS
) const {
228 for (const auto &M
: *this)
232 raw_ostream
&clang::driver::operator<<(raw_ostream
&OS
, const MultilibSet
&MS
) {