blob: 6491473de3217e6d159e31bcb539fc6b79ca2b90 [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"
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000016#include "ResourceScriptParser.h"
Marek Sokolowski8f193432017-09-29 17:14:09 +000017#include "ResourceScriptStmt.h"
18#include "ResourceScriptToken.h"
Marek Sokolowski719e22d2017-08-10 16:21:44 +000019
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +000020#include "llvm/Option/Arg.h"
21#include "llvm/Option/ArgList.h"
22#include "llvm/Support/Error.h"
Marek Sokolowski8f193432017-09-29 17:14:09 +000023#include "llvm/Support/FileSystem.h"
Rui Ueyama197194b2018-04-13 18:26:06 +000024#include "llvm/Support/InitLLVM.h"
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +000025#include "llvm/Support/ManagedStatic.h"
Zachary Turnerbd159d32017-11-17 01:00:35 +000026#include "llvm/Support/MemoryBuffer.h"
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +000027#include "llvm/Support/PrettyStackTrace.h"
28#include "llvm/Support/Process.h"
29#include "llvm/Support/Signals.h"
30#include "llvm/Support/raw_ostream.h"
31
32#include <system_error>
33
34using namespace llvm;
Marek Sokolowski8f193432017-09-29 17:14:09 +000035using namespace llvm::rc;
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +000036
37namespace {
38
39// Input options tables.
40
41enum ID {
42 OPT_INVALID = 0, // This is not a correct option ID.
43#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
44 HELPTEXT, METAVAR, VALUES) \
45 OPT_##ID,
46#include "Opts.inc"
47#undef OPTION
48};
49
50#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
51#include "Opts.inc"
52#undef PREFIX
53
54static const opt::OptTable::Info InfoTable[] = {
55#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
56 HELPTEXT, METAVAR, VALUES) \
57 { \
58 PREFIX, NAME, HELPTEXT, \
59 METAVAR, OPT_##ID, opt::Option::KIND##Class, \
60 PARAM, FLAGS, OPT_##GROUP, \
61 OPT_##ALIAS, ALIASARGS, VALUES},
62#include "Opts.inc"
63#undef OPTION
64};
65
66class RcOptTable : public opt::OptTable {
67public:
68 RcOptTable() : OptTable(InfoTable, /* IgnoreCase = */ true) {}
69};
70
71static ExitOnError ExitOnErr;
Marek Sokolowski719e22d2017-08-10 16:21:44 +000072
Zachary Turnerfa0ca6c2017-10-11 20:12:09 +000073LLVM_ATTRIBUTE_NORETURN static void fatalError(const Twine &Message) {
Marek Sokolowski719e22d2017-08-10 16:21:44 +000074 errs() << Message << "\n";
75 exit(1);
76}
77
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +000078} // anonymous namespace
79
Rui Ueyama197194b2018-04-13 18:26:06 +000080int main(int Argc, const char **Argv) {
81 InitLLVM X(Argc, Argv);
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +000082 ExitOnErr.setBanner("llvm-rc: ");
83
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +000084 RcOptTable T;
85 unsigned MAI, MAC;
Rui Ueyama197194b2018-04-13 18:26:06 +000086 ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, Argc - 1);
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +000087 opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC);
88
89 // The tool prints nothing when invoked with no command-line arguments.
Marek Sokolowski719e22d2017-08-10 16:21:44 +000090 if (InputArgs.hasArg(OPT_HELP)) {
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +000091 T.PrintHelp(outs(), "rc", "Resource Converter", false);
Marek Sokolowski719e22d2017-08-10 16:21:44 +000092 return 0;
93 }
94
95 const bool BeVerbose = InputArgs.hasArg(OPT_VERBOSE);
96
97 std::vector<std::string> InArgsInfo = InputArgs.getAllArgValues(OPT_INPUT);
98 if (InArgsInfo.size() != 1) {
99 fatalError("Exactly one input file should be provided.");
100 }
101
102 // Read and tokenize the input file.
Zachary Turnerfa0ca6c2017-10-11 20:12:09 +0000103 ErrorOr<std::unique_ptr<MemoryBuffer>> File =
104 MemoryBuffer::getFile(InArgsInfo[0]);
Marek Sokolowski719e22d2017-08-10 16:21:44 +0000105 if (!File) {
Zachary Turnerfa0ca6c2017-10-11 20:12:09 +0000106 fatalError("Error opening file '" + Twine(InArgsInfo[0]) +
Marek Sokolowski719e22d2017-08-10 16:21:44 +0000107 "': " + File.getError().message());
108 }
109
110 std::unique_ptr<MemoryBuffer> FileContents = std::move(*File);
111 StringRef Contents = FileContents->getBuffer();
112
113 std::vector<RCToken> Tokens = ExitOnErr(tokenizeRC(Contents));
114
115 if (BeVerbose) {
116 const Twine TokenNames[] = {
117#define TOKEN(Name) #Name,
118#define SHORT_TOKEN(Name, Ch) #Name,
David Blaikieb961d292017-11-21 00:23:19 +0000119#include "ResourceScriptTokenList.def"
Marek Sokolowski719e22d2017-08-10 16:21:44 +0000120 };
121
122 for (const RCToken &Token : Tokens) {
123 outs() << TokenNames[static_cast<int>(Token.kind())] << ": "
124 << Token.value();
125 if (Token.kind() == RCToken::Kind::Int)
126 outs() << "; int value = " << Token.intValue();
127
128 outs() << "\n";
129 }
130 }
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +0000131
Zachary Turnerfa0ca6c2017-10-11 20:12:09 +0000132 SearchParams Params;
133 SmallString<128> InputFile(InArgsInfo[0]);
134 llvm::sys::fs::make_absolute(InputFile);
135 Params.InputFilePath = InputFile;
136 Params.Include = InputArgs.getAllArgValues(OPT_INCLUDE);
137 Params.NoInclude = InputArgs.getAllArgValues(OPT_NOINCLUDE);
138
Marek Sokolowski8f193432017-09-29 17:14:09 +0000139 std::unique_ptr<ResourceFileWriter> Visitor;
140 bool IsDryRun = InputArgs.hasArg(OPT_DRY_RUN);
141
142 if (!IsDryRun) {
143 auto OutArgsInfo = InputArgs.getAllArgValues(OPT_FILEOUT);
144 if (OutArgsInfo.size() != 1)
145 fatalError(
146 "Exactly one output file should be provided (using /FO flag).");
147
148 std::error_code EC;
149 auto FOut =
150 llvm::make_unique<raw_fd_ostream>(OutArgsInfo[0], EC, sys::fs::F_RW);
151 if (EC)
152 fatalError("Error opening output file '" + OutArgsInfo[0] +
153 "': " + EC.message());
Zachary Turnerfa0ca6c2017-10-11 20:12:09 +0000154 Visitor = llvm::make_unique<ResourceFileWriter>(Params, std::move(FOut));
Zachary Turnerda366692017-10-06 21:30:55 +0000155 Visitor->AppendNull = InputArgs.hasArg(OPT_ADD_NULL);
Marek Sokolowski8f193432017-09-29 17:14:09 +0000156
157 ExitOnErr(NullResource().visit(Visitor.get()));
158
159 // Set the default language; choose en-US arbitrarily.
160 ExitOnErr(LanguageResource(0x09, 0x01).visit(Visitor.get()));
161 }
162
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000163 rc::RCParser Parser{std::move(Tokens)};
164 while (!Parser.isEof()) {
165 auto Resource = ExitOnErr(Parser.parseSingleResource());
166 if (BeVerbose)
167 Resource->log(outs());
Marek Sokolowski8f193432017-09-29 17:14:09 +0000168 if (!IsDryRun)
169 ExitOnErr(Resource->visit(Visitor.get()));
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000170 }
171
Zachary Turnerda366692017-10-06 21:30:55 +0000172 // STRINGTABLE resources come at the very end.
173 if (!IsDryRun)
174 ExitOnErr(Visitor->dumpAllStringTables());
175
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +0000176 return 0;
177}