[llvm-size] Switch command line parsing from llvm::cl to OptTable

Part of https://lists.llvm.org/pipermail/llvm-dev/2021-July/151622.html
"Binary utilities: switch command line parsing from llvm::cl to OptTable"

* `--totals=false` and `--totals=0` cannot be used. Omit the option.
* `--help-list` is removed. This is a `cl::` specific option.

OptTable avoids global option collision if we decide to support multiplexing for binary utilities.

Note: because the tool is simple, and its long options are uncommon, I just drop
the one-dash forms except `-arch <value>` (Darwin style).

Reviewed By: jhenderson

Differential Revision: https://reviews.llvm.org/D105598
This commit is contained in:
Fangrui Song 2021-07-09 10:26:53 -07:00
parent 214f63b272
commit 47db32e542
9 changed files with 175 additions and 94 deletions

View File

@ -125,10 +125,6 @@ OPTIONS
Display a summary of command line options.
.. option:: --help-list
Display an uncategorized summary of command line options.
.. option:: -m
Equivalent to :option:`--format` with a value of ``darwin``.

View File

@ -1,15 +1,10 @@
## Show that help text is printed correctly when requested.
RUN: llvm-size -h | FileCheck %s --check-prefixes=CHECK,CATEG
RUN: llvm-size --help | FileCheck %s --check-prefixes=CHECK,CATEG
RUN: llvm-size --help-list \
RUN: | FileCheck %s --check-prefixes=CHECK,LIST
RUN: llvm-size -h | FileCheck %s
RUN: llvm-size --help | FileCheck %s
CHECK: OVERVIEW: llvm object size dumper
CHECK: USAGE: llvm-size{{(.exe)?}} [options] <input files>{{$}}
CHECK: OVERVIEW: LLVM object size dumper
CHECK: USAGE: {{.*}}llvm-size{{(.exe)?}} [options] <input object files>{{$}}
CHECK: OPTIONS:
CATEG: Generic Options:
LIST-NOT: Generic Options:
CATEG: llvm-size Options:
LIST-NOT: llvm-size Options:
CHECK: OPTIONS (Mach-O specific):
CHECK: @FILE

View File

@ -121,7 +121,7 @@
# RUN: not llvm-size %t1.o --radix=bad 2>&1 \
# RUN: | FileCheck %s --check-prefix=BAD-VAL -DNUM=bad
# BAD-VAL: {{.*}}llvm-size{{.*}}: for the --radix option: Cannot find option named '[[NUM]]'!
# BAD-VAL: {{.*}}llvm-size{{.*}}: error: --radix value should be one of: 8, 10, 16
--- !ELF
FileHeader:

View File

@ -1,4 +1,4 @@
## Show that the an error is emitted if an unknown value is passed to --format.
# RUN: not llvm-size --format=unknown 2>&1 | FileCheck %s
# CHECK: {{.*}}llvm-size{{.*}}: for the --format option: Cannot find option named 'unknown'!
# CHECK: {{.*}}llvm-size{{.*}}: error: --format value should be one of: 'berkeley', 'darwin', 'sysv'

View File

@ -1,10 +1,17 @@
set(LLVM_LINK_COMPONENTS
Object
Option
Support
)
set(LLVM_TARGET_DEFINITIONS Opts.td)
tablegen(LLVM Opts.inc -gen-opt-parser-defs)
add_public_tablegen_target(SizeOptsTableGen)
add_llvm_tool(llvm-size
llvm-size.cpp
DEPENDS
SizeOptsTableGen
)
if(LLVM_INSTALL_BINUTILS_SYMLINKS)

View File

@ -0,0 +1,32 @@
include "llvm/Option/OptParser.td"
class F<string letter, string help> : Flag<["-"], letter>, HelpText<help>;
class FF<string name, string help> : Flag<["--"], name>, HelpText<help>;
multiclass Eq<string name, string help> {
def NAME #_EQ : Joined<["--"], name #"=">,
HelpText<help>;
def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>;
}
def common : FF<"common", "Print common symbols in the ELF file. When using Berkeley format, this is added to bss">;
defm format : Eq<"format", "Specify output format">;
def help : FF<"help", "Display this help">;
defm radix : Eq<"radix", "Print size in radix">;
def totals : FF<"totals", "Print totals of all objects - Berkeley format only">;
def version : FF<"version", "Display the version">;
// Mach-O specific options.
def grp_mach_o : OptionGroup<"kind">, HelpText<"OPTIONS (Mach-O specific)">;
def arch_EQ : Joined<["--"], "arch=">, HelpText<"architecture(s) from a Mach-O file to dump">, Group<grp_mach_o>;
def : Separate<["--", "-"], "arch">, Alias<arch_EQ>;
def l : F<"l", "When format is darwin, use long format to include addresses and offsets">, Group<grp_mach_o>;
def : F<"A", "Alias for --format">, Alias<format_EQ>, AliasArgs<["sysv"]>;
def : F<"B", "Alias for --format">, Alias<format_EQ>, AliasArgs<["berkeley"]>;
def : F<"d", "Alias for --radix=10">, Alias<radix_EQ>, AliasArgs<["10"]>;
def : F<"h", "Alias for --help">, Alias<help>;
def : F<"m", "Alias for --format">, Alias<format_EQ>, AliasArgs<["darwin"]>;
def : F<"o", "Alias for --radix=8">, Alias<radix_EQ>, AliasArgs<["8"]>;
def : F<"t", "Alias for --totals">, Alias<totals>;
def : F<"x", "Alias for --radix=16">, Alias<radix_EQ>, AliasArgs<["16"]>;

