[llvm] [readtapi] Use OptParser.td for options (PR #72183)

Cyndy Ishida via llvm-commits llvm-commits at lists.llvm.org
Tue Nov 14 15:19:20 PST 2023


https://github.com/cyndyishida updated https://github.com/llvm/llvm-project/pull/72183

>From 23a185d0d09a46b826f49b87d8d9fa88ee9b9eb5 Mon Sep 17 00:00:00 2001
From: Cyndy Ishida <cyndy_ishida at apple.com>
Date: Tue, 14 Nov 2023 15:18:31 -0800
Subject: [PATCH] [readtapi] Use OptParser.td for options

his includes revamping how --compare was handled and adds `-o` ontop of tablegen approach.

This will be used to add more complex options.
---
 llvm/include/llvm/TextAPI/TextAPIError.h      |   3 +
 llvm/lib/TextAPI/TextAPIError.cpp             |  11 +-
 .../tools/llvm-readtapi/command-line.test     |   7 +
 .../tools/llvm-readtapi/compare-flags.test    |  23 +--
 llvm/tools/llvm-readtapi/CMakeLists.txt       |   8 +
 llvm/tools/llvm-readtapi/TapiOpts.td          |  16 ++
 llvm/tools/llvm-readtapi/llvm-readtapi.cpp    | 147 ++++++++++++------
 7 files changed, 156 insertions(+), 59 deletions(-)
 create mode 100644 llvm/test/tools/llvm-readtapi/command-line.test
 create mode 100644 llvm/tools/llvm-readtapi/TapiOpts.td

diff --git a/llvm/include/llvm/TextAPI/TextAPIError.h b/llvm/include/llvm/TextAPI/TextAPIError.h
index a8484063d7948de..de19f7894d35986 100644
--- a/llvm/include/llvm/TextAPI/TextAPIError.h
+++ b/llvm/include/llvm/TextAPI/TextAPIError.h
@@ -28,8 +28,11 @@ class TextAPIError : public llvm::ErrorInfo<TextAPIError> {
 public:
   static char ID;
   TextAPIErrorCode EC;
+  std::string Msg;
 
   TextAPIError(TextAPIErrorCode EC) : EC(EC) {}
+  TextAPIError(TextAPIErrorCode EC, std::string Msg)
+      : EC(EC), Msg(std::move(Msg)) {}
 
   void log(raw_ostream &OS) const override;
   std::error_code convertToErrorCode() const override;
diff --git a/llvm/lib/TextAPI/TextAPIError.cpp b/llvm/lib/TextAPI/TextAPIError.cpp
index dd97381ce094c2f..23954a9e3466de2 100644
--- a/llvm/lib/TextAPI/TextAPIError.cpp
+++ b/llvm/lib/TextAPI/TextAPIError.cpp
@@ -21,14 +21,17 @@ char TextAPIError::ID = 0;
 void TextAPIError::log(raw_ostream &OS) const {
   switch (EC) {
   case TextAPIErrorCode::NoSuchArchitecture:
-    OS << "no such architecture\n";
-    return;
+    OS << "no such architecture";
+    break;
   case TextAPIErrorCode::InvalidInputFormat:
-    OS << "invalid input format\n";
-    return;
+    OS << "invalid input format";
+    break;
   default:
     llvm_unreachable("unhandled TextAPIErrorCode");
   }
+  if (!Msg.empty())
+    OS << ": " << Msg;
+  OS << "\n";
 }
 
 std::error_code TextAPIError::convertToErrorCode() const {
diff --git a/llvm/test/tools/llvm-readtapi/command-line.test b/llvm/test/tools/llvm-readtapi/command-line.test
new file mode 100644
index 000000000000000..3b17194a7c14792
--- /dev/null
+++ b/llvm/test/tools/llvm-readtapi/command-line.test
@@ -0,0 +1,7 @@
+; RUN: llvm-readtapi --help 2>&1 | FileCheck %s 
+; RUN: llvm-readtapi -help 2>&1 | FileCheck %s 
+
+CHECK: OVERVIEW: LLVM TAPI file reader and manipulator
+CHECK: USAGE: llvm-readtapi [options] <inputs>
+CHECK: OPTIONS:
+CHECK:   -help    display this help
diff --git a/llvm/test/tools/llvm-readtapi/compare-flags.test b/llvm/test/tools/llvm-readtapi/compare-flags.test
index 06184eeb7661e10..82e0230d4cd898d 100644
--- a/llvm/test/tools/llvm-readtapi/compare-flags.test
+++ b/llvm/test/tools/llvm-readtapi/compare-flags.test
@@ -1,16 +1,21 @@
 ; RUN: rm -rf %t
 ; RUN: split-file %s %t  
-; RUN: not llvm-readtapi --compare %t/tbdv4.tbd  %t/tbdv5.tbd 2>&1 | FileCheck %s 
+; RUN: not llvm-readtapi --compare %t/tbdv4.tbd  %t/tbdv5.tbd -o %t/output.txt 2>&1 | FileCheck %s --allow-empty 
+; RUN: FileCheck %s --check-prefix FILEOUT <  %t/output.txt 
 
-; CHECK: < {{.*}}tbdv4.tbd
-; CHECK: > {{.*}}tbdv5.tbd
+; CHECK-NOT: error: 
+; CHECK-NOT: warning: 
+
+; FILEOUT: < {{.*}}tbdv4.tbd
+; FILEOUT: > {{.*}}tbdv5.tbd
+
+; FILEOUT:      Two Level Namespace
+; FILEOUT-NEXT:         < true
+; FILEOUT-NEXT:         > false
+; FILEOUT-NEXT: Shared Cache Ineligible
+; FILEOUT-NEXT:         < true
+; FILEOUT-NEXT:         > false
 
-CHECK:      Two Level Namespace
-CHECK-NEXT:         < true
-CHECK-NEXT:         > false
-CHECK-NEXT: Shared Cache Ineligible
-CHECK-NEXT:         < true
-CHECK-NEXT:         > false
 
 
 //--- tbdv4.tbd
diff --git a/llvm/tools/llvm-readtapi/CMakeLists.txt b/llvm/tools/llvm-readtapi/CMakeLists.txt
index 277011cf13c8e41..6aa030079e91e4a 100644
--- a/llvm/tools/llvm-readtapi/CMakeLists.txt
+++ b/llvm/tools/llvm-readtapi/CMakeLists.txt
@@ -1,12 +1,20 @@
 set(LLVM_LINK_COMPONENTS
   Object
   Support
+  Option
   TextAPI
   )
 
+set(LLVM_TARGET_DEFINITIONS TapiOpts.td)
+tablegen(LLVM TapiOpts.inc -gen-opt-parser-defs)
+add_public_tablegen_target(ReadTAPIOptsTableGen)
+
 add_llvm_tool(llvm-readtapi
   llvm-readtapi.cpp
   DiffEngine.cpp
+
+  DEPENDS
+  ReadTAPIOptsTableGen
   )
 
 if(LLVM_INSTALL_BINUTILS_SYMLINKS)
diff --git a/llvm/tools/llvm-readtapi/TapiOpts.td b/llvm/tools/llvm-readtapi/TapiOpts.td
new file mode 100644
index 000000000000000..fd2f96ba57fd41c
--- /dev/null
+++ b/llvm/tools/llvm-readtapi/TapiOpts.td
@@ -0,0 +1,16 @@
+// Include the common option parsing interfaces.
+include "llvm/Option/OptParser.td"
+
+class FF<string name, string help>: Flag<["-", "--"], name>, HelpText<help>;
+class JS<string name, string help, string var = ""> : JoinedOrSeparate<["-", "--"], name>, HelpText<help>, MetaVarName<var>;
+
+//
+// General Driver options 
+//
+def help : FF<"help", "display this help">;
+def output: JS<"o", "write output to <file>","<file>">;
+
+//
+// Compare options
+//
+def compare : FF<"compare", "compare tapi files for library differences">;
diff --git a/llvm/tools/llvm-readtapi/llvm-readtapi.cpp b/llvm/tools/llvm-readtapi/llvm-readtapi.cpp
index 1a3a66e2a7a1c3c..2aae1c464c0ed23 100644
--- a/llvm/tools/llvm-readtapi/llvm-readtapi.cpp
+++ b/llvm/tools/llvm-readtapi/llvm-readtapi.cpp
@@ -11,12 +11,16 @@
 //===----------------------------------------------------------------------===//
 #include "DiffEngine.h"
 #include "llvm/Object/TapiUniversal.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/InitLLVM.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/WithColor.h"
 #include "llvm/Support/raw_ostream.h"
+#include "llvm/TextAPI/TextAPIError.h"
 #include <cstdlib>
 
 using namespace llvm;
@@ -24,26 +28,41 @@ using namespace MachO;
 using namespace object;
 
 namespace {
-cl::OptionCategory TapiCat("llvm-readtapi options");
-cl::OptionCategory CompareCat("llvm-readtapi --compare options");
-
-cl::opt<std::string> InputFileName(cl::Positional, cl::desc("<tapi file>"),
-                                   cl::Required, cl::cat(TapiCat));
-cl::opt<std::string> CompareInputFileName(cl::Positional,
-                                          cl::desc("<comparison file>"),
-                                          cl::Required, cl::cat(CompareCat));
-enum OutputKind {
-  Compare,
+using namespace llvm::opt;
+enum ID {
+  OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
+#include "TapiOpts.inc"
+#undef OPTION
 };
 
-cl::opt<OutputKind>
-    Output(cl::desc("choose command action:"),
-           cl::values(clEnumValN(Compare, "compare",
-                                 "compare tapi file for library differences")),
-           cl::init(OutputKind::Compare), cl::cat(TapiCat));
-} // anonymous namespace
+#define PREFIX(NAME, VALUE)                                                    \
+  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
+  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
+                                                std::size(NAME##_init) - 1);
+#include "TapiOpts.inc"
+#undef PREFIX
+
+static constexpr opt::OptTable::Info InfoTable[] = {
+#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
+#include "TapiOpts.inc"
+#undef OPTION
+};
+
+class TAPIOptTable : public opt::GenericOptTable {
+public:
+  TAPIOptTable() : opt::GenericOptTable(InfoTable) {
+    setGroupedShortOptions(true);
+  }
+};
+
+struct Context {
+  std::vector<std::string> Inputs;
+  std::unique_ptr<llvm::raw_fd_stream> OutStream;
+};
 
-Expected<std::unique_ptr<Binary>> convertFileToBinary(std::string &Filename) {
+Expected<std::unique_ptr<Binary>>
+convertFileToBinary(const StringRef Filename) {
   ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
       MemoryBuffer::getFileOrSTDIN(Filename);
   if (BufferOrErr.getError())
@@ -51,40 +70,76 @@ Expected<std::unique_ptr<Binary>> convertFileToBinary(std::string &Filename) {
   return createBinary(BufferOrErr.get()->getMemBufferRef());
 }
 
-int main(int Argc, char **Argv) {
-  InitLLVM X(Argc, Argv);
-  cl::HideUnrelatedOptions(TapiCat);
-  cl::ParseCommandLineOptions(Argc, Argv,
-                              "TAPI File Reader and Manipulator Tool");
-
-  if (Output == OutputKind::Compare) {
-    if (InputFileName.empty() || CompareInputFileName.empty()) {
-      cl::PrintHelpMessage();
-      return EXIT_FAILURE;
-    }
+// Use unique exit code to differentiate failures not directly caused from
+// TextAPI operations. This is used for wrapping `compare` operations in
+// automation and scripting.
+const int NON_TAPI_EXIT_CODE = 2;
 
-    ExitOnError ExitOnErr("error: '" + InputFileName + "' ",
-                          /*DefaultErrorExitCode=*/2);
-    auto BinLHS = ExitOnErr(convertFileToBinary(InputFileName));
+bool handleCompareAction(const Context &Ctx) {
+  ExitOnError ExitOnErr("error: ", /*DefaultErrorExitCode=*/NON_TAPI_EXIT_CODE);
+  if (Ctx.Inputs.size() != 2) {
+    ExitOnErr(make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
+                                       "compare only supports 2 input files"));
+  }
+  StringRef InputFileName = Ctx.Inputs.front();
+  ExitOnErr.setBanner("error: '" + InputFileName.str() + "' ");
+  auto BinLHS = ExitOnErr(convertFileToBinary(InputFileName));
 
-    TapiUniversal *FileLHS = dyn_cast<TapiUniversal>(BinLHS.get());
-    if (!FileLHS) {
-      ExitOnErr(createStringError(std::errc::executable_format_error,
-                                  "unsupported file format"));
-    }
+  TapiUniversal *FileLHS = dyn_cast<TapiUniversal>(BinLHS.get());
+  if (!FileLHS) {
+    ExitOnErr(createStringError(std::errc::executable_format_error,
+                                "unsupported file format"));
+  }
 
-    ExitOnErr.setBanner("error: '" + CompareInputFileName + "' ");
-    auto BinRHS = ExitOnErr(convertFileToBinary(CompareInputFileName));
+  StringRef CompareInputFileName = Ctx.Inputs.at(1);
+  ExitOnErr.setBanner("error: '" + CompareInputFileName.str() + "' ");
+  auto BinRHS = ExitOnErr(convertFileToBinary(CompareInputFileName));
 
-    TapiUniversal *FileRHS = dyn_cast<TapiUniversal>(BinRHS.get());
-    if (!FileRHS) {
-      ExitOnErr(createStringError(std::errc::executable_format_error,
-                                  "unsupported file format"));
-    }
+  TapiUniversal *FileRHS = dyn_cast<TapiUniversal>(BinRHS.get());
+  if (!FileRHS) {
+    ExitOnErr(createStringError(std::errc::executable_format_error,
+                                "unsupported file format"));
+  }
+
+  raw_ostream &OS = Ctx.OutStream ? *Ctx.OutStream : outs();
+  return DiffEngine(FileLHS, FileRHS).compareFiles(OS);
+}
 
-    raw_ostream &OS = outs();
-    return DiffEngine(FileLHS, FileRHS).compareFiles(OS);
+} // anonymous namespace
+
+int main(int Argc, char **Argv) {
+  InitLLVM X(Argc, Argv);
+  BumpPtrAllocator A;
+  StringSaver Saver(A);
+  TAPIOptTable Tbl;
+  Context Ctx;
+  opt::InputArgList Args =
+      Tbl.parseArgs(Argc, Argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) {
+        WithColor::error(errs(), "llvm-readtapi") << Msg << "\n";
+        exit(1);
+      });
+  if (Args.hasArg(OPT_help)) {
+    Tbl.printHelp(outs(), "llvm-readtapi [options] <inputs>",
+                  "LLVM TAPI file reader and manipulator");
+    return EXIT_SUCCESS;
+  }
+
+  for (opt::Arg *A : Args.filtered(OPT_INPUT))
+    Ctx.Inputs.push_back(A->getValue());
+
+  if (opt::Arg *A = Args.getLastArg(OPT_output)) {
+    std::string OutputLoc = std::move(A->getValue());
+    std::error_code EC;
+    Ctx.OutStream = std::make_unique<llvm::raw_fd_stream>(OutputLoc, EC);
+    if (EC) {
+      llvm::errs() << "error opening the file '" << OutputLoc
+                   << "': " << EC.message() << "\n";
+      return NON_TAPI_EXIT_CODE;
+    }
   }
 
-  return 0;
+  if (Args.hasArg(OPT_compare))
+    return handleCompareAction(Ctx);
+
+  return EXIT_SUCCESS;
 }



More information about the llvm-commits mailing list