xref: /llvm-project/llvm/tools/llvm-mt/llvm-mt.cpp (revision dd647e3e608ed0b2bac7c588d5859b80ef4a5976)
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(errs(), "llvm-mt") << Msg << '\n';
68   exit(1);
69 }
70 
71 static void reportError(StringRef Input, std::error_code EC) {
72   reportError(Twine(Input) + ": " + EC.message());
73 }
74 
75 static void error(Error EC) {
76   if (EC)
77     handleAllErrors(std::move(EC), [&](const ErrorInfoBase &EI) {
78       reportError(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(ArgsArr, MAI, MAC);
87 
88   for (auto *Arg : InputArgs.filtered(OPT_INPUT)) {
89     auto ArgString = Arg->getAsString(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(ArgString, Nearest) < 2)
96       OS << ", did you mean '" << Nearest << "'?";
97 
98     reportError(OS.str());
99   }
100 
101   for (auto &Arg : InputArgs) {
102     if (Arg->getOption().matches(OPT_unsupported)) {
103       outs() << "llvm-mt: ignoring unsupported '" << Arg->getOption().getName()
104              << "' option\n";
105     }
106   }
107 
108   if (InputArgs.hasArg(OPT_help)) {
109     T.printHelp(outs(), "llvm-mt [options] file...", "Manifest Tool", false);
110     return 0;
111   }
112 
113   std::vector<std::string> InputFiles = InputArgs.getAllArgValues(OPT_manifest);
114 
115   if (InputFiles.size() == 0) {
116     reportError("no input file specified");
117   }
118 
119   StringRef OutputFile;
120   if (InputArgs.hasArg(OPT_out)) {
121     OutputFile = InputArgs.getLastArgValue(OPT_out);
122   } else if (InputFiles.size() == 1) {
123     OutputFile = InputFiles[0];
124   } else {
125     reportError("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(File);
133     if (!ManifestOrErr)
134       reportError(File, ManifestOrErr.getError());
135     error(Merger.merge(*ManifestOrErr.get()));
136   }
137 
138   std::unique_ptr<MemoryBuffer> OutputBuffer = Merger.getMergedManifest();
139   if (!OutputBuffer)
140     reportError("empty manifest not written");
141 
142   int ExitCode = 0;
143   if (InputArgs.hasArg(OPT_notify_update)) {
144     ErrorOr<std::unique_ptr<MemoryBuffer>> OutBuffOrErr =
145         MemoryBuffer::getFile(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           OutputBuffer->getBufferStart(), OutputBuffer->getBufferEnd(),
153           FileBuffer->getBufferStart(), 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(OutputFile, OutputBuffer->getBufferSize());
166   if (!FileOrErr)
167     reportError(OutputFile, errorToErrorCode(FileOrErr.takeError()));
168   std::unique_ptr<FileOutputBuffer> FileBuffer = std::move(*FileOrErr);
169   std::copy(OutputBuffer->getBufferStart(), OutputBuffer->getBufferEnd(),
170             FileBuffer->getBufferStart());
171   error(FileBuffer->commit());
172   return ExitCode;
173 }
174