1 | //===- llvm-mt.cpp - Merge .manifest files ---------------------*- 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 | // Merge .manifest files. This is intended to be a platform-independent port |
10 | // of Microsoft's mt.exe. |
11 | // |
12 | //===---------------------------------------------------------------------===// |
13 | |
14 | #include "llvm/Config/llvm-config.h" // for LLVM_ON_UNIX |
15 | #include "llvm/Option/Arg.h" |
16 | #include "llvm/Option/ArgList.h" |
17 | #include "llvm/Option/Option.h" |
18 | #include "llvm/Support/Error.h" |
19 | #include "llvm/Support/FileOutputBuffer.h" |
20 | #include "llvm/Support/LLVMDriver.h" |
21 | #include "llvm/Support/MemoryBuffer.h" |
22 | #include "llvm/Support/Path.h" |
23 | #include "llvm/Support/PrettyStackTrace.h" |
24 | #include "llvm/Support/Process.h" |
25 | #include "llvm/Support/Signals.h" |
26 | #include "llvm/Support/WithColor.h" |
27 | #include "llvm/Support/raw_ostream.h" |
28 | #include "llvm/WindowsManifest/WindowsManifestMerger.h" |
29 | |
30 | #include <system_error> |
31 | |
32 | using namespace llvm; |
33 | |
34 | namespace { |
35 | |
36 | enum ID { |
37 | OPT_INVALID = 0, // This is not an option ID. |
38 | #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__), |
39 | #include "Opts.inc" |
40 | #undef OPTION |
41 | }; |
42 | |
43 | #define OPTTABLE_STR_TABLE_CODE |
44 | #include "Opts.inc" |
45 | #undef OPTTABLE_STR_TABLE_CODE |
46 | |
47 | #define OPTTABLE_PREFIXES_TABLE_CODE |
48 | #include "Opts.inc" |
49 | #undef OPTTABLE_PREFIXES_TABLE_CODE |
50 | |
51 | using namespace llvm::opt; |
52 | static constexpr opt::OptTable::Info InfoTable[] = { |
53 | #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), |
54 | #include "Opts.inc" |
55 | #undef OPTION |
56 | }; |
57 | |
58 | class CvtResOptTable : public opt::GenericOptTable { |
59 | public: |
60 | CvtResOptTable() |
61 | : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable, |
62 | true) {} |
63 | }; |
64 | } // namespace |
65 | |
66 | [[noreturn]] static void reportError(Twine Msg) { |
67 | WithColor::error(OS&: errs(), Prefix: "llvm-mt" ) << Msg << '\n'; |
68 | exit(status: 1); |
69 | } |
70 | |
71 | static void reportError(StringRef Input, std::error_code EC) { |
72 | reportError(Msg: Twine(Input) + ": " + EC.message()); |
73 | } |
74 | |
75 | static void error(Error EC) { |
76 | if (EC) |
77 | handleAllErrors(E: std::move(EC), Handlers: [&](const ErrorInfoBase &EI) { |
78 | reportError(Msg: EI.message()); |
79 | }); |
80 | } |
81 | |
82 | int llvm_mt_main(int Argc, char **Argv, const llvm::ToolContext &) { |
83 | CvtResOptTable T; |
84 | unsigned MAI, MAC; |
85 | ArrayRef<const char *> ArgsArr = ArrayRef(Argv + 1, Argc - 1); |
86 | opt::InputArgList InputArgs = T.ParseArgs(Args: ArgsArr, MissingArgIndex&: MAI, MissingArgCount&: MAC); |
87 | |
88 | for (auto *Arg : InputArgs.filtered(Ids: OPT_INPUT)) { |
89 | auto ArgString = Arg->getAsString(Args: InputArgs); |
90 | std::string Diag; |
91 | raw_string_ostream OS(Diag); |
92 | OS << "invalid option '" << ArgString << "'" ; |
93 | |
94 | std::string Nearest; |
95 | if (T.findNearest(Option: ArgString, NearestString&: Nearest) < 2) |
96 | OS << ", did you mean '" << Nearest << "'?" ; |
97 | |
98 | reportError(Msg: OS.str()); |
99 | } |
100 | |
101 | for (auto &Arg : InputArgs) { |
102 | if (Arg->getOption().matches(ID: OPT_unsupported)) { |
103 | outs() << "llvm-mt: ignoring unsupported '" << Arg->getOption().getName() |
104 | << "' option\n" ; |
105 | } |
106 | } |
107 | |
108 | if (InputArgs.hasArg(Ids: OPT_help)) { |
109 | T.printHelp(OS&: outs(), Usage: "llvm-mt [options] file..." , Title: "Manifest Tool" , ShowHidden: false); |
110 | return 0; |
111 | } |
112 | |
113 | std::vector<std::string> InputFiles = InputArgs.getAllArgValues(Id: OPT_manifest); |
114 | |
115 | if (InputFiles.size() == 0) { |
116 | reportError(Msg: "no input file specified" ); |
117 | } |
118 | |
119 | StringRef OutputFile; |
120 | if (InputArgs.hasArg(Ids: OPT_out)) { |
121 | OutputFile = InputArgs.getLastArgValue(Id: OPT_out); |
122 | } else if (InputFiles.size() == 1) { |
123 | OutputFile = InputFiles[0]; |
124 | } else { |
125 | reportError(Msg: "no output file specified" ); |
126 | } |
127 | |
128 | windows_manifest::WindowsManifestMerger Merger; |
129 | |
130 | for (const auto &File : InputFiles) { |
131 | ErrorOr<std::unique_ptr<MemoryBuffer>> ManifestOrErr = |
132 | MemoryBuffer::getFile(Filename: File); |
133 | if (!ManifestOrErr) |
134 | reportError(Input: File, EC: ManifestOrErr.getError()); |
135 | error(EC: Merger.merge(Manifest: *ManifestOrErr.get())); |
136 | } |
137 | |
138 | std::unique_ptr<MemoryBuffer> OutputBuffer = Merger.getMergedManifest(); |
139 | if (!OutputBuffer) |
140 | reportError(Msg: "empty manifest not written" ); |
141 | |
142 | int ExitCode = 0; |
143 | if (InputArgs.hasArg(Ids: OPT_notify_update)) { |
144 | ErrorOr<std::unique_ptr<MemoryBuffer>> OutBuffOrErr = |
145 | MemoryBuffer::getFile(Filename: OutputFile); |
146 | // Assume if we couldn't open the output file then it doesn't exist meaning |
147 | // there was a change. |
148 | bool Same = false; |
149 | if (OutBuffOrErr) { |
150 | const std::unique_ptr<MemoryBuffer> &FileBuffer = *OutBuffOrErr; |
151 | Same = std::equal( |
152 | first1: OutputBuffer->getBufferStart(), last1: OutputBuffer->getBufferEnd(), |
153 | first2: FileBuffer->getBufferStart(), last2: FileBuffer->getBufferEnd()); |
154 | } |
155 | if (!Same) { |
156 | #if LLVM_ON_UNIX |
157 | ExitCode = 0xbb; |
158 | #elif defined(_WIN32) |
159 | ExitCode = 0x41020001; |
160 | #endif |
161 | } |
162 | } |
163 | |
164 | Expected<std::unique_ptr<FileOutputBuffer>> FileOrErr = |
165 | FileOutputBuffer::create(FilePath: OutputFile, Size: OutputBuffer->getBufferSize()); |
166 | if (!FileOrErr) |
167 | reportError(Input: OutputFile, EC: errorToErrorCode(Err: FileOrErr.takeError())); |
168 | std::unique_ptr<FileOutputBuffer> FileBuffer = std::move(*FileOrErr); |
169 | std::copy(first: OutputBuffer->getBufferStart(), last: OutputBuffer->getBufferEnd(), |
170 | result: FileBuffer->getBufferStart()); |
171 | error(EC: FileBuffer->commit()); |
172 | return ExitCode; |
173 | } |
174 | |