Skip to content

Commit

Permalink
[Driver] Move GCC multilib/multiarch paths support from Linux.cpp to …
Browse files Browse the repository at this point in the history
…Gnu.cpp

The current code for GNU/Linux is actually completely generic, and can be moved to ToolChains/Gnu.cpp,
so that it can benefit GNU/Hurd and GNU/kFreeBSD.

Reviewed By: MaskRay, phosek

Differential Revision: https://reviews.llvm.org/D73845
  • Loading branch information
sthibaul authored and MaskRay committed Apr 24, 2020
1 parent c3770c5 commit 6395980
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 115 deletions.
135 changes: 135 additions & 0 deletions clang/lib/Driver/ToolChains/Gnu.cpp
Expand Up @@ -35,6 +35,7 @@ using namespace clang;
using namespace llvm::opt;

using tools::addMultilibFlag;
using tools::addPathIfExists;

void tools::GnuTool::anchor() {}

Expand Down Expand Up @@ -2670,6 +2671,140 @@ bool Generic_GCC::IsIntegratedAssemblerDefault() const {
}
}

static void addMultilibsFilePaths(const Driver &D, const MultilibSet &Multilibs,
const Multilib &Multilib,
StringRef InstallPath,
ToolChain::path_list &Paths) {
if (const auto &PathsCallback = Multilibs.filePathsCallback())
for (const auto &Path : PathsCallback(Multilib))
addPathIfExists(D, InstallPath + Path, Paths);
}

void Generic_GCC::PushPPaths(ToolChain::path_list &PPaths) {
// Cross-compiling binutils and GCC installations (vanilla and openSUSE at
// least) put various tools in a triple-prefixed directory off of the parent
// of the GCC installation. We use the GCC triple here to ensure that we end
// up with tools that support the same amount of cross compiling as the
// detected GCC installation. For example, if we find a GCC installation
// targeting x86_64, but it is a bi-arch GCC installation, it can also be
// used to target i386.
if (GCCInstallation.isValid()) {
PPaths.push_back(Twine(GCCInstallation.getParentLibPath() + "/../" +
GCCInstallation.getTriple().str() + "/bin")
.str());
}
}

void Generic_GCC::AddMultilibPaths(const Driver &D,
const std::string &SysRoot,
const std::string &OSLibDir,
const std::string &MultiarchTriple,
path_list &Paths) {
// Add the multilib suffixed paths where they are available.
if (GCCInstallation.isValid()) {
const llvm::Triple &GCCTriple = GCCInstallation.getTriple();
const std::string &LibPath =
std::string(GCCInstallation.getParentLibPath());

// Add toolchain / multilib specific file paths.
addMultilibsFilePaths(D, Multilibs, SelectedMultilib,
GCCInstallation.getInstallPath(), Paths);

// Sourcery CodeBench MIPS toolchain holds some libraries under
// a biarch-like suffix of the GCC installation.
addPathIfExists(
D, GCCInstallation.getInstallPath() + SelectedMultilib.gccSuffix(),
Paths);

// GCC cross compiling toolchains will install target libraries which ship
// as part of the toolchain under <prefix>/<triple>/<libdir> rather than as
// any part of the GCC installation in
// <prefix>/<libdir>/gcc/<triple>/<version>. This decision is somewhat
// debatable, but is the reality today. We need to search this tree even
// when we have a sysroot somewhere else. It is the responsibility of
// whomever is doing the cross build targeting a sysroot using a GCC
// installation that is *not* within the system root to ensure two things:
//
// 1) Any DSOs that are linked in from this tree or from the install path
// above must be present on the system root and found via an
// appropriate rpath.
// 2) There must not be libraries installed into
// <prefix>/<triple>/<libdir> unless they should be preferred over
// those within the system root.
//
// Note that this matches the GCC behavior. See the below comment for where
// Clang diverges from GCC's behavior.
addPathIfExists(D,
LibPath + "/../" + GCCTriple.str() + "/lib/../" + OSLibDir +
SelectedMultilib.osSuffix(),
Paths);

// If the GCC installation we found is inside of the sysroot, we want to
// prefer libraries installed in the parent prefix of the GCC installation.
// It is important to *not* use these paths when the GCC installation is
// outside of the system root as that can pick up unintended libraries.
// This usually happens when there is an external cross compiler on the
// host system, and a more minimal sysroot available that is the target of
// the cross. Note that GCC does include some of these directories in some
// configurations but this seems somewhere between questionable and simply
// a bug.
if (StringRef(LibPath).startswith(SysRoot)) {
addPathIfExists(D, LibPath + "/" + MultiarchTriple, Paths);
addPathIfExists(D, LibPath + "/../" + OSLibDir, Paths);
}
}
}

