xref: /freebsd/contrib/llvm-project/clang/lib/Driver/Multilib.cpp (revision 5d6d6278979b9eab598a23c804d23e930d2f7268)
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"
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"
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,
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));
43 }
44 
45 LLVM_DUMP_METHOD void Multilib::dump() const {
46   print(llvm::errs());
47 }
48 
49 void Multilib::print(raw_ostream &OS) const {
50   if (GCCSuffix.empty())
51     OS << ".";
52   else {
53     OS << StringRef(GCCSuffix).drop_front();
54   }
55   OS << ";";
56   for (StringRef Flag : Flags) {
57     if (Flag.front() == '-')
58       OS << "@" << Flag.substr(1);
59   }
60 }
61 
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)
67     MyFlags.insert(Flag);
68 
69   for (const auto &Flag : Other.Flags)
70     if (!MyFlags.contains(Flag))
71       return false;
72 
73   if (osSuffix() != Other.osSuffix())
74     return false;
75 
76   if (gccSuffix() != Other.gccSuffix())
77     return false;
78 
79   if (includeSuffix() != Other.includeSuffix())
80     return false;
81 
82   return true;
83 }
84 
85 raw_ostream &clang::driver::operator<<(raw_ostream &OS, const Multilib &M) {
86   M.print(OS);
87   return OS;
88 }
89 
90 MultilibSet &MultilibSet::FilterOut(FilterCallback F) {
91   llvm::erase_if(Multilibs, F);
92   return *this;
93 }
94 
95 void MultilibSet::push_back(const Multilib &M) { Multilibs.push_back(M); }
96 
97 bool MultilibSet::select(const Multilib::flags_list &Flags,
98                          llvm::SmallVectorImpl<Multilib> &Selected) const {
99   llvm::StringSet<> FlagSet(expandFlags(Flags));
100   Selected.clear();
101 
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);
108         }))
109       continue;
110 
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.
116       //
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);
120       if (!Inserted)
121         continue;
122     }
123 
124     // Select this multilib.
125     Selected.push_back(M);
126   }
127 
128   // We iterated in reverse order, so now put Selected back the right way
129   // round.
130   std::reverse(Selected.begin(), Selected.end());
131 
132   return !Selected.empty();
133 }
134 
135 llvm::StringSet<>
136 MultilibSet::expandFlags(const Multilib::flags_list &InFlags) const {
137   llvm::StringSet<> Result;
138   for (const auto &F : InFlags)
139     Result.insert(F);
140   for (const FlagMatcher &M : FlagMatchers) {
141     std::string RegexString(M.Match);
142 
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('$');
148 
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());
154     }
155   }
156   return Result;
157 }
158 
159 namespace {
160 
161 // When updating this also update MULTILIB_VERSION in MultilibTest.cpp
162 static const VersionTuple MultilibVersionCurrent(1, 0);
163 
164 struct MultilibSerialization {
165   std::string Dir;
166   std::vector<std::string> Flags;
167   std::string Group;
168 };
169 
170 enum class MultilibGroupType {
171   /*
172    * The only group type currently supported is 'Exclusive', which indicates a
173    * group of multilibs of which at most one may be selected.
174    */
175   Exclusive,
176 
177   /*
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.
181    *
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.
185    *
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.
189    */
190 };
191 
192 struct MultilibGroupSerialization {
193   std::string Name;
194   MultilibGroupType Type;
195 };
196 
197 struct MultilibSetSerialization {
198   llvm::VersionTuple MultilibVersion;
199   std::vector<MultilibGroupSerialization> Groups;
200   std::vector<MultilibSerialization> Multilibs;
201   std::vector<MultilibSet::FlagMatcher> FlagMatchers;
202 };
203 
204 } // end anonymous namespace
205 
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);
211   }
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{};
216   }
217 };
218 
219 template <> struct llvm::yaml::ScalarEnumerationTraits<MultilibGroupType> {
220   static void enumeration(IO &io, MultilibGroupType &Val) {
221     io.enumCase(Val, "Exclusive", MultilibGroupType::Exclusive);
222   }
223 };
224 
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);
229   }
230 };
231 
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);
236   }
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))
241       return RegexError;
242     if (M.Flags.empty())
243       return "value required for 'Flags'";
244     return std::string{};
245   }
246 };
247 
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);
254   }
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() +
260              " is unsupported";
261     if (M.MultilibVersion.getMinor() > MultilibVersionCurrent.getMinor())
262       return "multilib version " + M.MultilibVersion.getAsString() +
263              " is unsupported";
264     for (const MultilibSerialization &Lib : M.Multilibs) {
265       if (!Lib.Group.empty()) {
266         bool Found = false;
267         for (const MultilibGroupSerialization &Group : M.Groups)
268           if (Group.Name == Lib.Group) {
269             Found = true;
270             break;
271           }
272         if (!Found)
273           return "multilib \"" + Lib.Dir +
274                  "\" specifies undefined group name \"" + Lib.Group + "\"";
275       }
276     }
277     return std::string{};
278   }
279 };
280 
281 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSerialization)
282 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibGroupSerialization)
283 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSet::FlagMatcher)
284 
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);
291   YamlInput >> MS;
292   if (YamlInput.error())
293     return YamlInput.error();
294 
295   multilib_list Multilibs;
296   Multilibs.reserve(MS.Multilibs.size());
297   for (const auto &M : MS.Multilibs) {
298     std::string Dir;
299     if (M.Dir != ".")
300       Dir = "/" + M.Dir;
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);
306   }
307 
308   return MultilibSet(std::move(Multilibs), std::move(MS.FlagMatchers));
309 }
310 
311 LLVM_DUMP_METHOD void MultilibSet::dump() const {
312   print(llvm::errs());
313 }
314 
315 void MultilibSet::print(raw_ostream &OS) const {
316   for (const auto &M : *this)
317     OS << M << "\n";
318 }
319 
320 raw_ostream &clang::driver::operator<<(raw_ostream &OS, const MultilibSet &MS) {
321   MS.print(OS);
322   return OS;
323 }
324