//===-- 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 "llvm-c/Support.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/Triple.h" #include "llvm/ADT/Twine.h" #include "llvm/Config/config.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Host.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/raw_ostream.h" #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 opt; template class opt; template class opt; template class opt; template class opt; } } // end namespace llvm::cl // 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() {} //===----------------------------------------------------------------------===// namespace { 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 the different option categories that have been registered. SmallPtrSet RegisteredOptionCategories; // This collects the different subcommands that have been registered. SmallPtrSet RegisteredSubCommands; CommandLineParser() : ActiveSubCommand(nullptr) { registerSubCommand(&*TopLevelSubCommand); registerSubCommand(&*AllSubCommands); } void ResetAllOptionOccurrences(); bool ParseCommandLineOptions(int argc, const char *const *argv, StringRef Overview, raw_ostream *Errs = nullptr); 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 == &*AllSubCommands) { for (const auto &Sub : RegisteredSubCommands) { if (SC == Sub) continue; addLiteralOption(Opt, Sub, Name); } } } void addLiteralOption(Option &Opt, StringRef Name) { if (Opt.Subs.empty()) addLiteralOption(Opt, &*TopLevelSubCommand, Name); else { for (auto SC : Opt.Subs) addLiteralOption(Opt, SC, Name); } } void addOption(Option *O, SubCommand *SC) { bool HadErrors = false; if (O->hasArgStr()) { // 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 == &*AllSubCommands) { for (const auto &Sub : RegisteredSubCommands) { if (SC == Sub) continue; addOption(O, Sub); } } } void addOption(Option *O) { if (O->Subs.empty()) { addOption(O, &*TopLevelSubCommand); } 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; for (auto Name : OptionNames) Sub.OptionsMap.erase(Name); 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, &*TopLevelSubCommand); 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, &*TopLevelSubCommand); 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 != &*AllSubCommands) { for (auto &E : AllSubCommands->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(); TopLevelSubCommand->reset(); AllSubCommands->reset(); registerSubCommand(&*TopLevelSubCommand); registerSubCommand(&*AllSubCommands); } private: SubCommand *ActiveSubCommand; Option *LookupOption(SubCommand &Sub, StringRef &Arg, StringRef &Value); 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; } // Initialise the general option category. OptionCategory llvm::cl::GeneralCategory("General options"); void OptionCategory::registerCategory() { GlobalParser->registerCategory(this); } // A special subcommand representing no subcommand ManagedStatic llvm::cl::TopLevelSubCommand; // A special subcommand that can be used to put an option into all subcommands. ManagedStatic llvm::cl::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 != &*AllSubCommands); size_t EqualPos = Arg.find('='); // If we have an equals sign, remember the value. if (EqualPos == StringRef::npos) { // Look up the option. auto I = Sub.OptionsMap.find(Arg); if (I == Sub.OptionsMap.end()) return nullptr; return I != Sub.OptionsMap.end() ? I->second : nullptr; } // 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 &*TopLevelSubCommand; for (auto S : RegisteredSubCommands) { if (S == &*AllSubCommands) continue; if (S->getName().empty()) continue; if (StringRef(S->getName()) == StringRef(Name)) return S; } return &*TopLevelSubCommand; } /// 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