blob: 54997e900a233aad63b79af61b8e27e1ce113806 [file] [log] [blame]
Marek Sokolowski6c9cbed2017-08-10 16:21:44 +00001//===-- llvm-rc.cpp - Compile .rc scripts into .res -------------*- C++ -*-===//
Marek Sokolowskic6f54fe2017-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 Sokolowskie37621b2017-09-29 17:14:09 +000015#include "ResourceFileWriter.h"
Martin Storsjod5e93362018-05-09 18:21:03 +000016#include "ResourceScriptCppFilter.h"
Marek Sokolowski0b33df92017-08-18 18:24:17 +000017#include "ResourceScriptParser.h"
Marek Sokolowskie37621b2017-09-29 17:14:09 +000018#include "ResourceScriptStmt.h"
19#include "ResourceScriptToken.h"
Marek Sokolowski6c9cbed2017-08-10 16:21:44 +000020
Marek Sokolowskic6f54fe2017-07-25 00:25:18 +000021#include "llvm/Option/Arg.h"
22#include "llvm/Option/ArgList.h"
23#include "llvm/Support/Error.h"
Marek Sokolowskie37621b2017-09-29 17:14:09 +000024#include "llvm/Support/FileSystem.h"
Rui Ueyama0b9d56a2018-04-13 18:26:06 +000025#include "llvm/Support/InitLLVM.h"
Marek Sokolowskic6f54fe2017-07-25 00:25:18 +000026#include "llvm/Support/ManagedStatic.h"
Zachary Turnere847ec42017-11-17 01:00:35 +000027#include "llvm/Support/MemoryBuffer.h"
Martin Storsjo8f8e40c2018-05-02 21:15:24 +000028#include "llvm/Support/Path.h"
Marek Sokolowskic6f54fe2017-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
Martin Storsjob13b0252019-01-16 08:09:22 +000034#include <algorithm>
Marek Sokolowskic6f54fe2017-07-25 00:25:18 +000035#include <system_error>
36
37using namespace llvm;
Marek Sokolowskie37621b2017-09-29 17:14:09 +000038using namespace llvm::rc;
Marek Sokolowskic6f54fe2017-07-25 00:25:18 +000039
40namespace {
41
42// Input options tables.
43
44enum ID {
45 OPT_INVALID = 0, // This is not a correct option ID.
46#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
47 HELPTEXT, METAVAR, VALUES) \
48 OPT_##ID,
49#include "Opts.inc"
50#undef OPTION
51};
52
53#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
54#include "Opts.inc"
55#undef PREFIX
56
57static const opt::OptTable::Info InfoTable[] = {
58#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
59 HELPTEXT, METAVAR, VALUES) \
60 { \
61 PREFIX, NAME, HELPTEXT, \
62 METAVAR, OPT_##ID, opt::Option::KIND##Class, \
63 PARAM, FLAGS, OPT_##GROUP, \
64 OPT_##ALIAS, ALIASARGS, VALUES},
65#include "Opts.inc"
66#undef OPTION
67};
68
69class RcOptTable : public opt::OptTable {
70public:
71 RcOptTable() : OptTable(InfoTable, /* IgnoreCase = */ true) {}
72};
73
74static ExitOnError ExitOnErr;
Marek Sokolowski6c9cbed2017-08-10 16:21:44 +000075
Zachary Turnere315d732017-10-11 20:12:09 +000076LLVM_ATTRIBUTE_NORETURN static void fatalError(const Twine &Message) {
Marek Sokolowski6c9cbed2017-08-10 16:21:44 +000077 errs() << Message << "\n";
78 exit(1);
79}
80
Marek Sokolowskic6f54fe2017-07-25 00:25:18 +000081} // anonymous namespace
82
Rui Ueyama0b9d56a2018-04-13 18:26:06 +000083int main(int Argc, const char **Argv) {
84 InitLLVM X(Argc, Argv);
Marek Sokolowskic6f54fe2017-07-25 00:25:18 +000085 ExitOnErr.setBanner("llvm-rc: ");
86
Marek Sokolowskic6f54fe2017-07-25 00:25:18 +000087 RcOptTable T;
88 unsigned MAI, MAC;
Martin Storsjob13b0252019-01-16 08:09:22 +000089 const char **DashDash = std::find_if(
90 Argv + 1, Argv + Argc, [](StringRef Str) { return Str == "--"; });
91 ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, DashDash);
92
Marek Sokolowskic6f54fe2017-07-25 00:25:18 +000093 opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC);
94
95 // The tool prints nothing when invoked with no command-line arguments.
Marek Sokolowski6c9cbed2017-08-10 16:21:44 +000096 if (InputArgs.hasArg(OPT_HELP)) {
Fangrui Song42f63b62018-10-10 00:15:31 +000097 T.PrintHelp(outs(), "rc [options] file...", "Resource Converter", false);
Marek Sokolowski6c9cbed2017-08-10 16:21:44 +000098 return 0;
99 }
100
101 const bool BeVerbose = InputArgs.hasArg(OPT_VERBOSE);
102
103 std::vector<std::string> InArgsInfo = InputArgs.getAllArgValues(OPT_INPUT);
Martin Storsjob13b0252019-01-16 08:09:22 +0000104 if (DashDash != Argv + Argc)
105 InArgsInfo.insert(InArgsInfo.end(), DashDash + 1, Argv + Argc);
Marek Sokolowski6c9cbed2017-08-10 16:21:44 +0000106 if (InArgsInfo.size() != 1) {
107 fatalError("Exactly one input file should be provided.");
108 }
109
110 // Read and tokenize the input file.
Zachary Turnere315d732017-10-11 20:12:09 +0000111 ErrorOr<std::unique_ptr<MemoryBuffer>> File =
112 MemoryBuffer::getFile(InArgsInfo[0]);
Marek Sokolowski6c9cbed2017-08-10 16:21:44 +0000113 if (!File) {
Zachary Turnere315d732017-10-11 20:12:09 +0000114 fatalError("Error opening file '" + Twine(InArgsInfo[0]) +
Marek Sokolowski6c9cbed2017-08-10 16:21:44 +0000115 "': " + File.getError().message());
116 }
117
118 std::unique_ptr<MemoryBuffer> FileContents = std::move(*File);
119 StringRef Contents = FileContents->getBuffer();
120
Martin Storsjod5e93362018-05-09 18:21:03 +0000121 std::string FilteredContents = filterCppOutput(Contents);
122 std::vector<RCToken> Tokens = ExitOnErr(tokenizeRC(FilteredContents));
Marek Sokolowski6c9cbed2017-08-10 16:21:44 +0000123
124 if (BeVerbose) {
125 const Twine TokenNames[] = {
126#define TOKEN(Name) #Name,
127#define SHORT_TOKEN(Name, Ch) #Name,
David Blaikiebb5acf92017-11-21 00:23:19 +0000128#include "ResourceScriptTokenList.def"
Marek Sokolowski6c9cbed2017-08-10 16:21:44 +0000129 };
130
131 for (const RCToken &Token : Tokens) {
132 outs() << TokenNames[static_cast<int>(Token.kind())] << ": "
133 << Token.value();
134 if (Token.kind() == RCToken::Kind::Int)
135 outs() << "; int value = " << Token.intValue();
136
137 outs() << "\n";
138 }
139 }
Marek Sokolowskic6f54fe2017-07-25 00:25:18 +0000140
Martin Storsjoe99f5b42018-05-02 19:43:44 +0000141 WriterParams Params;
Zachary Turnere315d732017-10-11 20:12:09 +0000142 SmallString<128> InputFile(InArgsInfo[0]);
143 llvm::sys::fs::make_absolute(InputFile);
144 Params.InputFilePath = InputFile;
145 Params.Include = InputArgs.getAllArgValues(OPT_INCLUDE);
146 Params.NoInclude = InputArgs.getAllArgValues(OPT_NOINCLUDE);
147
Martin Storsjoe99f5b42018-05-02 19:43:44 +0000148 if (InputArgs.hasArg(OPT_CODEPAGE)) {
149 if (InputArgs.getLastArgValue(OPT_CODEPAGE)
150 .getAsInteger(10, Params.CodePage))
151 fatalError("Invalid code page: " +
152 InputArgs.getLastArgValue(OPT_CODEPAGE));
153 switch (Params.CodePage) {
154 case CpAcp:
155 case CpWin1252:
156 case CpUtf8:
157 break;
158 default:
159 fatalError(
160 "Unsupported code page, only 0, 1252 and 65001 are supported!");
161 }
162 }
163
Marek Sokolowskie37621b2017-09-29 17:14:09 +0000164 std::unique_ptr<ResourceFileWriter> Visitor;
165 bool IsDryRun = InputArgs.hasArg(OPT_DRY_RUN);
166
167 if (!IsDryRun) {
168 auto OutArgsInfo = InputArgs.getAllArgValues(OPT_FILEOUT);
Martin Storsjo8f8e40c2018-05-02 21:15:24 +0000169 if (OutArgsInfo.empty()) {
170 SmallString<128> OutputFile = InputFile;
171 llvm::sys::path::replace_extension(OutputFile, "res");
172 OutArgsInfo.push_back(OutputFile.str());
173 }
174
Marek Sokolowskie37621b2017-09-29 17:14:09 +0000175 if (OutArgsInfo.size() != 1)
176 fatalError(
Martin Storsjo8f8e40c2018-05-02 21:15:24 +0000177 "No more than one output file should be provided (using /FO flag).");
Marek Sokolowskie37621b2017-09-29 17:14:09 +0000178
179 std::error_code EC;
Zachary Turner03bcb212018-06-07 19:58:58 +0000180 auto FOut = llvm::make_unique<raw_fd_ostream>(
181 OutArgsInfo[0], EC, sys::fs::FA_Read | sys::fs::FA_Write);
Marek Sokolowskie37621b2017-09-29 17:14:09 +0000182 if (EC)
183 fatalError("Error opening output file '" + OutArgsInfo[0] +
184 "': " + EC.message());
Zachary Turnere315d732017-10-11 20:12:09 +0000185 Visitor = llvm::make_unique<ResourceFileWriter>(Params, std::move(FOut));
Zachary Turner84ad96b2017-10-06 21:30:55 +0000186 Visitor->AppendNull = InputArgs.hasArg(OPT_ADD_NULL);
Marek Sokolowskie37621b2017-09-29 17:14:09 +0000187
188 ExitOnErr(NullResource().visit(Visitor.get()));
189
190 // Set the default language; choose en-US arbitrarily.
191 ExitOnErr(LanguageResource(0x09, 0x01).visit(Visitor.get()));
192 }
193
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000194 rc::RCParser Parser{std::move(Tokens)};
195 while (!Parser.isEof()) {
196 auto Resource = ExitOnErr(Parser.parseSingleResource());
197 if (BeVerbose)
198 Resource->log(outs());
Marek Sokolowskie37621b2017-09-29 17:14:09 +0000199 if (!IsDryRun)
200 ExitOnErr(Resource->visit(Visitor.get()));
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000201 }
202
Zachary Turner84ad96b2017-10-06 21:30:55 +0000203 // STRINGTABLE resources come at the very end.
204 if (!IsDryRun)
205 ExitOnErr(Visitor->dumpAllStringTables());
206
Marek Sokolowskic6f54fe2017-07-25 00:25:18 +0000207 return 0;
208}