[dsymutil] Do not create temporary files in -no-output mode.
[oota-llvm.git] / tools / dsymutil / dsymutil.cpp
1 //===-- dsymutil.cpp - Debug info dumping utility for llvm ----------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This program is a utility that aims to be a dropin replacement for
11 // Darwin's dsymutil.
12 //
13 //===----------------------------------------------------------------------===//
14
15 #include "DebugMap.h"
16 #include "MachOUtils.h"
17 #include "dsymutil.h"
18 #include "llvm/Object/MachO.h"
19 #include "llvm/Support/FileUtilities.h"
20 #include "llvm/Support/ManagedStatic.h"
21 #include "llvm/Support/Options.h"
22 #include "llvm/Support/PrettyStackTrace.h"
23 #include "llvm/Support/Signals.h"
24 #include "llvm/Support/raw_ostream.h"
25 #include "llvm/Support/TargetSelect.h"
26 #include <string>
27
28 using namespace llvm::dsymutil;
29
30 namespace {
31 using namespace llvm::cl;
32
33 OptionCategory DsymCategory("Specific Options");
34 static opt<bool> Help("h", desc("Alias for -help"), Hidden);
35 static opt<bool> Version("v", desc("Alias for -version"), Hidden);
36
37 static list<std::string> InputFiles(Positional, OneOrMore,
38                                     desc("<input files>"), cat(DsymCategory));
39
40 static opt<std::string>
41     OutputFileOpt("o",
42                   desc("Specify the output file. default: <input file>.dwarf"),
43                   value_desc("filename"), cat(DsymCategory));
44
45 static opt<std::string> OsoPrependPath(
46     "oso-prepend-path",
47     desc("Specify a directory to prepend to the paths of object files."),
48     value_desc("path"), cat(DsymCategory));
49
50 static opt<bool> Verbose("verbose", desc("Verbosity level"), init(false),
51                          cat(DsymCategory));
52
53 static opt<bool>
54     NoOutput("no-output",
55              desc("Do the link in memory, but do not emit the result file."),
56              init(false), cat(DsymCategory));
57
58 static list<std::string> ArchFlags(
59     "arch",
60     desc("Link DWARF debug information only for specified CPU architecture\n"
61          "types. This option can be specified multiple times, once for each\n"
62          "desired architecture.  All cpu architectures will be linked by\n"
63          "default."),
64     ZeroOrMore, cat(DsymCategory));
65
66 static opt<bool>
67     NoODR("no-odr",
68           desc("Do not use ODR (One Definition Rule) for type uniquing."),
69           init(false), cat(DsymCategory));
70
71 static opt<bool> DumpDebugMap(
72     "dump-debug-map",
73     desc("Parse and dump the debug map to standard output. Not DWARF link "
74          "will take place."),
75     init(false), cat(DsymCategory));
76
77 static opt<bool> InputIsYAMLDebugMap(
78     "y", desc("Treat the input file is a YAML debug map rather than a binary."),
79     init(false), cat(DsymCategory));
80 }
81
82 static std::error_code getUniqueFile(const llvm::Twine &Model, int &ResultFD,
83                                      llvm::SmallVectorImpl<char> &ResultPath) {
84   // If in NoOutput mode, use the createUniqueFile variant that
85   // doesn't open the file but still generates a somewhat unique
86   // name. In the real usage scenario, we'll want to ensure that the
87   // file is trully unique, and creating it is the only way to achieve
88   // that.
89   if (NoOutput)
90     return llvm::sys::fs::createUniqueFile(Model, ResultPath);
91   return llvm::sys::fs::createUniqueFile(Model, ResultFD, ResultPath);
92 }
93
94 static std::string getOutputFileName(llvm::StringRef InputFile,
95                                      bool TempFile = false) {
96   if (TempFile) {
97     llvm::Twine OutputFile = InputFile + ".tmp%%%%%%.dwarf";
98     int FD;
99     llvm::SmallString<128> UniqueFile;
100     if (auto EC = getUniqueFile(OutputFile, FD, UniqueFile)) {
101       llvm::errs() << "error: failed to create temporary outfile '"
102                    << OutputFile << "': " << EC.message() << '\n';
103       return "";
104     }
105     llvm::sys::RemoveFileOnSignal(UniqueFile);
106     if (!NoOutput) {
107       // Close the file immediately. We know it is unique. It will be
108       // reopened and written to later.
109       llvm::raw_fd_ostream CloseImmediately(FD, true /* shouldClose */, true);
110     }
111     return UniqueFile.str();
112   }
113
114   if (OutputFileOpt.empty()) {
115     if (InputFile == "-")
116       return "a.out.dwarf";
117     return (InputFile + ".dwarf").str();
118   }
119   return OutputFileOpt;
120 }
121
122 void llvm::dsymutil::exitDsymutil(int ExitStatus) {
123   // Cleanup temporary files.
124   llvm::sys::RunInterruptHandlers();
125   exit(ExitStatus);
126 }
127
128 int main(int argc, char **argv) {
129   llvm::sys::PrintStackTraceOnErrorSignal();
130   llvm::PrettyStackTraceProgram StackPrinter(argc, argv);
131   llvm::llvm_shutdown_obj Shutdown;
132   LinkOptions Options;
133
134   HideUnrelatedOptions(DsymCategory);
135   llvm::cl::ParseCommandLineOptions(
136       argc, argv,
137       "manipulate archived DWARF debug symbol files.\n\n"
138       "dsymutil links the DWARF debug information found in the object files\n"
139       "for the executable <input file> by using debug symbols information\n"
140       "contained in its symbol table.\n");
141
142   if (Help)
143     PrintHelpMessage();
144
145   if (Version) {
146     llvm::cl::PrintVersionMessage();
147     return 0;
148   }
149
150   Options.Verbose = Verbose;
151   Options.NoOutput = NoOutput;
152   Options.NoODR = NoODR;
153
154   llvm::InitializeAllTargetInfos();
155   llvm::InitializeAllTargetMCs();
156   llvm::InitializeAllTargets();
157   llvm::InitializeAllAsmPrinters();
158
159   if (InputFiles.size() > 1 && !OutputFileOpt.empty()) {
160     llvm::errs() << "error: cannot use -o with multiple inputs\n";
161     return 1;
162   }
163
164   for (const auto &Arch : ArchFlags)
165     if (Arch != "*" && Arch != "all" &&
166         !llvm::object::MachOObjectFile::isValidArch(Arch)) {
167       llvm::errs() << "error: Unsupported cpu architecture: '" << Arch << "'\n";
168       exitDsymutil(1);
169     }
170
171   for (auto &InputFile : InputFiles) {
172     auto DebugMapPtrsOrErr = parseDebugMap(InputFile, ArchFlags, OsoPrependPath,
173                                            Verbose, InputIsYAMLDebugMap);
174
175     if (auto EC = DebugMapPtrsOrErr.getError()) {
176       llvm::errs() << "error: cannot parse the debug map for \"" << InputFile
177                    << "\": " << EC.message() << '\n';
178       exitDsymutil(1);
179     }
180
181     if (DebugMapPtrsOrErr->empty()) {
182       llvm::errs() << "error: no architecture to link\n";
183       exitDsymutil(1);
184     }
185
186     // If there is more than one link to execute, we need to generate
187     // temporary files.
188     bool NeedsTempFiles = !DumpDebugMap && (*DebugMapPtrsOrErr).size() != 1;
189     llvm::SmallVector<MachOUtils::ArchAndFilename, 4> TempFiles;
190     for (auto &Map : *DebugMapPtrsOrErr) {
191       if (Verbose || DumpDebugMap)
192         Map->print(llvm::outs());
193
194       if (DumpDebugMap)
195         continue;
196
197       std::string OutputFile = getOutputFileName(InputFile, NeedsTempFiles);
198       if (OutputFile.empty() || !linkDwarf(OutputFile, *Map, Options))
199         exitDsymutil(1);
200
201       if (NeedsTempFiles)
202         TempFiles.emplace_back(Map->getTriple().getArchName().str(),
203                                OutputFile);
204     }
205
206     if (NeedsTempFiles &&
207         !MachOUtils::generateUniversalBinary(
208             TempFiles, getOutputFileName(InputFile), Options))
209       exitDsymutil(1);
210   }
211
212   exitDsymutil(0);
213 }