Skip to content

Commit ec64af5

Browse files
authored
[readtapi] Use OptParser.td for options (#72183)
This includes revamping how --compare was handled and adds `-o` ontop of tablegen approach. This will be used to add more complex options.
1 parent 14a8451 commit ec64af5

File tree

7 files changed

+156
-59
lines changed

7 files changed

+156
-59
lines changed

llvm/include/llvm/TextAPI/TextAPIError.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,11 @@ class TextAPIError : public llvm::ErrorInfo<TextAPIError> {
2828
public:
2929
static char ID;
3030
TextAPIErrorCode EC;
31+
std::string Msg;
3132

3233
TextAPIError(TextAPIErrorCode EC) : EC(EC) {}
34+
TextAPIError(TextAPIErrorCode EC, std::string Msg)
35+
: EC(EC), Msg(std::move(Msg)) {}
3336

3437
void log(raw_ostream &OS) const override;
3538
std::error_code convertToErrorCode() const override;

llvm/lib/TextAPI/TextAPIError.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,17 @@ char TextAPIError::ID = 0;
2121
void TextAPIError::log(raw_ostream &OS) const {
2222
switch (EC) {
2323
case TextAPIErrorCode::NoSuchArchitecture:
24-
OS << "no such architecture\n";
25-
return;
24+
OS << "no such architecture";
25+
break;
2626
case TextAPIErrorCode::InvalidInputFormat:
27-
OS << "invalid input format\n";
28-
return;
27+
OS << "invalid input format";
28+
break;
2929
default:
3030
llvm_unreachable("unhandled TextAPIErrorCode");
3131
}
32+
if (!Msg.empty())
33+
OS << ": " << Msg;
34+
OS << "\n";
3235
}
3336

3437
std::error_code TextAPIError::convertToErrorCode() const {
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
; RUN: llvm-readtapi --help 2>&1 | FileCheck %s
2+
; RUN: llvm-readtapi -help 2>&1 | FileCheck %s
3+
4+
CHECK: OVERVIEW: LLVM TAPI file reader and manipulator
5+
CHECK: USAGE: llvm-readtapi [options] <inputs>
6+
CHECK: OPTIONS:
7+
CHECK: -help display this help

llvm/test/tools/llvm-readtapi/compare-flags.test

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
; RUN: rm -rf %t
22
; RUN: split-file %s %t
3-
; RUN: not llvm-readtapi --compare %t/tbdv4.tbd %t/tbdv5.tbd 2>&1 | FileCheck %s
3+
; RUN: not llvm-readtapi --compare %t/tbdv4.tbd %t/tbdv5.tbd -o %t/output.txt 2>&1 | FileCheck %s --allow-empty
4+
; RUN: FileCheck %s --check-prefix FILEOUT < %t/output.txt
45

5-
; CHECK: < {{.*}}tbdv4.tbd
6-
; CHECK: > {{.*}}tbdv5.tbd
6+
; CHECK-NOT: error:
7+
; CHECK-NOT: warning:
8+
9+
; FILEOUT: < {{.*}}tbdv4.tbd
10+
; FILEOUT: > {{.*}}tbdv5.tbd
11+
12+
; FILEOUT: Two Level Namespace
13+
; FILEOUT-NEXT: < true
14+
; FILEOUT-NEXT: > false
15+
; FILEOUT-NEXT: Shared Cache Ineligible
16+
; FILEOUT-NEXT: < true
17+
; FILEOUT-NEXT: > false
718

8-
CHECK: Two Level Namespace
9-
CHECK-NEXT: < true
10-
CHECK-NEXT: > false
11-
CHECK-NEXT: Shared Cache Ineligible
12-
CHECK-NEXT: < true
13-
CHECK-NEXT: > false
1419

1520

1621
//--- tbdv4.tbd

llvm/tools/llvm-readtapi/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
set(LLVM_LINK_COMPONENTS
22
Object
33
Support
4+
Option
45
TextAPI
56
)
67

8+
set(LLVM_TARGET_DEFINITIONS TapiOpts.td)
9+
tablegen(LLVM TapiOpts.inc -gen-opt-parser-defs)
10+
add_public_tablegen_target(ReadTAPIOptsTableGen)
11+
712
add_llvm_tool(llvm-readtapi
813
llvm-readtapi.cpp
914
DiffEngine.cpp
15+
16+
DEPENDS
17+
ReadTAPIOptsTableGen
1018
)
1119

1220
if(LLVM_INSTALL_BINUTILS_SYMLINKS)

llvm/tools/llvm-readtapi/TapiOpts.td

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Include the common option parsing interfaces.
2+
include "llvm/Option/OptParser.td"
3+
4+
class FF<string name, string help>: Flag<["-", "--"], name>, HelpText<help>;
5+
class JS<string name, string help, string var = ""> : JoinedOrSeparate<["-", "--"], name>, HelpText<help>, MetaVarName<var>;
6+
7+
//
8+
// General Driver options
9+
//
10+
def help : FF<"help", "display this help">;
11+
def output: JS<"o", "write output to <file>","<file>">;
12+
13+
//
14+
// Compare options
15+
//
16+
def compare : FF<"compare", "compare tapi files for library differences">;

llvm/tools/llvm-readtapi/llvm-readtapi.cpp

Lines changed: 101 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -11,80 +11,135 @@
1111
//===----------------------------------------------------------------------===//
1212
#include "DiffEngine.h"
1313
#include "llvm/Object/TapiUniversal.h"
14+
#include "llvm/Option/Arg.h"
15+
#include "llvm/Option/ArgList.h"
16+
#include "llvm/Option/Option.h"
1417
#include "llvm/Support/CommandLine.h"
1518
#include "llvm/Support/Error.h"
1619
#include "llvm/Support/InitLLVM.h"
1720
#include "llvm/Support/MemoryBuffer.h"
1821
#include "llvm/Support/WithColor.h"
1922
#include "llvm/Support/raw_ostream.h"
23+
#include "llvm/TextAPI/TextAPIError.h"
2024
#include <cstdlib>
2125

2226
using namespace llvm;
2327
using namespace MachO;
2428
using namespace object;
2529

2630
namespace {
27-
cl::OptionCategory TapiCat("llvm-readtapi options");
28-
cl::OptionCategory CompareCat("llvm-readtapi --compare options");
29-
30-
cl::opt<std::string> InputFileName(cl::Positional, cl::desc("<tapi file>"),
31-
cl::Required, cl::cat(TapiCat));
32-
cl::opt<std::string> CompareInputFileName(cl::Positional,
33-
cl::desc("<comparison file>"),
34-
cl::Required, cl::cat(CompareCat));
35-
enum OutputKind {
36-
Compare,
31+
using namespace llvm::opt;
32+
enum ID {
33+
OPT_INVALID = 0, // This is not an option ID.
34+
#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
35+
#include "TapiOpts.inc"
36+
#undef OPTION
3737
};
3838

39-
cl::opt<OutputKind>
40-
Output(cl::desc("choose command action:"),
41-
cl::values(clEnumValN(Compare, "compare",
42-
"compare tapi file for library differences")),
43-
cl::init(OutputKind::Compare), cl::cat(TapiCat));
44-
} // anonymous namespace
39+
#define PREFIX(NAME, VALUE) \
40+
static constexpr StringLiteral NAME##_init[] = VALUE; \
41+
static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \
42+
std::size(NAME##_init) - 1);
43+
#include "TapiOpts.inc"
44+
#undef PREFIX
45+
46+
static constexpr opt::OptTable::Info InfoTable[] = {
47+
#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
48+
#include "TapiOpts.inc"
49+
#undef OPTION
50+
};
51+
52+
class TAPIOptTable : public opt::GenericOptTable {
53+
public:
54+
TAPIOptTable() : opt::GenericOptTable(InfoTable) {
55+
setGroupedShortOptions(true);
56+
}
57+
};
58+
59+
struct Context {
60+
std::vector<std::string> Inputs;
61+
std::unique_ptr<llvm::raw_fd_stream> OutStream;
62+
};
4563

46-
Expected<std::unique_ptr<Binary>> convertFileToBinary(std::string &Filename) {
64+
Expected<std::unique_ptr<Binary>>
65+
convertFileToBinary(const StringRef Filename) {
4766
ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
4867
MemoryBuffer::getFileOrSTDIN(Filename);
4968
if (BufferOrErr.getError())
5069
return errorCodeToError(BufferOrErr.getError());
5170
return createBinary(BufferOrErr.get()->getMemBufferRef());
5271
}
5372

54-
int main(int Argc, char **Argv) {
55-
InitLLVM X(Argc, Argv);
56-
cl::HideUnrelatedOptions(TapiCat);
57-
cl::ParseCommandLineOptions(Argc, Argv,
58-
"TAPI File Reader and Manipulator Tool");
59-
60-
if (Output == OutputKind::Compare) {
61-
if (InputFileName.empty() || CompareInputFileName.empty()) {
62-
cl::PrintHelpMessage();
63-
return EXIT_FAILURE;
64-
}
73+
// Use unique exit code to differentiate failures not directly caused from
74+
// TextAPI operations. This is used for wrapping `compare` operations in
75+
// automation and scripting.
76+
const int NON_TAPI_EXIT_CODE = 2;
6577

66-
ExitOnError ExitOnErr("error: '" + InputFileName + "' ",
67-
/*DefaultErrorExitCode=*/2);
68-
auto BinLHS = ExitOnErr(convertFileToBinary(InputFileName));
78+
bool handleCompareAction(const Context &Ctx) {
79+
ExitOnError ExitOnErr("error: ", /*DefaultErrorExitCode=*/NON_TAPI_EXIT_CODE);
80+
if (Ctx.Inputs.size() != 2) {
81+
ExitOnErr(make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
82+
"compare only supports 2 input files"));
83+
}
84+
StringRef InputFileName = Ctx.Inputs.front();
85+
ExitOnErr.setBanner("error: '" + InputFileName.str() + "' ");
86+
auto BinLHS = ExitOnErr(convertFileToBinary(InputFileName));
6987

70-
TapiUniversal *FileLHS = dyn_cast<TapiUniversal>(BinLHS.get());
71-
if (!FileLHS) {
72-
ExitOnErr(createStringError(std::errc::executable_format_error,
73-
"unsupported file format"));
74-
}
88+
TapiUniversal *FileLHS = dyn_cast<TapiUniversal>(BinLHS.get());
89+
if (!FileLHS) {
90+
ExitOnErr(createStringError(std::errc::executable_format_error,
91+
"unsupported file format"));
92+
}
7593

76-
ExitOnErr.setBanner("error: '" + CompareInputFileName + "' ");
77-
auto BinRHS = ExitOnErr(convertFileToBinary(CompareInputFileName));
94+
StringRef CompareInputFileName = Ctx.Inputs.at(1);
95+
ExitOnErr.setBanner("error: '" + CompareInputFileName.str() + "' ");
96+
auto BinRHS = ExitOnErr(convertFileToBinary(CompareInputFileName));
7897

79-
TapiUniversal *FileRHS = dyn_cast<TapiUniversal>(BinRHS.get());
80-
if (!FileRHS) {
81-
ExitOnErr(createStringError(std::errc::executable_format_error,
82-
"unsupported file format"));
83-
}
98+
TapiUniversal *FileRHS = dyn_cast<TapiUniversal>(BinRHS.get());
99+
if (!FileRHS) {
100+
ExitOnErr(createStringError(std::errc::executable_format_error,
101+
"unsupported file format"));
102+
}
103+
104+
raw_ostream &OS = Ctx.OutStream ? *Ctx.OutStream : outs();
105+
return DiffEngine(FileLHS, FileRHS).compareFiles(OS);
106+
}
84107

85-
raw_ostream &OS = outs();
86-
return DiffEngine(FileLHS, FileRHS).compareFiles(OS);
108+
} // anonymous namespace
109+
110+
int main(int Argc, char **Argv) {
111+
InitLLVM X(Argc, Argv);
112+
BumpPtrAllocator A;
113+
StringSaver Saver(A);
114+
TAPIOptTable Tbl;
115+
Context Ctx;
116+
opt::InputArgList Args =
117+
Tbl.parseArgs(Argc, Argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) {
118+
WithColor::error(errs(), "llvm-readtapi") << Msg << "\n";
119+
exit(1);
120+
});
121+
if (Args.hasArg(OPT_help)) {
122+
Tbl.printHelp(outs(), "llvm-readtapi [options] <inputs>",
123+
"LLVM TAPI file reader and manipulator");
124+
return EXIT_SUCCESS;
125+
}
126+
127+
for (opt::Arg *A : Args.filtered(OPT_INPUT))
128+
Ctx.Inputs.push_back(A->getValue());
129+
130+
if (opt::Arg *A = Args.getLastArg(OPT_output)) {
131+
std::string OutputLoc = std::move(A->getValue());
132+
std::error_code EC;
133+
Ctx.OutStream = std::make_unique<llvm::raw_fd_stream>(OutputLoc, EC);
134+
if (EC) {
135+
llvm::errs() << "error opening the file '" << OutputLoc
136+
<< "': " << EC.message() << "\n";
137+
return NON_TAPI_EXIT_CODE;
138+
}
87139
}
88140

89-
return 0;
141+
if (Args.hasArg(OPT_compare))
142+
return handleCompareAction(Ctx);
143+
144+
return EXIT_SUCCESS;
90145
}

0 commit comments

Comments
 (0)