JDevlieghere created this revision.
JDevlieghere added reviewers: friss, aprantl, thegameg.
Herald added subscribers: llvm-commits, mgorny.
Herald added projects: LLDB, LLVM.

This patch reimplements command line option parsing in dsymutil with Tablegen 
and libOption. The main motivation for this change is to prevent clashes with 
other cl::opt options defined in llvm. Although it's a bit more heavyweight, it 
has some nice advantages such as no global static initializers and better 
separation between the code and the option definitions.

I also used this opportunity to improve how dsymutil deals with incompatible 
options. Instead of having checks spread across the code, everything is now 
grouped together in `verifyOptions`. The fact that the options are no longer 
global means that we need to pass them around a bit more, but I think it's 
worth the tradeoff.


Repository:
  rLLDB LLDB

https://reviews.llvm.org/D68361

Files:
  llvm/test/tools/dsymutil/cmdline.test
  llvm/tools/dsymutil/CMakeLists.txt
  llvm/tools/dsymutil/Options.td
  llvm/tools/dsymutil/dsymutil.cpp

Index: llvm/tools/dsymutil/dsymutil.cpp
===================================================================
--- llvm/tools/dsymutil/dsymutil.cpp
+++ llvm/tools/dsymutil/dsymutil.cpp
@@ -20,12 +20,16 @@
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
 #include "llvm/ADT/Triple.h"
 #include "llvm/DebugInfo/DIContext.h"
 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
 #include "llvm/DebugInfo/DWARF/DWARFVerifier.h"
 #include "llvm/Object/Binary.h"
 #include "llvm/Object/MachO.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/InitLLVM.h"
@@ -43,137 +47,227 @@
 #include <system_error>
 
 using namespace llvm;
-using namespace llvm::cl;
 using namespace llvm::dsymutil;
 using namespace object;
 