void Generic_GCC::AddMultiarchPaths(const Driver &D,
const std::string &SysRoot,
const std::string &OSLibDir,
path_list &Paths) {
// Try walking via the GCC triple path in case of biarch or multiarch GCC
// installations with strange symlinks.
if (GCCInstallation.isValid()) {
addPathIfExists(D,
SysRoot + "/usr/lib/" + GCCInstallation.getTriple().str() +
"/../../" + OSLibDir,
Paths);

// Add the 'other' biarch variant path
Multilib BiarchSibling;
if (GCCInstallation.getBiarchSibling(BiarchSibling)) {
addPathIfExists(
D, GCCInstallation.getInstallPath() + 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 =
std::string(GCCInstallation.getParentLibPath());
const llvm::Triple &GCCTriple = GCCInstallation.getTriple();
const Multilib &Multilib = GCCInstallation.getMultilib();
addPathIfExists(
D, LibPath + "/../" + GCCTriple.str() + "/lib" + Multilib.osSuffix(),
Paths);

// See comments above on the multilib variant for details of why this is
// only included from within the sysroot.
if (StringRef(LibPath).startswith(SysRoot))
addPathIfExists(D, LibPath, Paths);
}
}

void Generic_GCC::AddMultilibIncludeArgs(const ArgList &DriverArgs,
ArgStringList &CC1Args) const {
// Add include directories specific to the selected multilib set and multilib.
if (GCCInstallation.isValid()) {
const auto &Callback = Multilibs.includeDirsCallback();
if (Callback) {
for (const auto &Path : Callback(GCCInstallation.getMultilib()))
addExternCSystemIncludeIfExists(
DriverArgs, CC1Args, GCCInstallation.getInstallPath() + Path);
}
}
}

void Generic_GCC::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
ArgStringList &CC1Args) const {
if (DriverArgs.hasArg(options::OPT_nostdlibinc) ||
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/Driver/ToolChains/Gnu.h
Expand Up @@ -314,6 +314,16 @@ class LLVM_LIBRARY_VISIBILITY Generic_GCC : public ToolChain {
/// Check whether the target triple's architecture is 32-bits.
bool isTarget32Bit() const { return getTriple().isArch32Bit(); }

void PushPPaths(ToolChain::path_list &PPaths);
void AddMultilibPaths(const Driver &D, const std::string &SysRoot,
const std::string &OSLibDir,
const std::string &MultiarchTriple,
path_list &Paths);
void AddMultiarchPaths(const Driver &D, const std::string &SysRoot,
const std::string &OSLibDir, path_list &Paths);
void AddMultilibIncludeArgs(const llvm::opt::ArgList &DriverArgs,
llvm::opt::ArgStringList &CC1Args) const;

// FIXME: This should be final, but the CrossWindows toolchain does weird
// things that can't be easily generalized.
void AddClangCXXStdlibIncludeArgs(
Expand Down
120 changes: 5 additions & 115 deletions clang/lib/Driver/ToolChains/Linux.cpp
Expand Up @@ -208,37 +208,16 @@ static StringRef getOSLibDir(const llvm::Triple &Triple, const ArgList &Args) {
return Triple.isArch32Bit() ? "lib" : "lib64";
}

static void addMultilibsFilePaths(const Driver &D, const MultilibSet &Multilibs,
const Multilib &Multilib,
StringRef InstallPath,
ToolChain::path_list &Paths) {
if (const auto &PathsCallback = Multilibs.filePathsCallback())
for (const auto &Path : PathsCallback(Multilib))
addPathIfExists(D, InstallPath + Path, Paths);
}

Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args)
: Generic_ELF(D, Triple, Args) {
GCCInstallation.init(Triple, Args);
Multilibs = GCCInstallation.getMultilibs();
SelectedMultilib = GCCInstallation.getMultilib();
llvm::Triple::ArchType Arch = Triple.getArch();
std::string SysRoot = computeSysRoot();

// Cross-compiling binutils and GCC installations (vanilla and openSUSE at
// least) put various tools in a triple-prefixed directory off of the parent
// of the GCC installation. We use the GCC triple here to ensure that we end
// up with tools that support the same amount of cross compiling as the
// detected GCC installation. For example, if we find a GCC installation
// targeting x86_64, but it is a bi-arch GCC installation, it can also be
// used to target i386.
// FIXME: This seems unlikely to be Linux-specific.
ToolChain::path_list &PPaths = getProgramPaths();
if (GCCInstallation.isValid()) {
PPaths.push_back(Twine(GCCInstallation.getParentLibPath() + "/../" +
GCCInstallation.getTriple().str() + "/bin")
.str());
}

Generic_GCC::PushPPaths(PPaths);

Distro Distro(D.getVFS(), Triple);

Expand Down Expand Up @@ -316,58 +295,7 @@ Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args)
const std::string OSLibDir = std::string(getOSLibDir(Triple, Args));
const std::string MultiarchTriple = getMultiarchTriple(D, Triple, SysRoot);

// Add the multilib suffixed paths where they are available.
if (GCCInstallation.isValid()) {
const llvm::Triple &GCCTriple = GCCInstallation.getTriple();
const std::string &LibPath =
std::string(GCCInstallation.getParentLibPath());

// Add toolchain / multilib specific file paths.
addMultilibsFilePaths(D, Multilibs, SelectedMultilib,
GCCInstallation.getInstallPath(), Paths);

// Sourcery CodeBench MIPS toolchain holds some libraries under
// a biarch-like suffix of the GCC installation.
addPathIfExists(
D, GCCInstallation.getInstallPath() + SelectedMultilib.gccSuffix(),
Paths);

// GCC cross compiling toolchains will install target libraries which ship
// as part of the toolchain under <prefix>/<triple>/<libdir> rather than as
// any part of the GCC installation in
// <prefix>/<libdir>/gcc/<triple>/<version>. This decision is somewhat
// debatable, but is the reality today. We need to search this tree even
// when we have a sysroot somewhere else. It is the responsibility of
// whomever is doing the cross build targeting a sysroot using a GCC
// installation that is *not* within the system root to ensure two things:
//
// 1) Any DSOs that are linked in from this tree or from the install path
// above must be present on the system root and found via an
// appropriate rpath.
// 2) There must not be libraries installed into
// <prefix>/<triple>/<libdir> unless they should be preferred over
// those within the system root.
//
// Note that this matches the GCC behavior. See the below comment for where
// Clang diverges from GCC's behavior.
addPathIfExists(D, LibPath + "/../" + GCCTriple.str() + "/lib/../" +
OSLibDir + SelectedMultilib.osSuffix(),
Paths);

// If the GCC installation we found is inside of the sysroot, we want to
// prefer libraries installed in the parent prefix of the GCC installation.
// It is important to *not* use these paths when the GCC installation is
// outside of the system root as that can pick up unintended libraries.
// This usually happens when there is an external cross compiler on the
// host system, and a more minimal sysroot available that is the target of
// the cross. Note that GCC does include some of these directories in some
// configurations but this seems somewhere between questionable and simply
// a bug.
if (StringRef(LibPath).startswith(SysRoot)) {
addPathIfExists(D, LibPath + "/" + MultiarchTriple, Paths);
addPathIfExists(D, LibPath + "/../" + OSLibDir, Paths);
}
}
Generic_GCC::AddMultilibPaths(D, SysRoot, OSLibDir, MultiarchTriple, Paths);

// Similar to the logic for GCC above, if we currently running Clang inside
// of the requested system root, add its parent library paths to
Expand Down Expand Up @@ -411,37 +339,7 @@ Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args)
addPathIfExists(D, SysRoot + "/usr/" + OSLibDir + "/" + ABIName, Paths);
}

// Try walking via the GCC triple path in case of biarch or multiarch GCC
// installations with strange symlinks.
if (GCCInstallation.isValid()) {
addPathIfExists(D,
SysRoot + "/usr/lib/" + GCCInstallation.getTriple().str() +
"/../../" + OSLibDir,
Paths);

// Add the 'other' biarch variant path
Multilib BiarchSibling;
if (GCCInstallation.getBiarchSibling(BiarchSibling)) {
addPathIfExists(D, GCCInstallation.getInstallPath() +
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 =
std::string(GCCInstallation.getParentLibPath());
const llvm::Triple &GCCTriple = GCCInstallation.getTriple();
const Multilib &Multilib = GCCInstallation.getMultilib();
addPathIfExists(D, LibPath + "/../" + GCCTriple.str() + "/lib" +
Multilib.osSuffix(),
Paths);

// See comments above on the multilib variant for details of why this is
// only included from within the sysroot.
if (StringRef(LibPath).startswith(SysRoot))
addPathIfExists(D, LibPath, Paths);
}
Generic_GCC::AddMultiarchPaths(D, SysRoot, OSLibDir, Paths);

// Similar to the logic for GCC above, if we are currently running Clang
// inside of the requested system root, add its parent library path to those
Expand Down Expand Up @@ -684,15 +582,7 @@ void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
// Lacking those, try to detect the correct set of system includes for the
// target triple.

// Add include directories specific to the selected multilib set and multilib.
if (GCCInstallation.isValid()) {
const auto &Callback = Multilibs.includeDirsCallback();
if (Callback) {
for (const auto &Path : Callback(GCCInstallation.getMultilib()))
addExternCSystemIncludeIfExists(
DriverArgs, CC1Args, GCCInstallation.getInstallPath() + Path);
}
}
AddMultilibIncludeArgs(DriverArgs, CC1Args);

// Implement generic Debian multiarch support.
const StringRef X86_64MultiarchIncludeDirs[] = {
Expand Down

0 comments on commit 6395980

Please sign in to comment.