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