|
11 | 11 | //===----------------------------------------------------------------------===//
|
12 | 12 | #include "DiffEngine.h"
|
13 | 13 | #include "llvm/Object/TapiUniversal.h"
|
| 14 | +#include "llvm/Option/Arg.h" |
| 15 | +#include "llvm/Option/ArgList.h" |
| 16 | +#include "llvm/Option/Option.h" |
14 | 17 | #include "llvm/Support/CommandLine.h"
|
15 | 18 | #include "llvm/Support/Error.h"
|
16 | 19 | #include "llvm/Support/InitLLVM.h"
|
17 | 20 | #include "llvm/Support/MemoryBuffer.h"
|
18 | 21 | #include "llvm/Support/WithColor.h"
|
19 | 22 | #include "llvm/Support/raw_ostream.h"
|
| 23 | +#include "llvm/TextAPI/TextAPIError.h" |
20 | 24 | #include <cstdlib>
|
21 | 25 |
|
22 | 26 | using namespace llvm;
|
23 | 27 | using namespace MachO;
|
24 | 28 | using namespace object;
|
25 | 29 |
|
26 | 30 | 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 |
37 | 37 | };
|
38 | 38 |
|
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 | +}; |
45 | 63 |
|
46 |
| -Expected<std::unique_ptr<Binary>> convertFileToBinary(std::string &Filename) { |
| 64 | +Expected<std::unique_ptr<Binary>> |
| 65 | +convertFileToBinary(const StringRef Filename) { |
47 | 66 | ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
|
48 | 67 | MemoryBuffer::getFileOrSTDIN(Filename);
|
49 | 68 | if (BufferOrErr.getError())
|
50 | 69 | return errorCodeToError(BufferOrErr.getError());
|
51 | 70 | return createBinary(BufferOrErr.get()->getMemBufferRef());
|
52 | 71 | }
|
53 | 72 |
|
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; |
65 | 77 |
|
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)); |
69 | 87 |
|
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 | + } |
75 | 93 |
|
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)); |
78 | 97 |
|
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 | +} |
84 | 107 |
|
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 | + } |
87 | 139 | }
|
88 | 140 |
|
89 |
| - return 0; |
| 141 | + if (Args.hasArg(OPT_compare)) |
| 142 | + return handleCompareAction(Ctx); |
| 143 | + |
| 144 | + return EXIT_SUCCESS; |
90 | 145 | }
|
0 commit comments