xref: /llvm-project/clang/lib/Driver/Multilib.cpp (revision ea9d404032be1541e82e965bada82935629a98c8)
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 "llvm/ADT/SmallString.h"
12 #include "llvm/ADT/StringMap.h"
13 #include "llvm/ADT/StringRef.h"
14 #include "llvm/ADT/StringSet.h"
15 #include "llvm/Support/Compiler.h"
16 #include "llvm/Support/ErrorHandling.h"
17 #include "llvm/Support/Path.h"
18 #include "llvm/Support/Regex.h"
19 #include "llvm/Support/raw_ostream.h"
20 #include <algorithm>
21 #include <cassert>
22 #include <string>
23 
24 using namespace clang;
25 using namespace driver;
26 using namespace llvm::sys;
27 
28 Multilib::Multilib(StringRef GCCSuffix, StringRef OSSuffix,
29                    StringRef IncludeSuffix, int Priority,
30                    const flags_list &Flags)
31     : GCCSuffix(GCCSuffix), OSSuffix(OSSuffix), IncludeSuffix(IncludeSuffix),
32       Flags(Flags), Priority(Priority) {
33   assert(GCCSuffix.empty() ||
34          (StringRef(GCCSuffix).front() == '/' && GCCSuffix.size() > 1));
35   assert(OSSuffix.empty() ||
36          (StringRef(OSSuffix).front() == '/' && OSSuffix.size() > 1));
37   assert(IncludeSuffix.empty() ||
38          (StringRef(IncludeSuffix).front() == '/' && IncludeSuffix.size() > 1));
39 }
40 
41 LLVM_DUMP_METHOD void Multilib::dump() const {
42   print(llvm::errs());
43 }
44 
45 void Multilib::print(raw_ostream &OS) const {
46   if (GCCSuffix.empty())
47     OS << ".";
48   else {
49     OS << StringRef(GCCSuffix).drop_front();
50   }
51   OS << ";";
52   for (StringRef Flag : Flags) {
53     if (Flag.front() == '+')
54       OS << "@" << Flag.substr(1);
55   }
56 }
57 
58 bool Multilib::operator==(const Multilib &Other) const {
59   // Check whether the flags sets match
60   // allowing for the match to be order invariant
61   llvm::StringSet<> MyFlags;
62   for (const auto &Flag : Flags)
63     MyFlags.insert(Flag);
64 
65   for (const auto &Flag : Other.Flags)
66     if (!MyFlags.contains(Flag))
67       return false;
68 
69   if (osSuffix() != Other.osSuffix())
70     return false;
71 
72   if (gccSuffix() != Other.gccSuffix())
73     return false;
74 
75   if (includeSuffix() != Other.includeSuffix())
76     return false;
77 
78   return true;
79 }
80 
81 raw_ostream &clang::driver::operator<<(raw_ostream &OS, const Multilib &M) {
82   M.print(OS);
83   return OS;
84 }
85 
86 MultilibSet &MultilibSet::FilterOut(FilterCallback F) {
87   filterInPlace(F, Multilibs);
88   return *this;
89 }
90 
91 void MultilibSet::push_back(const Multilib &M) { Multilibs.push_back(M); }
92 
93 static bool isFlagEnabled(StringRef Flag) {
94   char Indicator = Flag.front();
95   assert(Indicator == '+' || Indicator == '-');
96   return Indicator == '+';
97 }
98 
99 bool MultilibSet::select(const Multilib::flags_list &Flags, Multilib &M) const {
100   llvm::StringMap<bool> FlagSet;
101 
102   // Stuff all of the flags into the FlagSet such that a true mappend indicates
103   // the flag was enabled, and a false mappend indicates the flag was disabled.
104   for (StringRef Flag : Flags)
105     FlagSet[Flag.substr(1)] = isFlagEnabled(Flag);
106 
107   multilib_list Filtered = filterCopy([&FlagSet](const Multilib &M) {
108     for (StringRef Flag : M.flags()) {
109       llvm::StringMap<bool>::const_iterator SI = FlagSet.find(Flag.substr(1));
110       if (SI != FlagSet.end())
111         if (SI->getValue() != isFlagEnabled(Flag))
112           return true;
113     }
114     return false;
115   }, Multilibs);
116 
117   if (Filtered.empty())
118     return false;
119   if (Filtered.size() == 1) {
120     M = Filtered[0];
121     return true;
122   }
123 
124   // Sort multilibs by priority and select the one with the highest priority.
125   llvm::sort(Filtered, [](const Multilib &a, const Multilib &b) -> bool {
126     return a.priority() > b.priority();
127   });
128 
129   if (Filtered[0].priority() > Filtered[1].priority()) {
130     M = Filtered[0];
131     return true;
132   }
133 
134   // TODO: We should consider returning llvm::Error rather than aborting.
135   assert(false && "More than one multilib with the same priority");
136   return false;
137 }
138 
139 LLVM_DUMP_METHOD void MultilibSet::dump() const {
140   print(llvm::errs());
141 }
142 
143 void MultilibSet::print(raw_ostream &OS) const {
144   for (const auto &M : *this)
145     OS << M << "\n";
146 }
147 
148 MultilibSet::multilib_list MultilibSet::filterCopy(FilterCallback F,
149                                                    const multilib_list &Ms) {
150   multilib_list Copy(Ms);
151   filterInPlace(F, Copy);
152   return Copy;
153 }
154 
155 void MultilibSet::filterInPlace(FilterCallback F, multilib_list &Ms) {
156   llvm::erase_if(Ms, F);
157 }
158 
159 raw_ostream &clang::driver::operator<<(raw_ostream &OS, const MultilibSet &MS) {
160   MS.print(OS);
161   return OS;
162 }
163