Index: include/lld/Driver/Driver.h =================================================================== --- include/lld/Driver/Driver.h +++ include/lld/Driver/Driver.h @@ -51,18 +51,6 @@ Driver() = delete; }; -/// Driver for "universal" lld tool which can mimic any linker command line -/// parsing once it figures out which command line flavor to use. -class UniversalDriver : public Driver { -public: - /// Determine flavor and pass control to Driver for that flavor. - static bool link(llvm::MutableArrayRef args, - raw_ostream &diag = llvm::errs()); - -private: - UniversalDriver() = delete; -}; - /// Driver for darwin/ld64 'ld' command line options. class DarwinLdDriver : public Driver { public: Index: lib/Driver/CMakeLists.txt =================================================================== --- lib/Driver/CMakeLists.txt +++ lib/Driver/CMakeLists.txt @@ -1,5 +1,3 @@ -set(LLVM_TARGET_DEFINITIONS UniversalDriverOptions.td) -tablegen(LLVM UniversalDriverOptions.inc -gen-opt-parser-defs) set(LLVM_TARGET_DEFINITIONS CoreOptions.td) tablegen(LLVM CoreOptions.inc -gen-opt-parser-defs) set(LLVM_TARGET_DEFINITIONS DarwinLdOptions.td) @@ -10,7 +8,6 @@ CoreDriver.cpp DarwinLdDriver.cpp Driver.cpp - UniversalDriver.cpp ADDITIONAL_HEADER_DIRS ${LLD_INCLUDE_DIR}/lld/Driver Index: lib/Driver/UniversalDriver.cpp =================================================================== --- lib/Driver/UniversalDriver.cpp +++ /dev/null @@ -1,220 +0,0 @@ -//===- lib/Driver/UniversalDriver.cpp -------------------------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// -/// Driver for "universal" lld tool which can mimic any linker command line -/// parsing once it figures out which command line flavor to use. -/// -//===----------------------------------------------------------------------===// - -#include "lld/Driver/Driver.h" -#include "lld/Config/Version.h" -#include "lld/Core/LLVM.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/ADT/StringSwitch.h" -#include "llvm/Option/Arg.h" -#include "llvm/Option/Option.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Host.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/raw_ostream.h" - -using namespace lld; - -namespace { - -// Create enum with OPT_xxx values for each option in GnuLdOptions.td -enum { - OPT_INVALID = 0, -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELP, META) \ - OPT_##ID, -#include "UniversalDriverOptions.inc" -#undef OPTION -}; - -// Create prefix string literals used in GnuLdOptions.td -#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; -#include "UniversalDriverOptions.inc" -#undef PREFIX - -// Create table mapping all options defined in GnuLdOptions.td -static const llvm::opt::OptTable::Info infoTable[] = { -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELPTEXT, METAVAR) \ - { \ - PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \ - PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS \ - } \ - , -#include "UniversalDriverOptions.inc" -#undef OPTION -}; - -// Create OptTable class for parsing actual command line arguments -class UniversalDriverOptTable : public llvm::opt::OptTable { -public: - UniversalDriverOptTable() - : OptTable(infoTable) {} -}; - -enum class Flavor { - invalid, - gnu_ld, // -flavor gnu - win_link, // -flavor link - darwin_ld, // -flavor darwin - core // -flavor core OR -core -}; - -struct ProgramNameParts { - StringRef _target; - StringRef _flavor; -}; - -} // anonymous namespace - -static Flavor strToFlavor(StringRef str) { - return llvm::StringSwitch(str) - .Case("gnu", Flavor::gnu_ld) - .Case("ld.lld", Flavor::gnu_ld) - .Case("link", Flavor::win_link) - .Case("lld-link", Flavor::win_link) - .Case("darwin", Flavor::darwin_ld) - .Case("core", Flavor::core) - .Case("ld", Flavor::gnu_ld) - .Default(Flavor::invalid); -} - -static ProgramNameParts parseProgramName(StringRef programName) { - SmallVector components; - llvm::SplitString(programName, components, "-"); - ProgramNameParts ret; - - using std::begin; - using std::end; - - // Erase any lld components. - components.erase(std::remove(components.begin(), components.end(), "lld"), - components.end()); - - // Find the flavor component. - auto flIter = std::find_if(components.begin(), components.end(), - [](StringRef str) -> bool { - return strToFlavor(str) != Flavor::invalid; - }); - - if (flIter != components.end()) { - ret._flavor = *flIter; - components.erase(flIter); - } - - // Any remaining component must be the target. - if (components.size() == 1) - ret._target = components[0]; - - return ret; -} - -// Removes the argument from argv along with its value, if exists, and updates -// argc. -static void removeArg(llvm::opt::Arg *arg, - llvm::MutableArrayRef &args) { - unsigned int numToRemove = arg->getNumValues() + 1; - auto sub = args.slice(arg->getIndex() + 1); - std::rotate(sub.begin(), sub.begin() + numToRemove, sub.end()); - args = args.drop_back(numToRemove); -} - -static Flavor getFlavor(llvm::MutableArrayRef &args, - const llvm::opt::InputArgList &parsedArgs) { - if (llvm::opt::Arg *argCore = parsedArgs.getLastArg(OPT_core)) { - removeArg(argCore, args); - return Flavor::core; - } - if (llvm::opt::Arg *argFlavor = parsedArgs.getLastArg(OPT_flavor)) { - removeArg(argFlavor, args); - return strToFlavor(argFlavor->getValue()); - } - -#if LLVM_ON_UNIX - if (llvm::sys::path::filename(args[0]).equals("ld")) { -#if __APPLE__ - // On a Darwin systems, if linker binary is named "ld", use Darwin driver. - return Flavor::darwin_ld; -#endif - // On a ELF based systems, if linker binary is named "ld", use gnu driver. - return Flavor::gnu_ld; - } -#endif - - StringRef name = llvm::sys::path::filename(args[0]); - if (name.endswith_lower(".exe")) - name = llvm::sys::path::stem(name); - return strToFlavor(parseProgramName(name)._flavor); -} - -namespace lld { - -bool UniversalDriver::link(llvm::MutableArrayRef args, - raw_ostream &diagnostics) { - // Parse command line options using GnuLdOptions.td - UniversalDriverOptTable table; - unsigned missingIndex; - unsigned missingCount; - - // Program name - StringRef programName = llvm::sys::path::stem(args[0]); - - llvm::opt::InputArgList parsedArgs = - table.ParseArgs(args.slice(1), missingIndex, missingCount); - - if (missingCount) { - diagnostics << "error: missing arg value for '" - << parsedArgs.getArgString(missingIndex) << "' expected " - << missingCount << " argument(s).\n"; - return false; - } - - // Handle -help - if (parsedArgs.getLastArg(OPT_help)) { - table.PrintHelp(llvm::outs(), programName.data(), "LLVM Linker", false); - return true; - } - - // Handle -version - if (parsedArgs.getLastArg(OPT_version)) { - diagnostics << "LLVM Linker Version: " << getLLDVersion() - << getLLDRepositoryVersion() << "\n"; - return true; - } - - Flavor flavor = getFlavor(args, parsedArgs); - - // Switch to appropriate driver. - switch (flavor) { - case Flavor::gnu_ld: - return elf::link(args, diagnostics); - case Flavor::darwin_ld: - return DarwinLdDriver::linkMachO(args, diagnostics); - case Flavor::win_link: - coff::link(args); - return true; - case Flavor::core: - return CoreDriver::link(args, diagnostics); - case Flavor::invalid: - diagnostics << "Select the appropriate flavor\n"; - table.PrintHelp(llvm::outs(), programName.data(), "LLVM Linker", false); - return false; - } - llvm_unreachable("Unrecognised flavor"); -} - -} // end namespace lld Index: lib/Driver/UniversalDriverOptions.td =================================================================== --- lib/Driver/UniversalDriverOptions.td +++ /dev/null @@ -1,19 +0,0 @@ -include "llvm/Option/OptParser.td" - -// Select an optional flavor -def flavor: Separate<["-"], "flavor">, - HelpText<"Flavor for linking, options are gnu/darwin/link">; - -// Select the core flavor -def core : Flag<["-"], "core">, - HelpText<"CORE linking">; - -def target: Separate<["-"], "target">, - HelpText<"Select the target">; - -def version: Flag<["-"], "version">, - HelpText<"Display the version">; - -// Help message -def help : Flag<["-"], "help">, - HelpText<"Display this help message">; Index: tools/lld/lld.cpp =================================================================== --- tools/lld/lld.cpp +++ tools/lld/lld.cpp @@ -6,32 +6,129 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// -/// -/// \file -/// -/// This is the entry point to the lld driver. This is a thin wrapper which -/// dispatches to the given platform specific driver. -/// +// +// This is the entry point to the lld driver. This is a thin wrapper which +// dispatches to the given platform specific driver. +// +// If there is -flavor option or -core option, it is dispatched according +// to the arguments. If the flavor parameter is not present, then it is +// dispatched according to argv[0]. +// //===----------------------------------------------------------------------===// #include "lld/Core/LLVM.h" #include "lld/Driver/Driver.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Host.h" #include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" +#include "llvm/Support/raw_ostream.h" using namespace lld; +using namespace llvm; +using namespace llvm::sys; + +enum Flavor { + Invalid, + Gnu, // -flavor gnu + WinLink, // -flavor link + Darwin, // -flavor darwin + Core // -flavor core or -core +}; + +LLVM_ATTRIBUTE_NORETURN static void die(const Twine &S) { + errs() << S << "\n"; + exit(1); +} + +static Flavor getFlavor(StringRef S) { + return StringSwitch(S) + .Case("ld", Gnu) + .Case("ld.lld", Gnu) + .Case("gnu", Gnu) + .Case("link", WinLink) + .Case("lld-link", WinLink) + .Case("darwin", Darwin) + .Case("core", Core) + .Default(Invalid); +} + +static Flavor parseProgname(StringRef Progname) { +#if __APPLE__ + // Use Darwin driver for "ld" on Darwin. + if (Progname == "ld") + return Darwin; +#endif +#if LLVM_ON_UNIX + // Use GNU driver for "ld" on other Unix-like system. + if (Progname == "ld") + return Gnu; +#endif + + // Progname may be something like "lld-gnu". Parse it. + SmallVector V; + SplitString(Progname, V, "-"); + for (StringRef S : V) + if (Flavor F = getFlavor(S)) + return F; + return Invalid; +} + +static Flavor parseFlavor(std::vector &V) { + // If the first argument is -core, then core driver. + if (V.size() > 1 && V[1] == StringRef("-core")) { + V.erase(V.begin() + 1); + return Core; + } + + // Parse -flavor option. + if (V.size() > 1 && V[1] == StringRef("-flavor")) { + if (V.size() <= 2) + die("missing arg value for '-flavor'"); + Flavor F = getFlavor(V[2]); + if (F == Invalid) + die("Unknown flavor: " + StringRef(V[2])); + V.erase(V.begin() + 1, V.begin() + 3); + return F; + } + + // Deduct the flavor from argv0. + StringRef Arg0 = path::filename(V[0]); + if (Arg0.endswith_lower(".exe")) + Arg0 = Arg0.drop_back(4); + return parseProgname(Arg0); +} + +static bool dispatch(std::vector Args) { + switch (parseFlavor(Args)) { + case Gnu: + return elf::link(Args, errs()); + case WinLink: + coff::link(Args); + return true; + case Darwin: + return DarwinLdDriver::linkMachO(Args, errs()); + case Core: + return CoreDriver::link(Args, errs()); + default: + die("-flavor option is missing. Available flavors are " + "gnu, darwin or link."); + } +} /// Universal linker main(). This linker emulates the gnu, darwin, or /// windows linker based on the tool name or if the first argument is /// -flavor. -int main(int argc, const char *argv[]) { +int main(int Argc, const char **Argv) { // Standard set up, so program fails gracefully. - llvm::sys::PrintStackTraceOnErrorSignal(); - llvm::PrettyStackTraceProgram stackPrinter(argc, argv); - llvm::llvm_shutdown_obj shutdown; - - return !UniversalDriver::link( - llvm::MutableArrayRef(argv, argc)); + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram StackPrinter(Argc, Argv); + llvm_shutdown_obj Shutdown; + return !dispatch(std::vector(Argv, Argv + Argc)); } Index: unittests/DriverTests/CMakeLists.txt =================================================================== --- unittests/DriverTests/CMakeLists.txt +++ unittests/DriverTests/CMakeLists.txt @@ -1,5 +1,4 @@ add_lld_unittest(DriverTests - UniversalDriverTest.cpp DarwinLdDriverTest.cpp ) Index: unittests/DriverTests/DarwinLdDriverTest.cpp =================================================================== --- unittests/DriverTests/DarwinLdDriverTest.cpp +++ unittests/DriverTests/DarwinLdDriverTest.cpp @@ -254,12 +254,3 @@ EXPECT_FALSE(parse("ld", "-mark_dead_strippable_dylib", "a.o", "-arch", "i386", nullptr)); } - -TEST_F(DarwinLdParserTest, llvmOptions) { - EXPECT_TRUE(parse("ld", "-mllvm", "-enable-tbaa", "-mllvm", "-enable-misched", "a.o", - "-arch", "i386", nullptr)); - const std::vector &options = _ctx.llvmOptions(); - EXPECT_EQ(options.size(), 2UL); - EXPECT_EQ(strcmp(options[0],"-enable-tbaa"), 0); - EXPECT_EQ(strcmp(options[1],"-enable-misched"), 0); -} Index: unittests/DriverTests/UniversalDriverTest.cpp =================================================================== --- unittests/DriverTests/UniversalDriverTest.cpp +++ /dev/null @@ -1,32 +0,0 @@ -//===- lld/unittest/UniversalDriverTest.cpp -------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// \brief Universal driver tests that depend on the value of argv[0]. -/// -//===----------------------------------------------------------------------===// - -#include "gtest/gtest.h" -#include "lld/Driver/Driver.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/Support/raw_ostream.h" - -using namespace llvm; -using namespace lld; - -TEST(UniversalDriver, flavor) { - const char *args[] = {"ld", "-flavor", "gnu"}; - - std::string diags; - raw_string_ostream os(diags); - UniversalDriver::link(args, os); - EXPECT_EQ(os.str().find("failed to determine driver flavor"), - std::string::npos); - EXPECT_NE(os.str().find("no input files"), std::string::npos); -}