-static OptionCategory DsymCategory("Specific Options");
-static opt<bool> Help("h", desc("Alias for -help"), Hidden);
-static opt<bool> Version("v", desc("Alias for -version"), Hidden);
-
-static list<std::string> InputFiles(Positional, OneOrMore,
-                                    desc("<input files>"), cat(DsymCategory));
-
-static opt<std::string>
-    OutputFileOpt("o",
-                  desc("Specify the output file. default: <input file>.dwarf"),
-                  value_desc("filename"), cat(DsymCategory));
-static alias OutputFileOptA("out", desc("Alias for -o"),
-                            aliasopt(OutputFileOpt));
-
-static opt<std::string> OsoPrependPath(
-    "oso-prepend-path",
-    desc("Specify a directory to prepend to the paths of object files."),
-    value_desc("path"), cat(DsymCategory));
-
-static opt<bool> Assembly(
-    "S",
-    desc("Output textual assembly instead of a binary dSYM companion file."),
-    init(false), cat(DsymCategory), cl::Hidden);
-
-static opt<bool> DumpStab(
-    "symtab",
-    desc("Dumps the symbol table found in executable or object file(s) and\n"
-         "exits."),
-    init(false), cat(DsymCategory));
-static alias DumpStabA("s", desc("Alias for --symtab"), aliasopt(DumpStab));
-
-static opt<bool> FlatOut("flat",
-                         desc("Produce a flat dSYM file (not a bundle)."),
-                         init(false), cat(DsymCategory));
-static alias FlatOutA("f", desc("Alias for --flat"), aliasopt(FlatOut));
-
-static opt<bool> Minimize(
-    "minimize",
-    desc("When used when creating a dSYM file with Apple accelerator tables,\n"
-         "this option will suppress the emission of the .debug_inlines, \n"
-         ".debug_pubnames, and .debug_pubtypes sections since dsymutil \n"
-         "has better equivalents: .apple_names and .apple_types. When used in\n"
-         "conjunction with --update option, this option will cause redundant\n"
-         "accelerator tables to be removed."),
-    init(false), cat(DsymCategory));
-static alias MinimizeA("z", desc("Alias for --minimize"), aliasopt(Minimize));
-
-static opt<bool> Update(
-    "update",
-    desc("Updates existing dSYM files to contain the latest accelerator\n"
-         "tables and other DWARF optimizations."),
-    init(false), cat(DsymCategory));
-static alias UpdateA("u", desc("Alias for --update"), aliasopt(Update));
-
-static opt<std::string> SymbolMap(
-    "symbol-map",
-    desc("Updates the existing dSYMs inplace using symbol map specified."),
-    value_desc("bcsymbolmap"), cat(DsymCategory));
-
-static cl::opt<AccelTableKind> AcceleratorTable(
-    "accelerator", cl::desc("Output accelerator tables."),
-    cl::values(clEnumValN(AccelTableKind::Default, "Default",
-                          "Default for input."),
-               clEnumValN(AccelTableKind::Apple, "Apple", "Apple"),
-               clEnumValN(AccelTableKind::Dwarf, "Dwarf", "DWARF")),
-    cl::init(AccelTableKind::Default), cat(DsymCategory));
-
-static opt<unsigned> NumThreads(
-    "num-threads",
-    desc("Specifies the maximum number (n) of simultaneous threads to use\n"
-         "when linking multiple architectures."),
-    value_desc("n"), init(0), cat(DsymCategory));
-static alias NumThreadsA("j", desc("Alias for --num-threads"),
-                         aliasopt(NumThreads));
-
-static opt<bool> Verbose("verbose", desc("Verbosity level"), init(false),
-                         cat(DsymCategory));
-
-static opt<bool>
-    NoOutput("no-output",
-             desc("Do the link in memory, but do not emit the result file."),
-             init(false), cat(DsymCategory));
-
-static opt<bool>
-    NoTimestamp("no-swiftmodule-timestamp",
-                desc("Don't check timestamp for swiftmodule files."),
-                init(false), cat(DsymCategory));
-
-static list<std::string> ArchFlags(
-    "arch",
-    desc("Link DWARF debug information only for specified CPU architecture\n"
-         "types. This option can be specified multiple times, once for each\n"
-         "desired architecture. All CPU architectures will be linked by\n"
-         "default."),
-    value_desc("arch"), ZeroOrMore, cat(DsymCategory));
-
-static opt<bool>
-    NoODR("no-odr",
-          desc("Do not use ODR (One Definition Rule) for type uniquing."),
-          init(false), cat(DsymCategory));
-
-static opt<bool> DumpDebugMap(
-    "dump-debug-map",
-    desc("Parse and dump the debug map to standard output. Not DWARF link "
-         "will take place."),
-    init(false), cat(DsymCategory));
-
-static opt<bool> InputIsYAMLDebugMap(
-    "y", desc("Treat the input file is a YAML debug map rather than a binary."),
-    init(false), cat(DsymCategory));
-
-static opt<bool> Verify("verify", desc("Verify the linked DWARF debug info."),
-                        cat(DsymCategory));
-
-static opt<std::string>
-    Toolchain("toolchain", desc("Embed toolchain information in dSYM bundle."),
-              cat(DsymCategory));
-
-static opt<bool>
-    PaperTrailWarnings("papertrail",
-                       desc("Embed warnings in the linked DWARF debug info."),
-                       cat(DsymCategory));
-
-static Error createPlistFile(llvm::StringRef Bin, llvm::StringRef BundleRoot) {
-  if (NoOutput)
-    return Error::success();
+namespace {
+enum ID {
+  OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
+               HELPTEXT, METAVAR, VALUES)                                      \
+  OPT_##ID,
+#include "Options.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Options.inc"
+#undef PREFIX
+
+const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
+               HELPTEXT, METAVAR, VALUES)                                      \
+  {                                                                            \
+      PREFIX,      NAME,      HELPTEXT,                                        \
+      METAVAR,     OPT_##ID,  opt::Option::KIND##Class,                        \
+      PARAM,       FLAGS,     OPT_##GROUP,                                     \
+      OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Options.inc"
+#undef OPTION
+};
+
+class DsymutilOptTable : public opt::OptTable {
+public:
+  DsymutilOptTable() : OptTable(InfoTable) {}
+};
+} // namespace
+
+struct DsymutilOptions {
+  bool DumpDebugMap = false;
+  bool DumpStab = false;
+  bool Flat = false;
+  bool InputIsYAMLDebugMap = false;
+  bool PaperTrailWarnings = false;
+  bool Verify = false;
+  std::string SymbolMap;
+  std::string OutputFile;
+  std::string Toolchain;
+  std::vector<std::string> Archs;
+  std::vector<std::string> InputFiles;
+  unsigned NumThreads;
+  LinkOptions LinkOptions;
+};
+
+/// Return a list of input files. This function has logic for dealing with the
+/// special case where we might have dSYM bundles as input. The function
+/// returns an error when the directory structure doesn't match that of a dSYM
+/// bundle.
+static Expected<std::vector<std::string>> getInputs(opt::InputArgList &Args,
+                                                    bool DsymAsInput) {
+  std::vector<std::string> InputFiles;
+  for (auto *File : Args.filtered(OPT_INPUT))
+    InputFiles.push_back(File->getValue());
+
+  if (!DsymAsInput)
+    return InputFiles;
+
+  // If we are updating, we might get dSYM bundles as input.
+  std::vector<std::string> Inputs;
+  for (const auto &Input : InputFiles) {
+    if (!llvm::sys::fs::is_directory(Input)) {
+      Inputs.push_back(Input);
+      continue;
+    }
+
+    // Make sure that we're dealing with a dSYM bundle.
+    SmallString<256> BundlePath(Input);
+    sys::path::append(BundlePath, "Contents", "Resources", "DWARF");
+    if (!llvm::sys::fs::is_directory(BundlePath))
+      return make_error<StringError>(
+          Input + " is a directory, but doesn't look like a dSYM bundle.",
+          inconvertibleErrorCode());
+
+    // Create a directory iterator to iterate over all the entries in the
+    // bundle.
+    std::error_code EC;
+    llvm::sys::fs::directory_iterator DirIt(BundlePath, EC);
+    llvm::sys::fs::directory_iterator DirEnd;
+    if (EC)
+      return errorCodeToError(EC);
+
+    // Add each entry to the list of inputs.
+    while (DirIt != DirEnd) {
+      Inputs.push_back(DirIt->path());
+      DirIt.increment(EC);
+      if (EC)
+        return errorCodeToError(EC);
+    }
+  }
+  return Inputs;
+}
+
+// Verify that the given combination of options make sense.
+static llvm::Error verifyOptions(const DsymutilOptions &Options) {
+  if (Options.LinkOptions.Update &&
+      std::find(Options.InputFiles.begin(), Options.InputFiles.end(), "-") !=
+          Options.InputFiles.end()) {
+    // FIXME: We cannot use stdin for an update because stdin will be
+    // consumed by the BinaryHolder during the debugmap parsing, and
+    // then we will want to consume it again in DwarfLinker. If we
+    // used a unique BinaryHolder object that could cache multiple
+    // binaries this restriction would go away.
+    return make_error<StringError>(
+        "standard input cannot be used as input for a dSYM update.",
+        inconvertibleErrorCode());
+  }
+
+  if (!Options.Flat && Options.OutputFile == "-")
+    return make_error<StringError>(
+        "cannot emit to standard output without --flat.",
+        inconvertibleErrorCode());
+
+  if (Options.InputFiles.size() > 1 && Options.Flat &&
+      !Options.OutputFile.empty())
+    return make_error<StringError>(
+        "cannot use -o with multiple inputs in flat mode.",
+        inconvertibleErrorCode());
+
+  if (Options.PaperTrailWarnings && Options.InputIsYAMLDebugMap)
+    return make_error<StringError>(
+        "paper trail warnings are not supported for YAML input.",
+        inconvertibleErrorCode());
+
+  return Error::success();
+}
+
+static Expected<AccelTableKind> getAccelTableKind(opt::InputArgList &Args) {
+  if (opt::Arg *Accelerator = Args.getLastArg(OPT_accelerator)) {
+    StringRef S = Accelerator->getValue();
+    if (S == "Apple")
+      return AccelTableKind::Apple;
+    if (S == "Dwarf")
+      return AccelTableKind::Dwarf;
+    if (S == "Default")
+      return AccelTableKind::Default;
+    return make_error<StringError>(
+        "invalid accelerator type specified: '" + S +
+            "'. Support values are 'Apple', 'Dwarf' and 'Default'.",
+        inconvertibleErrorCode());
+  }
+  return AccelTableKind::Default;
+}
+
+/// Parses the command line options into the LinkOptions struct and performs
+/// some sanity checking. Returns an error in case the latter fails.
+static Expected<DsymutilOptions> getOptions(opt::InputArgList &Args) {
+  DsymutilOptions Options;
+
+  Options.DumpDebugMap = Args.hasArg(OPT_dump_debug_map);
+  Options.DumpStab = Args.hasArg(OPT_symtab);
+  Options.Flat = Args.hasArg(OPT_flat);
+  Options.InputIsYAMLDebugMap = Args.hasArg(OPT_yaml_input);
+  Options.PaperTrailWarnings = Args.hasArg(OPT_papertrail);
+  Options.Verify = Args.hasArg(OPT_verify);
+
+  Options.LinkOptions.Minimize = Args.hasArg(OPT_minimize);
+  Options.LinkOptions.NoODR = Args.hasArg(OPT_no_odr);
+  Options.LinkOptions.NoOutput = Args.hasArg(OPT_no_output);
+  Options.LinkOptions.NoTimestamp = Args.hasArg(OPT_no_swiftmodule_timestamp);
+  Options.LinkOptions.Update = Args.hasArg(OPT_update);
+  Options.LinkOptions.Verbose = Args.hasArg(OPT_verbose);
+
+  if (Expected<AccelTableKind> AccelKind = getAccelTableKind(Args)) {
+    Options.LinkOptions.TheAccelTableKind = *AccelKind;
+  } else {
+    return AccelKind.takeError();
+  }
+
+  if (opt::Arg *SymbolMap = Args.getLastArg(OPT_symbolmap))
+    Options.SymbolMap = SymbolMap->getValue();
+
+  if (Args.hasArg(OPT_symbolmap))
+    Options.LinkOptions.Update = true;
+
+  if (Expected<std::vector<std::string>> InputFiles =
+          getInputs(Args, Options.LinkOptions.Update)) {
+    Options.InputFiles = *InputFiles;
+  } else {
+    return InputFiles.takeError();
+  }
+
+  for (auto *Arch : Args.filtered(OPT_arch))
+    Options.Archs.push_back(Arch->getValue());
+
+  if (opt::Arg *OsoPrependPath = Args.getLastArg(OPT_oso_prepend_path))
+    Options.LinkOptions.PrependPath = OsoPrependPath->getValue();
+
+  if (opt::Arg *OutputFile = Args.getLastArg(OPT_output))
+    Options.OutputFile = OutputFile->getValue();
+
+  if (opt::Arg *Toolchain = Args.getLastArg(OPT_toolchain))
+    Options.Toolchain = Toolchain->getValue();
+
+  if (Args.hasArg(OPT_assembly))
+    Options.LinkOptions.FileType = OutputFileType::Assembly;
+
+  if (opt::Arg *NumThreads = Args.getLastArg(OPT_threads))
+    Options.LinkOptions.Threads = atoi(NumThreads->getValue());
+  else
+    Options.LinkOptions.Threads = llvm::thread::hardware_concurrency();
+
+  if (Options.DumpDebugMap || Options.LinkOptions.Verbose)
+    Options.LinkOptions.Threads = 1;
+
+  if (getenv("RC_DEBUG_OPTIONS"))
+    Options.PaperTrailWarnings = true;
+
+  if (Error E = verifyOptions(Options))
+    return std::move(E);
+  return Options;
+}
 
