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
32using namespace llvm;
33
34namespace {
35
36enum 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
51using namespace llvm::opt;
52static constexpr opt::OptTable::Info InfoTable[] = {
53#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
54#include "Opts.inc"
55#undef OPTION
56};
57
58class CvtResOptTable : public opt::GenericOptTable {
59public:
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
71static void reportError(StringRef Input, std::error_code EC) {
72 reportError(Msg: Twine(Input) + ": " + EC.message());
73}
74
75static 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
82int 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