Path: blob/main/contrib/llvm-project/clang/lib/Driver/Multilib.cpp
35233 views
//===- Multilib.cpp - Multilib Implementation -----------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "clang/Driver/Multilib.h"9#include "clang/Basic/LLVM.h"10#include "clang/Basic/Version.h"11#include "llvm/ADT/DenseSet.h"12#include "llvm/ADT/SmallString.h"13#include "llvm/ADT/StringRef.h"14#include "llvm/Support/Compiler.h"15#include "llvm/Support/Error.h"16#include "llvm/Support/ErrorHandling.h"17#include "llvm/Support/Path.h"18#include "llvm/Support/Regex.h"19#include "llvm/Support/VersionTuple.h"20#include "llvm/Support/YAMLParser.h"21#include "llvm/Support/YAMLTraits.h"22#include "llvm/Support/raw_ostream.h"23#include <algorithm>24#include <cassert>25#include <string>2627using namespace clang;28using namespace driver;29using namespace llvm::sys;3031Multilib::Multilib(StringRef GCCSuffix, StringRef OSSuffix,32StringRef IncludeSuffix, const flags_list &Flags,33StringRef ExclusiveGroup)34: GCCSuffix(GCCSuffix), OSSuffix(OSSuffix), IncludeSuffix(IncludeSuffix),35Flags(Flags), ExclusiveGroup(ExclusiveGroup) {36assert(GCCSuffix.empty() ||37(StringRef(GCCSuffix).front() == '/' && GCCSuffix.size() > 1));38assert(OSSuffix.empty() ||39(StringRef(OSSuffix).front() == '/' && OSSuffix.size() > 1));40assert(IncludeSuffix.empty() ||41(StringRef(IncludeSuffix).front() == '/' && IncludeSuffix.size() > 1));42}4344LLVM_DUMP_METHOD void Multilib::dump() const {45print(llvm::errs());46}4748void Multilib::print(raw_ostream &OS) const {49if (GCCSuffix.empty())50OS << ".";51else {52OS << StringRef(GCCSuffix).drop_front();53}54OS << ";";55for (StringRef Flag : Flags) {56if (Flag.front() == '-')57OS << "@" << Flag.substr(1);58}59}6061bool Multilib::operator==(const Multilib &Other) const {62// Check whether the flags sets match63// allowing for the match to be order invariant64llvm::StringSet<> MyFlags;65for (const auto &Flag : Flags)66MyFlags.insert(Flag);6768for (const auto &Flag : Other.Flags)69if (!MyFlags.contains(Flag))70return false;7172if (osSuffix() != Other.osSuffix())73return false;7475if (gccSuffix() != Other.gccSuffix())76return false;7778if (includeSuffix() != Other.includeSuffix())79return false;8081return true;82}8384raw_ostream &clang::driver::operator<<(raw_ostream &OS, const Multilib &M) {85M.print(OS);86return OS;87}8889MultilibSet &MultilibSet::FilterOut(FilterCallback F) {90llvm::erase_if(Multilibs, F);91return *this;92}9394void MultilibSet::push_back(const Multilib &M) { Multilibs.push_back(M); }9596bool MultilibSet::select(const Multilib::flags_list &Flags,97llvm::SmallVectorImpl<Multilib> &Selected) const {98llvm::StringSet<> FlagSet(expandFlags(Flags));99Selected.clear();100101// Decide which multilibs we're going to select at all.102llvm::DenseSet<StringRef> ExclusiveGroupsSelected;103for (const Multilib &M : llvm::reverse(Multilibs)) {104// If this multilib doesn't match all our flags, don't select it.105if (!llvm::all_of(M.flags(), [&FlagSet](const std::string &F) {106return FlagSet.contains(F);107}))108continue;109110const std::string &group = M.exclusiveGroup();111if (!group.empty()) {112// If this multilib has the same ExclusiveGroup as one we've already113// selected, skip it. We're iterating in reverse order, so the group114// member we've selected already is preferred.115//116// Otherwise, add the group name to the set of groups we've already117// selected a member of.118auto [It, Inserted] = ExclusiveGroupsSelected.insert(group);119if (!Inserted)120continue;121}122123// Select this multilib.124Selected.push_back(M);125}126127// We iterated in reverse order, so now put Selected back the right way128// round.129std::reverse(Selected.begin(), Selected.end());130131return !Selected.empty();132}133134llvm::StringSet<>135MultilibSet::expandFlags(const Multilib::flags_list &InFlags) const {136llvm::StringSet<> Result;137for (const auto &F : InFlags)138Result.insert(F);139for (const FlagMatcher &M : FlagMatchers) {140std::string RegexString(M.Match);141142// Make the regular expression match the whole string.143if (!StringRef(M.Match).starts_with("^"))144RegexString.insert(RegexString.begin(), '^');145if (!StringRef(M.Match).ends_with("$"))146RegexString.push_back('$');147148const llvm::Regex Regex(RegexString);149assert(Regex.isValid());150if (llvm::any_of(InFlags,151[&Regex](StringRef F) { return Regex.match(F); })) {152Result.insert(M.Flags.begin(), M.Flags.end());153}154}155return Result;156}157158namespace {159160// When updating this also update MULTILIB_VERSION in MultilibTest.cpp161static const VersionTuple MultilibVersionCurrent(1, 0);162163struct MultilibSerialization {164std::string Dir;165std::vector<std::string> Flags;166std::string Group;167};168169enum class MultilibGroupType {170/*171* The only group type currently supported is 'Exclusive', which indicates a172* group of multilibs of which at most one may be selected.173*/174Exclusive,175176/*177* Future possibility: a second group type indicating a set of library178* directories that are mutually _dependent_ rather than mutually exclusive:179* if you include one you must include them all.180*181* It might also be useful to allow groups to be members of other groups, so182* that a mutually exclusive group could contain a mutually dependent set of183* library directories, or vice versa.184*185* These additional features would need changes in the implementation, but186* the YAML schema is set up so they can be added without requiring changes187* in existing users' multilib.yaml files.188*/189};190191struct MultilibGroupSerialization {192std::string Name;193MultilibGroupType Type;194};195196struct MultilibSetSerialization {197llvm::VersionTuple MultilibVersion;198std::vector<MultilibGroupSerialization> Groups;199std::vector<MultilibSerialization> Multilibs;200std::vector<MultilibSet::FlagMatcher> FlagMatchers;201};202203} // end anonymous namespace204205template <> struct llvm::yaml::MappingTraits<MultilibSerialization> {206static void mapping(llvm::yaml::IO &io, MultilibSerialization &V) {207io.mapRequired("Dir", V.Dir);208io.mapRequired("Flags", V.Flags);209io.mapOptional("Group", V.Group);210}211static std::string validate(IO &io, MultilibSerialization &V) {212if (StringRef(V.Dir).starts_with("/"))213return "paths must be relative but \"" + V.Dir + "\" starts with \"/\"";214return std::string{};215}216};217218template <> struct llvm::yaml::ScalarEnumerationTraits<MultilibGroupType> {219static void enumeration(IO &io, MultilibGroupType &Val) {220io.enumCase(Val, "Exclusive", MultilibGroupType::Exclusive);221}222};223224template <> struct llvm::yaml::MappingTraits<MultilibGroupSerialization> {225static void mapping(llvm::yaml::IO &io, MultilibGroupSerialization &V) {226io.mapRequired("Name", V.Name);227io.mapRequired("Type", V.Type);228}229};230231template <> struct llvm::yaml::MappingTraits<MultilibSet::FlagMatcher> {232static void mapping(llvm::yaml::IO &io, MultilibSet::FlagMatcher &M) {233io.mapRequired("Match", M.Match);234io.mapRequired("Flags", M.Flags);235}236static std::string validate(IO &io, MultilibSet::FlagMatcher &M) {237llvm::Regex Regex(M.Match);238std::string RegexError;239if (!Regex.isValid(RegexError))240return RegexError;241if (M.Flags.empty())242return "value required for 'Flags'";243return std::string{};244}245};246247template <> struct llvm::yaml::MappingTraits<MultilibSetSerialization> {248static void mapping(llvm::yaml::IO &io, MultilibSetSerialization &M) {249io.mapRequired("MultilibVersion", M.MultilibVersion);250io.mapRequired("Variants", M.Multilibs);251io.mapOptional("Groups", M.Groups);252io.mapOptional("Mappings", M.FlagMatchers);253}254static std::string validate(IO &io, MultilibSetSerialization &M) {255if (M.MultilibVersion.empty())256return "missing required key 'MultilibVersion'";257if (M.MultilibVersion.getMajor() != MultilibVersionCurrent.getMajor())258return "multilib version " + M.MultilibVersion.getAsString() +259" is unsupported";260if (M.MultilibVersion.getMinor() > MultilibVersionCurrent.getMinor())261return "multilib version " + M.MultilibVersion.getAsString() +262" is unsupported";263for (const MultilibSerialization &Lib : M.Multilibs) {264if (!Lib.Group.empty()) {265bool Found = false;266for (const MultilibGroupSerialization &Group : M.Groups)267if (Group.Name == Lib.Group) {268Found = true;269break;270}271if (!Found)272return "multilib \"" + Lib.Dir +273"\" specifies undefined group name \"" + Lib.Group + "\"";274}275}276return std::string{};277}278};279280LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSerialization)281LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibGroupSerialization)282LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSet::FlagMatcher)283284llvm::ErrorOr<MultilibSet>285MultilibSet::parseYaml(llvm::MemoryBufferRef Input,286llvm::SourceMgr::DiagHandlerTy DiagHandler,287void *DiagHandlerCtxt) {288MultilibSetSerialization MS;289llvm::yaml::Input YamlInput(Input, nullptr, DiagHandler, DiagHandlerCtxt);290YamlInput >> MS;291if (YamlInput.error())292return YamlInput.error();293294multilib_list Multilibs;295Multilibs.reserve(MS.Multilibs.size());296for (const auto &M : MS.Multilibs) {297std::string Dir;298if (M.Dir != ".")299Dir = "/" + M.Dir;300// We transfer M.Group straight into the ExclusiveGroup parameter for the301// Multilib constructor. If we later support more than one type of group,302// we'll have to look up the group name in MS.Groups, check its type, and303// decide what to do here.304Multilibs.emplace_back(Dir, Dir, Dir, M.Flags, M.Group);305}306307return MultilibSet(std::move(Multilibs), std::move(MS.FlagMatchers));308}309310LLVM_DUMP_METHOD void MultilibSet::dump() const {311print(llvm::errs());312}313314void MultilibSet::print(raw_ostream &OS) const {315for (const auto &M : *this)316OS << M << "\n";317}318319raw_ostream &clang::driver::operator<<(raw_ostream &OS, const MultilibSet &MS) {320MS.print(OS);321return OS;322}323324325