mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-24 03:33:20 +01:00
3744006268
POSIX.1-2017 12.2 Utility Syntax Guidelines, Guideline 5 says: > One or more options without option-arguments, followed by at most one option that takes an option-argument, should be accepted when grouped behind one '-' delimiter. i.e. -abc represents -a -b -c. The grouped short options are very common. Many utilities extend the syntax by allowing (an option with an argument) following a sequence of short options. This patch adds the support to OptTable, similar to cl::Group for CommandLine (D58711). llvm-symbolizer will use the feature (D83530). CommandLine is exotic in some aspects. OptTable is preferred if the user wants to get rid of the behaviors. * `cl::opt<bool> i(...)` can be disabled via -i=false or -i=0, which is different from conventional --no-i. * Handling --foo & --no-foo requires a comparison of argument positions, which is a bit clumsy in user code. OptTable::parseOneArg (non-const reference InputArgList) is added along with ParseOneArg (const ArgList &). The duplicate does not look great at first glance. However, The implementation can be simpler if ArgList is mutable. (ParseOneArg is used by clang-cl (FlagsToInclude/FlagsToExclude) and lld COFF (case-insensitive). Adding grouped short options can make the function even more complex.) The implementation allows a long option following a group of short options. We probably should refine the code to disallow this in the future. Allowing this seems benign for now. Reviewed By: grimar, jhenderson Differential Revision: https://reviews.llvm.org/D83639
292 lines
9.2 KiB
C++
292 lines
9.2 KiB
C++
//===- Option.cpp - Abstract Driver Options -------------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "llvm/Config/llvm-config.h"
|
|
#include "llvm/Option/Arg.h"
|
|
#include "llvm/Option/ArgList.h"
|
|
#include "llvm/Option/Option.h"
|
|
#include "llvm/Option/OptTable.h"
|
|
#include "llvm/Support/Compiler.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <cassert>
|
|
#include <cstring>
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::opt;
|
|
|
|
Option::Option(const OptTable::Info *info, const OptTable *owner)
|
|
: Info(info), Owner(owner) {
|
|
// Multi-level aliases are not supported. This just simplifies option
|
|
// tracking, it is not an inherent limitation.
|
|
assert((!Info || !getAlias().isValid() || !getAlias().getAlias().isValid()) &&
|
|
"Multi-level aliases are not supported.");
|
|
|
|
if (Info && getAliasArgs()) {
|
|
assert(getAlias().isValid() && "Only alias options can have alias args.");
|
|
assert(getKind() == FlagClass && "Only Flag aliases can have alias args.");
|
|
assert(getAlias().getKind() != FlagClass &&
|
|
"Cannot provide alias args to a flag option.");
|
|
}
|
|
}
|
|
|
|
void Option::print(raw_ostream &O) const {
|
|
O << "<";
|
|
switch (getKind()) {
|
|
#define P(N) case N: O << #N; break
|
|
P(GroupClass);
|
|
P(InputClass);
|
|
P(UnknownClass);
|
|
P(FlagClass);
|
|
P(JoinedClass);
|
|
P(ValuesClass);
|
|
P(SeparateClass);
|
|
P(CommaJoinedClass);
|
|
P(MultiArgClass);
|
|
P(JoinedOrSeparateClass);
|
|
P(JoinedAndSeparateClass);
|
|
P(RemainingArgsClass);
|
|
P(RemainingArgsJoinedClass);
|
|
#undef P
|
|
}
|
|
|
|
if (Info->Prefixes) {
|
|
O << " Prefixes:[";
|
|
for (const char *const *Pre = Info->Prefixes; *Pre != nullptr; ++Pre) {
|
|
O << '"' << *Pre << (*(Pre + 1) == nullptr ? "\"" : "\", ");
|
|
}
|
|
O << ']';
|
|
}
|
|
|
|
O << " Name:\"" << getName() << '"';
|
|
|
|
const Option Group = getGroup();
|
|
if (Group.isValid()) {
|
|
O << " Group:";
|
|
Group.print(O);
|
|
}
|
|
|
|
const Option Alias = getAlias();
|
|
if (Alias.isValid()) {
|
|
O << " Alias:";
|
|
Alias.print(O);
|
|
}
|
|
|
|
if (getKind() == MultiArgClass)
|
|
O << " NumArgs:" << getNumArgs();
|
|
|
|
O << ">\n";
|
|
}
|
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
|
LLVM_DUMP_METHOD void Option::dump() const { print(dbgs()); }
|
|
#endif
|
|
|
|
bool Option::matches(OptSpecifier Opt) const {
|
|
// Aliases are never considered in matching, look through them.
|
|
const Option Alias = getAlias();
|
|
if (Alias.isValid())
|
|
return Alias.matches(Opt);
|
|
|
|
// Check exact match.
|
|
if (getID() == Opt.getID())
|
|
return true;
|
|
|
|
const Option Group = getGroup();
|
|
if (Group.isValid())
|
|
return Group.matches(Opt);
|
|
return false;
|
|
}
|
|
|
|
Arg *Option::acceptInternal(const ArgList &Args, StringRef Spelling,
|
|
unsigned &Index) const {
|
|
size_t ArgSize = Spelling.size();
|
|
switch (getKind()) {
|
|
case FlagClass: {
|
|
if (ArgSize != strlen(Args.getArgString(Index)))
|
|
return nullptr;
|
|
return new Arg(*this, Spelling, Index++);
|
|
}
|
|
case JoinedClass: {
|
|
const char *Value = Args.getArgString(Index) + ArgSize;
|
|
return new Arg(*this, Spelling, Index++, Value);
|
|
}
|
|
case CommaJoinedClass: {
|
|
// Always matches.
|
|
const char *Str = Args.getArgString(Index) + ArgSize;
|
|
Arg *A = new Arg(*this, Spelling, Index++);
|
|
|
|
// Parse out the comma separated values.
|
|
const char *Prev = Str;
|
|
for (;; ++Str) {
|
|
char c = *Str;
|
|
|
|
if (!c || c == ',') {
|
|
if (Prev != Str) {
|
|
char *Value = new char[Str - Prev + 1];
|
|
memcpy(Value, Prev, Str - Prev);
|
|
Value[Str - Prev] = '\0';
|
|
A->getValues().push_back(Value);
|
|
}
|
|
|
|
if (!c)
|
|
break;
|
|
|
|
Prev = Str + 1;
|
|
}
|
|
}
|
|
A->setOwnsValues(true);
|
|
|
|
return A;
|
|
}
|
|
case SeparateClass:
|
|
// Matches iff this is an exact match.
|
|
// FIXME: Avoid strlen.
|
|
if (ArgSize != strlen(Args.getArgString(Index)))
|
|
return nullptr;
|
|
|
|
Index += 2;
|
|
if (Index > Args.getNumInputArgStrings() ||
|
|
Args.getArgString(Index - 1) == nullptr)
|
|
return nullptr;
|
|
|
|
return new Arg(*this, Spelling, Index - 2, Args.getArgString(Index - 1));
|
|
case MultiArgClass: {
|
|
// Matches iff this is an exact match.
|
|
// FIXME: Avoid strlen.
|
|
if (ArgSize != strlen(Args.getArgString(Index)))
|
|
return nullptr;
|
|
|
|
Index += 1 + getNumArgs();
|
|
if (Index > Args.getNumInputArgStrings())
|
|
return nullptr;
|
|
|
|
Arg *A = new Arg(*this, Spelling, Index - 1 - getNumArgs(),
|
|
Args.getArgString(Index - getNumArgs()));
|
|
for (unsigned i = 1; i != getNumArgs(); ++i)
|
|
A->getValues().push_back(Args.getArgString(Index - getNumArgs() + i));
|
|
return A;
|
|
}
|
|
case JoinedOrSeparateClass: {
|
|
// If this is not an exact match, it is a joined arg.
|
|
// FIXME: Avoid strlen.
|
|
if (ArgSize != strlen(Args.getArgString(Index))) {
|
|
const char *Value = Args.getArgString(Index) + ArgSize;
|
|
return new Arg(*this, Spelling, Index++, Value);
|
|
}
|
|
|
|
// Otherwise it must be separate.
|
|
Index += 2;
|
|
if (Index > Args.getNumInputArgStrings() ||
|
|
Args.getArgString(Index - 1) == nullptr)
|
|
return nullptr;
|
|
|
|
return new Arg(*this, Spelling, Index - 2, Args.getArgString(Index - 1));
|
|
}
|
|
case JoinedAndSeparateClass:
|
|
// Always matches.
|
|
Index += 2;
|
|
if (Index > Args.getNumInputArgStrings() ||
|
|
Args.getArgString(Index - 1) == nullptr)
|
|
return nullptr;
|
|
|
|
return new Arg(*this, Spelling, Index - 2,
|
|
Args.getArgString(Index - 2) + ArgSize,
|
|
Args.getArgString(Index - 1));
|
|
case RemainingArgsClass: {
|
|
// Matches iff this is an exact match.
|
|
// FIXME: Avoid strlen.
|
|
if (ArgSize != strlen(Args.getArgString(Index)))
|
|
return nullptr;
|
|
Arg *A = new Arg(*this, Spelling, Index++);
|
|
while (Index < Args.getNumInputArgStrings() &&
|
|
Args.getArgString(Index) != nullptr)
|
|
A->getValues().push_back(Args.getArgString(Index++));
|
|
return A;
|
|
}
|
|
case RemainingArgsJoinedClass: {
|
|
Arg *A = new Arg(*this, Spelling, Index);
|
|
if (ArgSize != strlen(Args.getArgString(Index))) {
|
|
// An inexact match means there is a joined arg.
|
|
A->getValues().push_back(Args.getArgString(Index) + ArgSize);
|
|
}
|
|
Index++;
|
|
while (Index < Args.getNumInputArgStrings() &&
|
|
Args.getArgString(Index) != nullptr)
|
|
A->getValues().push_back(Args.getArgString(Index++));
|
|
return A;
|
|
}
|
|
|
|
default:
|
|
llvm_unreachable("Invalid option kind!");
|
|
}
|
|
}
|
|
|
|
Arg *Option::accept(const ArgList &Args, StringRef CurArg,
|
|
bool GroupedShortOption, unsigned &Index) const {
|
|
std::unique_ptr<Arg> A(GroupedShortOption && getKind() == FlagClass
|
|
? new Arg(*this, CurArg, Index)
|
|
: acceptInternal(Args, CurArg, Index));
|
|
if (!A)
|
|
return nullptr;
|
|
|
|
const Option &UnaliasedOption = getUnaliasedOption();
|
|
if (getID() == UnaliasedOption.getID())
|
|
return A.release();
|
|
|
|
// "A" is an alias for a different flag. For most clients it's more convenient
|
|
// if this function returns unaliased Args, so create an unaliased arg for
|
|
// returning.
|
|
|
|
// This creates a completely new Arg object for the unaliased Arg because
|
|
// the alias and the unaliased arg can have different Kinds and different
|
|
// Values (due to AliasArgs<>).
|
|
|
|
// Get the spelling from the unaliased option.
|
|
StringRef UnaliasedSpelling = Args.MakeArgString(
|
|
Twine(UnaliasedOption.getPrefix()) + Twine(UnaliasedOption.getName()));
|
|
|
|
// It's a bit weird that aliased and unaliased arg share one index, but
|
|
// the index is mostly use as a memory optimization in render().
|
|
// Due to this, ArgList::getArgString(A->getIndex()) will return the spelling
|
|
// of the aliased arg always, while A->getSpelling() returns either the
|
|
// unaliased or the aliased arg, depending on which Arg object it's called on.
|
|
Arg *UnaliasedA = new Arg(UnaliasedOption, UnaliasedSpelling, A->getIndex());
|
|
Arg *RawA = A.get();
|
|
UnaliasedA->setAlias(std::move(A));
|
|
|
|
if (getKind() != FlagClass) {
|
|
// Values are usually owned by the ArgList. The exception are
|
|
// CommaJoined flags, where the Arg owns the values. For aliased flags,
|
|
// make the unaliased Arg the owner of the values.
|
|
// FIXME: There aren't many uses of CommaJoined -- try removing
|
|
// CommaJoined in favor of just calling StringRef::split(',') instead.
|
|
UnaliasedA->getValues() = RawA->getValues();
|
|
UnaliasedA->setOwnsValues(RawA->getOwnsValues());
|
|
RawA->setOwnsValues(false);
|
|
return UnaliasedA;
|
|
}
|
|
|
|
// FlagClass aliases can have AliasArgs<>; add those to the unaliased arg.
|
|
if (const char *Val = getAliasArgs()) {
|
|
while (*Val != '\0') {
|
|
UnaliasedA->getValues().push_back(Val);
|
|
|
|
// Move past the '\0' to the next argument.
|
|
Val += strlen(Val) + 1;
|
|
}
|
|
}
|
|
if (UnaliasedOption.getKind() == JoinedClass && !getAliasArgs())
|
|
// A Flag alias for a Joined option must provide an argument.
|
|
UnaliasedA->getValues().push_back("");
|
|
return UnaliasedA;
|
|
}
|