[lit] Add argument check: --timeout must be non-negative integer
[llvm-core.git] / tools / llvm-rc / llvm-rc.cpp
blob97cc7c88b68bed9ba51ddc36dae0ae70dbf6785a
1 //===-- llvm-rc.cpp - Compile .rc scripts into .res -------------*- C++ -*-===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 //
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"
33 #include <algorithm>
34 #include <system_error>
36 using namespace llvm;
37 using namespace llvm::rc;
39 namespace {
41 // Input options tables.
43 enum 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
52 #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
53 #include "Opts.inc"
54 #undef PREFIX
56 static 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
68 class RcOptTable : public opt::OptTable {
69 public:
70 RcOptTable() : OptTable(InfoTable, /* IgnoreCase = */ true) {}
73 static ExitOnError ExitOnErr;
75 LLVM_ATTRIBUTE_NORETURN static void fatalError(const Twine &Message) {
76 errs() << Message << "\n";
77 exit(1);
80 } // anonymous namespace
82 int main(int Argc, const char **Argv) {
83 InitLLVM X(Argc, Argv);
84 ExitOnErr.setBanner("llvm-rc: ");
86 RcOptTable T;
87 unsigned MAI, MAC;
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);
97 return 0;
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]);
112 if (!File) {
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));
123 if (BeVerbose) {
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())] << ": "
132 << Token.value();
133 if (Token.kind() == RCToken::Kind::Int)
134 outs() << "; int value = " << Token.intValue();
136 outs() << "\n";
140 WriterParams 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);
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) {
153 case CpAcp:
154 case CpWin1252:
155 case CpUtf8:
156 break;
157 default:
158 fatalError(
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);
166 if (!IsDryRun) {
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)
175 fatalError(
176 "No more than one output file should be provided (using /FO flag).");
178 std::error_code EC;
179 auto FOut = std::make_unique<raw_fd_ostream>(
180 OutArgsInfo[0], EC, sys::fs::FA_Read | sys::fs::FA_Write);
181 if (EC)
182 fatalError("Error opening output file '" + OutArgsInfo[0] +
183 "': " + EC.message());
184 Visitor = std::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());
196 if (BeVerbose)
197 Resource->log(outs());
198 if (!IsDryRun)
199 ExitOnErr(Resource->visit(Visitor.get()));
202 // STRINGTABLE resources come at the very end.
203 if (!IsDryRun)
204 ExitOnErr(Visitor->dumpAllStringTables());
206 return 0;