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