View File

@ -18,6 +18,9 @@
#include "llvm/Object/MachO.h"
#include "llvm/Object/MachOUniversal.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
@ -33,23 +36,56 @@
using namespace llvm;
using namespace object;
cl::OptionCategory SizeCat("llvm-size Options");
namespace {
using namespace llvm::opt; // for HelpHidden in Opts.inc
enum ID {
OPT_INVALID = 0, // This is not an option ID.
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES) \
OPT_##ID,
#include "Opts.inc"
#undef OPTION
};
#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
#include "Opts.inc"
#undef PREFIX
const opt::OptTable::Info InfoTable[] = {
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES) \
{ \
PREFIX, NAME, HELPTEXT, \
METAVAR, OPT_##ID, opt::Option::KIND##Class, \
PARAM, FLAGS, OPT_##GROUP, \
OPT_##ALIAS, ALIASARGS, VALUES},
#include "Opts.inc"
#undef OPTION
};
class SizeOptTable : public opt::OptTable {
public:
SizeOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); }
};
enum OutputFormatTy { berkeley, sysv, darwin };
static cl::opt<OutputFormatTy>
OutputFormat("format", cl::desc("Specify output format"),
cl::values(clEnumVal(sysv, "System V format"),
clEnumVal(berkeley, "Berkeley format"),
clEnumVal(darwin, "Darwin -m format")),
cl::init(berkeley), cl::cat(SizeCat));
enum RadixTy { octal = 8, decimal = 10, hexadecimal = 16 };
} // namespace
static cl::opt<OutputFormatTy>
OutputFormatShort(cl::desc("Specify output format"),
cl::values(clEnumValN(sysv, "A", "System V format"),
clEnumValN(berkeley, "B", "Berkeley format"),
clEnumValN(darwin, "m", "Darwin -m format")),
cl::init(berkeley), cl::cat(SizeCat));
static bool ArchAll = false;
static std::vector<StringRef> ArchFlags;
static bool ELFCommons;
static OutputFormatTy OutputFormat;
static bool DarwinLongFormat;
static RadixTy Radix;
static bool TotalSizes;
static std::vector<std::string> InputFilenames;
static std::string ToolName;
// States
static bool HadError = false;
static bool BerkeleyHeaderPrinted = false;
static bool MoreThanOneFile = false;
static uint64_t TotalObjectText = 0;
@ -57,59 +93,13 @@ static uint64_t TotalObjectData = 0;
static uint64_t TotalObjectBss = 0;
static uint64_t TotalObjectTotal = 0;
cl::opt<bool>
DarwinLongFormat("l",
cl::desc("When format is darwin, use long format "
"to include addresses and offsets."),
cl::cat(SizeCat));
cl::opt<bool>
ELFCommons("common",
cl::desc("Print common symbols in the ELF file. When using "
"Berkeley format, this is added to bss."),
cl::init(false), cl::cat(SizeCat));
static cl::list<std::string>
ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"),
cl::ZeroOrMore, cl::cat(SizeCat));
static bool ArchAll = false;
enum RadixTy { octal = 8, decimal = 10, hexadecimal = 16 };
static cl::opt<RadixTy> Radix(
"radix", cl::desc("Print size in radix"), cl::init(decimal),
cl::values(clEnumValN(octal, "8", "Print size in octal"),
clEnumValN(decimal, "10", "Print size in decimal"),
clEnumValN(hexadecimal, "16", "Print size in hexadecimal")),
cl::cat(SizeCat));
static cl::opt<RadixTy> RadixShort(
cl::desc("Print size in radix:"),
cl::values(clEnumValN(octal, "o", "Print size in octal"),
clEnumValN(decimal, "d", "Print size in decimal"),
clEnumValN(hexadecimal, "x", "Print size in hexadecimal")),
cl::init(decimal), cl::cat(SizeCat));
static cl::opt<bool>
TotalSizes("totals",
cl::desc("Print totals of all objects - Berkeley format only"),
cl::init(false), cl::cat(SizeCat));
static cl::alias TotalSizesShort("t", cl::desc("Short for --totals"),
cl::aliasopt(TotalSizes));
static cl::list<std::string>
InputFilenames(cl::Positional, cl::desc("<input files>"), cl::ZeroOrMore);
static cl::extrahelp
HelpResponse("\nPass @FILE as argument to read options from FILE.\n");
static bool HadError = false;
static std::string ToolName;
static void error(const Twine &Message, StringRef File) {
static void error(const Twine &Message, StringRef File = "") {
HadError = true;
WithColor::error(errs(), ToolName) << "'" << File << "': " << Message << "\n";
if (File.empty())
WithColor::error(errs(), ToolName) << Message << '\n';
else
WithColor::error(errs(), ToolName)
<< "'" << File << "': " << Message << '\n';
}
// This version of error() prints the archive name and member name, for example:
@ -874,27 +864,66 @@ static void printBerkeleyTotals() {
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
cl::HideUnrelatedOptions(SizeCat);
cl::ParseCommandLineOptions(argc, argv, "llvm object size dumper\n");
BumpPtrAllocator A;
StringSaver Saver(A);
SizeOptTable Tbl;
ToolName = argv[0];
if (OutputFormatShort.getNumOccurrences())
OutputFormat = static_cast<OutputFormatTy>(OutputFormatShort);
if (RadixShort.getNumOccurrences())
Radix = RadixShort.getValue();
opt::InputArgList Args = Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver,
[&](StringRef Msg) { error(Msg); });
if (Args.hasArg(OPT_help)) {
Tbl.printHelp(
outs(),
(Twine(ToolName) + " [options] <input object files>").str().c_str(),
"LLVM object size dumper");
// TODO Replace this with OptTable API once it adds extrahelp support.
outs() << "\nPass @FILE as argument to read options from FILE.\n";
return 0;
}
if (Args.hasArg(OPT_version)) {
outs() << ToolName << '\n';
cl::PrintVersionMessage();
return 0;
}
for (StringRef Arch : ArchFlags) {
if (Arch == "all") {
ArchAll = true;
} else {
if (!MachOObjectFile::isValidArch(Arch)) {
ELFCommons = Args.hasArg(OPT_common);
DarwinLongFormat = Args.hasArg(OPT_l);
TotalSizes = Args.hasArg(OPT_totals);
StringRef V = Args.getLastArgValue(OPT_format_EQ, "berkeley");
if (V == "berkeley")
OutputFormat = berkeley;
else if (V == "darwin")
OutputFormat = darwin;
else if (V == "sysv")
OutputFormat = sysv;
else
error("--format value should be one of: 'berkeley', 'darwin', 'sysv'");
V = Args.getLastArgValue(OPT_radix_EQ, "10");
if (V == "8")
Radix = RadixTy::octal;
else if (V == "10")
Radix = RadixTy::decimal;
else if (V == "16")
Radix = RadixTy::hexadecimal;
else
error("--radix value should be one of: 8, 10, 16 ");
for (const auto *A : Args.filtered(OPT_arch_EQ)) {
SmallVector<StringRef, 2> Values;
llvm::SplitString(A->getValue(), Values, ",");
for (StringRef V : Values) {
if (V == "all")
ArchAll = true;
else if (MachOObjectFile::isValidArch(V))
ArchFlags.push_back(V);
else {
outs() << ToolName << ": for the -arch option: Unknown architecture "
<< "named '" << Arch << "'";
<< "named '" << V << "'";
return 1;
}
}
}
InputFilenames = Args.getAllArgValues(OPT_INPUT);
if (InputFilenames.empty())
InputFilenames.push_back("a.out");

View File

@ -1,6 +1,12 @@
import("//llvm/tools/binutils_symlinks.gni")
import("//llvm/utils/TableGen/tablegen.gni")
import("//llvm/utils/gn/build/symlink_or_copy.gni")
tablegen("Opts") {
visibility = [ ":llvm-size" ]
args = [ "-gen-opt-parser-defs" ]
}
if (llvm_install_binutils_symlinks) {
symlink_or_copy("size") {
deps = [ ":llvm-size" ]
@ -19,7 +25,9 @@ group("symlinks") {
executable("llvm-size") {
deps = [
":Opts",
"//llvm/lib/Object",
"//llvm/lib/Option",
"//llvm/lib/Support",
]
sources = [ "llvm-size.cpp" ]

View File

@ -3448,6 +3448,18 @@ cc_binary(
],
)
gentbl(
name = "SizeOptsTableGen",
strip_include_prefix = "tools/llvm-size",
tbl_outs = [(
"-gen-opt-parser-defs",
"tools/llvm-size/Opts.inc",
)],
tblgen = ":llvm-tblgen",
td_file = "tools/llvm-size/Opts.td",
td_srcs = ["include/llvm/Option/OptParser.td"],
)
cc_binary(
name = "llvm-size",
srcs = glob([
@ -3458,6 +3470,8 @@ cc_binary(
stamp = 0,
deps = [
":Object",
":Option",
":SizeOptsTableGen",
":Support",
],
)