Clang Project

clang_source_code/lib/Driver/Multilib.cpp
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
24using namespace clang;
25using namespace driver;
26using namespace llvm::sys;
27
28/// normalize Segment to "/foo/bar" or "".
29static void normalizePathSegment(std::string &Segment) {
30  StringRef seg = Segment;
31
32  // Prune trailing "/" or "./"
33  while (true) {
34    StringRef last = path::filename(seg);
35    if (last != ".")
36      break;
37    seg = path::parent_path(seg);
38  }
39
40  if (seg.empty() || seg == "/") {
41    Segment.clear();
42    return;
43  }
44
45  // Add leading '/'
46  if (seg.front() != '/') {
47    Segment = "/" + seg.str();
48  } else {
49    Segment = seg;
50  }
51}
52
53Multilib::Multilib(StringRef GCCSuffixStringRef OSSuffix,
54                   StringRef IncludeSuffix)
55    : GCCSuffix(GCCSuffix), OSSuffix(OSSuffix), IncludeSuffix(IncludeSuffix) {
56  normalizePathSegment(this->GCCSuffix);
57  normalizePathSegment(this->OSSuffix);
58  normalizePathSegment(this->IncludeSuffix);
59}
60
61Multilib &Multilib::gccSuffix(StringRef S) {
62  GCCSuffix = S;
63  normalizePathSegment(GCCSuffix);
64  return *this;
65}
66
67Multilib &Multilib::osSuffix(StringRef S) {
68  OSSuffix = S;
69  normalizePathSegment(OSSuffix);
70  return *this;
71}
72
73Multilib &Multilib::includeSuffix(StringRef S) {
74  IncludeSuffix = S;
75  normalizePathSegment(IncludeSuffix);
76  return *this;
77}
78
79LLVM_DUMP_METHOD void Multilib::dump() const {
80  print(llvm::errs());
81}
82
83void Multilib::print(raw_ostream &OSconst {
84  assert(GCCSuffix.empty() || (StringRef(GCCSuffix).front() == '/'));
85  if (GCCSuffix.empty())
86    OS << ".";
87  else {
88    OS << StringRef(GCCSuffix).drop_front();
89  }
90  OS << ";";
91  for (StringRef Flag : Flags) {
92    if (Flag.front() == '+')
93      OS << "@" << Flag.substr(1);
94  }
95}
96
97bool Multilib::isValid() const {
98  llvm::StringMap<int> FlagSet;
99  for (unsigned I = 0N = Flags.size(); I != N; ++I) {
100    StringRef Flag(Flags[I]);
101    llvm::StringMap<int>::iterator SI = FlagSet.find(Flag.substr(1));
102
103    assert(StringRef(Flag).front() == '+' || StringRef(Flag).front() == '-');
104
105    if (SI == FlagSet.end())
106      FlagSet[Flag.substr(1)] = I;
107    else if (Flags[I] != Flags[SI->getValue()])
108      return false;
109  }
110  return true;
111}
112
113bool Multilib::operator==(const Multilib &Otherconst {
114  // Check whether the flags sets match
115  // allowing for the match to be order invariant
116  llvm::StringSet<> MyFlags;
117  for (const auto &Flag : Flags)
118    MyFlags.insert(Flag);
119
120  for (const auto &Flag : Other.Flags)
121    if (MyFlags.find(Flag) == MyFlags.end())
122      return false;
123
124  if (osSuffix() != Other.osSuffix())
125    return false;
126
127  if (gccSuffix() != Other.gccSuffix())
128    return false;
129
130  if (includeSuffix() != Other.includeSuffix())
131    return false;
132
133  return true;
134}
135
136raw_ostream &clang::driver::operator<<(raw_ostream &OSconst Multilib &M) {
137  M.print(OS);
138  return OS;
139}
140
141MultilibSet &MultilibSet::Maybe(const Multilib &M) {
142  Multilib Opposite;
143  // Negate any '+' flags
144  for (StringRef Flag : M.flags()) {
145    if (Flag.front() == '+')
146      Opposite.flags().push_back(("-" + Flag.substr(1)).str());
147  }
148  return Either(MOpposite);
149}
150
151MultilibSet &MultilibSet::Either(const Multilib &M1const Multilib &M2) {
152  return Either({M1M2});
153}
154
155MultilibSet &MultilibSet::Either(const Multilib &M1const Multilib &M2,
156                                 const Multilib &M3) {
157  return Either({M1M2M3});
158}
159
160MultilibSet &MultilibSet::Either(const Multilib &M1const Multilib &M2,
161                                 const Multilib &M3const Multilib &M4) {
162  return Either({M1M2M3M4});
163}
164
165MultilibSet &MultilibSet::Either(const Multilib &M1const Multilib &M2,
166                                 const Multilib &M3const Multilib &M4,
167                                 const Multilib &M5) {
168  return Either({M1M2M3M4M5});
169}
170
171static Multilib compose(const Multilib &Baseconst Multilib &New) {
172  SmallString<128GCCSuffix;
173  llvm::sys::path::append(GCCSuffix, "/", Base.gccSuffix(), New.gccSuffix());
174  SmallString<128OSSuffix;
175  llvm::sys::path::append(OSSuffix, "/", Base.osSuffix(), New.osSuffix());
176  SmallString<128IncludeSuffix;
177  llvm::sys::path::append(IncludeSuffix, "/", Base.includeSuffix(),
178                          New.includeSuffix());
179
180  Multilib Composed(GCCSuffix, OSSuffix, IncludeSuffix);
181
182  Multilib::flags_list &Flags = Composed.flags();
183
184  Flags.insert(Flags.end(), Base.flags().begin(), Base.flags().end());
185  Flags.insert(Flags.end(), New.flags().begin(), New.flags().end());
186
187  return Composed;
188}
189
190MultilibSet &MultilibSet::Either(ArrayRef<MultilibMultilibSegments) {
191  multilib_list Composed;
192
193  if (Multilibs.empty())
194    Multilibs.insert(Multilibs.end(), MultilibSegments.begin(),
195                     MultilibSegments.end());
196  else {
197    for (const auto &New : MultilibSegments) {
198      for (const auto &Base : *this) {
199        Multilib MO = compose(Base, New);
200        if (MO.isValid())
201          Composed.push_back(MO);
202      }
203    }
204
205    Multilibs = Composed;
206  }
207
208  return *this;
209}
210
211MultilibSet &MultilibSet::FilterOut(FilterCallback F) {
212  filterInPlace(F, Multilibs);
213  return *this;
214}
215
216MultilibSet &MultilibSet::FilterOut(const char *Regex) {
217  llvm::Regex R(Regex);
218#ifndef NDEBUG
219  std::string Error;
220  if (!R.isValid(Error)) {
221    llvm::errs() << Error;
222    llvm_unreachable("Invalid regex!");
223  }
224#endif
225
226  filterInPlace([&R](const Multilib &M) { return R.match(M.gccSuffix()); },
227                Multilibs);
228  return *this;
229}
230
231void MultilibSet::push_back(const Multilib &M) { Multilibs.push_back(M); }
232
233void MultilibSet::combineWith(const MultilibSet &Other) {
234  Multilibs.insert(Multilibs.end(), Other.begin(), Other.end());
235}
236
237static bool isFlagEnabled(StringRef Flag) {
238  char Indicator = Flag.front();
239  assert(Indicator == '+' || Indicator == '-');
240  return Indicator == '+';
241}
242
243bool MultilibSet::select(const Multilib::flags_list &FlagsMultilib &Mconst {
244  llvm::StringMap<bool> FlagSet;
245
246  // Stuff all of the flags into the FlagSet such that a true mappend indicates
247  // the flag was enabled, and a false mappend indicates the flag was disabled.
248  for (StringRef Flag : Flags)
249    FlagSet[Flag.substr(1)] = isFlagEnabled(Flag);
250
251  multilib_list Filtered = filterCopy([&FlagSet](const Multilib &M) {
252    for (StringRef Flag : M.flags()) {
253      llvm::StringMap<bool>::const_iterator SI = FlagSet.find(Flag.substr(1));
254      if (SI != FlagSet.end())
255        if (SI->getValue() != isFlagEnabled(Flag))
256          return true;
257    }
258    return false;
259  }, Multilibs);
260
261  if (Filtered.empty())
262    return false;
263  if (Filtered.size() == 1) {
264    M = Filtered[0];
265    return true;
266  }
267
268  // TODO: pick the "best" multlib when more than one is suitable
269  assert(false);
270  return false;
271}
272
273LLVM_DUMP_METHOD void MultilibSet::dump() const {
274  print(llvm::errs());
275}
276
277void MultilibSet::print(raw_ostream &OSconst {
278  for (const auto &M : *this)
279    OS << M << "\n";
280}
281
282MultilibSet::multilib_list MultilibSet::filterCopy(FilterCallback F,
283                                                   const multilib_list &Ms) {
284  multilib_list Copy(Ms);
285  filterInPlace(F, Copy);
286  return Copy;
287}
288
289void MultilibSet::filterInPlace(FilterCallback Fmultilib_list &Ms) {
290  Ms.erase(std::remove_if(Ms.begin(), Ms.end(), F), Ms.end());
291}
292
293raw_ostream &clang::driver::operator<<(raw_ostream &OSconst MultilibSet &MS) {
294  MS.print(OS);
295  return OS;
296}
297
clang::driver::Multilib::gccSuffix
clang::driver::Multilib::osSuffix
clang::driver::Multilib::includeSuffix
clang::driver::Multilib::dump
clang::driver::Multilib::print
clang::driver::Multilib::isValid
clang::driver::MultilibSet::Maybe
clang::driver::MultilibSet::Either
clang::driver::MultilibSet::Either
clang::driver::MultilibSet::Either
clang::driver::MultilibSet::Either
clang::driver::MultilibSet::Either
clang::driver::MultilibSet::FilterOut
clang::driver::MultilibSet::FilterOut
clang::driver::MultilibSet::push_back
clang::driver::MultilibSet::combineWith
clang::driver::MultilibSet::select
clang::driver::MultilibSet::dump
clang::driver::MultilibSet::print
clang::driver::MultilibSet::filterCopy
clang::driver::MultilibSet::filterInPlace