Index: include/clang/Driver/Multilib.h =================================================================== --- /dev/null +++ include/clang/Driver/Multilib.h @@ -0,0 +1,163 @@ +//===--- Multilib.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_LIB_DRIVER_MULTILIB_H_ +#define CLANG_LIB_DRIVER_MULTILIB_H_ + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Option/Option.h" +#include +#include + +namespace clang { +namespace driver { + +/// This corresponds to a single GCC Multilib, or a segment of one controlled +/// by a command line flag +class Multilib { +public: + typedef std::vector flags_list; + +private: + std::string GCCSuffix; + std::string OSSuffix; + std::string IncludeSuffix; + flags_list Flags; + +public: + Multilib(StringRef GCCSuffix = "", StringRef OSSuffix = "", + StringRef IncludeSuffix = ""); + + /// \brief Get the detected GCC installation path suffix for the multi-arch + /// target variant. Always starts with a '/', unless empty + const std::string &gccSuffix() const { + assert(GCCSuffix.empty() || + (StringRef(GCCSuffix).front() == '/' && GCCSuffix.size() > 1)); + return GCCSuffix; + } + /// Set the GCC installation path suffix. + Multilib &gccSuffix(StringRef S); + + /// \brief Get the detected os path suffix for the multi-arch + /// target variant. Always starts with a '/', unless empty + const std::string &osSuffix() const { + assert(OSSuffix.empty() || + (StringRef(OSSuffix).front() == '/' && OSSuffix.size() > 1)); + return OSSuffix; + } + /// Set the os path suffix. + Multilib &osSuffix(StringRef S); + + /// \brief Get the include directory suffix. Always starts with a '/', unless + /// empty + const std::string &includeSuffix() const { + assert(IncludeSuffix.empty() || + (StringRef(IncludeSuffix).front() == '/' && IncludeSuffix.size() > 1)); + return IncludeSuffix; + } + /// Set the include directory suffix + Multilib &includeSuffix(StringRef S); + + /// \brief Get the flags that indicate or contraindicate this multilib's use + /// All elements begin with either '+' or '-' + const flags_list &flags() const { return Flags; } + flags_list &flags() { return Flags; } + /// Add a flag to the flags list + Multilib &flag(StringRef F) { + assert(F.front() == '+' || F.front() == '-'); + Flags.push_back(F); + return *this; + } + + /// \brief print summary of the Multilib + void print(raw_ostream &OS) const; + + /// Check whether any of the 'against' flags contradict the 'for' flags. + bool isValid() const; + + bool operator==(const Multilib &Other) const; +}; + +raw_ostream &operator<<(raw_ostream &OS, const Multilib &M); + +class MultilibSet { +public: + typedef std::vector multilib_list; + typedef multilib_list::iterator iterator; + typedef multilib_list::const_iterator const_iterator; + + struct FilterCallback { + virtual ~FilterCallback() {}; + /// \return true iff the filter should remove the Multilib from the set + virtual bool operator()(const Multilib &M) const = 0; + }; + +private: + multilib_list Multilibs; + +public: + MultilibSet() {} + + /// Add an optional Multilib segment + MultilibSet &Maybe(const Multilib &M); + + /// Add a set of mutually incompatible Multilib segments + MultilibSet &Either(const Multilib &M1, const Multilib &M2); + MultilibSet &Either(const Multilib &M1, const Multilib &M2, + const Multilib &M3); + MultilibSet &Either(const Multilib &M1, const Multilib &M2, + const Multilib &M3, const Multilib &M4); + MultilibSet &Either(const Multilib &M1, const Multilib &M2, + const Multilib &M3, const Multilib &M4, + const Multilib &M5); + MultilibSet &Either(const std::vector &Ms); + + /// Filter out some subset of the Multilibs using a user defined callback + MultilibSet &FilterOut(const FilterCallback &F); + /// Filter out those Multilibs whose gccSuffix matches the given expression + MultilibSet &FilterOut(std::string Regex); + + /// Add a completed Multilib to the set + void push_back(const Multilib &M); + + /// Union this set of multilibs with another + void combineWith(const MultilibSet &MS); + + /// Remove all of thie multilibs from the set + void clear() { Multilibs.clear(); } + + iterator begin() { return Multilibs.begin(); } + const_iterator begin() const { return Multilibs.begin(); } + + iterator end() { return Multilibs.end(); } + const_iterator end() const { return Multilibs.end(); } + + /// Pick the best multilib in the set, \returns false if none are compatible + bool select(const Multilib::flags_list &Flags, Multilib &M) const; + + unsigned size() const { return Multilibs.size(); } + + void print(raw_ostream &OS) const; + +private: + /// Apply the filter to Multilibs and return the subset that remains + static multilib_list filterCopy(const FilterCallback &F, + const multilib_list &Ms); + + /// Apply the filter to the multilib_list, removing those that don't match + static void filterInPlace(const FilterCallback &F, multilib_list &Ms); +}; + +raw_ostream &operator<<(raw_ostream &OS, const MultilibSet &MS); +} +} + +#endif + Index: include/clang/Driver/ToolChain.h =================================================================== --- include/clang/Driver/ToolChain.h +++ include/clang/Driver/ToolChain.h @@ -11,6 +11,7 @@ #define CLANG_DRIVER_TOOLCHAIN_H_ #include "clang/Driver/Action.h" +#include "clang/Driver/Multilib.h" #include "clang/Driver/Types.h" #include "clang/Driver/Util.h" #include "llvm/ADT/OwningPtr.h" @@ -76,6 +77,8 @@ mutable OwningPtr SanitizerArguments; protected: + MultilibSet Multilibs; + ToolChain(const Driver &D, const llvm::Triple &T, const llvm::opt::ArgList &Args); @@ -130,6 +133,8 @@ path_list &getProgramPaths() { return ProgramPaths; } const path_list &getProgramPaths() const { return ProgramPaths; } + const MultilibSet &getMultilibs() const { return Multilibs; } + const SanitizerArgs& getSanitizerArgs() const; // Tool access. Index: lib/Driver/CMakeLists.txt =================================================================== --- lib/Driver/CMakeLists.txt +++ lib/Driver/CMakeLists.txt @@ -11,6 +11,7 @@ Driver.cpp DriverOptions.cpp Job.cpp + Multilib.cpp Phases.cpp SanitizerArgs.cpp Tool.cpp Index: lib/Driver/Driver.cpp =================================================================== --- lib/Driver/Driver.cpp +++ lib/Driver/Driver.cpp @@ -747,54 +747,37 @@ } if (C.getArgs().hasArg(options::OPT_print_multi_lib)) { - // FIXME: We need tool chain support for this. - llvm::outs() << ".;\n"; + const MultilibSet &Multilibs = TC.getMultilibs(); - switch (C.getDefaultToolChain().getTriple().getArch()) { - default: - break; - - case llvm::Triple::x86_64: - llvm::outs() << "x86_64;@m64" << "\n"; - break; - - case llvm::Triple::ppc64: - llvm::outs() << "ppc64;@m64" << "\n"; - break; - - case llvm::Triple::ppc64le: - llvm::outs() << "ppc64le;@m64" << "\n"; - break; + for (MultilibSet::const_iterator I = Multilibs.begin(), E = Multilibs.end(); + I != E; ++I) { + llvm::outs() << *I << "\n"; } return false; } - // FIXME: What is the difference between print-multi-directory and - // print-multi-os-directory? - if (C.getArgs().hasArg(options::OPT_print_multi_directory) || - C.getArgs().hasArg(options::OPT_print_multi_os_directory)) { - switch (C.getDefaultToolChain().getTriple().getArch()) { - default: - case llvm::Triple::x86: - case llvm::Triple::ppc: - llvm::outs() << "." << "\n"; - break; - - case llvm::Triple::x86_64: - llvm::outs() << "x86_64" << "\n"; - break; - - case llvm::Triple::ppc64: - llvm::outs() << "ppc64" << "\n"; - break; - - case llvm::Triple::ppc64le: - llvm::outs() << "ppc64le" << "\n"; - break; + if (C.getArgs().hasArg(options::OPT_print_multi_directory)) { + const MultilibSet &Multilibs = TC.getMultilibs(); + for (MultilibSet::const_iterator I = Multilibs.begin(), E = Multilibs.end(); + I != E; ++I) { + if (I->gccSuffix().empty()) + llvm::outs() << ".\n"; + else { + StringRef Suffix(I->gccSuffix()); + assert(Suffix.front() == '/'); + llvm::outs() << Suffix.substr(1) << "\n"; + } } return false; } + if (C.getArgs().hasArg(options::OPT_print_multi_os_directory)) { + // FIXME: This should print out "lib/../lib", "lib/../lib64", or + // "lib/../lib32" as appropriate for the toolchain. For now, print + // nothing because it's not supported yet. + return false; + } + return true; } Index: lib/Driver/Multilib.cpp =================================================================== --- /dev/null +++ lib/Driver/Multilib.cpp @@ -0,0 +1,347 @@ +//===--- Multilib.cpp - Multilib Implementation ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Driver/Multilib.h" +#include "Tools.h" +#include "clang/Driver/Options.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/OptTable.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Regex.h" +#include "llvm/Support/YAMLParser.h" +#include "llvm/Support/YAMLTraits.h" +#include + +using namespace clang::driver; +using namespace clang; +using namespace llvm::opt; + +static void normalizePathSegment(std::string &Segment) { + StringRef SRS(Segment); + if (SRS.empty() || SRS == "/." || SRS == "/" || SRS == ".") { + SRS = ""; + } else { + if (SRS.back() == '/') + SRS = SRS.drop_back(); + if (SRS.front() != '/') + SRS = ("/" + SRS).str(); + } + Segment = SRS; +} + +Multilib::Multilib(StringRef GCCSuffix, StringRef OSSuffix, + StringRef IncludeSuffix) + : GCCSuffix(GCCSuffix), OSSuffix(OSSuffix), IncludeSuffix(IncludeSuffix) { + normalizePathSegment(this->GCCSuffix); + normalizePathSegment(this->OSSuffix); + normalizePathSegment(this->IncludeSuffix); +} + +Multilib &Multilib::gccSuffix(StringRef S) { + GCCSuffix = S; + normalizePathSegment(GCCSuffix); + return *this; +} + +Multilib &Multilib::osSuffix(StringRef S) { + OSSuffix = S; + normalizePathSegment(OSSuffix); + return *this; +} + +Multilib &Multilib::includeSuffix(StringRef S) { + IncludeSuffix = S; + normalizePathSegment(IncludeSuffix); + return *this; +} + +void Multilib::print(raw_ostream &OS) const { + assert(GCCSuffix.empty() || (StringRef(GCCSuffix).front() == '/')); + if (GCCSuffix.empty()) + OS << "."; + else { + OS << StringRef(GCCSuffix).drop_front(); + } + OS << ";"; + for (flags_list::const_iterator I = Flags.begin(), E = Flags.end(); I != E; + ++I) { + if (StringRef(*I).front() == '+') + OS << "@" << I->substr(1); + } +} + +bool Multilib::isValid() const { + llvm::StringMap FlagSet; + for (unsigned I = 0, N = Flags.size(); I != N; ++I) { + StringRef Flag(Flags[I]); + llvm::StringMap::iterator SI = FlagSet.find(Flag.substr(1)); + + assert(StringRef(Flag).front() == '+' || StringRef(Flag).front() == '-'); + + if (SI == FlagSet.end()) + FlagSet[Flag.substr(1)] = I; + else if (Flags[I] != Flags[SI->getValue()]) + return false; + } + return true; +} + +bool Multilib::operator==(const Multilib &Other) const { + // Check whether the flags sets match + // allowing for the match to be order invariant + llvm::StringSet<> MyFlags; + for (flags_list::const_iterator I = Flags.begin(), E = Flags.end(); I != E; + ++I) { + MyFlags.insert(*I); + } + for (flags_list::const_iterator I = Other.Flags.begin(), + E = Other.Flags.end(); + I != E; ++I) { + if (MyFlags.find(*I) == MyFlags.end()) + return false; + } + + if (osSuffix() != Other.osSuffix()) + return false; + + if (gccSuffix() != Other.gccSuffix()) + return false; + + if (includeSuffix() != Other.includeSuffix()) + return false; + + return true; +} + +raw_ostream &clang::driver::operator<<(raw_ostream &OS, const Multilib &M) { + M.print(OS); + return OS; +} + +MultilibSet &MultilibSet::Maybe(const Multilib &M) { + Multilib Opposite; + // Negate any '+' flags + for (Multilib::flags_list::const_iterator I = M.flags().begin(), + E = M.flags().end(); + I != E; ++I) { + StringRef Flag(*I); + if (Flag.front() == '+') + Opposite.flags().push_back(("-" + Flag.substr(1)).str()); + } + return Either(M, Opposite); +} + +MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2) { + std::vector Ms; + Ms.push_back(M1); + Ms.push_back(M2); + return Either(Ms); +} + +MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2, + const Multilib &M3) { + std::vector Ms; + Ms.push_back(M1); + Ms.push_back(M2); + Ms.push_back(M3); + return Either(Ms); +} + +MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2, + const Multilib &M3, const Multilib &M4) { + std::vector Ms; + Ms.push_back(M1); + Ms.push_back(M2); + Ms.push_back(M3); + Ms.push_back(M4); + return Either(Ms); +} + +MultilibSet &MultilibSet::Either(const Multilib &M1, const Multilib &M2, + const Multilib &M3, const Multilib &M4, + const Multilib &M5) { + std::vector Ms; + Ms.push_back(M1); + Ms.push_back(M2); + Ms.push_back(M3); + Ms.push_back(M4); + Ms.push_back(M5); + return Either(Ms); +} + +static Multilib compose(const Multilib &Base, const Multilib &New) { + SmallString<128> GCCSuffix; + llvm::sys::path::append(GCCSuffix, "/", Base.gccSuffix(), New.gccSuffix()); + SmallString<128> OSSuffix; + llvm::sys::path::append(OSSuffix, "/", Base.osSuffix(), New.osSuffix()); + SmallString<128> IncludeSuffix; + llvm::sys::path::append(IncludeSuffix, "/", Base.includeSuffix(), + New.includeSuffix()); + + Multilib Composed(GCCSuffix.str(), OSSuffix.str(), IncludeSuffix.str()); + + Multilib::flags_list &Flags = Composed.flags(); + + Flags.insert(Flags.end(), Base.flags().begin(), Base.flags().end()); + Flags.insert(Flags.end(), New.flags().begin(), New.flags().end()); + + return Composed; +} + +MultilibSet & +MultilibSet::Either(const std::vector &MultilibSegments) { + multilib_list Composed; + + if (Multilibs.empty()) + Multilibs.insert(Multilibs.end(), MultilibSegments.begin(), + MultilibSegments.end()); + else { + for (std::vector::const_iterator NewI = MultilibSegments.begin(), + NewE = MultilibSegments.end(); + NewI != NewE; ++NewI) { + for (const_iterator BaseI = begin(), BaseE = end(); BaseI != BaseE; + ++BaseI) { + Multilib MO = compose(*BaseI, *NewI); + if (MO.isValid()) + Composed.push_back(MO); + } + } + + Multilibs = Composed; + } + + return *this; +} + +MultilibSet &MultilibSet::FilterOut(const MultilibSet::FilterCallback &F) { + filterInPlace(F, Multilibs); + return *this; +} + +MultilibSet &MultilibSet::FilterOut(std::string Regex) { + class REFilter : public MultilibSet::FilterCallback { + mutable llvm::Regex R; + + public: + REFilter(std::string Regex) : R(Regex) {} + bool operator()(const Multilib &M) const LLVM_OVERRIDE { + std::string Error; + if (!R.isValid(Error)) { + llvm::errs() << Error; + assert(false); + return false; + } + return R.match(M.gccSuffix()); + } + }; + + REFilter REF(Regex); + filterInPlace(REF, Multilibs); + return *this; +} + +void MultilibSet::push_back(const Multilib &M) { Multilibs.push_back(M); } + +void MultilibSet::combineWith(const MultilibSet &Other) { + Multilibs.insert(Multilibs.end(), Other.begin(), Other.end()); +} + +bool MultilibSet::select(const Multilib::flags_list &Flags, Multilib &M) const { + class FilterFlagsMismatch : public MultilibSet::FilterCallback { + llvm::StringMap FlagSet; + + public: + FilterFlagsMismatch(const std::vector &Flags) { + // Stuff all of the flags into the FlagSet such that a true mappend + // indicates the flag was enabled, and a false mappend indicates the + // flag was disabled + for (Multilib::flags_list::const_iterator I = Flags.begin(), + E = Flags.end(); + I != E; ++I) { + FlagSet[StringRef(*I).substr(1)] = isFlagEnabled(*I); + } + } + bool operator()(const Multilib &M) const LLVM_OVERRIDE { + for (Multilib::flags_list::const_iterator I = M.flags().begin(), + E = M.flags().end(); + I != E; ++I) { + StringRef Flag(*I); + llvm::StringMap::const_iterator SI = FlagSet.find(Flag.substr(1)); + if (SI != FlagSet.end()) + if ((*SI).getValue() != isFlagEnabled(Flag)) + return true; + } + return false; + } + private: + bool isFlagEnabled(StringRef Flag) const { + char Indicator = Flag.front(); + assert(Indicator == '+' || Indicator == '-'); + return Indicator == '+'; + } + }; + + FilterFlagsMismatch FlagsMismatch(Flags); + + multilib_list Filtered = filterCopy(FlagsMismatch, Multilibs); + + if (Filtered.size() == 0) { + return false; + } else if (Filtered.size() == 1) { + M = Filtered[0]; + return true; + } + + // TODO: pick the "best" multlib when more than one is suitable + assert(false); + + return false; +} + +void MultilibSet::print(raw_ostream &OS) const { + for (const_iterator I = begin(), E = end(); I != E; ++I) + OS << *I << "\n"; +} + +MultilibSet::multilib_list +MultilibSet::filterCopy(const MultilibSet::FilterCallback &F, + const multilib_list &Ms) { + multilib_list Copy(Ms); + filterInPlace(F, Copy); + return Copy; +} + +namespace { +// Wrapper for FilterCallback to make operator() nonvirtual so it +// can be passed by value to std::remove_if +class FilterWrapper { + const MultilibSet::FilterCallback &F; +public: + FilterWrapper(const MultilibSet::FilterCallback &F) : F(F) {} + bool operator()(const Multilib &M) const { return F(M); } +}; +} // end anonymous namespace + +void MultilibSet::filterInPlace(const MultilibSet::FilterCallback &F, + multilib_list &Ms) { + Ms.erase(std::remove_if(Ms.begin(), Ms.end(), FilterWrapper(F)), Ms.end()); +} + +raw_ostream &clang::driver::operator<<(raw_ostream &OS, const MultilibSet &MS) { + MS.print(OS); + return OS; +} Index: lib/Driver/ToolChains.h =================================================================== --- lib/Driver/ToolChains.h +++ lib/Driver/ToolChains.h @@ -13,8 +13,10 @@ #include "Tools.h" #include "clang/Basic/VersionTuple.h" #include "clang/Driver/Action.h" +#include "clang/Driver/Multilib.h" #include "clang/Driver/ToolChain.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Optional.h" #include "llvm/Support/Compiler.h" #include #include @@ -67,7 +69,6 @@ bool operator>=(const GCCVersion &RHS) const { return !(*this < RHS); } }; - /// \brief This is a class to find a viable GCC installation for Clang to /// use. /// @@ -80,9 +81,13 @@ // FIXME: These might be better as path objects. std::string GCCInstallPath; - std::string GCCBiarchSuffix; std::string GCCParentLibPath; - std::string GCCMIPSABIDirSuffix; + + /// The primary multilib appropriate for the given flags. + Multilib SelectedMultilib; + /// On Biarch systems, this corresponds to the default multilib when + /// targeting the non-default multilib. Otherwise, it is empty. + llvm::Optional BiarchSibling; GCCVersion Version; @@ -90,6 +95,9 @@ // order to print out detailed information in verbose mode. std::set CandidateGCCInstallPaths; + /// The set of multilibs that the detected installation supports. + MultilibSet Multilibs; + public: GCCInstallationDetector() : IsValid(false) {} void init(const Driver &D, const llvm::Triple &TargetTriple, @@ -104,26 +112,18 @@ /// \brief Get the detected GCC installation path. StringRef getInstallPath() const { return GCCInstallPath; } - /// \brief Get the detected GCC installation path suffix for the bi-arch - /// target variant. - StringRef getBiarchSuffix() const { return GCCBiarchSuffix; } - /// \brief Get the detected GCC parent lib path. StringRef getParentLibPath() const { return GCCParentLibPath; } - /// \brief Get the detected GCC MIPS ABI directory suffix. - /// - /// This is used as a suffix both to the install directory of GCC and as - /// a suffix to its parent lib path in order to select a MIPS ABI-specific - /// subdirectory. - /// - /// This will always be empty for any non-MIPS target. - /// - // FIXME: This probably shouldn't exist at all, and should be factored - // into the multiarch and/or biarch support. Please don't add more uses of - // this interface, it is meant as a legacy crutch for the MIPS driver - // logic. - StringRef getMIPSABIDirSuffix() const { return GCCMIPSABIDirSuffix; } + /// \brief Get the detected Multilib + const Multilib &getMultilib() const { return SelectedMultilib; } + + /// \brief Get the whole MultilibSet + const MultilibSet &getMultilibs() const { return Multilibs; } + + /// Get the biarch sibling multilib (if it exists). + /// \return true iff such a sibling exists + bool getBiarchSibling(Multilib &M) const; /// \brief Get the detected GCC version string. const GCCVersion &getVersion() const { return Version; } @@ -140,15 +140,18 @@ SmallVectorImpl &BiarchLibDirs, SmallVectorImpl &BiarchTripleAliases); - void ScanLibDirForGCCTriple(llvm::Triple::ArchType TargetArch, + void ScanLibDirForGCCTriple(const llvm::Triple &TargetArch, const llvm::opt::ArgList &Args, const std::string &LibDir, StringRef CandidateTriple, bool NeedsBiarchSuffix = false); - void findMIPSABIDirSuffix(std::string &Suffix, - llvm::Triple::ArchType TargetArch, StringRef Path, - const llvm::opt::ArgList &Args); + bool findMIPSMultilibs(const llvm::Triple &TargetArch, StringRef Path, + const llvm::opt::ArgList &Args); + + bool findBiarchMultilibs(const llvm::Triple &TargetArch, StringRef Path, + const llvm::opt::ArgList &Args, + bool NeedsBiarchSuffix); }; GCCInstallationDetector GCCInstallation; @@ -665,9 +668,7 @@ private: static bool addLibStdCXXIncludePaths(Twine Base, Twine Suffix, - Twine TargetArchDir, - Twine BiarchSuffix, - Twine MIPSABIDirSuffix, + Twine TargetArchDir, Twine IncludeSuffix, const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args); static bool addLibStdCXXIncludePaths(Twine Base, Twine TargetArchDir, Index: lib/Driver/ToolChains.cpp =================================================================== --- lib/Driver/ToolChains.cpp +++ lib/Driver/ToolChains.cpp @@ -139,11 +139,11 @@ } static bool isSoftFloatABI(const ArgList &Args) { - Arg *A = Args.getLastArg(options::OPT_msoft_float, - options::OPT_mhard_float, + Arg *A = Args.getLastArg(options::OPT_msoft_float, options::OPT_mhard_float, options::OPT_mfloat_abi_EQ); - if (!A) return false; - + if (!A) + return false; + return A->getOption().matches(options::OPT_msoft_float) || (A->getOption().matches(options::OPT_mfloat_abi_EQ) && A->getValue() == StringRef("soft")); @@ -1175,7 +1175,7 @@ return false; } -static StringRef getGCCToolchainDir(const ArgList &Args) { +static llvm::StringRef getGCCToolchainDir(const ArgList &Args) { const Arg *A = Args.getLastArg(options::OPT_gcc_toolchain); if (A) return A->getValue(); @@ -1197,7 +1197,6 @@ llvm::Triple BiarchVariantTriple = TargetTriple.isArch32Bit() ? TargetTriple.get64BitArchVariant() : TargetTriple.get32BitArchVariant(); - llvm::Triple::ArchType TargetArch = TargetTriple.getArch(); // The library directories which may contain GCC installations. SmallVector CandidateLibDirs, CandidateBiarchLibDirs; // The compatible GCC triples for this particular architecture. @@ -1243,7 +1242,7 @@ if (!llvm::sys::fs::exists(LibDir)) continue; for (unsigned k = 0, ke = CandidateTripleAliases.size(); k < ke; ++k) - ScanLibDirForGCCTriple(TargetArch, Args, LibDir, + ScanLibDirForGCCTriple(TargetTriple, Args, LibDir, CandidateTripleAliases[k]); } for (unsigned j = 0, je = CandidateBiarchLibDirs.size(); j < je; ++j) { @@ -1252,7 +1251,7 @@ continue; for (unsigned k = 0, ke = CandidateBiarchTripleAliases.size(); k < ke; ++k) - ScanLibDirForGCCTriple(TargetArch, Args, LibDir, + ScanLibDirForGCCTriple(TargetTriple, Args, LibDir, CandidateBiarchTripleAliases[k], /*NeedsBiarchSuffix=*/ true); } @@ -1267,6 +1266,19 @@ OS << "Found candidate GCC installation: " << *I << "\n"; OS << "Selected GCC installation: " << GCCInstallPath << "\n"; + for (MultilibSet::const_iterator I = Multilibs.begin(), E = Multilibs.end(); + I != E; ++I) { + OS << "Candidate multiilb: " << *I << "\n"; + } + OS << "Selected multilib: " << SelectedMultilib << "\n"; +} + +bool Generic_GCC::GCCInstallationDetector::getBiarchSibling(Multilib &M) const { + if (BiarchSibling.hasValue()) { + M = BiarchSibling.getValue(); + return true; + } + return false; } /*static*/ void Generic_GCC::GCCInstallationDetector::CollectLibDirsAndTriples( @@ -1502,11 +1514,45 @@ BiarchTripleAliases.push_back(BiarchTriple.str()); } +namespace { +// Filter to remove Multilibs that don't exist as a suffix to Path +class FilterNonExistant : public MultilibSet::FilterCallback { + std::string Base; +public: + FilterNonExistant(std::string Base) : Base(Base) {} + bool operator()(const Multilib &M) const LLVM_OVERRIDE { + return !llvm::sys::fs::exists(Base + M.gccSuffix() + "/crtbegin.o"); + } +}; +} // end anonymous namespace + +static void addMultilibFlag(bool Enabled, const char *const Flag, + std::vector &Flags) { + if (Enabled) + Flags.push_back(std::string("+") + Flag); + else + Flags.push_back(std::string("-") + Flag); +} + static bool isMipsArch(llvm::Triple::ArchType Arch) { - return Arch == llvm::Triple::mips || - Arch == llvm::Triple::mipsel || - Arch == llvm::Triple::mips64 || - Arch == llvm::Triple::mips64el; + return Arch == llvm::Triple::mips || Arch == llvm::Triple::mipsel || + Arch == llvm::Triple::mips64 || Arch == llvm::Triple::mips64el; +} + +static bool isMips32(llvm::Triple::ArchType Arch) { + return Arch == llvm::Triple::mips || Arch == llvm::Triple::mipsel; +} + +static bool isMips64(llvm::Triple::ArchType Arch) { + return Arch == llvm::Triple::mips64 || Arch == llvm::Triple::mips64el; +} + +static bool isMipsEL(llvm::Triple::ArchType Arch) { + return Arch == llvm::Triple::mipsel || Arch == llvm::Triple::mips64el; +} + +static bool isMipsEB(llvm::Triple::ArchType Arch) { + return Arch == llvm::Triple::mips || Arch == llvm::Triple::mips64; } static bool isMips16(const ArgList &Args) { @@ -1545,39 +1591,8 @@ return A && A->getValue() == StringRef("2008"); } -// FIXME: There is the same routine in the Tools.cpp. -static bool hasMipsABIArg(const ArgList &Args, const char *Value) { - Arg *A = Args.getLastArg(options::OPT_mabi_EQ); - return A && (A->getValue() == StringRef(Value)); -} - -static bool hasCrtBeginObj(Twine Path) { - return llvm::sys::fs::exists(Path + "/crtbegin.o"); -} - -static bool findTargetBiarchSuffix(std::string &Suffix, StringRef Path, - llvm::Triple::ArchType TargetArch, - const ArgList &Args) { - // FIXME: This routine was only intended to model bi-arch toolchains which - // use -m32 and -m64 to swap between variants of a target. It shouldn't be - // doing ABI-based builtin location for MIPS. - if (hasMipsABIArg(Args, "n32")) - Suffix = "/n32"; - else if (TargetArch == llvm::Triple::x86_64 || - TargetArch == llvm::Triple::ppc64 || - TargetArch == llvm::Triple::sparcv9 || - TargetArch == llvm::Triple::systemz || - TargetArch == llvm::Triple::mips64 || - TargetArch == llvm::Triple::mips64el) - Suffix = "/64"; - else - Suffix = "/32"; - - return hasCrtBeginObj(Path + Suffix); -} - -void Generic_GCC::GCCInstallationDetector::findMIPSABIDirSuffix( - std::string &Suffix, llvm::Triple::ArchType TargetArch, StringRef Path, +bool Generic_GCC::GCCInstallationDetector::findMIPSMultilibs( + const llvm::Triple &TargetTriple, StringRef Path, const llvm::opt::ArgList &Args) { // Some MIPS toolchains put libraries and object files compiled // using different options in to the sub-directoris which names @@ -1604,69 +1619,286 @@ // /usr // /lib <= crt*.o files compiled with '-mips32' - // Check FSF Toolchain path - Suffix.clear(); - if (TargetArch == llvm::Triple::mips || - TargetArch == llvm::Triple::mipsel) { - if (isMicroMips(Args)) - Suffix += "/micromips"; - else if (isMips32r2(Args)) - Suffix += ""; - else - Suffix += "/mips32"; + FilterNonExistant NonExistant(Path); + + // Check for FSF toolchain multilibs + MultilibSet FSFMipsMultilibs; + { + Multilib MArchMips32 = Multilib() + .gccSuffix("/mips32") + .osSuffix("/mips32") + .includeSuffix("/mips32") + .flag("+m32").flag("-m64").flag("-mmicromips").flag("-march=mips32r2"); + + Multilib MArchMicroMips = Multilib() + .gccSuffix("/micromips") + .osSuffix("/micromips") + .includeSuffix("/micromips") + .flag("+m32").flag("-m64").flag("+mmicromips"); + + Multilib MArchMips64r2 = Multilib() + .gccSuffix("/mips64r2") + .osSuffix("/mips64r2") + .includeSuffix("/mips64r2") + .flag("-m32").flag("+m64").flag("+march=mips64r2"); + + Multilib MArchMips64 = Multilib() + .gccSuffix("/mips64") + .osSuffix("/mips64") + .includeSuffix("/mips64") + .flag("-m32").flag("+m64").flag("-march=mips64r2"); + + Multilib MArchDefault = Multilib() + .flag("+m32").flag("-m64").flag("+march=mips32r2"); + + Multilib Mips16 = Multilib() + .gccSuffix("/mips16") + .osSuffix("/mips16") + .includeSuffix("/mips16") + .flag("+mips16"); + + Multilib MAbi64 = Multilib() + .gccSuffix("/64") + .osSuffix("/64") + .includeSuffix("/64") + .flag("+mabi=64").flag("-mabi=n32").flag("-m32"); + + Multilib LittleEndian = Multilib() + .gccSuffix("/el") + .osSuffix("/el") + .includeSuffix("/el") + .flag("+EL").flag("-EB"); + + Multilib SoftFloat = Multilib() + .gccSuffix("/sof") + .osSuffix("/sof") + .includeSuffix("/sof") + .flag("+msoft-float"); + + Multilib FP64 = Multilib() + .gccSuffix("/fp64") + .osSuffix("/fp64") + .includeSuffix("/fp64") + .flag("+mfp64"); + + Multilib Nan2008 = Multilib() + .gccSuffix("/nan2008") + .osSuffix("/nan2008") + .includeSuffix("/nan2008") + .flag("+mnan=2008"); + + FSFMipsMultilibs = MultilibSet() + .Either(MArchMips32, MArchMicroMips, + MArchMips64r2, MArchMips64, MArchDefault) + .Maybe(Mips16) + .FilterOut("/mips64/mips16") + .FilterOut("/mips64r2/mips16") + .FilterOut("/micromips/mips16") + .Maybe(MAbi64) + .FilterOut("/micromips/64") + .FilterOut("/mips32/64") + .FilterOut("^/64") + .FilterOut("/mips16/64") + .Maybe(LittleEndian) + .Maybe(SoftFloat) + .Maybe(FP64) + .Maybe(Nan2008) + .FilterOut(".*sof/nan2008") + .FilterOut(".*sof/fp64") + .FilterOut(NonExistant); + } + + // Check for Code Sourcery toolchain multilibs + MultilibSet CSMipsMultilibs; + { + Multilib MArchMips16 = Multilib() + .gccSuffix("/mips16") + .osSuffix("/mips16") + .includeSuffix("/mips16") + .flag("+m32").flag("+mips16"); + + Multilib MArchMicroMips = Multilib() + .gccSuffix("/micromips") + .osSuffix("/micromips") + .includeSuffix("/micromips") + .flag("+m32").flag("+mmicromips"); + + Multilib MArchDefault = Multilib() + .flag("-mips16").flag("-mmicromips"); + + Multilib SoftFloat = Multilib() + .gccSuffix("/soft-float") + .osSuffix("/soft-float") + .includeSuffix("/soft-float") + .flag("+msoft-float"); + + Multilib Nan2008 = Multilib() + .gccSuffix("/nan2008") + .osSuffix("/nan2008") + .includeSuffix("/nan2008") + .flag("+mnan=2008"); + + Multilib DefaultFloat = Multilib() + .flag("-msoft-float").flag("-mnan=2008"); + + Multilib LittleEndian = Multilib() + .gccSuffix("/el") + .osSuffix("/el") + .includeSuffix("/el") + .flag("+EL").flag("-EB"); + + // Note that this one's osSuffix is "" + Multilib MAbi64 = Multilib() + .gccSuffix("/64") + .includeSuffix("/64") + .flag("+mabi=64").flag("-mabi=n32").flag("-m32"); + + CSMipsMultilibs = MultilibSet() + .Either(MArchMips16, MArchMicroMips, MArchDefault) + .Either(SoftFloat, Nan2008, DefaultFloat) + .FilterOut("/micromips/nan2008") + .FilterOut("/mips16/nan2008") + .Maybe(LittleEndian) + .Maybe(MAbi64) + .FilterOut("/mips16.*/64") + .FilterOut("/micromips.*/64") + .FilterOut(NonExistant); + } + + MultilibSet AndroidMipsMultilibs = MultilibSet() + .Maybe(Multilib("/mips-r2").flag("+march=mips32r2")) + .FilterOut(NonExistant); + + MultilibSet DebianMipsMultilibs; + { + Multilib MAbiN32 = Multilib() + .gccSuffix("/n32") + .includeSuffix("/n32") + .flag("+mabi=n32"); + + Multilib M64 = Multilib() + .gccSuffix("/64") + .includeSuffix("/64") + .flag("+m64").flag("-m32").flag("-mabi=n32"); + + Multilib M32 = Multilib() + .flag("-m64").flag("+m32").flag("-mabi=n32"); + + DebianMipsMultilibs = MultilibSet() + .Either(M32, M64, MAbiN32) + .FilterOut(NonExistant); + } + + Multilibs.clear(); + + // Decide which MultilibSet matches best for the given path + // (we do this rather than combining them all because there is a + // a bit of overlap in the directories that each specifies) + if (TargetTriple.getEnvironment() == llvm::Triple::Android) + Multilibs.combineWith(AndroidMipsMultilibs); + else if (DebianMipsMultilibs.size() == 3) { + Multilibs.combineWith(DebianMipsMultilibs); + BiarchSibling = Multilib(); + } else if (FSFMipsMultilibs.size() > CSMipsMultilibs.size()) + Multilibs.combineWith(FSFMipsMultilibs); + else + Multilibs.combineWith(CSMipsMultilibs); - if (isMips16(Args)) - Suffix += "/mips16"; - } else { - if (isMips64r2(Args)) - Suffix += hasMipsABIArg(Args, "n32") ? "/mips64r2" : "/mips64r2/64"; - else - Suffix += hasMipsABIArg(Args, "n32") ? "/mips64" : "/mips64/64"; - } + llvm::Triple::ArchType TargetArch = TargetTriple.getArch(); - if (TargetArch == llvm::Triple::mipsel || - TargetArch == llvm::Triple::mips64el) - Suffix += "/el"; + Multilib::flags_list Flags; + addMultilibFlag(isMips32(TargetArch), "m32", Flags); + addMultilibFlag(isMips64(TargetArch), "m64", Flags); + addMultilibFlag(isMips16(Args), "mips16", Flags); + addMultilibFlag(isMips32r2(Args), "march=mips32r2", Flags); + addMultilibFlag(isMips64r2(Args), "march=mips64r2", Flags); + addMultilibFlag(isMicroMips(Args), "mmicromips", Flags); + addMultilibFlag(isMipsFP64(Args), "mfp64", Flags); + addMultilibFlag(!isMipsFP64(Args), "mfp32", Flags); + addMultilibFlag(isMipsNan2008(Args), "mnan=2008", Flags); + addMultilibFlag(tools::mips::hasMipsAbiArg(Args, "n32"), "mabi=n32", Flags); + // Default is to assume mabi=64 + bool IsMABI64 = + tools::mips::hasMipsAbiArg(Args, "64") || + (!tools::mips::hasMipsAbiArg(Args, "n32") && isMips64(TargetArch)); + addMultilibFlag(IsMABI64, "mabi=64", Flags); + addMultilibFlag(isSoftFloatABI(Args), "msoft-float", Flags); + addMultilibFlag(isSoftFloatABI(Args), "mfloat-abi=soft", Flags); + addMultilibFlag(!isSoftFloatABI(Args), "mhard-float", Flags); + addMultilibFlag(!isSoftFloatABI(Args), "mfloat-abi=hard", Flags); + addMultilibFlag(isMipsEL(TargetArch), "EL", Flags); + addMultilibFlag(isMipsEB(TargetArch), "EB", Flags); + + return Multilibs.select(Flags, SelectedMultilib); +} + +bool Generic_GCC::GCCInstallationDetector::findBiarchMultilibs( + const llvm::Triple &TargetTriple, StringRef Path, const ArgList &Args, + bool NeedsBiarchSuffix) { - if (isSoftFloatABI(Args)) - Suffix += "/sof"; + // Some versions of SUSE and Fedora on ppc64 put 32-bit libs + // in what would normally be GCCInstallPath and put the 64-bit + // libs in a subdirectory named 64. The simple logic we follow is that + // *if* there is a subdirectory of the right name with crtbegin.o in it, + // we use that. If not, and if not a biarch triple alias, we look for + // crtbegin.o without the subdirectory. + + Multilib Default; + Multilib Alt64 = Multilib() + .gccSuffix("/64") + .includeSuffix("/64") + .flag("-m32").flag("+m64"); + Multilib Alt32 = Multilib() + .gccSuffix("/32") + .includeSuffix("/32") + .flag("+m32").flag("-m64"); + + FilterNonExistant NonExistant(Path); + + // Decide whether the default multilib is 32bit, correcting for + // when the default multilib and the alternate appear backwards + bool DefaultIs32Bit; + if (TargetTriple.isArch32Bit() && !NonExistant(Alt32)) + DefaultIs32Bit = false; + else if (TargetTriple.isArch64Bit() && !NonExistant(Alt64)) + DefaultIs32Bit = true; else { - if (isMipsFP64(Args)) - Suffix += "/fp64"; - - if (isMipsNan2008(Args)) - Suffix += "/nan2008"; + if (NeedsBiarchSuffix) + DefaultIs32Bit = TargetTriple.isArch64Bit(); + else + DefaultIs32Bit = TargetTriple.isArch32Bit(); } - if (hasCrtBeginObj(Path + Suffix)) - return; + if (DefaultIs32Bit) + Default.flag("+m32").flag("-m64"); + else + Default.flag("-m32").flag("+m64"); - // Check Code Sourcery Toolchain path - Suffix.clear(); - if (isMips16(Args)) - Suffix += "/mips16"; - else if (isMicroMips(Args)) - Suffix += "/micromips"; + Multilibs.push_back(Default); + Multilibs.push_back(Alt64); + Multilibs.push_back(Alt32); - if (isSoftFloatABI(Args)) - Suffix += "/soft-float"; - else if (isMipsNan2008(Args)) - Suffix += "/nan2008"; + Multilibs.FilterOut(NonExistant); - if (TargetArch == llvm::Triple::mipsel || - TargetArch == llvm::Triple::mips64el) - Suffix += "/el"; + Multilib::flags_list Flags; + addMultilibFlag(TargetTriple.isArch64Bit(), "m64", Flags); + addMultilibFlag(TargetTriple.isArch32Bit(), "m32", Flags); - if (hasCrtBeginObj(Path + Suffix)) - return; + if (!Multilibs.select(Flags, SelectedMultilib)) + return false; - Suffix.clear(); + if (SelectedMultilib == Alt64 || SelectedMultilib == Alt32) { + BiarchSibling = Default; + } + + return true; } void Generic_GCC::GCCInstallationDetector::ScanLibDirForGCCTriple( - llvm::Triple::ArchType TargetArch, const ArgList &Args, + const llvm::Triple &TargetTriple, const ArgList &Args, const std::string &LibDir, StringRef CandidateTriple, bool NeedsBiarchSuffix) { + llvm::Triple::ArchType TargetArch = TargetTriple.getArch(); // There are various different suffixes involving the triple we // check for. We also record what is necessary to walk from each back // up to the lib directory. @@ -1711,28 +1943,18 @@ if (CandidateVersion <= Version) continue; - std::string MIPSABIDirSuffix; - if (isMipsArch(TargetArch)) - findMIPSABIDirSuffix(MIPSABIDirSuffix, TargetArch, LI->path(), Args); - - // Some versions of SUSE and Fedora on ppc64 put 32-bit libs - // in what would normally be GCCInstallPath and put the 64-bit - // libs in a subdirectory named 64. The simple logic we follow is that - // *if* there is a subdirectory of the right name with crtbegin.o in it, - // we use that. If not, and if not a biarch triple alias, we look for - // crtbegin.o without the subdirectory. - - std::string BiarchSuffix; - if (findTargetBiarchSuffix(BiarchSuffix, - LI->path() + MIPSABIDirSuffix, - TargetArch, Args)) { - GCCBiarchSuffix = BiarchSuffix; - } else if (NeedsBiarchSuffix || - !hasCrtBeginObj(LI->path() + MIPSABIDirSuffix)) { + Multilibs.clear(); + SelectedMultilib = Multilib(); + BiarchSibling.reset(); + + // Debian mips multilibs behave more like the rest of the biarch ones, + // so handle them there + if (isMipsArch(TargetArch)) { + if (!findMIPSMultilibs(TargetTriple, LI->path(), Args)) + continue; + } else if (!findBiarchMultilibs(TargetTriple, LI->path(), Args, + NeedsBiarchSuffix)) continue; - } else { - GCCBiarchSuffix.clear(); - } Version = CandidateVersion; GCCTriple.setTriple(CandidateTriple); @@ -1741,7 +1963,6 @@ // Linux. GCCInstallPath = LibDir + LibSuffixes[i] + "/" + VersionText.str(); GCCParentLibPath = GCCInstallPath + InstallSuffixes[i]; - GCCMIPSABIDirSuffix = MIPSABIDirSuffix; IsValid = true; } } @@ -2250,9 +2471,9 @@ break; case llvm::Triple::mips64: case llvm::Triple::mips64el: - if (hasMipsABIArg(Args, "o32")) + if (tools::mips::hasMipsAbiArg(Args, "o32")) getFilePaths().push_back("=/usr/lib/o32"); - else if (hasMipsABIArg(Args, "64")) + else if (tools::mips::hasMipsAbiArg(Args, "64")) getFilePaths().push_back("=/usr/lib/64"); break; default: @@ -2566,25 +2787,24 @@ if (llvm::sys::fs::exists(Path)) Paths.push_back(Path.str()); } -static StringRef getMultilibDir(const llvm::Triple &Triple, - const ArgList &Args) { +static StringRef getOSLibDir(const llvm::Triple &Triple, const ArgList &Args) { if (isMipsArch(Triple.getArch())) { // lib32 directory has a special meaning on MIPS targets. // It contains N32 ABI binaries. Use this folder if produce // code for N32 ABI only. - if (hasMipsABIArg(Args, "n32")) + if (tools::mips::hasMipsAbiArg(Args, "n32")) return "lib32"; return Triple.isArch32Bit() ? "lib" : "lib64"; } - // It happens that only x86 and PPC use the 'lib32' variant of multilib, and + // It happens that only x86 and PPC use the 'lib32' variant of oslibdir, and // using that variant while targeting other architectures causes problems // because the libraries are laid out in shared system roots that can't cope - // with a 'lib32' multilib search path being considered. So we only enable + // with a 'lib32' library search path being considered. So we only enable // them when we know we may need it. // // FIXME: This is a bit of a hack. We should really unify this code for - // reasoning about multilib spellings with the lib dir spellings in the + // reasoning about oslibdir spellings with the lib dir spellings in the // GCCInstallationDetector, but that is a more significant refactoring. if (Triple.getArch() == llvm::Triple::x86 || Triple.getArch() == llvm::Triple::ppc) @@ -2596,6 +2816,7 @@ Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) : Generic_ELF(D, Triple, Args) { GCCInstallation.init(D, Triple, Args); + Multilibs = GCCInstallation.getMultilibs(); llvm::Triple::ArchType Arch = Triple.getArch(); std::string SysRoot = computeSysRoot(); @@ -2663,29 +2884,20 @@ // to the link paths. path_list &Paths = getFilePaths(); - const std::string Multilib = getMultilibDir(Triple, Args); + const std::string OSLibDir = getOSLibDir(Triple, Args); const std::string MultiarchTriple = getMultiarchTriple(Triple, SysRoot); // Add the multilib suffixed paths where they are available. if (GCCInstallation.isValid()) { const llvm::Triple &GCCTriple = GCCInstallation.getTriple(); const std::string &LibPath = GCCInstallation.getParentLibPath(); + const Multilib &Multilib = GCCInstallation.getMultilib(); // Sourcery CodeBench MIPS toolchain holds some libraries under // a biarch-like suffix of the GCC installation. - // - // FIXME: It would be cleaner to model this as a variant of bi-arch. IE, - // instead of a '64' biarch suffix it would be 'el' or something. - if (IsAndroid && IsMips && isMips32r2(Args)) { - assert(GCCInstallation.getBiarchSuffix().empty() && - "Unexpected bi-arch suffix"); - addPathIfExists(GCCInstallation.getInstallPath() + "/mips-r2", Paths); - } else { - addPathIfExists((GCCInstallation.getInstallPath() + - GCCInstallation.getMIPSABIDirSuffix() + - GCCInstallation.getBiarchSuffix()), - Paths); - } + addPathIfExists((GCCInstallation.getInstallPath() + + Multilib.gccSuffix()), + Paths); // GCC cross compiling toolchains will install target libraries which ship // as part of the toolchain under // rather than as @@ -2705,8 +2917,8 @@ // // Note that this matches the GCC behavior. See the below comment for where // Clang diverges from GCC's behavior. - addPathIfExists(LibPath + "/../" + GCCTriple.str() + "/lib/../" + Multilib + - GCCInstallation.getMIPSABIDirSuffix(), + addPathIfExists(LibPath + "/../" + GCCTriple.str() + "/lib/../" + OSLibDir + + Multilib.osSuffix(), Paths); // If the GCC installation we found is inside of the sysroot, we want to @@ -2720,42 +2932,45 @@ // a bug. if (StringRef(LibPath).startswith(SysRoot)) { addPathIfExists(LibPath + "/" + MultiarchTriple, Paths); - addPathIfExists(LibPath + "/../" + Multilib, Paths); + addPathIfExists(LibPath + "/../" + OSLibDir, Paths); } } // Similar to the logic for GCC above, if we currently running Clang inside - // of the requested system root, add its parent multilib library paths to + // of the requested system root, add its parent library paths to // those searched. // FIXME: It's not clear whether we should use the driver's installed // directory ('Dir' below) or the ResourceDir. if (StringRef(D.Dir).startswith(SysRoot)) { addPathIfExists(D.Dir + "/../lib/" + MultiarchTriple, Paths); - addPathIfExists(D.Dir + "/../" + Multilib, Paths); + addPathIfExists(D.Dir + "/../" + OSLibDir, Paths); } addPathIfExists(SysRoot + "/lib/" + MultiarchTriple, Paths); - addPathIfExists(SysRoot + "/lib/../" + Multilib, Paths); + addPathIfExists(SysRoot + "/lib/../" + OSLibDir, Paths); addPathIfExists(SysRoot + "/usr/lib/" + MultiarchTriple, Paths); - addPathIfExists(SysRoot + "/usr/lib/../" + Multilib, Paths); + addPathIfExists(SysRoot + "/usr/lib/../" + OSLibDir, Paths); // Try walking via the GCC triple path in case of biarch or multiarch GCC // installations with strange symlinks. if (GCCInstallation.isValid()) { addPathIfExists(SysRoot + "/usr/lib/" + GCCInstallation.getTriple().str() + - "/../../" + Multilib, Paths); + "/../../" + OSLibDir, Paths); - // Add the non-multilib suffixed paths (if potentially different). - const std::string &LibPath = GCCInstallation.getParentLibPath(); - const llvm::Triple &GCCTriple = GCCInstallation.getTriple(); - if (!GCCInstallation.getBiarchSuffix().empty()) + // Add the 'other' biarch variant path + Multilib BiarchSibling; + if (GCCInstallation.getBiarchSibling(BiarchSibling)) { addPathIfExists(GCCInstallation.getInstallPath() + - GCCInstallation.getMIPSABIDirSuffix(), Paths); + BiarchSibling.gccSuffix(), Paths); + } // See comments above on the multilib variant for details of why this is // included even from outside the sysroot. + const std::string &LibPath = GCCInstallation.getParentLibPath(); + const llvm::Triple &GCCTriple = GCCInstallation.getTriple(); + const Multilib &Multilib = GCCInstallation.getMultilib(); addPathIfExists(LibPath + "/../" + GCCTriple.str() + - "/lib" + GCCInstallation.getMIPSABIDirSuffix(), Paths); + "/lib" + Multilib.osSuffix(), Paths); // See comments above on the multilib variant for details of why this is // only included from within the sysroot. @@ -2804,15 +3019,15 @@ const StringRef InstallDir = GCCInstallation.getInstallPath(); const StringRef TripleStr = GCCInstallation.getTriple().str(); - const StringRef MIPSABIDirSuffix = GCCInstallation.getMIPSABIDirSuffix(); + const Multilib &Multilib = GCCInstallation.getMultilib(); std::string Path = (InstallDir + "/../../../../" + TripleStr + "/libc" + - MIPSABIDirSuffix).str(); + Multilib.osSuffix()).str(); if (llvm::sys::fs::exists(Path)) return Path; - Path = (InstallDir + "/../../../../sysroot" + MIPSABIDirSuffix).str(); + Path = (InstallDir + "/../../../../sysroot" + Multilib.osSuffix()).str(); if (llvm::sys::fs::exists(Path)) return Path; @@ -2968,17 +3183,16 @@ /// libstdc++ installation. /*static*/ bool Linux::addLibStdCXXIncludePaths(Twine Base, Twine Suffix, Twine TargetArchDir, - Twine BiarchSuffix, - Twine MIPSABIDirSuffix, + Twine IncludeSuffix, const ArgList &DriverArgs, ArgStringList &CC1Args) { if (!addLibStdCXXIncludePaths(Base + Suffix, - TargetArchDir + MIPSABIDirSuffix + BiarchSuffix, + TargetArchDir + IncludeSuffix, DriverArgs, CC1Args)) return false; addSystemInclude(DriverArgs, CC1Args, Base + "/" + TargetArchDir + Suffix - + MIPSABIDirSuffix + BiarchSuffix); + + IncludeSuffix); return true; } @@ -3022,13 +3236,12 @@ StringRef LibDir = GCCInstallation.getParentLibPath(); StringRef InstallDir = GCCInstallation.getInstallPath(); StringRef TripleStr = GCCInstallation.getTriple().str(); - StringRef MIPSABIDirSuffix = GCCInstallation.getMIPSABIDirSuffix(); - StringRef BiarchSuffix = GCCInstallation.getBiarchSuffix(); + const Multilib &Multilib = GCCInstallation.getMultilib(); const GCCVersion &Version = GCCInstallation.getVersion(); if (addLibStdCXXIncludePaths(LibDir.str() + "/../include", - "/c++/" + Version.Text, TripleStr, BiarchSuffix, - MIPSABIDirSuffix, DriverArgs, CC1Args)) + "/c++/" + Version.Text, TripleStr, + Multilib.includeSuffix(), DriverArgs, CC1Args)) return; const std::string LibStdCXXIncludePathCandidates[] = { @@ -3047,7 +3260,7 @@ for (unsigned i = 0; i < llvm::array_lengthof(LibStdCXXIncludePathCandidates); ++i) { if (addLibStdCXXIncludePaths(LibStdCXXIncludePathCandidates[i], - TripleStr + MIPSABIDirSuffix + BiarchSuffix, + TripleStr + Multilib.includeSuffix(), DriverArgs, CC1Args)) break; } Index: lib/Driver/Tools.h =================================================================== --- lib/Driver/Tools.h +++ lib/Driver/Tools.h @@ -209,6 +209,10 @@ const char* getLLVMArchSuffixForARM(StringRef CPU); } +namespace mips { + bool hasMipsAbiArg(const llvm::opt::ArgList &Args, const char *Value); +} + namespace darwin { llvm::Triple::ArchType getArchTypeForMachOArchName(StringRef Str); void setTripleTypeForMachOArchName(llvm::Triple &T, StringRef Str); Index: lib/Driver/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp +++ lib/Driver/Tools.cpp @@ -44,11 +44,6 @@ using namespace clang; using namespace llvm::opt; -static bool hasMipsABIArg(const ArgList &Args, const char *Value) { - Arg *A = Args.getLastArg(options::OPT_mabi_EQ); - return A && (A->getValue() == StringRef(Value)); -} - /// CheckPreprocessingOptions - Perform some validation of preprocessing /// arguments that is shared with gcc. static void CheckPreprocessingOptions(const Driver &D, const ArgList &Args) { @@ -480,7 +475,7 @@ case llvm::Triple::arm: case llvm::Triple::ppc: case llvm::Triple::ppc64: - if (Triple.isOSBinFormatMachO()) + if (Triple.isOSDarwin()) return true; return false; @@ -751,7 +746,7 @@ const char *ABIName = 0; if (Arg *A = Args.getLastArg(options::OPT_mabi_EQ)) { ABIName = A->getValue(); - } else if (Triple.isOSBinFormatMachO()) { + } else if (Triple.isOSDarwin()) { // The backend is hardwired to assume AAPCS for M-class processors, ensure // the frontend matches that. if (Triple.getEnvironment() == llvm::Triple::EABI || @@ -1197,7 +1192,7 @@ const llvm::Triple &Triple) { if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) { if (StringRef(A->getValue()) != "native") { - if (Triple.isOSBinFormatMachO() && Triple.getArchName() == "x86_64h") + if (Triple.isOSDarwin() && Triple.getArchName() == "x86_64h") return "core-avx2"; return A->getValue(); @@ -1222,7 +1217,7 @@ bool Is64Bit = Triple.getArch() == llvm::Triple::x86_64; // FIXME: Need target hooks. - if (Triple.isOSBinFormatMachO()) { + if (Triple.isOSDarwin()) { if (Triple.getArchName() == "x86_64h") return "core-avx2"; return Is64Bit ? "core2" : "yonah"; @@ -2607,7 +2602,7 @@ // -gline-tables-only. CmdArgs.push_back("-gline-tables-only"); // Default is dwarf-2 for darwin. - if (getToolChain().getTriple().isOSBinFormatMachO()) + if (getToolChain().getTriple().isOSDarwin()) CmdArgs.push_back("-gdwarf-2"); } else if (A->getOption().matches(options::OPT_gdwarf_2)) CmdArgs.push_back("-gdwarf-2"); @@ -2618,7 +2613,7 @@ else if (!A->getOption().matches(options::OPT_g0) && !A->getOption().matches(options::OPT_ggdb0)) { // Default is dwarf-2 for darwin. - if (getToolChain().getTriple().isOSBinFormatMachO()) + if (getToolChain().getTriple().isOSDarwin()) CmdArgs.push_back("-gdwarf-2"); else CmdArgs.push_back("-g"); @@ -3927,7 +3922,7 @@ // -fnext-runtime } else if (runtimeArg->getOption().matches(options::OPT_fnext_runtime)) { // On Darwin, make this use the default behavior for the toolchain. - if (getToolChain().getTriple().isOSBinFormatMachO()) { + if (getToolChain().getTriple().isOSDarwin()) { runtime = getToolChain().getDefaultObjCRuntime(isNonFragile); // Otherwise, build for a generic macosx port. @@ -4174,7 +4169,7 @@ // If using a driver driver, force the arch. llvm::Triple::ArchType Arch = getToolChain().getArch(); - if (getToolChain().getTriple().isOSBinFormatMachO()) { + if (getToolChain().getTriple().isOSDarwin()) { CmdArgs.push_back("-arch"); // FIXME: Remove these special cases. @@ -4669,6 +4664,11 @@ .Default(""); } +bool mips::hasMipsAbiArg(const ArgList &Args, const char *Value) { + Arg *A = Args.getLastArg(options::OPT_mabi_EQ); + return A && (A->getValue() == StringRef(Value)); +} + llvm::Triple::ArchType darwin::getArchTypeForMachOArchName(StringRef Str) { // See arch(3) and llvm-gcc's driver-driver.c. We don't implement support for // archs which Darwin doesn't use. @@ -6141,13 +6141,13 @@ break; case llvm::Triple::mips64: case llvm::Triple::mips64el: - if (hasMipsABIArg(Args, "32")) { + if (mips::hasMipsAbiArg(Args, "32")) { CmdArgs.push_back("-m"); if (getToolChain().getArch() == llvm::Triple::mips64) CmdArgs.push_back("elf32btsmip"); else CmdArgs.push_back("elf32ltsmip"); - } else if (hasMipsABIArg(Args, "64")) { + } else if (mips::hasMipsAbiArg(Args, "64")) { CmdArgs.push_back("-m"); if (getToolChain().getArch() == llvm::Triple::mips64) CmdArgs.push_back("elf64btsmip"); @@ -6451,7 +6451,7 @@ return "/lib/ld.so.1"; else if (ToolChain.getArch() == llvm::Triple::mips64 || ToolChain.getArch() == llvm::Triple::mips64el) { - if (hasMipsABIArg(Args, "n32")) + if (mips::hasMipsAbiArg(Args, "n32")) return "/lib32/ld.so.1"; else return "/lib64/ld.so.1"; @@ -6534,13 +6534,13 @@ else if (ToolChain.getArch() == llvm::Triple::mipsel) CmdArgs.push_back("elf32ltsmip"); else if (ToolChain.getArch() == llvm::Triple::mips64) { - if (hasMipsABIArg(Args, "n32")) + if (mips::hasMipsAbiArg(Args, "n32")) CmdArgs.push_back("elf32btsmipn32"); else CmdArgs.push_back("elf64btsmip"); } else if (ToolChain.getArch() == llvm::Triple::mips64el) { - if (hasMipsABIArg(Args, "n32")) + if (mips::hasMipsAbiArg(Args, "n32")) CmdArgs.push_back("elf32ltsmipn32"); else CmdArgs.push_back("elf64ltsmip"); Index: unittests/CMakeLists.txt =================================================================== --- unittests/CMakeLists.txt +++ unittests/CMakeLists.txt @@ -11,6 +11,7 @@ add_subdirectory(Basic) add_subdirectory(Lex) +add_subdirectory(Driver) if(CLANG_ENABLE_STATIC_ANALYZER) add_subdirectory(Frontend) endif() Index: unittests/Driver/CMakeLists.txt =================================================================== --- /dev/null +++ unittests/Driver/CMakeLists.txt @@ -0,0 +1,11 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + +add_clang_unittest(DriverTests + MultilibTest.cpp + ) + +target_link_libraries(DriverTests + clangDriver + ) Index: unittests/Driver/Makefile =================================================================== --- /dev/null +++ unittests/Driver/Makefile @@ -0,0 +1,16 @@ +##===- unittests/Driver/Makefile ---------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL = ../.. +TESTNAME = Multilib +include $(CLANG_LEVEL)/../../Makefile.config +LINK_COMPONENTS := $(TARGETS_TO_BUILD) support option +USEDLIBS = clangDriver.a clangBasic.a + +include $(CLANG_LEVEL)/unittests/Makefile Index: unittests/Driver/MultilibTest.cpp =================================================================== --- /dev/null +++ unittests/Driver/MultilibTest.cpp @@ -0,0 +1,356 @@ +//===- unittests/Driver/MultilibTest.cpp --- Multilib tests ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Unit tests for Multilib and MultilibSet +// +//===----------------------------------------------------------------------===// + +#include "clang/Driver/Multilib.h" +#include "clang/Basic/LLVM.h" +#include "gtest/gtest.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/StringRef.h" + +using namespace clang::driver; +using namespace clang; + +TEST(MultilibTest, MultilibValidity) { + + ASSERT_TRUE(Multilib().isValid()) << "Empty multilib is not valid"; + + ASSERT_TRUE(Multilib().flag("+foo").isValid()) + << "Single indicative flag is not valid"; + + ASSERT_TRUE(Multilib().flag("-foo").isValid()) + << "Single contraindicative flag is not valid"; + + ASSERT_FALSE(Multilib().flag("+foo").flag("-foo").isValid()) + << "Conflicting flags should invalidate the Multilib"; + + ASSERT_TRUE(Multilib().flag("+foo").flag("+foo").isValid()) + << "Multilib should be valid even if it has the same flag twice"; + + ASSERT_TRUE(Multilib().flag("+foo").flag("-foobar").isValid()) + << "Seemingly conflicting prefixes shouldn't actually conflict"; +} + +TEST(MultilibTest, OpEqReflexivity1) { + Multilib M; + ASSERT_TRUE(M == M) << "Multilib::operator==() is not reflexive"; +} + +TEST(MultilibTest, OpEqReflexivity2) { + ASSERT_TRUE(Multilib() == Multilib()) + << "Separately constructed default multilibs are not equal"; +} + +TEST(MultilibTest, OpEqReflexivity3) { + Multilib M1, M2; + M1.flag("+foo"); + M2.flag("+foo"); + ASSERT_TRUE(M1 == M2) << "Multilibs with the same flag should be the same"; +} + +TEST(MultilibTest, OpEqInequivalence1) { + Multilib M1, M2; + M1.flag("+foo"); + M2.flag("-foo"); + ASSERT_FALSE(M1 == M2) << "Multilibs with conflicting flags are not the same"; + ASSERT_FALSE(M2 == M1) + << "Multilibs with conflicting flags are not the same (commuted)"; +} + +TEST(MultilibTest, OpEqInequivalence2) { + Multilib M1, M2; + M2.flag("+foo"); + ASSERT_FALSE(M1 == M2) << "Flags make Multilibs different"; +} + +TEST(MultilibTest, OpEqEquivalence1) { + Multilib M1, M2; + M1.flag("+foo"); + M2.flag("+foo").flag("+foo"); + ASSERT_TRUE(M1 == M2) << "Flag duplication shouldn't affect equivalence"; + ASSERT_TRUE(M2 == M1) + << "Flag duplication shouldn't affect equivalence (commuted)"; +} + +TEST(MultilibTest, OpEqEquivalence2) { + Multilib M1("64"); + Multilib M2; + M2.gccSuffix("/64"); + ASSERT_TRUE(M1 == M2) + << "Constructor argument must match Multilib::gccSuffix()"; + ASSERT_TRUE(M2 == M1) + << "Constructor argument must match Multilib::gccSuffix() (commuted)"; +} + +TEST(MultilibTest, OpEqEquivalence3) { + Multilib M1("", "32"); + Multilib M2; + M2.osSuffix("/32"); + ASSERT_TRUE(M1 == M2) + << "Constructor argument must match Multilib::osSuffix()"; + ASSERT_TRUE(M2 == M1) + << "Constructor argument must match Multilib::osSuffix() (commuted)"; +} + +TEST(MultilibTest, OpEqEquivalence4) { + Multilib M1("", "", "16"); + Multilib M2; + M2.includeSuffix("/16"); + ASSERT_TRUE(M1 == M2) + << "Constructor argument must match Multilib::includeSuffix()"; + ASSERT_TRUE(M2 == M1) + << "Constructor argument must match Multilib::includeSuffix() (commuted)"; +} + +TEST(MultilibTest, OpEqInequivalence3) { + Multilib M1("foo"); + Multilib M2("bar"); + ASSERT_FALSE(M1 == M2) << "Differing gccSuffixes should be different"; + ASSERT_FALSE(M2 == M1) + << "Differing gccSuffixes should be different (commuted)"; +} + +TEST(MultilibTest, OpEqInequivalence4) { + Multilib M1("", "foo"); + Multilib M2("", "bar"); + ASSERT_FALSE(M1 == M2) << "Differing osSuffixes should be different"; + ASSERT_FALSE(M2 == M1) + << "Differing osSuffixes should be different (commuted)"; +} + +TEST(MultilibTest, OpEqInequivalence5) { + Multilib M1("", "", "foo"); + Multilib M2("", "", "bar"); + ASSERT_FALSE(M1 == M2) << "Differing includeSuffixes should be different"; + ASSERT_FALSE(M2 == M1) + << "Differing includeSuffixes should be different (commuted)"; +} + +TEST(MultilibTest, Construction1) { + Multilib M("gcc64", "os64", "inc64"); + ASSERT_TRUE(M.gccSuffix() == "/gcc64"); + ASSERT_TRUE(M.osSuffix() == "/os64"); + ASSERT_TRUE(M.includeSuffix() == "/inc64"); +} + +TEST(MultilibTest, Construction2) { + Multilib M1; + Multilib M2(""); + Multilib M3("", ""); + Multilib M4("", "", ""); + ASSERT_TRUE(M1 == M2) + << "Default arguments to Multilib constructor broken (first argument)"; + ASSERT_TRUE(M1 == M3) + << "Default arguments to Multilib constructor broken (second argument)"; + ASSERT_TRUE(M1 == M4) + << "Default arguments to Multilib constructor broken (third argument)"; +} + +TEST(MultilibTest, Construction3) { + Multilib M = Multilib().flag("+f1").flag("+f2").flag("-f3"); + for (Multilib::flags_list::const_iterator I = M.flags().begin(), + E = M.flags().end(); + I != E; ++I) { + ASSERT_TRUE(llvm::StringSwitch(*I) + .Cases("+f1", "+f2", "-f3", true) + .Default(false)); + } +} + +static bool hasFlag(const Multilib &M, StringRef Flag) { + for (Multilib::flags_list::const_iterator I = M.flags().begin(), + E = M.flags().end(); + I != E; ++I) { + if (*I == Flag) + return true; + else if (StringRef(*I).substr(1) == Flag.substr(1)) + return false; + } + return false; +} + +TEST(MultilibTest, SetConstruction1) { + // Single maybe + MultilibSet MS; + ASSERT_TRUE(MS.size() == 0); + MS.Maybe(Multilib("64").flag("+m64")); + ASSERT_TRUE(MS.size() == 2); + for (MultilibSet::const_iterator I = MS.begin(), E = MS.end(); I != E; ++I) { + if (I->gccSuffix() == "/64") + ASSERT_TRUE(I->flags()[0] == "+m64"); + else if (I->gccSuffix() == "") + ASSERT_TRUE(I->flags()[0] == "-m64"); + else + FAIL() << "Unrecognized gccSufix: " << I->gccSuffix(); + } +} + +TEST(MultilibTest, SetConstruction2) { + // Double maybe + MultilibSet MS; + MS.Maybe(Multilib("sof").flag("+sof")); + MS.Maybe(Multilib("el").flag("+EL")); + ASSERT_TRUE(MS.size() == 4); + for (MultilibSet::const_iterator I = MS.begin(), E = MS.end(); I != E; ++I) { + ASSERT_TRUE(I->isValid()) << "Multilb " << *I << " should be valid"; + ASSERT_TRUE(llvm::StringSwitch(I->gccSuffix()) + .Cases("", "/sof", "/el", "/sof/el", true) + .Default(false)) + << "Multilib " << *I << " wasn't expected"; + ASSERT_TRUE(llvm::StringSwitch(I->gccSuffix()) + .Case("", hasFlag(*I, "-sof")) + .Case("/sof", hasFlag(*I, "+sof")) + .Case("/el", hasFlag(*I, "-sof")) + .Case("/sof/el", hasFlag(*I, "+sof")) + .Default(false)) + << "Multilib " << *I << " didn't have the appropriate {+,-}sof flag"; + ASSERT_TRUE(llvm::StringSwitch(I->gccSuffix()) + .Case("", hasFlag(*I, "-EL")) + .Case("/sof", hasFlag(*I, "-EL")) + .Case("/el", hasFlag(*I, "+EL")) + .Case("/sof/el", hasFlag(*I, "+EL")) + .Default(false)) + << "Multilib " << *I << " didn't have the appropriate {+,-}EL flag"; + } +} + +TEST(MultilibTest, SetPushback) { + MultilibSet MS; + MS.push_back(Multilib("one")); + MS.push_back(Multilib("two")); + ASSERT_TRUE(MS.size() == 2); + for (MultilibSet::const_iterator I = MS.begin(), E = MS.end(); I != E; ++I) { + ASSERT_TRUE(llvm::StringSwitch(I->gccSuffix()) + .Cases("/one", "/two", true) + .Default(false)); + } + MS.clear(); + ASSERT_TRUE(MS.size() == 0); +} + +TEST(MultilibTest, SetRegexFilter) { + MultilibSet MS; + MS.Maybe(Multilib("one")); + MS.Maybe(Multilib("two")); + MS.Maybe(Multilib("three")); + ASSERT_EQ(MS.size(), (unsigned)2 * 2 * 2) + << "Size before filter was incorrect. Contents:\n" << MS; + MS.FilterOut("/one/two/three"); + ASSERT_EQ(MS.size(), (unsigned)2 * 2 * 2 - 1) + << "Size after filter was incorrect. Contents:\n" << MS; + for (MultilibSet::const_iterator I = MS.begin(), E = MS.end(); I != E; ++I) { + ASSERT_TRUE(I->gccSuffix() != "/one/two/three") + << "The filter should have removed " << *I; + } +} + +TEST(MultilibTest, SetFilterObject) { + // Filter object + struct StartsWithP : public MultilibSet::FilterCallback { + bool operator()(const Multilib &M) const LLVM_OVERRIDE { + return StringRef(M.gccSuffix()).startswith("/p"); + } + }; + MultilibSet MS; + MS.Maybe(Multilib("orange")); + MS.Maybe(Multilib("pear")); + MS.Maybe(Multilib("plum")); + ASSERT_EQ((int)MS.size(), 1 /* Default */ + + 1 /* pear */ + + 1 /* plum */ + + 1 /* pear/plum */ + + 1 /* orange */ + + 1 /* orange/pear */ + + 1 /* orange/plum */ + + 1 /* orange/pear/plum */ ) + << "Size before filter was incorrect. Contents:\n" << MS; + MS.FilterOut(StartsWithP()); + ASSERT_EQ((int)MS.size(), 1 /* Default */ + + 1 /* orange */ + + 1 /* orange/pear */ + + 1 /* orange/plum */ + + 1 /* orange/pear/plum */ ) + << "Size after filter was incorrect. Contents:\n" << MS; + for (MultilibSet::const_iterator I = MS.begin(), E = MS.end(); I != E; ++I) { + ASSERT_FALSE(StringRef(I->gccSuffix()).startswith("/p")) + << "The filter should have removed " << *I; + } +} + +TEST(MultilibTest, SetSelection1) { + MultilibSet MS1 = MultilibSet() + .Maybe(Multilib("64").flag("+m64")); + + Multilib::flags_list FlagM64; + FlagM64.push_back("+m64"); + Multilib SelectionM64; + ASSERT_TRUE(MS1.select(FlagM64, SelectionM64)) + << "Flag set was {\"+m64\"}, but selection not found"; + ASSERT_TRUE(SelectionM64.gccSuffix() == "/64") + << "Selection picked " << SelectionM64 << " which was not expected"; + + Multilib::flags_list FlagNoM64; + FlagNoM64.push_back("-m64"); + Multilib SelectionNoM64; + ASSERT_TRUE(MS1.select(FlagNoM64, SelectionNoM64)) + << "Flag set was {\"-m64\"}, but selection not found"; + ASSERT_TRUE(SelectionNoM64.gccSuffix() == "") + << "Selection picked " << SelectionNoM64 << " which was not expected"; +} + +TEST(MultilibTest, SetSelection2) { + MultilibSet MS2 = MultilibSet() + .Maybe(Multilib("el").flag("+EL")) + .Maybe(Multilib("sf").flag("+SF")); + + for (unsigned I = 0; I < 4; ++I) { + bool IsEL = I & 0x1; + bool IsSF = I & 0x2; + Multilib::flags_list Flags; + if (IsEL) + Flags.push_back("+EL"); + else + Flags.push_back("-EL"); + + if (IsSF) + Flags.push_back("+SF"); + else + Flags.push_back("-SF"); + + Multilib Selection; + ASSERT_TRUE(MS2.select(Flags, Selection)) << "Selection failed for " + << (IsEL ? "+EL" : "-EL") << " " + << (IsSF ? "+SF" : "-SF"); + + std::string Suffix; + if (IsEL) + Suffix += "/el"; + if (IsSF) + Suffix += "/sf"; + + ASSERT_EQ(Selection.gccSuffix(), Suffix) << "Selection picked " << Selection + << " which was not expected "; + } +} + +TEST(MultilibTest, SetCombineWith) { + MultilibSet Coffee; + Coffee.push_back(Multilib("coffee")); + MultilibSet Milk; + Milk.push_back(Multilib("milk")); + MultilibSet Latte; + ASSERT_EQ(Latte.size(), (unsigned)0); + Latte.combineWith(Coffee); + ASSERT_EQ(Latte.size(), (unsigned)1); + Latte.combineWith(Milk); + ASSERT_EQ(Latte.size(), (unsigned)2); +} Index: unittests/Makefile =================================================================== --- unittests/Makefile +++ unittests/Makefile @@ -14,7 +14,7 @@ IS_UNITTEST_LEVEL := 1 CLANG_LEVEL := .. -PARALLEL_DIRS = Basic Lex +PARALLEL_DIRS = Basic Lex Driver include $(CLANG_LEVEL)/../..//Makefile.config