brintos

brintos / llvm-project-archived public Read only

0
0
Text · 24.9 KiB · 38bf03f Raw
770 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// Compile .rc scripts into .res files. This is intended to be a10// platform-independent port of Microsoft's rc.exe tool.11//12//===----------------------------------------------------------------------===//13 14#include "ResourceFileWriter.h"15#include "ResourceScriptCppFilter.h"16#include "ResourceScriptParser.h"17#include "ResourceScriptStmt.h"18#include "ResourceScriptToken.h"19 20#include "llvm/Config/llvm-config.h"21#include "llvm/Object/WindowsResource.h"22#include "llvm/Option/Arg.h"23#include "llvm/Option/ArgList.h"24#include "llvm/Option/OptTable.h"25#include "llvm/Support/CommandLine.h"26#include "llvm/Support/Error.h"27#include "llvm/Support/FileSystem.h"28#include "llvm/Support/FileUtilities.h"29#include "llvm/Support/LLVMDriver.h"30#include "llvm/Support/MemoryBuffer.h"31#include "llvm/Support/Path.h"32#include "llvm/Support/PrettyStackTrace.h"33#include "llvm/Support/Process.h"34#include "llvm/Support/Program.h"35#include "llvm/Support/Signals.h"36#include "llvm/Support/StringSaver.h"37#include "llvm/Support/raw_ostream.h"38#include "llvm/TargetParser/Host.h"39#include "llvm/TargetParser/Triple.h"40 41#include <algorithm>42#include <system_error>43 44using namespace llvm;45using namespace llvm::rc;46using namespace llvm::opt;47 48namespace {49 50// Input options tables.51 52enum ID {53  OPT_INVALID = 0, // This is not a correct option ID.54#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),55#include "Opts.inc"56#undef OPTION57};58 59namespace rc_opt {60#define OPTTABLE_STR_TABLE_CODE61#include "Opts.inc"62#undef OPTTABLE_STR_TABLE_CODE63 64#define OPTTABLE_PREFIXES_TABLE_CODE65#include "Opts.inc"66#undef OPTTABLE_PREFIXES_TABLE_CODE67 68static constexpr opt::OptTable::Info InfoTable[] = {69#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),70#include "Opts.inc"71#undef OPTION72};73} // namespace rc_opt74 75class RcOptTable : public opt::GenericOptTable {76public:77  RcOptTable()78      : GenericOptTable(rc_opt::OptionStrTable, rc_opt::OptionPrefixesTable,79                        rc_opt::InfoTable,80                        /* IgnoreCase = */ true) {}81};82 83enum Windres_ID {84  WINDRES_INVALID = 0, // This is not a correct option ID.85#define OPTION(...) LLVM_MAKE_OPT_ID_WITH_ID_PREFIX(WINDRES_, __VA_ARGS__),86#include "WindresOpts.inc"87#undef OPTION88};89 90namespace windres_opt {91#define OPTTABLE_STR_TABLE_CODE92#include "WindresOpts.inc"93#undef OPTTABLE_STR_TABLE_CODE94 95#define OPTTABLE_PREFIXES_TABLE_CODE96#include "WindresOpts.inc"97#undef OPTTABLE_PREFIXES_TABLE_CODE98 99static constexpr opt::OptTable::Info InfoTable[] = {100#define OPTION(...)                                                            \101  LLVM_CONSTRUCT_OPT_INFO_WITH_ID_PREFIX(WINDRES_, __VA_ARGS__),102#include "WindresOpts.inc"103#undef OPTION104};105} // namespace windres_opt106 107class WindresOptTable : public opt::GenericOptTable {108public:109  WindresOptTable()110      : GenericOptTable(windres_opt::OptionStrTable,111                        windres_opt::OptionPrefixesTable,112                        windres_opt::InfoTable,113                        /* IgnoreCase = */ false) {}114};115 116static ExitOnError ExitOnErr;117static FileRemover TempPreprocFile;118static FileRemover TempResFile;119 120[[noreturn]] static void fatalError(const Twine &Message) {121  errs() << Message << "\n";122  exit(1);123}124 125std::string createTempFile(const Twine &Prefix, StringRef Suffix) {126  std::error_code EC;127  SmallString<128> FileName;128  if ((EC = sys::fs::createTemporaryFile(Prefix, Suffix, FileName)))129    fatalError("Unable to create temp file: " + EC.message());130  return static_cast<std::string>(FileName);131}132 133ErrorOr<std::string> findClang(const char *Argv0, StringRef Triple) {134  // This just needs to be some symbol in the binary.135  void *P = (void*) (intptr_t) findClang;136  std::string MainExecPath = llvm::sys::fs::getMainExecutable(Argv0, P);137  if (MainExecPath.empty())138    MainExecPath = Argv0;139 140  ErrorOr<std::string> Path = std::error_code();141  std::string TargetClang = (Triple + "-clang").str();142  std::string VersionedClang = ("clang-" + Twine(LLVM_VERSION_MAJOR)).str();143  for (const auto *Name :144       {TargetClang.c_str(), VersionedClang.c_str(), "clang", "clang-cl"}) {145    for (const StringRef Parent :146         {llvm::sys::path::parent_path(MainExecPath),147          llvm::sys::path::parent_path(Argv0)}) {148      // Look for various versions of "clang" first in the MainExecPath parent149      // directory and then in the argv[0] parent directory.150      // On Windows (but not Unix) argv[0] is overwritten with the eqiuvalent151      // of MainExecPath by InitLLVM.152      Path = sys::findProgramByName(Name, Parent);153      if (Path)154        return Path;155    }156  }157 158  // If no parent directory known, or not found there, look everywhere in PATH159  for (const auto *Name : {"clang", "clang-cl"}) {160    Path = sys::findProgramByName(Name);161    if (Path)162      return Path;163  }164  return Path;165}166 167bool isUsableArch(Triple::ArchType Arch) {168  switch (Arch) {169  case Triple::x86:170  case Triple::x86_64:171  case Triple::arm:172  case Triple::thumb:173  case Triple::aarch64:174    // These work properly with the clang driver, setting the expected175    // defines such as _WIN32 etc.176    return true;177  default:178    // Other archs aren't set up for use with windows as target OS, (clang179    // doesn't define e.g. _WIN32 etc), so with them we need to set a180    // different default arch.181    return false;182  }183}184 185Triple::ArchType getDefaultFallbackArch() {186  return Triple::x86_64;187}188 189std::string getClangClTriple() {190  Triple T(sys::getDefaultTargetTriple());191  if (!isUsableArch(T.getArch()))192    T.setArch(getDefaultFallbackArch());193  T.setOS(Triple::Win32);194  T.setVendor(Triple::PC);195  T.setEnvironment(Triple::MSVC);196  T.setObjectFormat(Triple::COFF);197  return T.str();198}199 200std::string getMingwTriple() {201  Triple T(sys::getDefaultTargetTriple());202  if (!isUsableArch(T.getArch()))203    T.setArch(getDefaultFallbackArch());204  if (T.isOSCygMing())205    return T.str();206  // Write out the literal form of the vendor/env here, instead of207  // constructing them with enum values (which end up with them in208  // normalized form). The literal form of the triple can matter for209  // finding include files.210  return (Twine(T.getArchName()) + "-w64-mingw32").str();211}212 213enum Format { Rc, Res, Coff, Unknown };214 215struct RcOptions {216  bool Preprocess = true;217  bool PrintCmdAndExit = false;218  std::string Triple;219  std::optional<std::string> Preprocessor;220  std::vector<std::string> PreprocessArgs;221 222  std::string InputFile;223  Format InputFormat = Rc;224  std::string OutputFile;225  Format OutputFormat = Res;226 227  bool IsWindres = false;228  bool BeVerbose = false;229  WriterParams Params;230  bool AppendNull = false;231  bool IsDryRun = false;232  // Set the default language; choose en-US arbitrarily.233  unsigned LangId = (/*PrimaryLangId*/ 0x09) | (/*SubLangId*/ 0x01 << 10);234};235 236void preprocess(StringRef Src, StringRef Dst, const RcOptions &Opts,237                const char *Argv0) {238  std::string Clang;239  if (Opts.PrintCmdAndExit || Opts.Preprocessor) {240    Clang = "clang";241  } else {242    ErrorOr<std::string> ClangOrErr = findClang(Argv0, Opts.Triple);243    if (ClangOrErr) {244      Clang = *ClangOrErr;245    } else {246      errs() << "llvm-rc: Unable to find clang for preprocessing."247             << "\n";248      StringRef OptionName =249          Opts.IsWindres ? "--no-preprocess" : "-no-preprocess";250      errs() << "Pass " << OptionName << " to disable preprocessing.\n";251      fatalError("llvm-rc: Unable to preprocess.");252    }253  }254 255  SmallVector<StringRef, 8> Args = {256      Clang, "--driver-mode=gcc", "-target", Opts.Triple, "-E",257      "-xc", "-DRC_INVOKED"};258  std::string PreprocessorExecutable;259  if (Opts.Preprocessor) {260    Args.clear();261    Args.push_back(*Opts.Preprocessor);262    if (!sys::fs::can_execute(Args[0])) {263      if (auto P = sys::findProgramByName(Args[0])) {264        PreprocessorExecutable = *P;265        Args[0] = PreprocessorExecutable;266      }267    }268  }269  llvm::append_range(Args, Opts.PreprocessArgs);270  Args.push_back(Src);271  Args.push_back("-o");272  Args.push_back(Dst);273  if (Opts.PrintCmdAndExit || Opts.BeVerbose) {274    for (const auto &A : Args) {275      outs() << " ";276      sys::printArg(outs(), A, Opts.PrintCmdAndExit);277    }278    outs() << "\n";279    if (Opts.PrintCmdAndExit)280      exit(0);281  }282  // The llvm Support classes don't handle reading from stdout of a child283  // process; otherwise we could avoid using a temp file.284  std::string ErrMsg;285  int Res =286      sys::ExecuteAndWait(Args[0], Args, /*Env=*/std::nullopt, /*Redirects=*/{},287                          /*SecondsToWait=*/0, /*MemoryLimit=*/0, &ErrMsg);288  if (Res) {289    if (!ErrMsg.empty())290      fatalError("llvm-rc: Preprocessing failed: " + ErrMsg);291    else292      fatalError("llvm-rc: Preprocessing failed.");293  }294}295 296static std::pair<bool, std::string> isWindres(llvm::StringRef Argv0) {297  StringRef ProgName = llvm::sys::path::stem(Argv0);298  // x86_64-w64-mingw32-windres -> x86_64-w64-mingw32, windres299  // llvm-rc -> "", llvm-rc300  // aarch64-w64-mingw32-llvm-windres-10.exe -> aarch64-w64-mingw32, llvm-windres301  ProgName = ProgName.rtrim("0123456789.-");302  if (!ProgName.consume_back_insensitive("windres"))303    return std::make_pair<bool, std::string>(false, "");304  ProgName.consume_back_insensitive("llvm-");305  ProgName.consume_back_insensitive("-");306  return std::make_pair<bool, std::string>(true, ProgName.str());307}308 309Format parseFormat(StringRef S) {310  Format F = StringSwitch<Format>(S.lower())311                 .Case("rc", Rc)312                 .Case("res", Res)313                 .Case("coff", Coff)314                 .Default(Unknown);315  if (F == Unknown)316    fatalError("Unable to parse '" + Twine(S) + "' as a format");317  return F;318}319 320void deduceFormat(Format &Dest, StringRef File) {321  Format F = StringSwitch<Format>(sys::path::extension(File.lower()))322                 .Case(".rc", Rc)323                 .Case(".res", Res)324                 .Case(".o", Coff)325                 .Case(".obj", Coff)326                 .Default(Unknown);327  if (F != Unknown)328    Dest = F;329}330 331std::string unescape(StringRef S) {332  std::string Out;333  Out.reserve(S.size());334  for (int I = 0, E = S.size(); I < E; I++) {335    if (S[I] == '\\') {336      if (I + 1 < E)337        Out.push_back(S[++I]);338      else339        fatalError("Unterminated escape");340      continue;341    } else if (S[I] == '"') {342      // This eats an individual unescaped quote, like a shell would do.343      continue;344    }345    Out.push_back(S[I]);346  }347  return Out;348}349 350RcOptions parseWindresOptions(ArrayRef<const char *> ArgsArr,351                              ArrayRef<const char *> InputArgsArray,352                              std::string Prefix) {353  WindresOptTable T;354  RcOptions Opts;355  unsigned MAI, MAC;356  opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC);357 358  Opts.IsWindres = true;359 360  // The tool prints nothing when invoked with no command-line arguments.361  if (InputArgs.hasArg(WINDRES_help)) {362    T.printHelp(outs(), "windres [options] file...",363                "LLVM windres (GNU windres compatible)", false, true);364    exit(0);365  }366 367  if (InputArgs.hasArg(WINDRES_version)) {368    outs() << "llvm-windres, compatible with GNU windres\n";369    cl::PrintVersionMessage();370    exit(0);371  }372 373  std::vector<std::string> FileArgs = InputArgs.getAllArgValues(WINDRES_INPUT);374  llvm::append_range(FileArgs, InputArgsArray);375 376  if (InputArgs.hasArg(WINDRES_input)) {377    Opts.InputFile = InputArgs.getLastArgValue(WINDRES_input).str();378  } else if (!FileArgs.empty()) {379    Opts.InputFile = FileArgs.front();380    FileArgs.erase(FileArgs.begin());381  } else {382    // TODO: GNU windres takes input on stdin in this case.383    fatalError("Missing input file");384  }385 386  if (InputArgs.hasArg(WINDRES_output)) {387    Opts.OutputFile = InputArgs.getLastArgValue(WINDRES_output).str();388  } else if (!FileArgs.empty()) {389    Opts.OutputFile = FileArgs.front();390    FileArgs.erase(FileArgs.begin());391  } else {392    // TODO: GNU windres writes output in rc form to stdout in this case.393    fatalError("Missing output file");394  }395 396  if (InputArgs.hasArg(WINDRES_input_format)) {397    Opts.InputFormat =398        parseFormat(InputArgs.getLastArgValue(WINDRES_input_format));399  } else {400    deduceFormat(Opts.InputFormat, Opts.InputFile);401  }402  if (Opts.InputFormat == Coff)403    fatalError("Unsupported input format");404 405  if (InputArgs.hasArg(WINDRES_output_format)) {406    Opts.OutputFormat =407        parseFormat(InputArgs.getLastArgValue(WINDRES_output_format));408  } else {409    // The default in windres differs from the default in RcOptions410    Opts.OutputFormat = Coff;411    deduceFormat(Opts.OutputFormat, Opts.OutputFile);412  }413  if (Opts.OutputFormat == Rc)414    fatalError("Unsupported output format");415  if (Opts.InputFormat == Opts.OutputFormat) {416    outs() << "Nothing to do.\n";417    exit(0);418  }419 420  Opts.PrintCmdAndExit = InputArgs.hasArg(WINDRES__HASH_HASH_HASH);421  Opts.Preprocess = !InputArgs.hasArg(WINDRES_no_preprocess);422  Triple TT(Prefix);423  if (InputArgs.hasArg(WINDRES_target)) {424    StringRef Value = InputArgs.getLastArgValue(WINDRES_target);425    if (Value == "pe-i386")426      Opts.Triple = "i686-w64-mingw32";427    else if (Value == "pe-x86-64")428      Opts.Triple = "x86_64-w64-mingw32";429    else430      // Implicit extension; if the --target value isn't one of the known431      // BFD targets, allow setting the full triple string via this instead.432      Opts.Triple = Value.str();433  } else if (TT.getArch() != Triple::UnknownArch)434    Opts.Triple = Prefix;435  else436    Opts.Triple = getMingwTriple();437 438  for (const auto *Arg :439       InputArgs.filtered(WINDRES_include_dir, WINDRES_define, WINDRES_undef,440                          WINDRES_preprocessor_arg)) {441    // GNU windres passes the arguments almost as-is on to popen() (it only442    // backslash escapes spaces in the arguments), where a shell would443    // unescape backslash escapes for quotes and similar. This means that444    // when calling GNU windres, callers need to double escape chars like445    // quotes, e.g. as -DSTRING=\\\"1.2.3\\\".446    //447    // Exactly how the arguments are interpreted depends on the platform448    // though - but the cases where this matters (where callers would have449    // done this double escaping) probably is confined to cases like these450    // quoted string defines, and those happen to work the same across unix451    // and windows.452    //453    // If GNU windres is executed with --use-temp-file, it doesn't use454    // popen() to invoke the preprocessor, but uses another function which455    // actually preserves tricky characters better. To mimic this behaviour,456    // don't unescape arguments here.457    std::string Value = Arg->getValue();458    if (!InputArgs.hasArg(WINDRES_use_temp_file))459      Value = unescape(Value);460    switch (Arg->getOption().getID()) {461    case WINDRES_include_dir:462      // Technically, these are handled the same way as e.g. defines, but463      // the way we consistently unescape the unix way breaks windows paths464      // with single backslashes. Alternatively, our unescape function would465      // need to mimic the platform specific command line parsing/unescaping466      // logic.467      Opts.Params.Include.push_back(Arg->getValue());468      Opts.PreprocessArgs.push_back("-I");469      Opts.PreprocessArgs.push_back(Arg->getValue());470      break;471    case WINDRES_define:472      Opts.PreprocessArgs.push_back("-D");473      Opts.PreprocessArgs.push_back(Value);474      break;475    case WINDRES_undef:476      Opts.PreprocessArgs.push_back("-U");477      Opts.PreprocessArgs.push_back(Value);478      break;479    case WINDRES_preprocessor_arg:480      Opts.PreprocessArgs.push_back(Value);481      break;482    }483  }484  if (InputArgs.hasArg(WINDRES_preprocessor))485    Opts.Preprocessor = InputArgs.getLastArgValue(WINDRES_preprocessor);486 487  Opts.Params.CodePage = CpWin1252; // Different default488  if (InputArgs.hasArg(WINDRES_codepage)) {489    if (InputArgs.getLastArgValue(WINDRES_codepage)490            .getAsInteger(0, Opts.Params.CodePage))491      fatalError("Invalid code page: " +492                 InputArgs.getLastArgValue(WINDRES_codepage));493  }494  if (InputArgs.hasArg(WINDRES_language)) {495    StringRef Val = InputArgs.getLastArgValue(WINDRES_language);496    Val.consume_front_insensitive("0x");497    if (Val.getAsInteger(16, Opts.LangId))498      fatalError("Invalid language id: " +499                 InputArgs.getLastArgValue(WINDRES_language));500  }501 502  Opts.BeVerbose = InputArgs.hasArg(WINDRES_verbose);503 504  return Opts;505}506 507RcOptions parseRcOptions(ArrayRef<const char *> ArgsArr,508                         ArrayRef<const char *> InputArgsArray) {509  RcOptTable T;510  RcOptions Opts;511  unsigned MAI, MAC;512  opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC);513 514  // The tool prints nothing when invoked with no command-line arguments.515  if (InputArgs.hasArg(OPT_help)) {516    T.printHelp(outs(), "llvm-rc [options] file...", "LLVM Resource Converter",517                false);518    exit(0);519  }520 521  std::vector<std::string> InArgsInfo = InputArgs.getAllArgValues(OPT_INPUT);522  llvm::append_range(InArgsInfo, InputArgsArray);523  if (InArgsInfo.size() != 1) {524    fatalError("Exactly one input file should be provided.");525  }526 527  Opts.PrintCmdAndExit = InputArgs.hasArg(OPT__HASH_HASH_HASH);528  Opts.Triple = getClangClTriple();529  for (const auto *Arg :530       InputArgs.filtered(OPT_includepath, OPT_define, OPT_undef)) {531    switch (Arg->getOption().getID()) {532    case OPT_includepath:533      Opts.PreprocessArgs.push_back("-I");534      break;535    case OPT_define:536      Opts.PreprocessArgs.push_back("-D");537      break;538    case OPT_undef:539      Opts.PreprocessArgs.push_back("-U");540      break;541    }542    Opts.PreprocessArgs.push_back(Arg->getValue());543  }544 545  Opts.InputFile = InArgsInfo[0];546  Opts.BeVerbose = InputArgs.hasArg(OPT_verbose);547  Opts.Preprocess = !InputArgs.hasArg(OPT_no_preprocess);548  Opts.Params.Include = InputArgs.getAllArgValues(OPT_includepath);549  Opts.Params.NoInclude = InputArgs.hasArg(OPT_noinclude);550  if (Opts.Params.NoInclude) {551    // Clear the INLCUDE variable for the external preprocessor552#ifdef _WIN32553    ::_putenv("INCLUDE=");554#else555    ::unsetenv("INCLUDE");556#endif557  }558  if (InputArgs.hasArg(OPT_codepage)) {559    if (InputArgs.getLastArgValue(OPT_codepage)560            .getAsInteger(10, Opts.Params.CodePage))561      fatalError("Invalid code page: " +562                 InputArgs.getLastArgValue(OPT_codepage));563  }564  Opts.IsDryRun = InputArgs.hasArg(OPT_dry_run);565  auto OutArgsInfo = InputArgs.getAllArgValues(OPT_fileout);566  if (OutArgsInfo.empty()) {567    SmallString<128> OutputFile(Opts.InputFile);568    llvm::sys::fs::make_absolute(OutputFile);569    llvm::sys::path::replace_extension(OutputFile, "res");570    OutArgsInfo.push_back(std::string(OutputFile));571  }572  if (!Opts.IsDryRun) {573    if (OutArgsInfo.size() != 1)574      fatalError(575          "No more than one output file should be provided (using /FO flag).");576    Opts.OutputFile = OutArgsInfo[0];577  }578  Opts.AppendNull = InputArgs.hasArg(OPT_add_null);579  if (InputArgs.hasArg(OPT_lang_id)) {580    StringRef Val = InputArgs.getLastArgValue(OPT_lang_id);581    Val.consume_front_insensitive("0x");582    if (Val.getAsInteger(16, Opts.LangId))583      fatalError("Invalid language id: " +584                 InputArgs.getLastArgValue(OPT_lang_id));585  }586  return Opts;587}588 589RcOptions getOptions(const char *Argv0, ArrayRef<const char *> ArgsArr,590                     ArrayRef<const char *> InputArgs) {591  std::string Prefix;592  bool IsWindres;593  std::tie(IsWindres, Prefix) = isWindres(Argv0);594  if (IsWindres)595    return parseWindresOptions(ArgsArr, InputArgs, Prefix);596  else597    return parseRcOptions(ArgsArr, InputArgs);598}599 600void doRc(std::string Src, std::string Dest, RcOptions &Opts,601          const char *Argv0) {602  std::string PreprocessedFile = Src;603  if (Opts.Preprocess) {604    std::string OutFile = createTempFile("preproc", "rc");605    TempPreprocFile.setFile(OutFile);606    preprocess(Src, OutFile, Opts, Argv0);607    PreprocessedFile = OutFile;608  }609 610  // Read and tokenize the input file.611  ErrorOr<std::unique_ptr<MemoryBuffer>> File =612      MemoryBuffer::getFile(PreprocessedFile, /*IsText=*/true);613  if (!File) {614    fatalError("Error opening file '" + Twine(PreprocessedFile) +615               "': " + File.getError().message());616  }617 618  std::unique_ptr<MemoryBuffer> FileContents = std::move(*File);619  StringRef Contents = FileContents->getBuffer();620 621  std::string FilteredContents = filterCppOutput(Contents);622  std::vector<RCToken> Tokens =623      ExitOnErr(tokenizeRC(FilteredContents, Opts.IsWindres));624 625  if (Opts.BeVerbose) {626    const Twine TokenNames[] = {627#define TOKEN(Name) #Name,628#define SHORT_TOKEN(Name, Ch) #Name,629#include "ResourceScriptTokenList.def"630    };631 632    for (const RCToken &Token : Tokens) {633      outs() << TokenNames[static_cast<int>(Token.kind())] << ": "634             << Token.value();635      if (Token.kind() == RCToken::Kind::Int)636        outs() << "; int value = " << Token.intValue();637 638      outs() << "\n";639    }640  }641 642  WriterParams &Params = Opts.Params;643  SmallString<128> InputFile(Src);644  llvm::sys::fs::make_absolute(InputFile);645  Params.InputFilePath = InputFile;646 647  switch (Params.CodePage) {648  case CpAcp:649  case CpWin1252:650  case CpUtf8:651    break;652  default:653    fatalError("Unsupported code page, only 0, 1252 and 65001 are supported!");654  }655 656  std::unique_ptr<ResourceFileWriter> Visitor;657 658  if (!Opts.IsDryRun) {659    std::error_code EC;660    auto FOut = std::make_unique<raw_fd_ostream>(661        Dest, EC, sys::fs::FA_Read | sys::fs::FA_Write);662    if (EC)663      fatalError("Error opening output file '" + Dest + "': " + EC.message());664    Visitor = std::make_unique<ResourceFileWriter>(Params, std::move(FOut));665    Visitor->AppendNull = Opts.AppendNull;666 667    ExitOnErr(NullResource().visit(Visitor.get()));668 669    unsigned PrimaryLangId = Opts.LangId & 0x3ff;670    unsigned SubLangId = Opts.LangId >> 10;671    ExitOnErr(LanguageResource(PrimaryLangId, SubLangId).visit(Visitor.get()));672  }673 674  rc::RCParser Parser{std::move(Tokens)};675  while (!Parser.isEof()) {676    auto Resource = ExitOnErr(Parser.parseSingleResource());677    if (Opts.BeVerbose)678      Resource->log(outs());679    if (!Opts.IsDryRun)680      ExitOnErr(Resource->visit(Visitor.get()));681  }682 683  // STRINGTABLE resources come at the very end.684  if (!Opts.IsDryRun)685    ExitOnErr(Visitor->dumpAllStringTables());686}687 688void doCvtres(std::string Src, std::string Dest, std::string TargetTriple) {689  object::WindowsResourceParser Parser;690 691  ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =692      MemoryBuffer::getFile(Src, /*IsText=*/true);693  if (!BufferOrErr)694    fatalError("Error opening file '" + Twine(Src) +695               "': " + BufferOrErr.getError().message());696  std::unique_ptr<MemoryBuffer> &Buffer = BufferOrErr.get();697  std::unique_ptr<object::WindowsResource> Binary =698      ExitOnErr(object::WindowsResource::createWindowsResource(699          Buffer->getMemBufferRef()));700 701  std::vector<std::string> Duplicates;702  ExitOnErr(Parser.parse(Binary.get(), Duplicates));703  for (const auto &DupeDiag : Duplicates)704    fatalError("Duplicate resources: " + DupeDiag);705 706  Triple T(TargetTriple);707  COFF::MachineTypes MachineType;708  switch (T.getArch()) {709  case Triple::x86:710    MachineType = COFF::IMAGE_FILE_MACHINE_I386;711    break;712  case Triple::x86_64:713    MachineType = COFF::IMAGE_FILE_MACHINE_AMD64;714    break;715  case Triple::arm:716  case Triple::thumb:717    MachineType = COFF::IMAGE_FILE_MACHINE_ARMNT;718    break;719  case Triple::aarch64:720    if (T.isWindowsArm64EC())721      MachineType = COFF::IMAGE_FILE_MACHINE_ARM64EC;722    else723      MachineType = COFF::IMAGE_FILE_MACHINE_ARM64;724    break;725  default:726    fatalError("Unsupported architecture in target '" + Twine(TargetTriple) +727               "'");728  }729 730  std::unique_ptr<MemoryBuffer> OutputBuffer =731      ExitOnErr(object::writeWindowsResourceCOFF(MachineType, Parser,732                                                 /*DateTimeStamp*/ 0));733  std::unique_ptr<FileOutputBuffer> FileBuffer =734      ExitOnErr(FileOutputBuffer::create(Dest, OutputBuffer->getBufferSize()));735  std::copy(OutputBuffer->getBufferStart(), OutputBuffer->getBufferEnd(),736            FileBuffer->getBufferStart());737  ExitOnErr(FileBuffer->commit());738}739 740} // anonymous namespace741 742int llvm_rc_main(int Argc, char **Argv, const llvm::ToolContext &) {743  ExitOnErr.setBanner("llvm-rc: ");744 745  char **DashDash = std::find_if(Argv + 1, Argv + Argc,746                                 [](StringRef Str) { return Str == "--"; });747  ArrayRef<const char *> ArgsArr = ArrayRef(Argv + 1, DashDash);748  ArrayRef<const char *> FileArgsArr;749  if (DashDash != Argv + Argc)750    FileArgsArr = ArrayRef(DashDash + 1, Argv + Argc);751 752  RcOptions Opts = getOptions(Argv[0], ArgsArr, FileArgsArr);753 754  std::string ResFile = Opts.OutputFile;755  if (Opts.InputFormat == Rc) {756    if (Opts.OutputFormat == Coff) {757      ResFile = createTempFile("rc", "res");758      TempResFile.setFile(ResFile);759    }760    doRc(Opts.InputFile, ResFile, Opts, Argv[0]);761  } else {762    ResFile = Opts.InputFile;763  }764  if (Opts.OutputFormat == Coff) {765    doCvtres(ResFile, Opts.OutputFile, Opts.Triple);766  }767 768  return 0;769}770