//===-- CommandLine.cpp - Command line parser implementation --------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This class implements a command line argument processor that is useful when // creating a tool. It provides a simple, minimalistic interface that is easily // extensible and supports nonlocal (library) command line options. // // Note that rather than trying to figure out what this code does, you could try // reading the library documentation located in docs/CommandLine.html // //===----------------------------------------------------------------------===// #include "llvm/Support/CommandLine.h" #include "DebugOptions.h" #include "llvm-c/Support.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLFunctionalExtras.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/Config/config.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" #include "llvm/Support/StringSaver.h" #include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/raw_ostream.h" #include #include #include using namespace llvm; using namespace cl; #define DEBUG_TYPE "commandline" //===----------------------------------------------------------------------===// // Template instantiations and anchors. // namespace llvm { namespace cl { template class basic_parser; template class basic_parser; template class basic_parser; template class basic_parser; template class basic_parser; template class basic_parser; template class basic_parser; template class basic_parser; template class basic_parser; template class basic_parser; template class basic_parser; template class basic_parser; template class opt; template class opt; template class opt; template class opt; template class opt; } // namespace cl } // namespace llvm // Pin the vtables to this file. void GenericOptionValue::anchor() {} void OptionValue::anchor() {} void OptionValue::anchor() {} void Option::anchor() {} void basic_parser_impl::anchor() {} void parser::anchor() {} void parser::anchor() {} void parser::anchor() {} void parser::anchor() {} void parser::anchor() {} void parser::anchor() {} void parser::anchor() {} void parser::anchor() {} void parser::anchor() {} void parser::anchor() {} void parser::anchor() {} void parser::anchor() {} //===----------------------------------------------------------------------===// const static size_t DefaultPad = 2; static StringRef ArgPrefix = "-"; static StringRef ArgPrefixLong = "--"; static StringRef ArgHelpPrefix = " - "; static size_t argPlusPrefixesSize(StringRef ArgName, size_t Pad = DefaultPad) { size_t Len = ArgName.size(); if (Len == 1) return Len + Pad + ArgPrefix.size() + ArgHelpPrefix.size(); return Len + Pad + ArgPrefixLong.size() + ArgHelpPrefix.size(); } static SmallString<8> argPrefix(StringRef ArgName, size_t Pad = DefaultPad) { SmallString<8> Prefix; for (size_t I = 0; I < Pad; ++I) { Prefix.push_back(' '); } Prefix.append(ArgName.size() > 1 ? ArgPrefixLong : ArgPrefix); return Prefix; } // Option predicates... static inline bool isGrouping(const Option *O) { return O->getMiscFlags() & cl::Grouping; } static inline bool isPrefixedOrGrouping(const Option *O) { return isGrouping(O) || O->getFormattingFlag() == cl::Prefix || O->getFormattingFlag() == cl::AlwaysPrefix; } namespace { class PrintArg { StringRef ArgName; size_t Pad; public: PrintArg(StringRef ArgName, size_t Pad = DefaultPad) : ArgName(ArgName), Pad(Pad) {} friend raw_ostream &operator<<(raw_ostream &OS, const PrintArg &); }; raw_ostream &operator<<(raw_ostream &OS, const PrintArg& Arg) { OS << argPrefix(Arg.ArgName, Arg.Pad) << Arg.ArgName; return OS; } class CommandLineParser { public: // Globals for name and overview of program. Program name is not a string to // avoid static ctor/dtor issues. std::string ProgramName; StringRef ProgramOverview; // This collects additional help to be printed. std::vector MoreHelp; // This collects Options added with the cl::DefaultOption flag. Since they can // be overridden, they are not added to the appropriate SubCommands until // ParseCommandLineOptions actually runs. SmallVector DefaultOptions; // This collects the different option categories that have been registered. SmallPtrSet RegisteredOptionCategories; // This collects the different subcommands that have been registered. SmallPtrSet RegisteredSubCommands; CommandLineParser() { registerSubCommand(&SubCommand::getTopLevel()); registerSubCommand(&SubCommand::getAll()); } void ResetAllOptionOccurrences(); bool ParseCommandLineOptions(int argc, const char *const *argv, StringRef Overview, raw_ostream *Errs = nullptr, bool LongOptionsUseDoubleDash = false); void addLiteralOption(Option &Opt, SubCommand *SC, StringRef Name) { if (Opt.hasArgStr()) return; if (!SC->OptionsMap.insert(std::make_pair(Name, &Opt)).second) { errs() << ProgramName << ": CommandLine Error: Option '" << Name << "' registered more than once!\n"; report_fatal_error("inconsistency in registered CommandLine options"); } // If we're adding this to all sub-commands, add it to the ones that have // already been registered. if (SC == &SubCommand::getAll()) { for (auto *Sub : RegisteredSubCommands) { if (SC == Sub) continue; addLiteralOption(Opt, Sub, Name); } } } void addLiteralOption(Option &Opt, StringRef Name) { if (Opt.Subs.empty()) addLiteralOption(Opt, &SubCommand::getTopLevel(), Name); else { for (auto *SC : Opt.Subs) addLiteralOption(Opt, SC, Name); } } void addOption(Option *O, SubCommand *SC) { bool HadErrors = false; if (O->hasArgStr()) { // If it's a DefaultOption, check to make sure it isn't already there. if (O->isDefaultOption() && SC->OptionsMap.find(O->ArgStr) != SC->OptionsMap.end()) return; // Add argument to the argument map! if (!SC->OptionsMap.insert(std::make_pair(O->ArgStr, O)).second) { errs() << ProgramName << ": CommandLine Error: Option '" << O->ArgStr << "' registered more than once!\n"; HadErrors = true; } } // Remember information about positional options. if (O->getFormattingFlag() == cl::Positional) SC->PositionalOpts.push_back(O); else if (O->getMiscFlags() & cl::Sink) // Remember sink options SC->SinkOpts.push_back(O); else if (O->getNumOccurrencesFlag() == cl::ConsumeAfter) { if (SC->ConsumeAfterOpt) { O->error("Cannot specify more than one option with cl::ConsumeAfter!"); HadErrors = true; } SC->ConsumeAfterOpt = O; } // Fail hard if there were errors. These are strictly unrecoverable and // indicate serious issues such as conflicting option names or an // incorrectly // linked LLVM distribution. if (HadErrors) report_fatal_error("inconsistency in registered CommandLine options"); // If we're adding this to all sub-commands, add it to the ones that have // already been registered. if (SC == &SubCommand::getAll()) { for (auto *Sub : RegisteredSubCommands) { if (SC == Sub) continue; addOption(O, Sub); } } } void addOption(Option *O, bool ProcessDefaultOption = false) { if (!ProcessDefaultOption && O->isDefaultOption()) { DefaultOptions.push_back(O); return; } if (O->Subs.empty()) { addOption(O, &SubCommand::getTopLevel()); } else { for (auto *SC : O->Subs) addOption(O, SC); } } void removeOption(Option *O, SubCommand *SC) { SmallVector OptionNames; O->getExtraOptionNames(OptionNames); if (O->hasArgStr()) OptionNames.push_back(O->ArgStr); SubCommand &Sub = *SC; auto End = Sub.OptionsMap.end(); for (auto Name : OptionNames) { auto I = Sub.OptionsMap.find(Name); if (I != End && I->getValue() == O) Sub.OptionsMap.erase(I); } if (O->getFormattingFlag() == cl::Positional) for (auto *Opt = Sub.PositionalOpts.begin(); Opt != Sub.PositionalOpts.end(); ++Opt) { if (*Opt == O) { Sub.PositionalOpts.erase(Opt); break; } } else if (O->getMiscFlags() & cl::Sink) for (auto *Opt = Sub.SinkOpts.begin(); Opt != Sub.SinkOpts.end(); ++Opt) { if (*Opt == O) { Sub.SinkOpts.erase(Opt); break; } } else if (O == Sub.ConsumeAfterOpt) Sub.ConsumeAfterOpt = nullptr; } void removeOption(Option *O) { if (O->Subs.empty()) removeOption(O, &SubCommand::getTopLevel()); else { if (O->isInAllSubCommands()) { for (auto *SC : RegisteredSubCommands) removeOption(O, SC); } else { for (auto *SC : O->Subs) removeOption(O, SC); } } } bool hasOptions(const SubCommand &Sub) const { return (!Sub.OptionsMap.empty() || !Sub.PositionalOpts.empty() || nullptr != Sub.ConsumeAfterOpt); } bool hasOptions() const { for (const auto *S : RegisteredSubCommands) { if (hasOptions(*S)) return true; } return false; } SubCommand *getActiveSubCommand() { return ActiveSubCommand; } void updateArgStr(Option *O, StringRef NewName, SubCommand *SC) { SubCommand &Sub = *SC; if (!Sub.OptionsMap.insert(std::make_pair(NewName, O)).second) { errs() << ProgramName << ": CommandLine Error: Option '" << O->ArgStr << "' registered more than once!\n"; report_fatal_error("inconsistency in registered CommandLine options"); } Sub.OptionsMap.erase(O->ArgStr); } void updateArgStr(Option *O, StringRef NewName) { if (O->Subs.empty()) updateArgStr(O, NewName, &SubCommand::getTopLevel()); else { if (O->isInAllSubCommands()) { for (auto *SC : RegisteredSubCommands) updateArgStr(O, NewName, SC); } else { for (auto *SC : O->Subs) updateArgStr(O, NewName, SC); } } } void printOptionValues(); void registerCategory(OptionCategory *cat) { assert(count_if(RegisteredOptionCategories, [cat](const OptionCategory *Category) { return cat->getName() == Category->getName(); }) == 0 && "Duplicate option categories"); RegisteredOptionCategories.insert(cat); } void registerSubCommand(SubCommand *sub) { assert(count_if(RegisteredSubCommands, [sub](const SubCommand *Sub) { return (!sub->getName().empty()) && (Sub->getName() == sub->getName()); }) == 0 && "Duplicate subcommands"); RegisteredSubCommands.insert(sub); // For all options that have been registered for all subcommands, add the // option to this subcommand now. if (sub != &SubCommand::getAll()) { for (auto &E : SubCommand::getAll().OptionsMap) { Option *O = E.second; if ((O->isPositional() || O->isSink() || O->isConsumeAfter()) || O->hasArgStr()) addOption(O, sub); else addLiteralOption(*O, sub, E.first()); } } } void unregisterSubCommand(SubCommand *sub) { RegisteredSubCommands.erase(sub); } iterator_range::iterator> getRegisteredSubcommands() { return make_range(RegisteredSubCommands.begin(), RegisteredSubCommands.end()); } void reset() { ActiveSubCommand = nullptr; ProgramName.clear(); ProgramOverview = StringRef(); MoreHelp.clear(); RegisteredOptionCategories.clear(); ResetAllOptionOccurrences(); RegisteredSubCommands.clear(); SubCommand::getTopLevel().reset(); SubCommand::getAll().reset(); registerSubCommand(&SubCommand::getTopLevel()); registerSubCommand(&SubCommand::getAll()); DefaultOptions.clear(); } private: SubCommand *ActiveSubCommand = nullptr; Option *LookupOption(SubCommand &Sub, StringRef &Arg, StringRef &Value); Option *LookupLongOption(SubCommand &Sub, StringRef &Arg, StringRef &Value, bool LongOptionsUseDoubleDash, bool HaveDoubleDash) { Option *Opt = LookupOption(Sub, Arg, Value); if (Opt && LongOptionsUseDoubleDash && !HaveDoubleDash && !isGrouping(Opt)) return nullptr; return Opt; } SubCommand *LookupSubCommand(StringRef Name); }; } // namespace static ManagedStatic GlobalParser; void cl::AddLiteralOption(Option &O, StringRef Name) { GlobalParser->addLiteralOption(O, Name); } extrahelp::extrahelp(StringRef Help) : morehelp(Help) { GlobalParser->MoreHelp.push_back(Help); } void Option::addArgument() { GlobalParser->addOption(this); FullyInitialized = true; } void Option::removeArgument() { GlobalParser->removeOption(this); } void Option::setArgStr(StringRef S) { if (FullyInitialized) GlobalParser->updateArgStr(this, S); assert((S.empty() || S[0] != '-') && "Option can't start with '-"); ArgStr = S; if (ArgStr.size() == 1) setMiscFlag(Grouping); } void Option::addCategory(OptionCategory &C) { assert(!Categories.empty() && "Categories cannot be empty."); // Maintain backward compatibility by replacing the default GeneralCategory // if it's still set. Otherwise, just add the new one. The GeneralCategory // must be explicitly added if you want multiple categories that include it. if (&C != &getGeneralCategory() && Categories[0] == &getGeneralCategory()) Categories[0] = &C; else if (!is_contained(Categories, &C)) Categories.push_back(&C); } void Option::reset() { NumOccurrences = 0; setDefault(); if (isDefaultOption()) removeArgument(); } void OptionCategory::registerCategory() { GlobalParser->registerCategory(this); } // A special subcommand representing no subcommand. It is particularly important // that this ManagedStatic uses constant initailization and not dynamic // initialization because it is referenced from cl::opt constructors, which run // dynamically in an arbitrary order. LLVM_REQUIRE_CONSTANT_INITIALIZATION ManagedStatic llvm::cl::TopLevelSubCommand; // A special subcommand that can be used to put an option into all subcommands. ManagedStatic llvm::cl::AllSubCommands; SubCommand &SubCommand::getTopLevel() { return *TopLevelSubCommand; } SubCommand &SubCommand::getAll() { return *AllSubCommands; } void SubCommand::registerSubCommand() { GlobalParser->registerSubCommand(this); } void SubCommand::unregisterSubCommand() { GlobalParser->unregisterSubCommand(this); } void SubCommand::reset() { PositionalOpts.clear(); SinkOpts.clear(); OptionsMap.clear(); ConsumeAfterOpt = nullptr; } SubCommand::operator bool() const { return (GlobalParser->getActiveSubCommand() == this); } //===----------------------------------------------------------------------===// // Basic, shared command line option processing machinery. // /// LookupOption - Lookup the option specified by the specified option on the /// command line. If there is a value specified (after an equal sign) return /// that as well. This assumes that leading dashes have already been stripped. Option *CommandLineParser::LookupOption(SubCommand &Sub, StringRef &Arg, StringRef &Value) { // Reject all dashes. if (Arg.empty()) return nullptr; assert(&Sub != &SubCommand::getAll()); size_t EqualPos = Arg.find('='); // If we have an equals sign, remember the value. if (EqualPos == StringRef::npos) { // Look up the option. return Sub.OptionsMap.lookup(Arg); } // If the argument before the = is a valid option name and the option allows // non-prefix form (ie is not AlwaysPrefix), we match. If not, signal match // failure by returning nullptr. auto I = Sub.OptionsMap.find(Arg.substr(0, EqualPos)); if (I == Sub.OptionsMap.end()) return nullptr; auto *O = I->second; if (O->getFormattingFlag() == cl::AlwaysPrefix) return nullptr; Value = Arg.substr(EqualPos + 1); Arg = Arg.substr(0, EqualPos); return I->second; } SubCommand *CommandLineParser::LookupSubCommand(StringRef Name) { if (Name.empty()) return &SubCommand::getTopLevel(); for (auto *S : RegisteredSubCommands) { if (S == &SubCommand::getAll()) continue; if (S->getName().empty()) continue; if (StringRef(S->getName()) == StringRef(Name)) return S; } return &SubCommand::getTopLevel(); } /// LookupNearestOption - Lookup the closest match to the option specified by /// the specified option on the command line. If there is a value specified /// (after an equal sign) return that as well. This assumes that leading dashes /// have already been stripped. static Option *LookupNearestOption(StringRef Arg, const StringMap