1 //===-- llvm-rc.cpp - Compile .rc scripts into .res -------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // Compile .rc scripts into .res files. This is intended to be a
10 // platform-independent port of Microsoft's rc.exe tool.
12 //===----------------------------------------------------------------------===//
14 #include "ResourceFileWriter.h"
15 #include "ResourceScriptCppFilter.h"
16 #include "ResourceScriptParser.h"
17 #include "ResourceScriptStmt.h"
18 #include "ResourceScriptToken.h"
20 #include "llvm/Option/Arg.h"
21 #include "llvm/Option/ArgList.h"
22 #include "llvm/Support/Error.h"
23 #include "llvm/Support/FileSystem.h"
24 #include "llvm/Support/InitLLVM.h"
25 #include "llvm/Support/ManagedStatic.h"
26 #include "llvm/Support/MemoryBuffer.h"
27 #include "llvm/Support/Path.h"
28 #include "llvm/Support/PrettyStackTrace.h"
29 #include "llvm/Support/Process.h"
30 #include "llvm/Support/Signals.h"
31 #include "llvm/Support/raw_ostream.h"
34 #include <system_error>
37 using namespace llvm::rc
;
41 // Input options tables.
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) \
52 #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
56 static const opt::OptTable::Info InfoTable
[] = {
57 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
58 HELPTEXT, METAVAR, VALUES) \
60 PREFIX, NAME, HELPTEXT, \
61 METAVAR, OPT_##ID, opt::Option::KIND##Class, \
62 PARAM, FLAGS, OPT_##GROUP, \
63 OPT_##ALIAS, ALIASARGS, VALUES},
68 class RcOptTable
: public opt::OptTable
{
70 RcOptTable() : OptTable(InfoTable
, /* IgnoreCase = */ true) {}
73 static ExitOnError ExitOnErr
;
75 LLVM_ATTRIBUTE_NORETURN
static void fatalError(const Twine
&Message
) {
76 errs() << Message
<< "\n";
80 } // anonymous namespace
82 int main(int Argc
, const char **Argv
) {
83 InitLLVM
X(Argc
, Argv
);
84 ExitOnErr
.setBanner("llvm-rc: ");
88 const char **DashDash
= std::find_if(
89 Argv
+ 1, Argv
+ Argc
, [](StringRef Str
) { return Str
== "--"; });
90 ArrayRef
<const char *> ArgsArr
= makeArrayRef(Argv
+ 1, DashDash
);
92 opt::InputArgList InputArgs
= T
.ParseArgs(ArgsArr
, MAI
, MAC
);
94 // The tool prints nothing when invoked with no command-line arguments.
95 if (InputArgs
.hasArg(OPT_HELP
)) {
96 T
.PrintHelp(outs(), "rc [options] file...", "Resource Converter", false);
100 const bool BeVerbose
= InputArgs
.hasArg(OPT_VERBOSE
);
102 std::vector
<std::string
> InArgsInfo
= InputArgs
.getAllArgValues(OPT_INPUT
);
103 if (DashDash
!= Argv
+ Argc
)
104 InArgsInfo
.insert(InArgsInfo
.end(), DashDash
+ 1, Argv
+ Argc
);
105 if (InArgsInfo
.size() != 1) {
106 fatalError("Exactly one input file should be provided.");
109 // Read and tokenize the input file.
110 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> File
=
111 MemoryBuffer::getFile(InArgsInfo
[0]);
113 fatalError("Error opening file '" + Twine(InArgsInfo
[0]) +
114 "': " + File
.getError().message());
117 std::unique_ptr
<MemoryBuffer
> FileContents
= std::move(*File
);
118 StringRef Contents
= FileContents
->getBuffer();
120 std::string FilteredContents
= filterCppOutput(Contents
);
121 std::vector
<RCToken
> Tokens
= ExitOnErr(tokenizeRC(FilteredContents
));
124 const Twine TokenNames
[] = {
125 #define TOKEN(Name) #Name,
126 #define SHORT_TOKEN(Name, Ch) #Name,
127 #include "ResourceScriptTokenList.def"
130 for (const RCToken
&Token
: Tokens
) {
131 outs() << TokenNames
[static_cast<int>(Token
.kind())] << ": "
133 if (Token
.kind() == RCToken::Kind::Int
)
134 outs() << "; int value = " << Token
.intValue();
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
);
147 if (InputArgs
.hasArg(OPT_CODEPAGE
)) {
148 if (InputArgs
.getLastArgValue(OPT_CODEPAGE
)
149 .getAsInteger(10, Params
.CodePage
))
150 fatalError("Invalid code page: " +
151 InputArgs
.getLastArgValue(OPT_CODEPAGE
));
152 switch (Params
.CodePage
) {
159 "Unsupported code page, only 0, 1252 and 65001 are supported!");
163 std::unique_ptr
<ResourceFileWriter
> Visitor
;
164 bool IsDryRun
= InputArgs
.hasArg(OPT_DRY_RUN
);
167 auto OutArgsInfo
= InputArgs
.getAllArgValues(OPT_FILEOUT
);
168 if (OutArgsInfo
.empty()) {
169 SmallString
<128> OutputFile
= InputFile
;
170 llvm::sys::path::replace_extension(OutputFile
, "res");
171 OutArgsInfo
.push_back(OutputFile
.str());
174 if (OutArgsInfo
.size() != 1)
176 "No more than one output file should be provided (using /FO flag).");
179 auto FOut
= llvm::make_unique
<raw_fd_ostream
>(
180 OutArgsInfo
[0], EC
, sys::fs::FA_Read
| sys::fs::FA_Write
);
182 fatalError("Error opening output file '" + OutArgsInfo
[0] +
183 "': " + EC
.message());
184 Visitor
= llvm::make_unique
<ResourceFileWriter
>(Params
, std::move(FOut
));
185 Visitor
->AppendNull
= InputArgs
.hasArg(OPT_ADD_NULL
);
187 ExitOnErr(NullResource().visit(Visitor
.get()));
189 // Set the default language; choose en-US arbitrarily.
190 ExitOnErr(LanguageResource(0x09, 0x01).visit(Visitor
.get()));
193 rc::RCParser Parser
{std::move(Tokens
)};
194 while (!Parser
.isEof()) {
195 auto Resource
= ExitOnErr(Parser
.parseSingleResource());
197 Resource
->log(outs());
199 ExitOnErr(Resource
->visit(Visitor
.get()));
202 // STRINGTABLE resources come at the very end.
204 ExitOnErr(Visitor
->dumpAllStringTables());