//===- 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/Core/LLVM.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/Host.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" using namespace lld; namespace { enum class Flavor { invalid, gnu_ld, // -flavor gnu win_link, // -flavor link darwin_ld, // -flavor darwin core // -flavor core OR -core }; Flavor strToFlavor(StringRef str) { return llvm::StringSwitch(str) .Case("gnu", Flavor::gnu_ld) .Case("link", Flavor::win_link) .Case("darwin", Flavor::darwin_ld) .Case("core", Flavor::core) .Case("ld", Flavor::gnu_ld) // deprecated .Default(Flavor::invalid); } struct ProgramNameParts { StringRef _target; StringRef _flavor; }; 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; } Flavor selectFlavor(std::vector &args, raw_ostream &diag) { // -core as first arg is shorthand for -flavor core. if (args.size() > 1 && StringRef(args[1]) == "-core") { args.erase(args.begin() + 1); return Flavor::core; } // Handle -flavor as first arg. if (args.size() > 2 && StringRef(args[1]) == "-flavor") { Flavor flavor = strToFlavor(args[2]); args.erase(args.begin() + 1); args.erase(args.begin() + 1); if (flavor == Flavor::invalid) diag << "error: '" << args[2] << "' invalid value for -flavor.\n"; return flavor; } Flavor flavor = strToFlavor(parseProgramName(llvm::sys::path::stem(args[0]))._flavor); // If flavor still undetermined, then error out. if (flavor == Flavor::invalid) diag << "error: failed to determine driver flavor from program name" << " '" << args[0] << "'.\n" << "select a flavor with -flavor [gnu|darwin|link|core].\n"; return flavor; } } namespace lld { bool UniversalDriver::link(int argc, const char *argv[], raw_ostream &diagnostics) { // Convert argv[] C-array to vector. std::vector args(argv, argv + argc); // Determine flavor of link based on command name or -flavor argument. // Note: 'args' is modified to remove -flavor option. Flavor flavor = selectFlavor(args, diagnostics); // Switch to appropriate driver. switch (flavor) { case Flavor::gnu_ld: return GnuLdDriver::linkELF(args.size(), args.data(), diagnostics); case Flavor::darwin_ld: return DarwinLdDriver::linkMachO(args.size(), args.data(), diagnostics); case Flavor::win_link: return WinLinkDriver::linkPECOFF(args.size(), args.data(), diagnostics); case Flavor::core: return CoreDriver::link(args.size(), args.data(), diagnostics); case Flavor::invalid: return true; } llvm_unreachable("Unrecognised flavor"); } } // end namespace lld