Index: clang/lib/Driver/ToolChains/Gnu.cpp =================================================================== --- clang/lib/Driver/ToolChains/Gnu.cpp +++ clang/lib/Driver/ToolChains/Gnu.cpp @@ -2130,17 +2130,31 @@ // and gcc-toolsets. if (SysRoot.empty() && TargetTriple.getOS() == llvm::Triple::Linux && D.getVFS().exists("/opt/rh")) { - Prefixes.push_back("/opt/rh/gcc-toolset-11/root/usr"); - Prefixes.push_back("/opt/rh/gcc-toolset-10/root/usr"); - Prefixes.push_back("/opt/rh/devtoolset-11/root/usr"); - Prefixes.push_back("/opt/rh/devtoolset-10/root/usr"); - Prefixes.push_back("/opt/rh/devtoolset-9/root/usr"); - Prefixes.push_back("/opt/rh/devtoolset-8/root/usr"); - Prefixes.push_back("/opt/rh/devtoolset-7/root/usr"); - Prefixes.push_back("/opt/rh/devtoolset-6/root/usr"); - Prefixes.push_back("/opt/rh/devtoolset-4/root/usr"); - Prefixes.push_back("/opt/rh/devtoolset-3/root/usr"); - Prefixes.push_back("/opt/rh/devtoolset-2/root/usr"); + // Find the directory in /opt/rh/ starting with gcc-toolset-* or + // devtoolset-* with the highest version number and add that + // one to our prefixes. + std::string ChosenToolsetDir; + unsigned ChosenToolsetVersion = 0; + std::error_code EC; + for (llvm::vfs::directory_iterator LI = D.getVFS().dir_begin("/opt/rh", EC), + LE; + !EC && LI != LE; LI = LI.increment(EC)) { + StringRef ToolsetDir = llvm::sys::path::filename(LI->path()); + unsigned ToolsetVersion; + if ((!ToolsetDir.startswith("gcc-toolset-") && + !ToolsetDir.startswith("devtoolset-")) || + ToolsetDir.substr(ToolsetDir.rfind('-') + 1) + .getAsInteger(10, ToolsetVersion)) + continue; + + if (ToolsetVersion > ChosenToolsetVersion) { + ChosenToolsetVersion = ToolsetVersion; + ChosenToolsetDir = "/opt/rh/" + ToolsetDir.str(); + } + } + + if (ChosenToolsetVersion > 0) + Prefixes.push_back(ChosenToolsetDir); } // Fall back to /usr which is used by most non-Solaris systems. Index: clang/unittests/Driver/ToolChainTest.cpp =================================================================== --- clang/unittests/Driver/ToolChainTest.cpp +++ clang/unittests/Driver/ToolChainTest.cpp @@ -18,6 +18,7 @@ #include "clang/Driver/Driver.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/Host.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/raw_ostream.h" @@ -610,4 +611,94 @@ DiagConsumer->clear(); } +TEST(ToolChainTest, Toolsets) { + // Ignore this test on Windows hosts. + llvm::Triple Host(llvm::sys::getProcessTriple()); + if (Host.isOSWindows()) + GTEST_SKIP(); + + IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); + IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); + + // Check (newer) GCC toolset installation. + { + IntrusiveRefCntPtr InMemoryFileSystem( + new llvm::vfs::InMemoryFileSystem); + + // These should be ignored. + InMemoryFileSystem->addFile("/opt/rh/gcc-toolset-2", 0, + llvm::MemoryBuffer::getMemBuffer("\n")); + InMemoryFileSystem->addFile("/opt/rh/gcc-toolset-", 0, + llvm::MemoryBuffer::getMemBuffer("\n")); + InMemoryFileSystem->addFile("/opt/rh/gcc-toolset--", 0, + llvm::MemoryBuffer::getMemBuffer("\n")); + InMemoryFileSystem->addFile("/opt/rh/gcc-toolset--1", 0, + llvm::MemoryBuffer::getMemBuffer("\n")); + + // File needed for GCC installation detection. + InMemoryFileSystem->addFile( + "/opt/rh/gcc-toolset-12/lib/gcc/x86_64-redhat-linux/11/crtbegin.o", 0, + llvm::MemoryBuffer::getMemBuffer("\n")); + + DiagnosticsEngine Diags(DiagID, &*DiagOpts, new SimpleDiagnosticConsumer); + Driver TheDriver("/bin/clang", "x86_64-redhat-linux", Diags, + "clang LLVM compiler", InMemoryFileSystem); + std::unique_ptr C( + TheDriver.BuildCompilation({"--gcc-toolchain="})); + ASSERT_TRUE(C); + std::string S; + { + llvm::raw_string_ostream OS(S); + C->getDefaultToolChain().printVerboseInfo(OS); + } + EXPECT_EQ("Found candidate GCC installation: " + "/opt/rh/gcc-toolset-12/lib/gcc/x86_64-redhat-linux/11\n" + "Selected GCC installation: " + "/opt/rh/gcc-toolset-12/lib/gcc/x86_64-redhat-linux/11\n" + "Candidate multilib: .;@m64\n" + "Selected multilib: .;@m64\n", + S); + } + + // And older devtoolset. + { + IntrusiveRefCntPtr InMemoryFileSystem( + new llvm::vfs::InMemoryFileSystem); + + // These should be ignored. + InMemoryFileSystem->addFile("/opt/rh/devtoolset-2", 0, + llvm::MemoryBuffer::getMemBuffer("\n")); + InMemoryFileSystem->addFile("/opt/rh/devtoolset-", 0, + llvm::MemoryBuffer::getMemBuffer("\n")); + InMemoryFileSystem->addFile("/opt/rh/devtoolset--", 0, + llvm::MemoryBuffer::getMemBuffer("\n")); + InMemoryFileSystem->addFile("/opt/rh/devtoolset--1", 0, + llvm::MemoryBuffer::getMemBuffer("\n")); + + // File needed for GCC installation detection. + InMemoryFileSystem->addFile( + "/opt/rh/devtoolset-12/lib/gcc/x86_64-redhat-linux/11/crtbegin.o", 0, + llvm::MemoryBuffer::getMemBuffer("\n")); + + DiagnosticsEngine Diags(DiagID, &*DiagOpts, new SimpleDiagnosticConsumer); + Driver TheDriver("/bin/clang", "x86_64-redhat-linux", Diags, + "clang LLVM compiler", InMemoryFileSystem); + std::unique_ptr C( + TheDriver.BuildCompilation({"--gcc-toolchain="})); + ASSERT_TRUE(C); + std::string S; + { + llvm::raw_string_ostream OS(S); + C->getDefaultToolChain().printVerboseInfo(OS); + } + EXPECT_EQ("Found candidate GCC installation: " + "/opt/rh/devtoolset-12/lib/gcc/x86_64-redhat-linux/11\n" + "Selected GCC installation: " + "/opt/rh/devtoolset-12/lib/gcc/x86_64-redhat-linux/11\n" + "Candidate multilib: .;@m64\n" + "Selected multilib: .;@m64\n", + S); + } +} + } // end anonymous namespace.