[dsymutil] Implement support for handling mach-o universal binaries as main input...
[oota-llvm.git] / tools / dsymutil / dsymutil.cpp
index d9899be6b4e1779810f991db53872f7175497a41..48eb9a14f0a9d1b7a0d161d96a2d88f4077cfbb5 100644 (file)
 //===----------------------------------------------------------------------===//
 
 #include "DebugMap.h"
+#include "MachOUtils.h"
 #include "dsymutil.h"
+#include "llvm/Support/FileUtilities.h"
 #include "llvm/Support/ManagedStatic.h"
 #include "llvm/Support/Options.h"
 #include "llvm/Support/PrettyStackTrace.h"
 #include "llvm/Support/Signals.h"
 #include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/TargetSelect.h"
 #include <string>
 
 using namespace llvm::dsymutil;
@@ -26,25 +29,77 @@ using namespace llvm::dsymutil;
 namespace {
 using namespace llvm::cl;
 
-static opt<std::string> InputFile(Positional, desc("<input file>"),
-                                  init("a.out"));
+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 opt<std::string> OutputFileOpt("o", desc("Specify the output file."
-                                                " default: <input file>.dwarf"),
-                                      value_desc("filename"));
+static list<std::string> InputFiles(Positional, OneOrMore,
+                                    desc("<input files>"), cat(DsymCategory));
 
-static opt<std::string> OsoPrependPath("oso-prepend-path",
-                                       desc("Specify a directory to prepend "
-                                            "to the paths of object files."),
-                                       value_desc("path"));
+static opt<std::string>
+    OutputFileOpt("o",
+                  desc("Specify the output file. default: <input file>.dwarf"),
+                  value_desc("filename"), cat(DsymCategory));
 
-static opt<bool> Verbose("v", desc("Verbosity level"), init(false));
+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> 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>
-    ParseOnly("parse-only",
-              desc("Only parse the debug map, do not actaully link "
-                   "the DWARF."),
-              init(false));
+    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 std::string getOutputFileName(llvm::StringRef InputFile,
+                                     bool TempFile = false) {
+  if (TempFile) {
+    std::string OutputFile = (InputFile + ".tmp%%%%%%.dwarf").str();
+    int FD;
+    llvm::SmallString<128> UniqueFile;
+    if (auto EC = llvm::sys::fs::createUniqueFile(OutputFile, FD, UniqueFile)) {
+      llvm::errs() << "error: failed to create temporary outfile '"
+                   << OutputFile << "': " << EC.message() << '\n';
+      return "";
+    }
+    llvm::sys::RemoveFileOnSignal(UniqueFile);
+    // Close the file immediately. We know it is unique. It will be
+    // reopened and written to later.
+    llvm::raw_fd_ostream CloseImmediately(FD, true /* shouldClose */, true);
+    return UniqueFile.str();
+  }
+
+  if (OutputFileOpt.empty()) {
+    if (InputFile == "-")
+      return "a.out.dwarf";
+    return (InputFile + ".dwarf").str();
+  }
+  return OutputFileOpt;
+}
+
+void llvm::dsymutil::exitDsymutil(int ExitStatus) {
+  // Cleanup temporary files.
+  llvm::sys::RunInterruptHandlers();
+  exit(ExitStatus);
 }
 
 int main(int argc, char **argv) {
@@ -53,32 +108,71 @@ int main(int argc, char **argv) {
   llvm::llvm_shutdown_obj Shutdown;
   LinkOptions Options;
 
-  llvm::cl::ParseCommandLineOptions(argc, argv, "llvm dsymutil\n");
-  auto DebugMapPtrOrErr = parseDebugMap(InputFile, OsoPrependPath, Verbose);
+  HideUnrelatedOptions(DsymCategory);
+  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");
 
-  Options.Verbose = Verbose;
+  if (Help)
+    PrintHelpMessage();
 
-  if (auto EC = DebugMapPtrOrErr.getError()) {
-    llvm::errs() << "error: cannot parse the debug map for \"" << InputFile
-                 << "\": " << EC.message() << '\n';
-    return 1;
+  if (Version) {
+    llvm::cl::PrintVersionMessage();
+    return 0;
   }
 
-  if (Verbose)
-    (*DebugMapPtrOrErr)->print(llvm::outs());
+  Options.Verbose = Verbose;
+  Options.NoOutput = NoOutput;
+  Options.NoODR = NoODR;
 
-  if (ParseOnly)
-    return 0;
+  llvm::InitializeAllTargetInfos();
+  llvm::InitializeAllTargetMCs();
+  llvm::InitializeAllTargets();
+  llvm::InitializeAllAsmPrinters();
 
-  std::string OutputFile;
-  if (OutputFileOpt.empty()) {
-    if (InputFile == "-")
-      OutputFile = "a.out.dwarf";
-    else
-      OutputFile = InputFile + ".dwarf";
-  } else {
-    OutputFile = OutputFileOpt;
+  if (InputFiles.size() > 1 && !OutputFileOpt.empty()) {
+    llvm::errs() << "error: cannot use -o with multiple inputs\n";
+    return 1;
+  }
+
+  for (auto &InputFile : InputFiles) {
+    auto DebugMapPtrsOrErr =
+        parseDebugMap(InputFile, OsoPrependPath, Verbose, InputIsYAMLDebugMap);
+
+    if (auto EC = DebugMapPtrsOrErr.getError()) {
+      llvm::errs() << "error: cannot parse the debug map for \"" << InputFile
+                   << "\": " << EC.message() << '\n';
+      exitDsymutil(1);
+    }
+
+    // If there is more than one link to execute, we need to generate
+    // temporary files.
+    bool NeedsTempFiles = !DumpDebugMap && (*DebugMapPtrsOrErr).size() != 1;
+    llvm::SmallVector<MachOUtils::ArchAndFilename, 4> TempFiles;
+    for (auto &Map : *DebugMapPtrsOrErr) {
+      if (Verbose || DumpDebugMap)
+        Map->print(llvm::outs());
+
+      if (DumpDebugMap)
+        continue;
+
+      std::string OutputFile = getOutputFileName(InputFile, NeedsTempFiles);
+      if (OutputFile.empty() || !linkDwarf(OutputFile, *Map, Options))
+        exitDsymutil(1);
+
+      if (NeedsTempFiles)
+        TempFiles.emplace_back(Map->getTriple().getArchName().str(),
+                               OutputFile);
+    }
+
+    if (NeedsTempFiles &&
+        !MachOUtils::generateUniversalBinary(
+            TempFiles, getOutputFileName(InputFile), Options))
+      exitDsymutil(1);
   }
 
-  return !linkDwarf(OutputFile, **DebugMapPtrOrErr, Options);
+  exitDsymutil(0);
 }