123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- //===-- llvm-rc.cpp - Compile .rc scripts into .res -------------*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // Compile .rc scripts into .res files. This is intended to be a
- // platform-independent port of Microsoft's rc.exe tool.
- //
- //===----------------------------------------------------------------------===//
- #include "ResourceFileWriter.h"
- #include "ResourceScriptCppFilter.h"
- #include "ResourceScriptParser.h"
- #include "ResourceScriptStmt.h"
- #include "ResourceScriptToken.h"
- #include "llvm/Option/Arg.h"
- #include "llvm/Option/ArgList.h"
- #include "llvm/Support/Error.h"
- #include "llvm/Support/FileSystem.h"
- #include "llvm/Support/InitLLVM.h"
- #include "llvm/Support/ManagedStatic.h"
- #include "llvm/Support/MemoryBuffer.h"
- #include "llvm/Support/Path.h"
- #include "llvm/Support/PrettyStackTrace.h"
- #include "llvm/Support/Process.h"
- #include "llvm/Support/Signals.h"
- #include "llvm/Support/raw_ostream.h"
- #include <algorithm>
- #include <system_error>
- using namespace llvm;
- using namespace llvm::rc;
- namespace {
- // Input options tables.
- enum ID {
- OPT_INVALID = 0, // This is not a correct option ID.
- #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
- HELPTEXT, METAVAR, VALUES) \
- OPT_##ID,
- #include "Opts.inc"
- #undef OPTION
- };
- #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
- #include "Opts.inc"
- #undef PREFIX
- static const opt::OptTable::Info InfoTable[] = {
- #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
- HELPTEXT, METAVAR, VALUES) \
- { \
- PREFIX, NAME, HELPTEXT, \
- METAVAR, OPT_##ID, opt::Option::KIND##Class, \
- PARAM, FLAGS, OPT_##GROUP, \
- OPT_##ALIAS, ALIASARGS, VALUES},
- #include "Opts.inc"
- #undef OPTION
- };
- class RcOptTable : public opt::OptTable {
- public:
- RcOptTable() : OptTable(InfoTable, /* IgnoreCase = */ true) {}
- };
- static ExitOnError ExitOnErr;
- LLVM_ATTRIBUTE_NORETURN static void fatalError(const Twine &Message) {
- errs() << Message << "\n";
- exit(1);
- }
- } // anonymous namespace
- int main(int Argc, const char **Argv) {
- InitLLVM X(Argc, Argv);
- ExitOnErr.setBanner("llvm-rc: ");
- RcOptTable T;
- unsigned MAI, MAC;
- const char **DashDash = std::find_if(
- Argv + 1, Argv + Argc, [](StringRef Str) { return Str == "--"; });
- ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, DashDash);
- opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC);
- // The tool prints nothing when invoked with no command-line arguments.
- if (InputArgs.hasArg(OPT_help)) {
- T.PrintHelp(outs(), "rc [options] file...", "Resource Converter", false);
- return 0;
- }
- const bool BeVerbose = InputArgs.hasArg(OPT_verbose);
- std::vector<std::string> InArgsInfo = InputArgs.getAllArgValues(OPT_INPUT);
- if (DashDash != Argv + Argc)
- InArgsInfo.insert(InArgsInfo.end(), DashDash + 1, Argv + Argc);
- if (InArgsInfo.size() != 1) {
- fatalError("Exactly one input file should be provided.");
- }
- // Read and tokenize the input file.
- ErrorOr<std::unique_ptr<MemoryBuffer>> File =
- MemoryBuffer::getFile(InArgsInfo[0]);
- if (!File) {
- fatalError("Error opening file '" + Twine(InArgsInfo[0]) +
- "': " + File.getError().message());
- }
- std::unique_ptr<MemoryBuffer> FileContents = std::move(*File);
- StringRef Contents = FileContents->getBuffer();
- std::string FilteredContents = filterCppOutput(Contents);
- std::vector<RCToken> Tokens = ExitOnErr(tokenizeRC(FilteredContents));
- if (BeVerbose) {
- const Twine TokenNames[] = {
- #define TOKEN(Name) #Name,
- #define SHORT_TOKEN(Name, Ch) #Name,
- #include "ResourceScriptTokenList.def"
- };
- for (const RCToken &Token : Tokens) {
- outs() << TokenNames[static_cast<int>(Token.kind())] << ": "
- << Token.value();
- if (Token.kind() == RCToken::Kind::Int)
- outs() << "; int value = " << Token.intValue();
- outs() << "\n";
- }
- }
- WriterParams Params;
- SmallString<128> InputFile(InArgsInfo[0]);
- llvm::sys::fs::make_absolute(InputFile);
- Params.InputFilePath = InputFile;
- Params.Include = InputArgs.getAllArgValues(OPT_includepath);
- Params.NoInclude = InputArgs.getAllArgValues(OPT_noinclude);
- if (InputArgs.hasArg(OPT_codepage)) {
- if (InputArgs.getLastArgValue(OPT_codepage)
- .getAsInteger(10, Params.CodePage))
- fatalError("Invalid code page: " +
- InputArgs.getLastArgValue(OPT_codepage));
- switch (Params.CodePage) {
- case CpAcp:
- case CpWin1252:
- case CpUtf8:
- break;
- default:
- fatalError(
- "Unsupported code page, only 0, 1252 and 65001 are supported!");
- }
- }
- std::unique_ptr<ResourceFileWriter> Visitor;
- bool IsDryRun = InputArgs.hasArg(OPT_dry_run);
- if (!IsDryRun) {
- auto OutArgsInfo = InputArgs.getAllArgValues(OPT_fileout);
- if (OutArgsInfo.empty()) {
- SmallString<128> OutputFile = InputFile;
- llvm::sys::path::replace_extension(OutputFile, "res");
- OutArgsInfo.push_back(std::string(OutputFile.str()));
- }
- if (OutArgsInfo.size() != 1)
- fatalError(
- "No more than one output file should be provided (using /FO flag).");
- std::error_code EC;
- auto FOut = std::make_unique<raw_fd_ostream>(
- OutArgsInfo[0], EC, sys::fs::FA_Read | sys::fs::FA_Write);
- if (EC)
- fatalError("Error opening output file '" + OutArgsInfo[0] +
- "': " + EC.message());
- Visitor = std::make_unique<ResourceFileWriter>(Params, std::move(FOut));
- Visitor->AppendNull = InputArgs.hasArg(OPT_add_null);
- ExitOnErr(NullResource().visit(Visitor.get()));
- // Set the default language; choose en-US arbitrarily.
- unsigned PrimaryLangId = 0x09, SubLangId = 0x01;
- if (InputArgs.hasArg(OPT_lang_id)) {
- unsigned LangId;
- if (InputArgs.getLastArgValue(OPT_lang_id).getAsInteger(16, LangId))
- fatalError("Invalid language id: " +
- InputArgs.getLastArgValue(OPT_lang_id));
- PrimaryLangId = LangId & 0x3ff;
- SubLangId = LangId >> 10;
- }
- ExitOnErr(LanguageResource(PrimaryLangId, SubLangId).visit(Visitor.get()));
- }
- rc::RCParser Parser{std::move(Tokens)};
- while (!Parser.isEof()) {
- auto Resource = ExitOnErr(Parser.parseSingleResource());
- if (BeVerbose)
- Resource->log(outs());
- if (!IsDryRun)
- ExitOnErr(Resource->visit(Visitor.get()));
- }
- // STRINGTABLE resources come at the very end.
- if (!IsDryRun)
- ExitOnErr(Visitor->dumpAllStringTables());
- return 0;
- }
|