| 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(OS&: errs(), Prefix: "llvm-mt" ) << Msg << '\n'; |
| 68 | exit(status: 1); |
| 69 | } |
| 70 | |
| 71 | static void reportError(StringRef Input, std::error_code EC) { |
| 72 | reportError(Msg: Twine(Input) + ": " + EC.message()); |
| 73 | } |
| 74 | |
| 75 | static 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 | |
| 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(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 | |