From 918910d6e53db4b620ae1167dc59e83f3031750e Mon Sep 17 00:00:00 2001 From: Serge Pavlov Date: Tue, 29 Aug 2017 05:22:26 +0000 Subject: [PATCH] Use class to pass information about executable name Information about clang executable name components, such as target and driver mode, was passes in std::pair. With this change it is passed in a special structure. It improves readability and makes access to this information more convenient. NFC. Differential Revision: https://reviews.llvm.org/D36057 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@311981 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Driver/Driver.h | 5 ++ include/clang/Driver/ToolChain.h | 33 ++++++++-- lib/Driver/Driver.cpp | 5 +- lib/Driver/ToolChain.cpp | 41 +++++++------ lib/Tooling/Tooling.cpp | 9 +-- tools/driver/driver.cpp | 30 ++++------ unittests/Driver/CMakeLists.txt | 1 + unittests/Driver/ToolChainTest.cpp | 96 ++++++++++++++++++++++++++++++ 8 files changed, 172 insertions(+), 48 deletions(-) diff --git a/include/clang/Driver/Driver.h b/include/clang/Driver/Driver.h index 86b785ef53..e81fe4635f 100644 --- a/include/clang/Driver/Driver.h +++ b/include/clang/Driver/Driver.h @@ -129,6 +129,9 @@ public: /// The original path to the clang executable. std::string ClangExecutable; + /// Target and driver mode components extracted from clang executable name. + ParsedClangName ClangNameParts; + /// The path to the installed clang directory, if any. std::string InstalledDir; @@ -284,6 +287,8 @@ public: void setCheckInputsExist(bool Value) { CheckInputsExist = Value; } + void setTargetAndMode(const ParsedClangName &TM) { ClangNameParts = TM; } + const std::string &getTitle() { return DriverTitle; } void setTitle(std::string Value) { DriverTitle = std::move(Value); } diff --git a/include/clang/Driver/ToolChain.h b/include/clang/Driver/ToolChain.h index e354acb6d0..ff08911813 100644 --- a/include/clang/Driver/ToolChain.h +++ b/include/clang/Driver/ToolChain.h @@ -46,6 +46,28 @@ namespace driver { class Tool; class XRayArgs; +/// Helper structure used to pass information extracted from clang executable +/// name such as `i686-linux-android-g++`. +/// +struct ParsedClangName { + /// Target part of the executable name, as `i686-linux-android`. + std::string TargetPrefix; + /// Driver mode part of the executable name, as `g++`. + std::string ModeSuffix; + /// Corresponding driver mode argument, as '--driver-mode=g++' + const char *DriverMode; + /// True if TargetPrefix is recognized as a registered target name. + bool TargetIsValid; + + ParsedClangName() : DriverMode(nullptr), TargetIsValid(false) {} + ParsedClangName(std::string Suffix, const char *Mode) + : ModeSuffix(Suffix), DriverMode(Mode), TargetIsValid(false) {} + ParsedClangName(std::string Target, std::string Suffix, const char *Mode, + bool IsRegistered) + : TargetPrefix(Target), ModeSuffix(Suffix), DriverMode(Mode), + TargetIsValid(IsRegistered) {} +}; + /// ToolChain - Access to tools for a single platform. class ToolChain { public: @@ -193,13 +215,16 @@ public: /// For example, when called with i686-linux-android-g++, the first element /// of the return value will be set to `"i686-linux-android"` and the second /// will be set to "--driver-mode=g++"`. + /// It is OK if the target name is not registered. In this case the return + /// value contains false in the field TargetIsValid. /// /// \pre `llvm::InitializeAllTargets()` has been called. /// \param ProgName The name the Clang driver was invoked with (from, - /// e.g., argv[0]) - /// \return A pair of (`target`, `mode-flag`), where one or both may be empty. - static std::pair - getTargetAndModeFromProgramName(StringRef ProgName); + /// e.g., argv[0]). + /// \return A structure of type ParsedClangName that contains the executable + /// name parts. + /// + static ParsedClangName getTargetAndModeFromProgramName(StringRef ProgName); // Tool access. diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 6b275c52d0..5340d8abe1 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -119,9 +119,8 @@ Driver::Driver(StringRef ClangExecutable, StringRef DefaultTargetTriple, void Driver::ParseDriverMode(StringRef ProgramName, ArrayRef Args) { - auto Default = ToolChain::getTargetAndModeFromProgramName(ProgramName); - StringRef DefaultMode(Default.second); - setDriverModeFromOption(DefaultMode); + ClangNameParts = ToolChain::getTargetAndModeFromProgramName(ProgramName); + setDriverModeFromOption(ClangNameParts.DriverMode); for (const char *ArgPtr : Args) { // Ingore nullptrs, they are response file's EOL markers diff --git a/lib/Driver/ToolChain.cpp b/lib/Driver/ToolChain.cpp index 2660c5a2c0..384481b3ea 100644 --- a/lib/Driver/ToolChain.cpp +++ b/lib/Driver/ToolChain.cpp @@ -113,7 +113,7 @@ struct DriverSuffix { const char *ModeFlag; }; -const DriverSuffix *FindDriverSuffix(StringRef ProgName) { +const DriverSuffix *FindDriverSuffix(StringRef ProgName, size_t &Pos) { // A list of known driver suffixes. Suffixes are compared against the // program name in order. If there is a match, the frontend type is updated as // necessary by applying the ModeFlag. @@ -132,9 +132,13 @@ const DriverSuffix *FindDriverSuffix(StringRef ProgName) { {"++", "--driver-mode=g++"}, }; - for (size_t i = 0; i < llvm::array_lengthof(DriverSuffixes); ++i) - if (ProgName.endswith(DriverSuffixes[i].Suffix)) + for (size_t i = 0; i < llvm::array_lengthof(DriverSuffixes); ++i) { + StringRef Suffix(DriverSuffixes[i].Suffix); + if (ProgName.endswith(Suffix)) { + Pos = ProgName.size() - Suffix.size(); return &DriverSuffixes[i]; + } + } return nullptr; } @@ -149,7 +153,7 @@ std::string normalizeProgramName(llvm::StringRef Argv0) { return ProgName; } -const DriverSuffix *parseDriverSuffix(StringRef ProgName) { +const DriverSuffix *parseDriverSuffix(StringRef ProgName, size_t &Pos) { // Try to infer frontend type and default target from the program name by // comparing it against DriverSuffixes in order. @@ -157,47 +161,46 @@ const DriverSuffix *parseDriverSuffix(StringRef ProgName) { // E.g. "x86_64-linux-clang" as interpreted as suffix "clang" with target // prefix "x86_64-linux". If such a target prefix is found, it may be // added via -target as implicit first argument. - const DriverSuffix *DS = FindDriverSuffix(ProgName); + const DriverSuffix *DS = FindDriverSuffix(ProgName, Pos); if (!DS) { // Try again after stripping any trailing version number: // clang++3.5 -> clang++ ProgName = ProgName.rtrim("0123456789."); - DS = FindDriverSuffix(ProgName); + DS = FindDriverSuffix(ProgName, Pos); } if (!DS) { // Try again after stripping trailing -component. // clang++-tot -> clang++ ProgName = ProgName.slice(0, ProgName.rfind('-')); - DS = FindDriverSuffix(ProgName); + DS = FindDriverSuffix(ProgName, Pos); } return DS; } } // anonymous namespace -std::pair +ParsedClangName ToolChain::getTargetAndModeFromProgramName(StringRef PN) { std::string ProgName = normalizeProgramName(PN); - const DriverSuffix *DS = parseDriverSuffix(ProgName); + size_t SuffixPos; + const DriverSuffix *DS = parseDriverSuffix(ProgName, SuffixPos); if (!DS) - return std::make_pair("", ""); - std::string ModeFlag = DS->ModeFlag == nullptr ? "" : DS->ModeFlag; + return ParsedClangName(); + size_t SuffixEnd = SuffixPos + strlen(DS->Suffix); - std::string::size_type LastComponent = - ProgName.rfind('-', ProgName.size() - strlen(DS->Suffix)); + size_t LastComponent = ProgName.rfind('-', SuffixPos); if (LastComponent == std::string::npos) - return std::make_pair("", ModeFlag); + return ParsedClangName(ProgName.substr(0, SuffixEnd), DS->ModeFlag); + std::string ModeSuffix = ProgName.substr(LastComponent + 1, + SuffixEnd - LastComponent - 1); // Infer target from the prefix. StringRef Prefix(ProgName); Prefix = Prefix.slice(0, LastComponent); std::string IgnoredError; - std::string Target; - if (llvm::TargetRegistry::lookupTarget(Prefix, IgnoredError)) { - Target = Prefix; - } - return std::make_pair(Target, ModeFlag); + bool IsRegistered = llvm::TargetRegistry::lookupTarget(Prefix, IgnoredError); + return ParsedClangName{Prefix, ModeSuffix, DS->ModeFlag, IsRegistered}; } StringRef ToolChain::getDefaultUniversalArchName() const { diff --git a/lib/Tooling/Tooling.cpp b/lib/Tooling/Tooling.cpp index 662f02dca2..df9d7df694 100644 --- a/lib/Tooling/Tooling.cpp +++ b/lib/Tooling/Tooling.cpp @@ -190,11 +190,12 @@ void addTargetAndModeForProgramName(std::vector &CommandLine, } auto TargetMode = clang::driver::ToolChain::getTargetAndModeFromProgramName(InvokedAs); - if (!AlreadyHasMode && !TargetMode.second.empty()) { - CommandLine.insert(++CommandLine.begin(), TargetMode.second); + if (!AlreadyHasMode && TargetMode.DriverMode) { + CommandLine.insert(++CommandLine.begin(), TargetMode.DriverMode); } - if (!AlreadyHasTarget && !TargetMode.first.empty()) { - CommandLine.insert(++CommandLine.begin(), {"-target", TargetMode.first}); + if (!AlreadyHasTarget && TargetMode.TargetIsValid) { + CommandLine.insert(++CommandLine.begin(), {"-target", + TargetMode.TargetPrefix}); } } } diff --git a/tools/driver/driver.cpp b/tools/driver/driver.cpp index 9f37c428ff..e1461f112b 100644 --- a/tools/driver/driver.cpp +++ b/tools/driver/driver.cpp @@ -206,23 +206,19 @@ extern int cc1_main(ArrayRef Argv, const char *Argv0, extern int cc1as_main(ArrayRef Argv, const char *Argv0, void *MainAddr); -static void insertTargetAndModeArgs(StringRef Target, StringRef Mode, +static void insertTargetAndModeArgs(const ParsedClangName &NameParts, SmallVectorImpl &ArgVector, std::set &SavedStrings) { - if (!Mode.empty()) { + if (NameParts.DriverMode) { // Add the mode flag to the arguments. - auto it = ArgVector.begin(); - if (it != ArgVector.end()) - ++it; - ArgVector.insert(it, GetStableCStr(SavedStrings, Mode)); + ArgVector.insert(ArgVector.end(), + GetStableCStr(SavedStrings, NameParts.DriverMode)); } - if (!Target.empty()) { - auto it = ArgVector.begin(); - if (it != ArgVector.end()) - ++it; - const char *arr[] = {"-target", GetStableCStr(SavedStrings, Target)}; - ArgVector.insert(it, std::begin(arr), std::end(arr)); + if (NameParts.TargetIsValid) { + const char *arr[] = {"-target", GetStableCStr(SavedStrings, + NameParts.TargetPrefix)}; + ArgVector.insert(ArgVector.end(), std::begin(arr), std::end(arr)); } } @@ -330,9 +326,7 @@ int main(int argc_, const char **argv_) { } llvm::InitializeAllTargets(); - std::string ProgName = argv[0]; - std::pair TargetAndMode = - ToolChain::getTargetAndModeFromProgramName(ProgName); + auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(argv[0]); llvm::BumpPtrAllocator A; llvm::StringSaver Saver(A); @@ -345,7 +339,7 @@ int main(int argc_, const char **argv_) { // Finally, our -cc1 tools don't care which tokenization mode we use because // response files written by clang will tokenize the same way in either mode. bool ClangCLMode = false; - if (TargetAndMode.second == "--driver-mode=cl" || + if (StringRef(TargetAndMode.DriverMode).equals("--driver-mode=cl") || std::find_if(argv.begin(), argv.end(), [](const char *F) { return F && strcmp(F, "--driver-mode=cl") == 0; }) != argv.end()) { @@ -454,9 +448,9 @@ int main(int argc_, const char **argv_) { Driver TheDriver(Path, llvm::sys::getDefaultTargetTriple(), Diags); SetInstallDir(argv, TheDriver, CanonicalPrefixes); + TheDriver.setTargetAndMode(TargetAndMode); - insertTargetAndModeArgs(TargetAndMode.first, TargetAndMode.second, argv, - SavedStrings); + insertTargetAndModeArgs(TargetAndMode, argv, SavedStrings); SetBackdoorDriverOutputsFromEnvVars(TheDriver); diff --git a/unittests/Driver/CMakeLists.txt b/unittests/Driver/CMakeLists.txt index a4f75d26f6..2a3f41d63b 100644 --- a/unittests/Driver/CMakeLists.txt +++ b/unittests/Driver/CMakeLists.txt @@ -1,4 +1,5 @@ set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} Support Option ) diff --git a/unittests/Driver/ToolChainTest.cpp b/unittests/Driver/ToolChainTest.cpp index ec50560b20..93cf12b3c2 100644 --- a/unittests/Driver/ToolChainTest.cpp +++ b/unittests/Driver/ToolChainTest.cpp @@ -18,6 +18,8 @@ #include "clang/Basic/VirtualFileSystem.h" #include "clang/Driver/Compilation.h" #include "clang/Driver/Driver.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" #include "llvm/Support/raw_ostream.h" #include "gtest/gtest.h" using namespace clang; @@ -164,4 +166,98 @@ TEST(ToolChainTest, InvalidArgument) { EXPECT_TRUE(C->containsError()); } +TEST(ToolChainTest, ParsedClangName) { + ParsedClangName Empty; + EXPECT_TRUE(Empty.TargetPrefix.empty()); + EXPECT_TRUE(Empty.ModeSuffix.empty()); + EXPECT_TRUE(Empty.DriverMode == nullptr); + EXPECT_FALSE(Empty.TargetIsValid); + + ParsedClangName DriverOnly("clang", nullptr); + EXPECT_TRUE(DriverOnly.TargetPrefix.empty()); + EXPECT_TRUE(DriverOnly.ModeSuffix == "clang"); + EXPECT_TRUE(DriverOnly.DriverMode == nullptr); + EXPECT_FALSE(DriverOnly.TargetIsValid); + + ParsedClangName DriverOnly2("clang++", "--driver-mode=g++"); + EXPECT_TRUE(DriverOnly2.TargetPrefix.empty()); + EXPECT_TRUE(DriverOnly2.ModeSuffix == "clang++"); + EXPECT_STREQ(DriverOnly2.DriverMode, "--driver-mode=g++"); + EXPECT_FALSE(DriverOnly2.TargetIsValid); + + ParsedClangName TargetAndMode("i386", "clang-g++", "--driver-mode=g++", true); + EXPECT_TRUE(TargetAndMode.TargetPrefix == "i386"); + EXPECT_TRUE(TargetAndMode.ModeSuffix == "clang-g++"); + EXPECT_STREQ(TargetAndMode.DriverMode, "--driver-mode=g++"); + EXPECT_TRUE(TargetAndMode.TargetIsValid); +} + +TEST(ToolChainTest, GetTargetAndMode) { + llvm::InitializeAllTargets(); + std::string IgnoredError; + if (!llvm::TargetRegistry::lookupTarget("x86_64", IgnoredError)) + return; + + ParsedClangName Res = ToolChain::getTargetAndModeFromProgramName("clang"); + EXPECT_TRUE(Res.TargetPrefix.empty()); + EXPECT_TRUE(Res.ModeSuffix == "clang"); + EXPECT_TRUE(Res.DriverMode == nullptr); + EXPECT_FALSE(Res.TargetIsValid); + + Res = ToolChain::getTargetAndModeFromProgramName("clang++"); + EXPECT_TRUE(Res.TargetPrefix.empty()); + EXPECT_TRUE(Res.ModeSuffix == "clang++"); + EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++"); + EXPECT_FALSE(Res.TargetIsValid); + + Res = ToolChain::getTargetAndModeFromProgramName("clang++6.0"); + EXPECT_TRUE(Res.TargetPrefix.empty()); + EXPECT_TRUE(Res.ModeSuffix == "clang++"); + EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++"); + EXPECT_FALSE(Res.TargetIsValid); + + Res = ToolChain::getTargetAndModeFromProgramName("clang++-release"); + EXPECT_TRUE(Res.TargetPrefix.empty()); + EXPECT_TRUE(Res.ModeSuffix == "clang++"); + EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++"); + EXPECT_FALSE(Res.TargetIsValid); + + Res = ToolChain::getTargetAndModeFromProgramName("x86_64-clang++"); + EXPECT_TRUE(Res.TargetPrefix == "x86_64"); + EXPECT_TRUE(Res.ModeSuffix == "clang++"); + EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++"); + EXPECT_TRUE(Res.TargetIsValid); + + Res = ToolChain::getTargetAndModeFromProgramName( + "x86_64-linux-gnu-clang-c++"); + EXPECT_TRUE(Res.TargetPrefix == "x86_64-linux-gnu"); + EXPECT_TRUE(Res.ModeSuffix == "clang-c++"); + EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++"); + EXPECT_TRUE(Res.TargetIsValid); + + Res = ToolChain::getTargetAndModeFromProgramName( + "x86_64-linux-gnu-clang-c++-tot"); + EXPECT_TRUE(Res.TargetPrefix == "x86_64-linux-gnu"); + EXPECT_TRUE(Res.ModeSuffix == "clang-c++"); + EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++"); + EXPECT_TRUE(Res.TargetIsValid); + + Res = ToolChain::getTargetAndModeFromProgramName("qqq"); + EXPECT_TRUE(Res.TargetPrefix.empty()); + EXPECT_TRUE(Res.ModeSuffix.empty()); + EXPECT_TRUE(Res.DriverMode == nullptr); + EXPECT_FALSE(Res.TargetIsValid); + + Res = ToolChain::getTargetAndModeFromProgramName("x86_64-qqq"); + EXPECT_TRUE(Res.TargetPrefix.empty()); + EXPECT_TRUE(Res.ModeSuffix.empty()); + EXPECT_TRUE(Res.DriverMode == nullptr); + EXPECT_FALSE(Res.TargetIsValid); + + Res = ToolChain::getTargetAndModeFromProgramName("qqq-clang-cl"); + EXPECT_TRUE(Res.TargetPrefix == "qqq"); + EXPECT_TRUE(Res.ModeSuffix == "clang-cl"); + EXPECT_STREQ(Res.DriverMode, "--driver-mode=cl"); + EXPECT_FALSE(Res.TargetIsValid); +} } // end anonymous namespace. -- 2.40.0