blob: 4a1f52e66dee0de9931500574347a548c5b5a5ed [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"
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +000024#include "llvm/Support/ManagedStatic.h"
Zachary Turnerbd159d32017-11-17 01:00:35 +000025#include "llvm/Support/MemoryBuffer.h"
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +000026#include "llvm/Support/PrettyStackTrace.h"
27#include "llvm/Support/Process.h"
28#include "llvm/Support/Signals.h"
29#include "llvm/Support/raw_ostream.h"
30
31#include <system_error>
32
33using namespace llvm;
Marek Sokolowski8f193432017-09-29 17:14:09 +000034using namespace llvm::rc;
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +000035
36namespace {
37
38// Input options tables.
39
40enum ID {
41 OPT_INVALID = 0, // This is not a correct option ID.
42#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
43 HELPTEXT, METAVAR, VALUES) \
44 OPT_##ID,
45#include "Opts.inc"
46#undef OPTION
47};
48
49#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
50#include "Opts.inc"
51#undef PREFIX
52
53static const opt::OptTable::Info InfoTable[] = {
54#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
55 HELPTEXT, METAVAR, VALUES) \
56 { \
57 PREFIX, NAME, HELPTEXT, \
58 METAVAR, OPT_##ID, opt::Option::KIND##Class, \
59 PARAM, FLAGS, OPT_##GROUP, \
60 OPT_##ALIAS, ALIASARGS, VALUES},
61#include "Opts.inc"
62#undef OPTION
63};
64
65class RcOptTable : public opt::OptTable {
66public:
67 RcOptTable() : OptTable(InfoTable, /* IgnoreCase = */ true) {}
68};
69
70static ExitOnError ExitOnErr;
Marek Sokolowski719e22d2017-08-10 16:21:44 +000071
Zachary Turnerfa0ca6c2017-10-11 20:12:09 +000072LLVM_ATTRIBUTE_NORETURN static void fatalError(const Twine &Message) {
Marek Sokolowski719e22d2017-08-10 16:21:44 +000073 errs() << Message << "\n";
74 exit(1);
75}
76
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +000077} // anonymous namespace
78
79int main(int argc_, const char *argv_[]) {
80 sys::PrintStackTraceOnErrorSignal(argv_[0]);
81 PrettyStackTraceProgram X(argc_, argv_);
82
83 ExitOnErr.setBanner("llvm-rc: ");
84
85 SmallVector<const char *, 256> argv;
86 SpecificBumpPtrAllocator<char> ArgAllocator;
87 ExitOnErr(errorCodeToError(sys::Process::GetArgumentVector(
88 argv, makeArrayRef(argv_, argc_), ArgAllocator)));
89
90 llvm_shutdown_obj Y;
91
92 RcOptTable T;
93 unsigned MAI, MAC;
94 ArrayRef<const char *> ArgsArr = makeArrayRef(argv_ + 1, argc_);
95 opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC);
96
97 // The tool prints nothing when invoked with no command-line arguments.
Marek Sokolowski719e22d2017-08-10 16:21:44 +000098 if (InputArgs.hasArg(OPT_HELP)) {
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +000099 T.PrintHelp(outs(), "rc", "Resource Converter", false);
Marek Sokolowski719e22d2017-08-10 16:21:44 +0000100 return 0;
101 }
102
103 const bool BeVerbose = InputArgs.hasArg(OPT_VERBOSE);
104
105 std::vector<std::string> InArgsInfo = InputArgs.getAllArgValues(OPT_INPUT);
106 if (InArgsInfo.size() != 1) {
107 fatalError("Exactly one input file should be provided.");
108 }
109
110 // Read and tokenize the input file.
Zachary Turnerfa0ca6c2017-10-11 20:12:09 +0000111 ErrorOr<std::unique_ptr<MemoryBuffer>> File =
112 MemoryBuffer::getFile(InArgsInfo[0]);
Marek Sokolowski719e22d2017-08-10 16:21:44 +0000113 if (!File) {
Zachary Turnerfa0ca6c2017-10-11 20:12:09 +0000114 fatalError("Error opening file '" + Twine(InArgsInfo[0]) +
Marek Sokolowski719e22d2017-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
121 std::vector<RCToken> Tokens = ExitOnErr(tokenizeRC(Contents));
122
123 if (BeVerbose) {
124 const Twine TokenNames[] = {
125#define TOKEN(Name) #Name,
126#define SHORT_TOKEN(Name, Ch) #Name,
David Blaikieb961d292017-11-21 00:23:19 +0000127#include "ResourceScriptTokenList.def"
Marek Sokolowski719e22d2017-08-10 16:21:44 +0000128 };
129
130 for (const RCToken &Token : Tokens) {
131 outs() << TokenNames[static_cast<int>(Token.kind())] << ": "
132 << Token.value();
133 if (Token.kind() == RCToken::Kind::Int)
134 outs() << "; int value = " << Token.intValue();
135
136 outs() << "\n";
137 }
138 }
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +0000139
Zachary Turnerfa0ca6c2017-10-11 20:12:09 +0000140 SearchParams Params;
141 SmallString<128> InputFile(InArgsInfo[0]);
142 llvm::sys::fs::make_absolute(InputFile);
143 Params.InputFilePath = InputFile;
144 Params.Include = InputArgs.getAllArgValues(OPT_INCLUDE);
145 Params.NoInclude = InputArgs.getAllArgValues(OPT_NOINCLUDE);
146
Marek Sokolowski8f193432017-09-29 17:14:09 +0000147 std::unique_ptr<ResourceFileWriter> Visitor;
148 bool IsDryRun = InputArgs.hasArg(OPT_DRY_RUN);
149
150 if (!IsDryRun) {
151 auto OutArgsInfo = InputArgs.getAllArgValues(OPT_FILEOUT);
152 if (OutArgsInfo.size() != 1)
153 fatalError(
154 "Exactly one output file should be provided (using /FO flag).");
155
156 std::error_code EC;
157 auto FOut =
158 llvm::make_unique<raw_fd_ostream>(OutArgsInfo[0], EC, sys::fs::F_RW);
159 if (EC)
160 fatalError("Error opening output file '" + OutArgsInfo[0] +
161 "': " + EC.message());
Zachary Turnerfa0ca6c2017-10-11 20:12:09 +0000162 Visitor = llvm::make_unique<ResourceFileWriter>(Params, std::move(FOut));
Zachary Turnerda366692017-10-06 21:30:55 +0000163 Visitor->AppendNull = InputArgs.hasArg(OPT_ADD_NULL);
Marek Sokolowski8f193432017-09-29 17:14:09 +0000164
165 ExitOnErr(NullResource().visit(Visitor.get()));
166
167 // Set the default language; choose en-US arbitrarily.
168 ExitOnErr(LanguageResource(0x09, 0x01).visit(Visitor.get()));
169 }
170
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000171 rc::RCParser Parser{std::move(Tokens)};
172 while (!Parser.isEof()) {
173 auto Resource = ExitOnErr(Parser.parseSingleResource());
174 if (BeVerbose)
175 Resource->log(outs());
Marek Sokolowski8f193432017-09-29 17:14:09 +0000176 if (!IsDryRun)
177 ExitOnErr(Resource->visit(Visitor.get()));
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000178 }
179
Zachary Turnerda366692017-10-06 21:30:55 +0000180 // STRINGTABLE resources come at the very end.
181 if (!IsDryRun)
182 ExitOnErr(Visitor->dumpAllStringTables());
183
Marek Sokolowski2ce2fa42017-07-25 00:25:18 +0000184 return 0;
185}