blob: 41360d2a7bdeca91150a489096082875e4b436fb [file] [log] [blame]
Marek Sokolowski719e22d2017-08-10 16:21:44 +00001//===-- llvm-rc.cpp - Compile .rc scripts into .res -------------*- C++ -*-===//
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +00002//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// Compile .rc scripts into .res files. This is intended to be a
11// platform-independent port of Microsoft's rc.exe tool.
12//
13//===----------------------------------------------------------------------===//
14
Marek Sokolowski8f193432017-09-29 17:14:09 +000015#include "ResourceFileWriter.h"
Martin Storsjo518b6c92018-05-09 18:21:03 +000016#include "ResourceScriptCppFilter.h"
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000017#include "ResourceScriptParser.h"
Marek Sokolowski8f193432017-09-29 17:14:09 +000018#include "ResourceScriptStmt.h"
19#include "ResourceScriptToken.h"
Marek Sokolowski719e22d2017-08-10 16:21:44 +000020
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +000021#include "llvm/Option/Arg.h"
22#include "llvm/Option/ArgList.h"
23#include "llvm/Support/Error.h"
Marek Sokolowski8f193432017-09-29 17:14:09 +000024#include "llvm/Support/FileSystem.h"
Rui Ueyama197194b2018-04-13 18:26:06 +000025#include "llvm/Support/InitLLVM.h"
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +000026#include "llvm/Support/ManagedStatic.h"
Zachary Turnerbd159d32017-11-17 01:00:35 +000027#include "llvm/Support/MemoryBuffer.h"
Martin Storsjoe3b43792018-05-02 21:15:24 +000028#include "llvm/Support/Path.h"
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +000029#include "llvm/Support/PrettyStackTrace.h"
30#include "llvm/Support/Process.h"
31#include "llvm/Support/Signals.h"
32#include "llvm/Support/raw_ostream.h"
33
34#include <system_error>
35
36using namespace llvm;
Marek Sokolowski8f193432017-09-29 17:14:09 +000037using namespace llvm::rc;
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +000038
39namespace {
40
41// Input options tables.
42
43enum ID {
44 OPT_INVALID = 0, // This is not a correct option ID.
45#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
46 HELPTEXT, METAVAR, VALUES) \
47 OPT_##ID,
48#include "Opts.inc"
49#undef OPTION
50};
51
52#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
53#include "Opts.inc"
54#undef PREFIX
55
56static const opt::OptTable::Info InfoTable[] = {
57#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
58 HELPTEXT, METAVAR, VALUES) \
59 { \
60 PREFIX, NAME, HELPTEXT, \
61 METAVAR, OPT_##ID, opt::Option::KIND##Class, \
62 PARAM, FLAGS, OPT_##GROUP, \
63 OPT_##ALIAS, ALIASARGS, VALUES},
64#include "Opts.inc"
65#undef OPTION
66};
67
68class RcOptTable : public opt::OptTable {
69public:
70 RcOptTable() : OptTable(InfoTable, /* IgnoreCase = */ true) {}
71};
72
73static ExitOnError ExitOnErr;
Marek Sokolowski719e22d2017-08-10 16:21:44 +000074
Zachary Turnerfa0ca6c2017-10-11 20:12:09 +000075LLVM_ATTRIBUTE_NORETURN static void fatalError(const Twine &Message) {
Marek Sokolowski719e22d2017-08-10 16:21:44 +000076 errs() << Message << "\n";
77 exit(1);
78}
79
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +000080} // anonymous namespace
81
Rui Ueyama197194b2018-04-13 18:26:06 +000082int main(int Argc, const char **Argv) {
83 InitLLVM X(Argc, Argv);
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +000084 ExitOnErr.setBanner("llvm-rc: ");
85
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +000086 RcOptTable T;
87 unsigned MAI, MAC;
Rui Ueyama197194b2018-04-13 18:26:06 +000088 ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, Argc - 1);
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +000089 opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC);
90
91 // The tool prints nothing when invoked with no command-line arguments.
Marek Sokolowski719e22d2017-08-10 16:21:44 +000092 if (InputArgs.hasArg(OPT_HELP)) {
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +000093 T.PrintHelp(outs(), "rc", "Resource Converter", false);
Marek Sokolowski719e22d2017-08-10 16:21:44 +000094 return 0;
95 }
96
97 const bool BeVerbose = InputArgs.hasArg(OPT_VERBOSE);
98
99 std::vector<std::string> InArgsInfo = InputArgs.getAllArgValues(OPT_INPUT);
100 if (InArgsInfo.size() != 1) {
101 fatalError("Exactly one input file should be provided.");
102 }
103
104 // Read and tokenize the input file.
Zachary Turnerfa0ca6c2017-10-11 20:12:09 +0000105 ErrorOr<std::unique_ptr<MemoryBuffer>> File =
106 MemoryBuffer::getFile(InArgsInfo[0]);
Marek Sokolowski719e22d2017-08-10 16:21:44 +0000107 if (!File) {
Zachary Turnerfa0ca6c2017-10-11 20:12:09 +0000108 fatalError("Error opening file '" + Twine(InArgsInfo[0]) +
Marek Sokolowski719e22d2017-08-10 16:21:44 +0000109 "': " + File.getError().message());
110 }
111
112 std::unique_ptr<MemoryBuffer> FileContents = std::move(*File);
113 StringRef Contents = FileContents->getBuffer();
114
Martin Storsjo518b6c92018-05-09 18:21:03 +0000115 std::string FilteredContents = filterCppOutput(Contents);
116 std::vector<RCToken> Tokens = ExitOnErr(tokenizeRC(FilteredContents));
Marek Sokolowski719e22d2017-08-10 16:21:44 +0000117
118 if (BeVerbose) {
119 const Twine TokenNames[] = {
120#define TOKEN(Name) #Name,
121#define SHORT_TOKEN(Name, Ch) #Name,
David Blaikieb961d292017-11-21 00:23:19 +0000122#include "ResourceScriptTokenList.def"
Marek Sokolowski719e22d2017-08-10 16:21:44 +0000123 };
124
125 for (const RCToken &Token : Tokens) {
126 outs() << TokenNames[static_cast<int>(Token.kind())] << ": "
127 << Token.value();
128 if (Token.kind() == RCToken::Kind::Int)
129 outs() << "; int value = " << Token.intValue();
130
131 outs() << "\n";
132 }
133 }
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +0000134
Martin Storsjod1d046a2018-05-02 19:43:44 +0000135 WriterParams Params;
Zachary Turnerfa0ca6c2017-10-11 20:12:09 +0000136 SmallString<128> InputFile(InArgsInfo[0]);
137 llvm::sys::fs::make_absolute(InputFile);
138 Params.InputFilePath = InputFile;
139 Params.Include = InputArgs.getAllArgValues(OPT_INCLUDE);
140 Params.NoInclude = InputArgs.getAllArgValues(OPT_NOINCLUDE);
141
Martin Storsjod1d046a2018-05-02 19:43:44 +0000142 if (InputArgs.hasArg(OPT_CODEPAGE)) {
143 if (InputArgs.getLastArgValue(OPT_CODEPAGE)
144 .getAsInteger(10, Params.CodePage))
145 fatalError("Invalid code page: " +
146 InputArgs.getLastArgValue(OPT_CODEPAGE));
147 switch (Params.CodePage) {
148 case CpAcp:
149 case CpWin1252:
150 case CpUtf8:
151 break;
152 default:
153 fatalError(
154 "Unsupported code page, only 0, 1252 and 65001 are supported!");
155 }
156 }
157
Marek Sokolowski8f193432017-09-29 17:14:09 +0000158 std::unique_ptr<ResourceFileWriter> Visitor;
159 bool IsDryRun = InputArgs.hasArg(OPT_DRY_RUN);
160
161 if (!IsDryRun) {
162 auto OutArgsInfo = InputArgs.getAllArgValues(OPT_FILEOUT);
Martin Storsjoe3b43792018-05-02 21:15:24 +0000163 if (OutArgsInfo.empty()) {
164 SmallString<128> OutputFile = InputFile;
165 llvm::sys::path::replace_extension(OutputFile, "res");
166 OutArgsInfo.push_back(OutputFile.str());
167 }
168
Marek Sokolowski8f193432017-09-29 17:14:09 +0000169 if (OutArgsInfo.size() != 1)
170 fatalError(
Martin Storsjoe3b43792018-05-02 21:15:24 +0000171 "No more than one output file should be provided (using /FO flag).");
Marek Sokolowski8f193432017-09-29 17:14:09 +0000172
173 std::error_code EC;
174 auto FOut =
175 llvm::make_unique<raw_fd_ostream>(OutArgsInfo[0], EC, sys::fs::F_RW);
176 if (EC)
177 fatalError("Error opening output file '" + OutArgsInfo[0] +
178 "': " + EC.message());
Zachary Turnerfa0ca6c2017-10-11 20:12:09 +0000179 Visitor = llvm::make_unique<ResourceFileWriter>(Params, std::move(FOut));
Zachary Turnerda366692017-10-06 21:30:55 +0000180 Visitor->AppendNull = InputArgs.hasArg(OPT_ADD_NULL);
Marek Sokolowski8f193432017-09-29 17:14:09 +0000181
182 ExitOnErr(NullResource().visit(Visitor.get()));
183
184 // Set the default language; choose en-US arbitrarily.
185 ExitOnErr(LanguageResource(0x09, 0x01).visit(Visitor.get()));
186 }
187
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000188 rc::RCParser Parser{std::move(Tokens)};
189 while (!Parser.isEof()) {
190 auto Resource = ExitOnErr(Parser.parseSingleResource());
191 if (BeVerbose)
192 Resource->log(outs());
Marek Sokolowski8f193432017-09-29 17:14:09 +0000193 if (!IsDryRun)
194 ExitOnErr(Resource->visit(Visitor.get()));
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000195 }
196
Zachary Turnerda366692017-10-06 21:30:55 +0000197 // STRINGTABLE resources come at the very end.
198 if (!IsDryRun)
199 ExitOnErr(Visitor->dumpAllStringTables());
200
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +0000201 return 0;
202}