xref: /llvm-project/clang/lib/Driver/Multilib.cpp (revision 26bf0b4ae7df7f5350f71afd40a57cdf8f98c588)
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 "clang/Driver/Driver.h"
13 #include "llvm/ADT/DenseSet.h"
14 #include "llvm/ADT/SmallString.h"
15 #include "llvm/ADT/StringRef.h"
16 #include "llvm/Support/Compiler.h"
17 #include "llvm/Support/Error.h"
18 #include "llvm/Support/ErrorHandling.h"
19 #include "llvm/Support/Path.h"
20 #include "llvm/Support/Regex.h"
21 #include "llvm/Support/VersionTuple.h"
22 #include "llvm/Support/YAMLParser.h"
23 #include "llvm/Support/YAMLTraits.h"
24 #include "llvm/Support/raw_ostream.h"
25 #include <algorithm>
26 #include <cassert>
27 #include <string>
28 
29 using namespace clang;
30 using namespace driver;
31 using namespace llvm::sys;
32 
33 Multilib::Multilib(StringRef GCCSuffix, StringRef OSSuffix,
34                    StringRef IncludeSuffix, const flags_list &Flags,
35                    StringRef ExclusiveGroup,
36                    std::optional<StringRef> FatalError)
37     : GCCSuffix(GCCSuffix), OSSuffix(OSSuffix), IncludeSuffix(IncludeSuffix),
38       Flags(Flags), ExclusiveGroup(ExclusiveGroup), FatalError(FatalError) {
39   assert(GCCSuffix.empty() ||
40          (StringRef(GCCSuffix).front() == '/' && GCCSuffix.size() > 1));
41   assert(OSSuffix.empty() ||
42          (StringRef(OSSuffix).front() == '/' && OSSuffix.size() > 1));
43   assert(IncludeSuffix.empty() ||
44          (StringRef(IncludeSuffix).front() == '/' && IncludeSuffix.size() > 1));
45 }
46 
47 LLVM_DUMP_METHOD void Multilib::dump() const {
48   print(llvm::errs());
49 }
50 
51 void Multilib::print(raw_ostream &OS) const {
52   if (GCCSuffix.empty())
53     OS << ".";
54   else {
55     OS << StringRef(GCCSuffix).drop_front();
56   }
57   OS << ";";
58   for (StringRef Flag : Flags) {
59     if (Flag.front() == '-')
60       OS << "@" << Flag.substr(1);
61   }
62 }
63 
64 bool Multilib::operator==(const Multilib &Other) const {
65   // Check whether the flags sets match
66   // allowing for the match to be order invariant
67   llvm::StringSet<> MyFlags;
68   for (const auto &Flag : Flags)
69     MyFlags.insert(Flag);
70 
71   for (const auto &Flag : Other.Flags)
72     if (!MyFlags.contains(Flag))
73       return false;
74 
75   if (osSuffix() != Other.osSuffix())
76     return false;
77 
78   if (gccSuffix() != Other.gccSuffix())
79     return false;
80 
81   if (includeSuffix() != Other.includeSuffix())
82     return false;
83 
84   return true;
85 }
86 
87 raw_ostream &clang::driver::operator<<(raw_ostream &OS, const Multilib &M) {
88   M.print(OS);
89   return OS;
90 }
91 
92 MultilibSet &MultilibSet::FilterOut(FilterCallback F) {
93   llvm::erase_if(Multilibs, F);
94   return *this;
95 }
96 
97 void MultilibSet::push_back(const Multilib &M) { Multilibs.push_back(M); }
98 
99 bool MultilibSet::select(const Driver &D, const Multilib::flags_list &Flags,
100                          llvm::SmallVectorImpl<Multilib> &Selected) const {
101   llvm::StringSet<> FlagSet(expandFlags(Flags));
102   Selected.clear();
103 
104   // Decide which multilibs we're going to select at all.
105   llvm::DenseSet<StringRef> ExclusiveGroupsSelected;
106   for (const Multilib &M : llvm::reverse(Multilibs)) {
107     // If this multilib doesn't match all our flags, don't select it.
108     if (!llvm::all_of(M.flags(), [&FlagSet](const std::string &F) {
109           return FlagSet.contains(F);
110         }))
111       continue;
112 
113     const std::string &group = M.exclusiveGroup();
114     if (!group.empty()) {
115       // If this multilib has the same ExclusiveGroup as one we've already
116       // selected, skip it. We're iterating in reverse order, so the group
117       // member we've selected already is preferred.
118       //
119       // Otherwise, add the group name to the set of groups we've already
120       // selected a member of.
121       auto [It, Inserted] = ExclusiveGroupsSelected.insert(group);
122       if (!Inserted)
123         continue;
124     }
125 
126     // If this multilib is actually a placeholder containing a fatal
127     // error message written by the multilib.yaml author, display that
128     // error message, and return failure.
129     if (M.isFatalError()) {
130       D.Diag(clang::diag::err_drv_multilib_custom_error) << M.getFatalError();
131       return false;
132     }
133 
134     // Select this multilib.
135     Selected.push_back(M);
136   }
137 
138   // We iterated in reverse order, so now put Selected back the right way
139   // round.
140   std::reverse(Selected.begin(), Selected.end());
141 
142   return !Selected.empty();
143 }
144 
145 llvm::StringSet<>
146 MultilibSet::expandFlags(const Multilib::flags_list &InFlags) const {
147   llvm::StringSet<> Result;
148   for (const auto &F : InFlags)
149     Result.insert(F);
150   for (const FlagMatcher &M : FlagMatchers) {
151     std::string RegexString(M.Match);
152 
153     // Make the regular expression match the whole string.
154     if (!StringRef(M.Match).starts_with("^"))
155       RegexString.insert(RegexString.begin(), '^');
156     if (!StringRef(M.Match).ends_with("$"))
157       RegexString.push_back('$');
158 
159     const llvm::Regex Regex(RegexString);
160     assert(Regex.isValid());
161     if (llvm::any_of(InFlags,
162                      [&Regex](StringRef F) { return Regex.match(F); })) {
163       Result.insert(M.Flags.begin(), M.Flags.end());
164     }
165   }
166   return Result;
167 }
168 
169 namespace {
170 
171 // When updating this also update MULTILIB_VERSION in MultilibTest.cpp
172 static const VersionTuple MultilibVersionCurrent(1, 0);
173 
174 struct MultilibSerialization {
175   std::string Dir;        // if this record successfully selects a library dir
176   std::string FatalError; // if this record reports a fatal error message
177   std::vector<std::string> Flags;
178   std::string Group;
179 };
180 
181 enum class MultilibGroupType {
182   /*
183    * The only group type currently supported is 'Exclusive', which indicates a
184    * group of multilibs of which at most one may be selected.
185    */
186   Exclusive,
187 
188   /*
189    * Future possibility: a second group type indicating a set of library
190    * directories that are mutually _dependent_ rather than mutually exclusive:
191    * if you include one you must include them all.
192    *
193    * It might also be useful to allow groups to be members of other groups, so
194    * that a mutually exclusive group could contain a mutually dependent set of
195    * library directories, or vice versa.
196    *
197    * These additional features would need changes in the implementation, but
198    * the YAML schema is set up so they can be added without requiring changes
199    * in existing users' multilib.yaml files.
200    */
201 };
202 
203 struct MultilibGroupSerialization {
204   std::string Name;
205   MultilibGroupType Type;
206 };
207 
208 struct MultilibSetSerialization {
209   llvm::VersionTuple MultilibVersion;
210   std::vector<MultilibGroupSerialization> Groups;
211   std::vector<MultilibSerialization> Multilibs;
212   std::vector<MultilibSet::FlagMatcher> FlagMatchers;
213 };
214 
215 } // end anonymous namespace
216 
217 template <> struct llvm::yaml::MappingTraits<MultilibSerialization> {
218   static void mapping(llvm::yaml::IO &io, MultilibSerialization &V) {
219     io.mapOptional("Dir", V.Dir);
220     io.mapOptional("FatalError", V.FatalError);
221     io.mapRequired("Flags", V.Flags);
222     io.mapOptional("Group", V.Group);
223   }
224   static std::string validate(IO &io, MultilibSerialization &V) {
225     if (V.Dir.empty() && V.FatalError.empty())
226       return "one of the 'Dir' and 'FatalError' keys must be specified";
227     if (!V.Dir.empty() && !V.FatalError.empty())
228       return "the 'Dir' and 'FatalError' keys may not both be specified";
229     if (StringRef(V.Dir).starts_with("/"))
230       return "paths must be relative but \"" + V.Dir + "\" starts with \"/\"";
231     return std::string{};
232   }
233 };
234 
235 template <> struct llvm::yaml::ScalarEnumerationTraits<MultilibGroupType> {
236   static void enumeration(IO &io, MultilibGroupType &Val) {
237     io.enumCase(Val, "Exclusive", MultilibGroupType::Exclusive);
238   }
239 };
240 
241 template <> struct llvm::yaml::MappingTraits<MultilibGroupSerialization> {
242   static void mapping(llvm::yaml::IO &io, MultilibGroupSerialization &V) {
243     io.mapRequired("Name", V.Name);
244     io.mapRequired("Type", V.Type);
245   }
246 };
247 
248 template <> struct llvm::yaml::MappingTraits<MultilibSet::FlagMatcher> {
249   static void mapping(llvm::yaml::IO &io, MultilibSet::FlagMatcher &M) {
250     io.mapRequired("Match", M.Match);
251     io.mapRequired("Flags", M.Flags);
252   }
253   static std::string validate(IO &io, MultilibSet::FlagMatcher &M) {
254     llvm::Regex Regex(M.Match);
255     std::string RegexError;
256     if (!Regex.isValid(RegexError))
257       return RegexError;
258     if (M.Flags.empty())
259       return "value required for 'Flags'";
260     return std::string{};
261   }
262 };
263 
264 template <> struct llvm::yaml::MappingTraits<MultilibSetSerialization> {
265   static void mapping(llvm::yaml::IO &io, MultilibSetSerialization &M) {
266     io.mapRequired("MultilibVersion", M.MultilibVersion);
267     io.mapRequired("Variants", M.Multilibs);
268     io.mapOptional("Groups", M.Groups);
269     io.mapOptional("Mappings", M.FlagMatchers);
270   }
271   static std::string validate(IO &io, MultilibSetSerialization &M) {
272     if (M.MultilibVersion.empty())
273       return "missing required key 'MultilibVersion'";
274     if (M.MultilibVersion.getMajor() != MultilibVersionCurrent.getMajor())
275       return "multilib version " + M.MultilibVersion.getAsString() +
276              " is unsupported";
277     if (M.MultilibVersion.getMinor() > MultilibVersionCurrent.getMinor())
278       return "multilib version " + M.MultilibVersion.getAsString() +
279              " is unsupported";
280     for (const MultilibSerialization &Lib : M.Multilibs) {
281       if (!Lib.Group.empty()) {
282         bool Found = false;
283         for (const MultilibGroupSerialization &Group : M.Groups)
284           if (Group.Name == Lib.Group) {
285             Found = true;
286             break;
287           }
288         if (!Found)
289           return "multilib \"" + Lib.Dir +
290                  "\" specifies undefined group name \"" + Lib.Group + "\"";
291       }
292     }
293     return std::string{};
294   }
295 };
296 
297 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSerialization)
298 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibGroupSerialization)
299 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSet::FlagMatcher)
300 
301 llvm::ErrorOr<MultilibSet>
302 MultilibSet::parseYaml(llvm::MemoryBufferRef Input,
303                        llvm::SourceMgr::DiagHandlerTy DiagHandler,
304                        void *DiagHandlerCtxt) {
305   MultilibSetSerialization MS;
306   llvm::yaml::Input YamlInput(Input, nullptr, DiagHandler, DiagHandlerCtxt);
307   YamlInput >> MS;
308   if (YamlInput.error())
309     return YamlInput.error();
310 
311   multilib_list Multilibs;
312   Multilibs.reserve(MS.Multilibs.size());
313   for (const auto &M : MS.Multilibs) {
314     if (!M.FatalError.empty()) {
315       Multilibs.emplace_back("", "", "", M.Flags, M.Group, M.FatalError);
316     } else {
317       std::string Dir;
318       if (M.Dir != ".")
319         Dir = "/" + M.Dir;
320       // We transfer M.Group straight into the ExclusiveGroup parameter for the
321       // Multilib constructor. If we later support more than one type of group,
322       // we'll have to look up the group name in MS.Groups, check its type, and
323       // decide what to do here.
324       Multilibs.emplace_back(Dir, Dir, Dir, M.Flags, M.Group);
325     }
326   }
327 
328   return MultilibSet(std::move(Multilibs), std::move(MS.FlagMatchers));
329 }
330 
331 LLVM_DUMP_METHOD void MultilibSet::dump() const {
332   print(llvm::errs());
333 }
334 
335 void MultilibSet::print(raw_ostream &OS) const {
336   for (const auto &M : *this)
337     OS << M << "\n";
338 }
339 
340 raw_ostream &clang::driver::operator<<(raw_ostream &OS, const MultilibSet &MS) {
341   MS.print(OS);
342   return OS;
343 }
344