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