+static Error createPlistFile(llvm::StringRef Bin, llvm::StringRef BundleRoot,
+                             llvm::StringRef Toolchain) {
   // Create plist file to write to.
   llvm::SmallString<128> InfoPlist(BundleRoot);
   llvm::sys::path::append(InfoPlist, "Contents/Info.plist");
@@ -237,9 +331,6 @@
 }
 
 static Error createBundleDir(llvm::StringRef BundleBase) {
-  if (NoOutput)
-    return Error::success();
-
   llvm::SmallString<128> Bundle(BundleBase);
   llvm::sys::path::append(Bundle, "Contents", "Resources", "DWARF");
   if (std::error_code EC =
@@ -250,7 +341,8 @@
   return Error::success();
 }
 
-static bool verify(llvm::StringRef OutputFile, llvm::StringRef Arch) {
+static bool verify(llvm::StringRef OutputFile, llvm::StringRef Arch,
+                   bool Verbose) {
   if (OutputFile == "-") {
     WithColor::warning() << "verification skipped for " << Arch
                          << "because writing to stdout.\n";
@@ -288,25 +380,27 @@
   std::string DWARFFile;
   llvm::Optional<std::string> ResourceDir;
 };
-}
+} // namespace
 
-static Expected<OutputLocation> getOutputFileName(llvm::StringRef InputFile) {
-  if (OutputFileOpt == "-")
-    return OutputLocation(OutputFileOpt);
+static Expected<OutputLocation>
+getOutputFileName(llvm::StringRef InputFile, const DsymutilOptions &Options) {
+  if (Options.OutputFile == "-")
+    return OutputLocation(Options.OutputFile);
 
   // When updating, do in place replacement.
-  if (OutputFileOpt.empty() && (Update || !SymbolMap.empty()))
+  if (Options.OutputFile.empty() &&
+      (Options.LinkOptions.Update || !Options.SymbolMap.empty()))
     return OutputLocation(InputFile);
 
   // If a flat dSYM has been requested, things are pretty simple.
-  if (FlatOut) {
-    if (OutputFileOpt.empty()) {
+  if (Options.Flat) {
+    if (Options.OutputFile.empty()) {
       if (InputFile == "-")
         return OutputLocation{"a.out.dwarf", {}};
       return OutputLocation((InputFile + ".dwarf").str());
     }
 
-    return OutputLocation(OutputFileOpt);
+    return OutputLocation(Options.OutputFile);
   }
 
   // We need to create/update a dSYM bundle.
@@ -319,13 +413,15 @@
   //                <DWARF file(s)>
   std::string DwarfFile =
       InputFile == "-" ? llvm::StringRef("a.out") : InputFile;
-  llvm::SmallString<128> Path(OutputFileOpt);
+  llvm::SmallString<128> Path(Options.OutputFile);
   if (Path.empty())
     Path = DwarfFile + ".dSYM";
-  if (auto E = createBundleDir(Path))
-    return std::move(E);
-  if (auto E = createPlistFile(DwarfFile, Path))
-    return std::move(E);
+  if (!Options.LinkOptions.NoOutput) {
+    if (auto E = createBundleDir(Path))
+      return std::move(E);
+    if (auto E = createPlistFile(DwarfFile, Path, Options.Toolchain))
+      return std::move(E);
+  }
 
   llvm::sys::path::append(Path, "Contents", "Resources");
   std::string ResourceDir = Path.str();
@@ -333,177 +429,71 @@
   return OutputLocation(Path.str(), ResourceDir);
 }
 
-/// Parses the command line options into the LinkOptions struct and performs
-/// some sanity checking. Returns an error in case the latter fails.
-static Expected<LinkOptions> getOptions() {
-  LinkOptions Options;
-
-  Options.Verbose = Verbose;
-  Options.NoOutput = NoOutput;
-  Options.NoODR = NoODR;
-  Options.Minimize = Minimize;
-  Options.Update = Update;
-  Options.NoTimestamp = NoTimestamp;
-  Options.PrependPath = OsoPrependPath;
-  Options.TheAccelTableKind = AcceleratorTable;
-
-  if (!SymbolMap.empty())
-    Options.Update = true;
-
-  if (Assembly)
-    Options.FileType = OutputFileType::Assembly;
-
-  if (Options.Update && std::find(InputFiles.begin(), InputFiles.end(), "-") !=
-                            InputFiles.end()) {
-    // FIXME: We cannot use stdin for an update because stdin will be
-    // consumed by the BinaryHolder during the debugmap parsing, and
-    // then we will want to consume it again in DwarfLinker. If we
-    // used a unique BinaryHolder object that could cache multiple
-    // binaries this restriction would go away.
-    return make_error<StringError>(
-        "standard input cannot be used as input for a dSYM update.",
-        inconvertibleErrorCode());
-  }
-
-  if (NumThreads == 0)
-    Options.Threads = llvm::thread::hardware_concurrency();
-  else
-    Options.Threads = NumThreads;
-  if (DumpDebugMap || Verbose)
-    Options.Threads = 1;
-
-  return Options;
-}
-
-/// Return a list of input files. This function has logic for dealing with the
-/// special case where we might have dSYM bundles as input. The function
-/// returns an error when the directory structure doesn't match that of a dSYM
-/// bundle.
-static Expected<std::vector<std::string>> getInputs(bool DsymAsInput) {
-  if (!DsymAsInput)
-    return InputFiles;
-
-  // If we are updating, we might get dSYM bundles as input.
-  std::vector<std::string> Inputs;
-  for (const auto &Input : InputFiles) {
-    if (!llvm::sys::fs::is_directory(Input)) {
-      Inputs.push_back(Input);
-      continue;
-    }
-
-    // Make sure that we're dealing with a dSYM bundle.
-    SmallString<256> BundlePath(Input);
-    sys::path::append(BundlePath, "Contents", "Resources", "DWARF");
-    if (!llvm::sys::fs::is_directory(BundlePath))
-      return make_error<StringError>(
-          Input + " is a directory, but doesn't look like a dSYM bundle.",
-          inconvertibleErrorCode());
-
-    // Create a directory iterator to iterate over all the entries in the
-    // bundle.
-    std::error_code EC;
-    llvm::sys::fs::directory_iterator DirIt(BundlePath, EC);
-    llvm::sys::fs::directory_iterator DirEnd;
-    if (EC)
-      return errorCodeToError(EC);
-
-    // Add each entry to the list of inputs.
-    while (DirIt != DirEnd) {
-      Inputs.push_back(DirIt->path());
-      DirIt.increment(EC);
-      if (EC)
-        return errorCodeToError(EC);
-    }
-  }
-  return Inputs;
-}
-
 int main(int argc, char **argv) {
   InitLLVM X(argc, argv);
 
+  // Parse arguments.
+  DsymutilOptTable T;
+  unsigned MAI;
+  unsigned MAC;
+  ArrayRef<const char *> ArgsArr = makeArrayRef(argv + 1, argc - 1);
+  opt::InputArgList Args = T.ParseArgs(ArgsArr, MAI, MAC);
+
   void *P = (void *)(intptr_t)getOutputFileName;
   std::string SDKPath = llvm::sys::fs::getMainExecutable(argv[0], P);
   SDKPath = llvm::sys::path::parent_path(SDKPath);
 
-  HideUnrelatedOptions({&DsymCategory, &ColorCategory});
-  llvm::cl::ParseCommandLineOptions(
-      argc, argv,
-      "manipulate archived DWARF debug symbol files.\n\n"
-      "dsymutil links the DWARF debug information found in the object files\n"
-      "for the executable <input file> by using debug symbols information\n"
-      "contained in its symbol table.\n");
-
-  if (Help) {
-    PrintHelpMessage();
+  if (Args.hasArg(OPT_help)) {
+    T.PrintHelp(
+        llvm::outs(),
+        (std::string(argv[0]) + " [options] <input files>").c_str(),
+        "manipulate archived DWARF debug symbol files.\n\n"
+        "dsymutil links the DWARF debug information found in the object files\n"
+        "for the executable <input file> by using debug symbols information\n"
+        "contained in its symbol table.\n",
+        false);
     return 0;
   }
 
-  if (Version) {
+  if (Args.hasArg(OPT_version)) {
     llvm::cl::PrintVersionMessage();
     return 0;
   }
 
-  auto OptionsOrErr = getOptions();
+  auto OptionsOrErr = getOptions(Args);
   if (!OptionsOrErr) {
     WithColor::error() << toString(OptionsOrErr.takeError());
     return 1;
   }
 
+  auto &Options = *OptionsOrErr;
+
   llvm::InitializeAllTargetInfos();
   llvm::InitializeAllTargetMCs();
   llvm::InitializeAllTargets();
   llvm::InitializeAllAsmPrinters();
 
-  auto InputsOrErr = getInputs(OptionsOrErr->Update);
-  if (!InputsOrErr) {
-    WithColor::error() << toString(InputsOrErr.takeError()) << '\n';
-    return 1;
-  }
-
-  if (!FlatOut && OutputFileOpt == "-") {
-    WithColor::error() << "cannot emit to standard output without --flat\n";
-    return 1;
-  }
-
-  if (InputsOrErr->size() > 1 && FlatOut && !OutputFileOpt.empty()) {
-    WithColor::error() << "cannot use -o with multiple inputs in flat mode\n";
-    return 1;
-  }
-
-  if (InputFiles.size() > 1 && !SymbolMap.empty() &&
-      !llvm::sys::fs::is_directory(SymbolMap)) {
-    WithColor::error() << "when unobfuscating multiple files, --symbol-map "
-                       << "needs to point to a directory.\n";
-    return 1;
-  }
-
-  if (getenv("RC_DEBUG_OPTIONS"))
-    PaperTrailWarnings = true;
-
-  if (PaperTrailWarnings && InputIsYAMLDebugMap)
-    WithColor::warning()
-        << "Paper trail warnings are not supported for YAML input";
-
-  for (const auto &Arch : ArchFlags)
+  for (const auto &Arch : Options.Archs)
     if (Arch != "*" && Arch != "all" &&
         !llvm::object::MachOObjectFile::isValidArch(Arch)) {
       WithColor::error() << "unsupported cpu architecture: '" << Arch << "'\n";
       return 1;
     }
 
-  SymbolMapLoader SymMapLoader(SymbolMap);
+  SymbolMapLoader SymMapLoader(Options.SymbolMap);
 
-  for (auto &InputFile : *InputsOrErr) {
+  for (auto &InputFile : Options.InputFiles) {
     // Dump the symbol table for each input file and requested arch
-    if (DumpStab) {
-      if (!dumpStab(InputFile, ArchFlags, OsoPrependPath))
+    if (Options.DumpStab) {
+      if (!dumpStab(InputFile, Options.Archs, Options.LinkOptions.PrependPath))
         return 1;
       continue;
     }
 
     auto DebugMapPtrsOrErr =
-        parseDebugMap(InputFile, ArchFlags, OsoPrependPath, PaperTrailWarnings,
-                      Verbose, InputIsYAMLDebugMap);
+        parseDebugMap(InputFile, Options.Archs, Options.LinkOptions.PrependPath,
+                      Options.PaperTrailWarnings, Options.LinkOptions.Verbose,
+                      Options.InputIsYAMLDebugMap);
 
     if (auto EC = DebugMapPtrsOrErr.getError()) {
       WithColor::error() << "cannot parse the debug map for '" << InputFile
@@ -511,7 +501,7 @@
       return 1;
     }
 
-    if (OptionsOrErr->Update) {
+    if (Options.LinkOptions.Update) {
       // The debug map should be empty. Add one object file corresponding to
       // the input file.
       for (auto &Map : *DebugMapPtrsOrErr)
@@ -528,27 +518,27 @@
     // Shared a single binary holder for all the link steps.
     BinaryHolder BinHolder;
 
-    unsigned ThreadCount =
-        std::min<unsigned>(OptionsOrErr->Threads, DebugMapPtrsOrErr->size());
+    unsigned ThreadCount = std::min<unsigned>(Options.LinkOptions.Threads,
+                                              DebugMapPtrsOrErr->size());
     llvm::ThreadPool Threads(ThreadCount);
 
     // If there is more than one link to execute, we need to generate
     // temporary files.
     bool NeedsTempFiles =
-        !DumpDebugMap && (OutputFileOpt != "-") &&
-        (DebugMapPtrsOrErr->size() != 1 || OptionsOrErr->Update);
+        !Options.DumpDebugMap && (Options.OutputFile != "-") &&
+        (DebugMapPtrsOrErr->size() != 1 || Options.LinkOptions.Update);
 
     llvm::SmallVector<MachOUtils::ArchAndFile, 4> TempFiles;
     std::atomic_char AllOK(1);
     for (auto &Map : *DebugMapPtrsOrErr) {
-      if (Verbose || DumpDebugMap)
+      if (Options.LinkOptions.Verbose || Options.DumpDebugMap)
         Map->print(llvm::outs());
 
-      if (DumpDebugMap)
+      if (Options.DumpDebugMap)
         continue;
 
-      if (!SymbolMap.empty())
-        OptionsOrErr->Translator = SymMapLoader.Load(InputFile, *Map);
+      if (!Options.SymbolMap.empty())
+        Options.LinkOptions.Translator = SymMapLoader.Load(InputFile, *Map);
 
       if (Map->begin() == Map->end())
         WithColor::warning()
@@ -560,12 +550,12 @@
       std::shared_ptr<raw_fd_ostream> OS;
 
       Expected<OutputLocation> OutputLocationOrErr =
-          getOutputFileName(InputFile);
+          getOutputFileName(InputFile, Options);
       if (!OutputLocationOrErr) {
         WithColor::error() << toString(OutputLocationOrErr.takeError());
         return 1;
       }
-      OptionsOrErr->ResourceDir = OutputLocationOrErr->getResourceDir();
+      Options.LinkOptions.ResourceDir = OutputLocationOrErr->getResourceDir();
 
       std::string OutputFile = OutputLocationOrErr->DWARFFile;
       if (NeedsTempFiles) {
@@ -583,30 +573,33 @@
         OutputFile = TempFile.TmpName;
       } else {
         std::error_code EC;
-        OS = std::make_shared<raw_fd_ostream>(NoOutput ? "-" : OutputFile, EC,
-                                              sys::fs::OF_None);
+        OS = std::make_shared<raw_fd_ostream>(
+            Options.LinkOptions.NoOutput ? "-" : OutputFile, EC,
+            sys::fs::OF_None);
         if (EC) {
           WithColor::error() << OutputFile << ": " << EC.message();
           return 1;
         }
       }
 
+      const bool Verify = Options.Verify && !Options.LinkOptions.NoOutput;
       auto LinkLambda = [&, OutputFile](std::shared_ptr<raw_fd_ostream> Stream,
                                         LinkOptions Options) {
         AllOK.fetch_and(
             linkDwarf(*Stream, BinHolder, *Map, std::move(Options)));
         Stream->flush();
-        if (Verify && !NoOutput)
-          AllOK.fetch_and(verify(OutputFile, Map->getTriple().getArchName()));
+        if (Verify)
+          AllOK.fetch_and(verify(OutputFile, Map->getTriple().getArchName(),
+                                 Options.Verbose));
       };
 
       // FIXME: The DwarfLinker can have some very deep recursion that can max
       // out the (significantly smaller) stack when using threads. We don't
       // want this limitation when we only have a single thread.
       if (ThreadCount == 1)
-        LinkLambda(OS, *OptionsOrErr);
+        LinkLambda(OS, Options.LinkOptions);
       else
-        Threads.async(LinkLambda, OS, *OptionsOrErr);
+        Threads.async(LinkLambda, OS, Options.LinkOptions);
     }
 
     Threads.wait();
@@ -615,14 +608,15 @@
       return 1;
 
     if (NeedsTempFiles) {
-      Expected<OutputLocation> OutputLocationOrErr = getOutputFileName(InputFile);
+      Expected<OutputLocation> OutputLocationOrErr =
+          getOutputFileName(InputFile, Options);
       if (!OutputLocationOrErr) {
         WithColor::error() << toString(OutputLocationOrErr.takeError());
         return 1;
       }
       if (!MachOUtils::generateUniversalBinary(TempFiles,
                                                OutputLocationOrErr->DWARFFile,
-                                               *OptionsOrErr, SDKPath))
+                                               Options.LinkOptions, SDKPath))
         return 1;
     }
   }
Index: llvm/tools/dsymutil/Options.td
===================================================================
--- /dev/null
+++ llvm/tools/dsymutil/Options.td
@@ -0,0 +1,146 @@
+include "llvm/Option/OptParser.td"
+
+class F<string name>: Flag<["--", "-"], name>;
+
+def grp_general : OptionGroup<"Dsymutil">, HelpText<"Dsymutil Options">;
+
+def help: F<"help">,
+  HelpText<"Prints this help output.">,
+  Group<grp_general>;
+def: Flag<["-"], "h">,
+  Alias<help>,
+  HelpText<"Alias for --help">,
+  Group<grp_general>;
+
+def version: F<"version">,
+  HelpText<"Prints the dsymutil version.">,
+  Group<grp_general>;
+def: Flag<["-"], "v">,
+  Alias<version>,
+  HelpText<"Alias for --version">,
+  Group<grp_general>;
+
+def verbose: F<"verbose">,
+  HelpText<"Enable verbose mode.">,
+  Group<grp_general>;
+
+def verify: F<"verify">,
+  HelpText<"Run the DWARF verifier on the linked DWARF debug info.">,
+  Group<grp_general>;
+
+def no_output: F<"no-output">,
+  HelpText<"Do the link in memory, but do not emit the result file.">,
+  Group<grp_general>;
+
+def no_swiftmodule_timestamp: F<"no-swiftmodule-timestamp">,
+  HelpText<"Don't check timestamp for swiftmodule files.">,
+  Group<grp_general>;
+
+def no_odr: F<"no-odr">,
+  HelpText<"Do not use ODR (One Definition Rule) for type uniquing.">,
+  Group<grp_general>;
+
+def dump_debug_map: F<"dump-debug-map">,
+  HelpText<"Parse and dump the debug map to standard output. Not DWARF link will take place.">,
+  Group<grp_general>;
+
+def yaml_input: F<"y">,
+  HelpText<"Treat the input file is a YAML debug map rather than a binary.">,
+  Group<grp_general>;
+
+def papertrail: F<"papertrail">,
+  HelpText<"Embed warnings in the linked DWARF debug info.">,
+  Group<grp_general>;
+
+def assembly: F<"S">,
+  HelpText<"Output textual assembly instead of a binary dSYM companion file.">,
+  Group<grp_general>;
+
+def symtab: F<"symtab">,
+  HelpText<"Dumps the symbol table found in executable or object file(s) and exits.">,
+  Group<grp_general>;
+def: Flag<["-"], "s">,
+  Alias<symtab>,
+  HelpText<"Alias for --symtab">,
+  Group<grp_general>;
+
+def flat: F<"flat">,
+  HelpText<"Produce a flat dSYM file (not a bundle).">,
+  Group<grp_general>;
+def: Flag<["-"], "f">,
+  Alias<flat>,
+  HelpText<"Alias for --flat">,
+  Group<grp_general>;
+
+def minimize: F<"minimize">,
+  HelpText<"When used when creating a dSYM file with Apple accelerator tables, "
+           "this option will suppress the emission of the .debug_inlines, "
+           ".debug_pubnames, and .debug_pubtypes sections since dsymutil "
+           "has better equivalents: .apple_names and .apple_types. When used in "
+           "conjunction with --update option, this option will cause redundant "
+           "accelerator tables to be removed.">,
+  Group<grp_general>;
+def: Flag<["-"], "z">,
+  Alias<minimize>,
+  HelpText<"Alias for --minimize">,
+  Group<grp_general>;
+
+def update: F<"update">,
+  HelpText<"Updates existing dSYM files to contain the latest accelerator tables and other DWARF optimizations.">,
+  Group<grp_general>;
+def: Flag<["-"], "u">,
+  Alias<update>,
+  HelpText<"Alias for --update">,
+  Group<grp_general>;
+
+def output: Separate<["--", "-"], "o">,
+  MetaVarName<"<filename>">,
+  HelpText<"Specify the output file. Defaults to <input file>.dwarf">,
+  Group<grp_general>;
+def: Separate<["-"], "out">,
+  Alias<output>,
+  HelpText<"Alias for --o">,
+  Group<grp_general>;
+def: Joined<["--", "-"], "out=">, Alias<output>;
+def: Joined<["--", "-"], "o=">, Alias<output>;
+
+def oso_prepend_path: Separate<["--", "-"], "oso-prepend-path">,
+  MetaVarName<"<path>">,
+  HelpText<"Specify a directory to prepend to the paths of object files.">,
+  Group<grp_general>;
+def: Joined<["--", "-"], "oso-prepend-path=">, Alias<oso_prepend_path>;
+
+def symbolmap: Separate<["--", "-"], "symbol-map">,
+  MetaVarName<"<bcsymbolmap>">,
+  HelpText<"Updates the existing dSYMs inplace using symbol map specified.">,
+  Group<grp_general>;
+def: Joined<["--", "-"], "symbol-map=">, Alias<symbolmap>;
+
+def arch: Separate<["--", "-"], "arch">,
+  MetaVarName<"<arch>">,
+  HelpText<"Link DWARF debug information only for specified CPU architecture"
+           "types. This option can be specified multiple times, once for each"
+           "desired architecture. All CPU architectures will be linked by"
+           "default.">,
+  Group<grp_general>;
+def: Joined<["--", "-"], "arch=">, Alias<arch>;
+
+def accelerator: Separate<["--", "-"], "accelerator">,
+  MetaVarName<"<accelerator type>">,
+  HelpText<"Specify the desired type of accelerator table. Valid options are 'Apple', 'Dwarf' and 'Default'">,
+  Group<grp_general>;
+def: Joined<["--", "-"], "accelerator=">, Alias<accelerator>;
+
+def toolchain: Separate<["--", "-"], "toolchain">,
+  MetaVarName<"<toolchain>">,
+  HelpText<"Embed toolchain information in dSYM bundle.">,
+  Group<grp_general>;
+
+def threads: Separate<["--", "-"], "num-threads">,
+  MetaVarName<"<threads>">,
+  HelpText<"Specifies the maximum number of simultaneous threads to use when linking multiple architectures.">,
+  Group<grp_general>;
+def: Separate<["-"], "j">,
+  Alias<threads>,
+  HelpText<"Alias for --num-threads">,
+  Group<grp_general>;
Index: llvm/tools/dsymutil/CMakeLists.txt
===================================================================
--- llvm/tools/dsymutil/CMakeLists.txt
+++ llvm/tools/dsymutil/CMakeLists.txt
@@ -1,3 +1,7 @@
+set(LLVM_TARGET_DEFINITIONS Options.td)
+tablegen(LLVM Options.inc -gen-opt-parser-defs)
+add_public_tablegen_target(DsymutilTableGen)
+
 set(LLVM_LINK_COMPONENTS
   AllTargetsAsmPrinters
   AllTargetsCodeGens
@@ -7,6 +11,7 @@
   DebugInfoDWARF
   MC
   Object
+  Option
   Support
   Target
   )
@@ -27,6 +32,7 @@
 
   DEPENDS
   intrinsics_gen
+  ${tablegen_deps}
   )
 
 if(APPLE)
Index: llvm/test/tools/dsymutil/cmdline.test
===================================================================
--- llvm/test/tools/dsymutil/cmdline.test
+++ llvm/test/tools/dsymutil/cmdline.test
@@ -1,21 +1,19 @@
 RUN: dsymutil -help 2>&1 | FileCheck --check-prefix=HELP %s
 HELP: OVERVIEW: manipulate archived DWARF debug symbol files.
-HELP: USAGE: dsymutil{{[^ ]*}} [options] <input files>
+HELP: USAGE: {{.*}}dsymutil{{[^ ]*}} [options] <input files>
 HELP-NOT: -reverse-iterate
-HELP: Color Options
-HELP: -color
-HELP: Specific Options:
+HELP: Dsymutil Options:
 HELP: -accelerator
-HELP: -arch=<arch>
+HELP: -arch <arch>
 HELP: -dump-debug-map
 HELP: -flat
 HELP: -minimize
 HELP: -no-odr
 HELP: -no-output
 HELP: -no-swiftmodule-timestamp
-HELP: -num-threads=<n>
-HELP: -o=<filename>
-HELP: -oso-prepend-path=<path>
+HELP: -num-threads <threads>
+HELP: -oso-prepend-path <path>
+HELP: -o <filename>
 HELP: -papertrail
 HELP: -symbol-map
 HELP: -symtab
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
  • [Lldb-commits] [PA... Jonas Devlieghere via Phabricator via lldb-commits

Reply via email to