xref: /llvm-project/llvm/tools/llvm-readtapi/llvm-readtapi.cpp (revision 3b48a7a008d7243218322fa816908a4e45455c11)
1 //===-- llvm-readtapi.cpp - tapi file reader and manipulator -----*- C++-*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines the command-line driver for llvm-readtapi.
10 //
11 //===----------------------------------------------------------------------===//
12 #include "DiffEngine.h"
13 #include "llvm/Object/TapiUniversal.h"
14 #include "llvm/Option/Arg.h"
15 #include "llvm/Option/ArgList.h"
16 #include "llvm/Option/Option.h"
17 #include "llvm/Support/CommandLine.h"
18 #include "llvm/Support/Error.h"
19 #include "llvm/Support/InitLLVM.h"
20 #include "llvm/Support/MemoryBuffer.h"
21 #include "llvm/Support/WithColor.h"
22 #include "llvm/Support/raw_ostream.h"
23 #include "llvm/TextAPI/TextAPIError.h"
24 #include <cstdlib>
25 
26 using namespace llvm;
27 using namespace MachO;
28 using namespace object;
29 
30 namespace {
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 };
38 
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 };
63 
64 Expected<std::unique_ptr<Binary>>
65 convertFileToBinary(const StringRef Filename) {
66   ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
67       MemoryBuffer::getFileOrSTDIN(Filename);
68   if (BufferOrErr.getError())
69     return errorCodeToError(BufferOrErr.getError());
70   return createBinary(BufferOrErr.get()->getMemBufferRef());
71 }
72 
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;
77 
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));
87 
88   TapiUniversal *FileLHS = dyn_cast<TapiUniversal>(BinLHS.get());
89   if (!FileLHS) {
90     ExitOnErr(createStringError(std::errc::executable_format_error,
91                                 "unsupported file format"));
92   }
93 
94   StringRef CompareInputFileName = Ctx.Inputs.at(1);
95   ExitOnErr.setBanner("error: '" + CompareInputFileName.str() + "' ");
96   auto BinRHS = ExitOnErr(convertFileToBinary(CompareInputFileName));
97 
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 }
107 
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_EQ)) {
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     }
139   }
140 
141   if (Args.hasArg(OPT_compare))
142     return handleCompareAction(Ctx);
143 
144   return EXIT_SUCCESS;
145 }
146