llvm-rc.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. //===-- llvm-rc.cpp - Compile .rc scripts into .res -------------*- 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. // Compile .rc scripts into .res files. This is intended to be a
  10. // platform-independent port of Microsoft's rc.exe tool.
  11. //
  12. //===----------------------------------------------------------------------===//
  13. #include "ResourceFileWriter.h"
  14. #include "ResourceScriptCppFilter.h"
  15. #include "ResourceScriptParser.h"
  16. #include "ResourceScriptStmt.h"
  17. #include "ResourceScriptToken.h"
  18. #include "llvm/Option/Arg.h"
  19. #include "llvm/Option/ArgList.h"
  20. #include "llvm/Support/Error.h"
  21. #include "llvm/Support/FileSystem.h"
  22. #include "llvm/Support/InitLLVM.h"
  23. #include "llvm/Support/ManagedStatic.h"
  24. #include "llvm/Support/MemoryBuffer.h"
  25. #include "llvm/Support/Path.h"
  26. #include "llvm/Support/PrettyStackTrace.h"
  27. #include "llvm/Support/Process.h"
  28. #include "llvm/Support/Signals.h"
  29. #include "llvm/Support/raw_ostream.h"
  30. #include <algorithm>
  31. #include <system_error>
  32. using namespace llvm;
  33. using namespace llvm::rc;
  34. namespace {
  35. // Input options tables.
  36. enum ID {
  37. OPT_INVALID = 0, // This is not a correct option ID.
  38. #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
  39. HELPTEXT, METAVAR, VALUES) \
  40. OPT_##ID,
  41. #include "Opts.inc"
  42. #undef OPTION
  43. };
  44. #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
  45. #include "Opts.inc"
  46. #undef PREFIX
  47. static const opt::OptTable::Info InfoTable[] = {
  48. #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
  49. HELPTEXT, METAVAR, VALUES) \
  50. { \
  51. PREFIX, NAME, HELPTEXT, \
  52. METAVAR, OPT_##ID, opt::Option::KIND##Class, \
  53. PARAM, FLAGS, OPT_##GROUP, \
  54. OPT_##ALIAS, ALIASARGS, VALUES},
  55. #include "Opts.inc"
  56. #undef OPTION
  57. };
  58. class RcOptTable : public opt::OptTable {
  59. public:
  60. RcOptTable() : OptTable(InfoTable, /* IgnoreCase = */ true) {}
  61. };
  62. static ExitOnError ExitOnErr;
  63. LLVM_ATTRIBUTE_NORETURN static void fatalError(const Twine &Message) {
  64. errs() << Message << "\n";
  65. exit(1);
  66. }
  67. } // anonymous namespace
  68. int main(int Argc, const char **Argv) {
  69. InitLLVM X(Argc, Argv);
  70. ExitOnErr.setBanner("llvm-rc: ");
  71. RcOptTable T;
  72. unsigned MAI, MAC;
  73. const char **DashDash = std::find_if(
  74. Argv + 1, Argv + Argc, [](StringRef Str) { return Str == "--"; });
  75. ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, DashDash);
  76. opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC);
  77. // The tool prints nothing when invoked with no command-line arguments.
  78. if (InputArgs.hasArg(OPT_help)) {
  79. T.PrintHelp(outs(), "rc [options] file...", "Resource Converter", false);
  80. return 0;
  81. }
  82. const bool BeVerbose = InputArgs.hasArg(OPT_verbose);
  83. std::vector<std::string> InArgsInfo = InputArgs.getAllArgValues(OPT_INPUT);
  84. if (DashDash != Argv + Argc)
  85. InArgsInfo.insert(InArgsInfo.end(), DashDash + 1, Argv + Argc);
  86. if (InArgsInfo.size() != 1) {
  87. fatalError("Exactly one input file should be provided.");
  88. }
  89. // Read and tokenize the input file.
  90. ErrorOr<std::unique_ptr<MemoryBuffer>> File =
  91. MemoryBuffer::getFile(InArgsInfo[0]);
  92. if (!File) {
  93. fatalError("Error opening file '" + Twine(InArgsInfo[0]) +
  94. "': " + File.getError().message());
  95. }
  96. std::unique_ptr<MemoryBuffer> FileContents = std::move(*File);
  97. StringRef Contents = FileContents->getBuffer();
  98. std::string FilteredContents = filterCppOutput(Contents);
  99. std::vector<RCToken> Tokens = ExitOnErr(tokenizeRC(FilteredContents));
  100. if (BeVerbose) {
  101. const Twine TokenNames[] = {
  102. #define TOKEN(Name) #Name,
  103. #define SHORT_TOKEN(Name, Ch) #Name,
  104. #include "ResourceScriptTokenList.def"
  105. };
  106. for (const RCToken &Token : Tokens) {
  107. outs() << TokenNames[static_cast<int>(Token.kind())] << ": "
  108. << Token.value();
  109. if (Token.kind() == RCToken::Kind::Int)
  110. outs() << "; int value = " << Token.intValue();
  111. outs() << "\n";
  112. }
  113. }
  114. WriterParams Params;
  115. SmallString<128> InputFile(InArgsInfo[0]);
  116. llvm::sys::fs::make_absolute(InputFile);
  117. Params.InputFilePath = InputFile;
  118. Params.Include = InputArgs.getAllArgValues(OPT_includepath);
  119. Params.NoInclude = InputArgs.getAllArgValues(OPT_noinclude);
  120. if (InputArgs.hasArg(OPT_codepage)) {
  121. if (InputArgs.getLastArgValue(OPT_codepage)
  122. .getAsInteger(10, Params.CodePage))
  123. fatalError("Invalid code page: " +
  124. InputArgs.getLastArgValue(OPT_codepage));
  125. switch (Params.CodePage) {
  126. case CpAcp:
  127. case CpWin1252:
  128. case CpUtf8:
  129. break;
  130. default:
  131. fatalError(
  132. "Unsupported code page, only 0, 1252 and 65001 are supported!");
  133. }
  134. }
  135. std::unique_ptr<ResourceFileWriter> Visitor;
  136. bool IsDryRun = InputArgs.hasArg(OPT_dry_run);
  137. if (!IsDryRun) {
  138. auto OutArgsInfo = InputArgs.getAllArgValues(OPT_fileout);
  139. if (OutArgsInfo.empty()) {
  140. SmallString<128> OutputFile = InputFile;
  141. llvm::sys::path::replace_extension(OutputFile, "res");
  142. OutArgsInfo.push_back(std::string(OutputFile.str()));
  143. }
  144. if (OutArgsInfo.size() != 1)
  145. fatalError(
  146. "No more than one output file should be provided (using /FO flag).");
  147. std::error_code EC;
  148. auto FOut = std::make_unique<raw_fd_ostream>(
  149. OutArgsInfo[0], EC, sys::fs::FA_Read | sys::fs::FA_Write);
  150. if (EC)
  151. fatalError("Error opening output file '" + OutArgsInfo[0] +
  152. "': " + EC.message());
  153. Visitor = std::make_unique<ResourceFileWriter>(Params, std::move(FOut));
  154. Visitor->AppendNull = InputArgs.hasArg(OPT_add_null);
  155. ExitOnErr(NullResource().visit(Visitor.get()));
  156. // Set the default language; choose en-US arbitrarily.
  157. unsigned PrimaryLangId = 0x09, SubLangId = 0x01;
  158. if (InputArgs.hasArg(OPT_lang_id)) {
  159. unsigned LangId;
  160. if (InputArgs.getLastArgValue(OPT_lang_id).getAsInteger(16, LangId))
  161. fatalError("Invalid language id: " +
  162. InputArgs.getLastArgValue(OPT_lang_id));
  163. PrimaryLangId = LangId & 0x3ff;
  164. SubLangId = LangId >> 10;
  165. }
  166. ExitOnErr(LanguageResource(PrimaryLangId, SubLangId).visit(Visitor.get()));
  167. }
  168. rc::RCParser Parser{std::move(Tokens)};
  169. while (!Parser.isEof()) {
  170. auto Resource = ExitOnErr(Parser.parseSingleResource());
  171. if (BeVerbose)
  172. Resource->log(outs());
  173. if (!IsDryRun)
  174. ExitOnErr(Resource->visit(Visitor.get()));
  175. }
  176. // STRINGTABLE resources come at the very end.
  177. if (!IsDryRun)
  178. ExitOnErr(Visitor->dumpAllStringTables());
  179. return 0;
  180. }