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/InitLLVM.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(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
38 HELPTEXT, METAVAR, VALUES) \
39 OPT_##ID,
40 #include "Opts.inc"
41 #undef OPTION
42 };
43
44 #define PREFIX(NAME, VALUE) \
45 static constexpr StringLiteral NAME##_init[] = VALUE; \
46 static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \
47 std::size(NAME##_init) - 1);
48 #include "Opts.inc"
49 #undef PREFIX
50
51 static constexpr opt::OptTable::Info InfoTable[] = {
52 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
53 HELPTEXT, METAVAR, VALUES) \
54 { \
55 PREFIX, NAME, HELPTEXT, \
56 METAVAR, OPT_##ID, opt::Option::KIND##Class, \
57 PARAM, FLAGS, OPT_##GROUP, \
58 OPT_##ALIAS, ALIASARGS, VALUES},
59 #include "Opts.inc"
60 #undef OPTION
61 };
62
63 class CvtResOptTable : public opt::GenericOptTable {
64 public:
CvtResOptTable()65 CvtResOptTable() : opt::GenericOptTable(InfoTable, true) {}
66 };
67 } // namespace
68
reportError(Twine Msg)69 [[noreturn]] static void reportError(Twine Msg) {
70 WithColor::error(errs(), "llvm-mt") << Msg << '\n';
71 exit(1);
72 }
73
reportError(StringRef Input,std::error_code EC)74 static void reportError(StringRef Input, std::error_code EC) {
75 reportError(Twine(Input) + ": " + EC.message());
76 }
77
error(Error EC)78 static void error(Error EC) {
79 if (EC)
80 handleAllErrors(std::move(EC), [&](const ErrorInfoBase &EI) {
81 reportError(EI.message());
82 });
83 }
84
llvm_mt_main(int Argc,char ** Argv)85 int llvm_mt_main(int Argc, char **Argv) {
86 InitLLVM X(Argc, Argv);
87
88 CvtResOptTable T;
89 unsigned MAI, MAC;
90 ArrayRef<const char *> ArgsArr = ArrayRef(Argv + 1, Argc - 1);
91 opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC);
92
93 for (auto *Arg : InputArgs.filtered(OPT_INPUT)) {
94 auto ArgString = Arg->getAsString(InputArgs);
95 std::string Diag;
96 raw_string_ostream OS(Diag);
97 OS << "invalid option '" << ArgString << "'";
98
99 std::string Nearest;
100 if (T.findNearest(ArgString, Nearest) < 2)
101 OS << ", did you mean '" << Nearest << "'?";
102
103 reportError(OS.str());
104 }
105
106 for (auto &Arg : InputArgs) {
107 if (Arg->getOption().matches(OPT_unsupported)) {
108 outs() << "llvm-mt: ignoring unsupported '" << Arg->getOption().getName()
109 << "' option\n";
110 }
111 }
112
113 if (InputArgs.hasArg(OPT_help)) {
114 T.printHelp(outs(), "llvm-mt [options] file...", "Manifest Tool", false);
115 return 0;
116 }
117
118 std::vector<std::string> InputFiles = InputArgs.getAllArgValues(OPT_manifest);
119
120 if (InputFiles.size() == 0) {
121 reportError("no input file specified");
122 }
123
124 StringRef OutputFile;
125 if (InputArgs.hasArg(OPT_out)) {
126 OutputFile = InputArgs.getLastArgValue(OPT_out);
127 } else if (InputFiles.size() == 1) {
128 OutputFile = InputFiles[0];
129 } else {
130 reportError("no output file specified");
131 }
132
133 windows_manifest::WindowsManifestMerger Merger;
134
135 for (const auto &File : InputFiles) {
136 ErrorOr<std::unique_ptr<MemoryBuffer>> ManifestOrErr =
137 MemoryBuffer::getFile(File);
138 if (!ManifestOrErr)
139 reportError(File, ManifestOrErr.getError());
140 error(Merger.merge(*ManifestOrErr.get()));
141 }
142
143 std::unique_ptr<MemoryBuffer> OutputBuffer = Merger.getMergedManifest();
144 if (!OutputBuffer)
145 reportError("empty manifest not written");
146
147 int ExitCode = 0;
148 if (InputArgs.hasArg(OPT_notify_update)) {
149 ErrorOr<std::unique_ptr<MemoryBuffer>> OutBuffOrErr =
150 MemoryBuffer::getFile(OutputFile);
151 // Assume if we couldn't open the output file then it doesn't exist meaning
152 // there was a change.
153 bool Same = false;
154 if (OutBuffOrErr) {
155 const std::unique_ptr<MemoryBuffer> &FileBuffer = *OutBuffOrErr;
156 Same = std::equal(
157 OutputBuffer->getBufferStart(), OutputBuffer->getBufferEnd(),
158 FileBuffer->getBufferStart(), FileBuffer->getBufferEnd());
159 }
160 if (!Same) {
161 #if LLVM_ON_UNIX
162 ExitCode = 0xbb;
163 #elif defined(_WIN32)
164 ExitCode = 0x41020001;
165 #endif
166 }
167 }
168
169 Expected<std::unique_ptr<FileOutputBuffer>> FileOrErr =
170 FileOutputBuffer::create(OutputFile, OutputBuffer->getBufferSize());
171 if (!FileOrErr)
172 reportError(OutputFile, errorToErrorCode(FileOrErr.takeError()));
173 std::unique_ptr<FileOutputBuffer> FileBuffer = std::move(*FileOrErr);
174 std::copy(OutputBuffer->getBufferStart(), OutputBuffer->getBufferEnd(),
175 FileBuffer->getBufferStart());
176 error(FileBuffer->commit());
177 return ExitCode;
178 }
179