150
|
1 //===- Multilib.cpp - Multilib Implementation -----------------------------===//
|
|
2 //
|
|
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
|
|
6 //
|
|
7 //===----------------------------------------------------------------------===//
|
|
8
|
|
9 #include "clang/Driver/Multilib.h"
|
|
10 #include "clang/Basic/LLVM.h"
|
252
|
11 #include "clang/Basic/Version.h"
|
150
|
12 #include "llvm/ADT/SmallString.h"
|
|
13 #include "llvm/ADT/StringMap.h"
|
|
14 #include "llvm/ADT/StringRef.h"
|
|
15 #include "llvm/Support/Compiler.h"
|
252
|
16 #include "llvm/Support/Error.h"
|
150
|
17 #include "llvm/Support/ErrorHandling.h"
|
|
18 #include "llvm/Support/Path.h"
|
|
19 #include "llvm/Support/Regex.h"
|
252
|
20 #include "llvm/Support/VersionTuple.h"
|
|
21 #include "llvm/Support/YAMLParser.h"
|
|
22 #include "llvm/Support/YAMLTraits.h"
|
150
|
23 #include "llvm/Support/raw_ostream.h"
|
|
24 #include <algorithm>
|
|
25 #include <cassert>
|
|
26 #include <string>
|
|
27
|
|
28 using namespace clang;
|
|
29 using namespace driver;
|
|
30 using namespace llvm::sys;
|
|
31
|
|
32 Multilib::Multilib(StringRef GCCSuffix, StringRef OSSuffix,
|
252
|
33 StringRef IncludeSuffix, const flags_list &Flags)
|
150
|
34 : GCCSuffix(GCCSuffix), OSSuffix(OSSuffix), IncludeSuffix(IncludeSuffix),
|
252
|
35 Flags(Flags) {
|
|
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));
|
150
|
42 }
|
|
43
|
|
44 LLVM_DUMP_METHOD void Multilib::dump() const {
|
|
45 print(llvm::errs());
|
|
46 }
|
|
47
|
|
48 void Multilib::print(raw_ostream &OS) const {
|
|
49 if (GCCSuffix.empty())
|
|
50 OS << ".";
|
|
51 else {
|
|
52 OS << StringRef(GCCSuffix).drop_front();
|
|
53 }
|
|
54 OS << ";";
|
|
55 for (StringRef Flag : Flags) {
|
252
|
56 if (Flag.front() == '-')
|
150
|
57 OS << "@" << Flag.substr(1);
|
|
58 }
|
|
59 }
|
|
60
|
|
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)
|
|
66 MyFlags.insert(Flag);
|
|
67
|
|
68 for (const auto &Flag : Other.Flags)
|
252
|
69 if (!MyFlags.contains(Flag))
|
150
|
70 return false;
|
|
71
|
|
72 if (osSuffix() != Other.osSuffix())
|
|
73 return false;
|
|
74
|
|
75 if (gccSuffix() != Other.gccSuffix())
|
|
76 return false;
|
|
77
|
|
78 if (includeSuffix() != Other.includeSuffix())
|
|
79 return false;
|
|
80
|
|
81 return true;
|
|
82 }
|
|
83
|
|
84 raw_ostream &clang::driver::operator<<(raw_ostream &OS, const Multilib &M) {
|
|
85 M.print(OS);
|
|
86 return OS;
|
|
87 }
|
|
88
|
|
89 MultilibSet &MultilibSet::FilterOut(FilterCallback F) {
|
252
|
90 llvm::erase_if(Multilibs, F);
|
150
|
91 return *this;
|
|
92 }
|
|
93
|
|
94 void MultilibSet::push_back(const Multilib &M) { Multilibs.push_back(M); }
|
|
95
|
252
|
96 bool MultilibSet::select(const Multilib::flags_list &Flags,
|
|
97 llvm::SmallVector<Multilib> &Selected) const {
|
|
98 llvm::StringSet<> FlagSet(expandFlags(Flags));
|
|
99 Selected.clear();
|
|
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))
|
|
104 return false;
|
|
105 return true;
|
|
106 });
|
|
107 return !Selected.empty();
|
150
|
108 }
|
|
109
|
252
|
110 llvm::StringSet<>
|
|
111 MultilibSet::expandFlags(const Multilib::flags_list &InFlags) const {
|
|
112 llvm::StringSet<> Result;
|
|
113 for (const auto &F : InFlags)
|
|
114 Result.insert(F);
|
|
115 for (const FlagMatcher &M : FlagMatchers) {
|
|
116 std::string RegexString(M.Match);
|
|
117
|
|
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('$');
|
|
123
|
|
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());
|
|
130 }
|
|
131 }
|
|
132 return Result;
|
150
|
133 }
|
|
134
|
252
|
135 namespace {
|
|
136
|
|
137 // When updating this also update MULTILIB_VERSION in MultilibTest.cpp
|
|
138 static const VersionTuple MultilibVersionCurrent(1, 0);
|
|
139
|
|
140 struct MultilibSerialization {
|
|
141 std::string Dir;
|
|
142 std::vector<std::string> Flags;
|
|
143 };
|
150
|
144
|
252
|
145 struct MultilibSetSerialization {
|
|
146 llvm::VersionTuple MultilibVersion;
|
|
147 std::vector<MultilibSerialization> Multilibs;
|
|
148 std::vector<MultilibSet::FlagMatcher> FlagMatchers;
|
|
149 };
|
|
150
|
|
151 } // end anonymous namespace
|
|
152
|
|
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);
|
|
157 }
|
|
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{};
|
|
162 }
|
|
163 };
|
150
|
164
|
252
|
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);
|
|
169 }
|
|
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))
|
|
174 return RegexError;
|
|
175 if (M.Flags.empty())
|
|
176 return "value required for 'Flags'";
|
|
177 return std::string{};
|
|
178 }
|
|
179 };
|
150
|
180
|
252
|
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);
|
|
186 }
|
|
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() +
|
|
192 " is unsupported";
|
|
193 if (M.MultilibVersion.getMinor() > MultilibVersionCurrent.getMinor())
|
|
194 return "multilib version " + M.MultilibVersion.getAsString() +
|
|
195 " is unsupported";
|
|
196 return std::string{};
|
|
197 }
|
|
198 };
|
|
199
|
|
200 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSerialization)
|
|
201 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSet::FlagMatcher)
|
|
202
|
|
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);
|
|
209 YamlInput >> MS;
|
|
210 if (YamlInput.error())
|
|
211 return YamlInput.error();
|
|
212
|
|
213 multilib_list Multilibs;
|
|
214 Multilibs.reserve(MS.Multilibs.size());
|
|
215 for (const auto &M : MS.Multilibs) {
|
|
216 std::string Dir;
|
|
217 if (M.Dir != ".")
|
|
218 Dir = "/" + M.Dir;
|
|
219 Multilibs.emplace_back(Dir, Dir, Dir, M.Flags);
|
150
|
220 }
|
|
221
|
252
|
222 return MultilibSet(std::move(Multilibs), std::move(MS.FlagMatchers));
|
150
|
223 }
|
|
224
|
|
225 LLVM_DUMP_METHOD void MultilibSet::dump() const {
|
|
226 print(llvm::errs());
|
|
227 }
|
|
228
|
|
229 void MultilibSet::print(raw_ostream &OS) const {
|
|
230 for (const auto &M : *this)
|
|
231 OS << M << "\n";
|
|
232 }
|
|
233
|
|
234 raw_ostream &clang::driver::operator<<(raw_ostream &OS, const MultilibSet &MS) {
|
|
235 MS.print(OS);
|
|
236 return OS;
|
|
237 }
|