Skip to content

Commit fb74cb1

Browse files
committedSep 28, 2017
[llvm-rc] Add VERSIONINFO parsing ability. [6/8]
This extends the set of llvm-rc parser's available resources by another one, VERSIONINFO. Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381058.aspx Thanks to Nico Weber for his original work in this area. Differential Revision: https://reviews.llvm.org/D37021 llvm-svn: 314468
1 parent 3b87336 commit fb74cb1

12 files changed

+376
-0
lines changed
 

‎llvm/test/tools/llvm-rc/Inputs/parser-correct-everything.rc

+33
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,36 @@ BEGIN
7878
END
7979

8080
26 DIALOGEX 1, 2, 3, 4 {}
81+
82+
1 VERSIONINFO
83+
FILEVERSION 1, 2, 3, 4
84+
PRODUCTVERSION 5, 6, 7, 8
85+
FILEFLAGSMASK 50
86+
FILEFLAGS 555
87+
FILEOS 110
88+
FILETYPE 555555
89+
FILESUBTYPE 14
90+
BEGIN
91+
BLOCK "StringFileInfo"
92+
BEGIN
93+
BLOCK "040904E4"
94+
{
95+
VALUE "CompanyName", "a"
96+
VALUE "FileDescription", "b"
97+
VALUE "FileVersion", "c"
98+
VALUE "InternalName", "d"
99+
VALUE "LegalCopyright", "e"
100+
VALUE "LegalTrademarks1", "f"
101+
VALUE "LegalTrademarks2", "g"
102+
VALUE "OriginalFilename", L"h"
103+
VALUE "ProductName", "ii", 2, 3
104+
VALUE "ProductVersion"
105+
}
106+
END
107+
108+
BLOCK "VarFileInfo"
109+
BEGIN
110+
VALUE "Translation", 0x409, 1252
111+
112+
END
113+
END
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
1 VERSIONINFO
2+
BEGIN
3+
INCORRECT "1", "2"
4+
END
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
1 VERSIONINFO
2+
BLOCK "hello"
3+
BEGIN
4+
END
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
1 VERSIONINFO
2+
FileVersion 1, 2, 3, 4
3+
PRODUCTVERSION 5, 6, 7, 8
4+
FILEVERSION 9, 10, 11, 12
5+
BEGIN
6+
END
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
1 VERSIONINFO
2+
BEGIN
3+
BLOCK {}
4+
END
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
1 VERSIONINFO
2+
BEGIN
3+
BLOCK "VarFileInfo"
4+
BEGIN
5+
VALUE
6+
END
7+
END
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
1 VERSIONINFO
2+
WEIRDFIXED 5
3+
BEGIN END

‎llvm/test/tools/llvm-rc/parser.test

+58
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,34 @@
6464
; PGOOD-NEXT: Control (5): EDITTEXT, title: , loc: (1, 2), size: [4, 7], style: 8
6565
; PGOOD-NEXT: Dialog (25): loc: (1, 2), size: [3, 4], help ID: 0
6666
; PGOOD-NEXT: DialogEx (26): loc: (1, 2), size: [3, 4], help ID: 0
67+
; PGOOD-NEXT: VersionInfo (1):
68+
; PGOOD-NEXT: Fixed: FILEVERSION: 1 2 3 4
69+
; PGOOD-NEXT: Fixed: PRODUCTVERSION: 5 6 7 8
70+
; PGOOD-NEXT: Fixed: FILEFLAGSMASK: 50
71+
; PGOOD-NEXT: Fixed: FILEFLAGS: 555
72+
; PGOOD-NEXT: Fixed: FILEOS: 110
73+
; PGOOD-NEXT: Fixed: FILETYPE: 555555
74+
; PGOOD-NEXT: Fixed: FILESUBTYPE: 14
75+
; PGOOD-NEXT: Start of block (name: )
76+
; PGOOD-NEXT: Start of block (name: "StringFileInfo")
77+
; PGOOD-NEXT: Start of block (name: "040904E4")
78+
; PGOOD-NEXT: "CompanyName" => "a"
79+
; PGOOD-NEXT: "FileDescription" => "b"
80+
; PGOOD-NEXT: "FileVersion" => "c"
81+
; PGOOD-NEXT: "InternalName" => "d"
82+
; PGOOD-NEXT: "LegalCopyright" => "e"
83+
; PGOOD-NEXT: "LegalTrademarks1" => "f"
84+
; PGOOD-NEXT: "LegalTrademarks2" => "g"
85+
; PGOOD-NEXT: "OriginalFilename" => L"h"
86+
; PGOOD-NEXT: "ProductName" => "ii" 2 3
87+
; PGOOD-NEXT: "ProductVersion" =>
88+
; PGOOD-NEXT: End of block
89+
; PGOOD-NEXT: End of block
90+
; PGOOD-NEXT: Start of block (name: "VarFileInfo")
91+
; PGOOD-NEXT: "Translation" => 1033 1252
92+
; PGOOD-NEXT: End of block
93+
; PGOOD-NEXT: End of block
94+
6795

6896

6997
; RUN: not llvm-rc /V %p/Inputs/parser-stringtable-no-string.rc 2>&1 | FileCheck %s --check-prefix PSTRINGTABLE1
@@ -184,3 +212,33 @@
184212
; RUN: not llvm-rc /V %p/Inputs/parser-dialog-unnecessary-string.rc 2>&1 | FileCheck %s --check-prefix PDIALOG5
185213

186214
; PDIALOG5: llvm-rc: Error parsing file: expected integer, got "This shouldn't be here"
215+
216+
217+
; RUN: not llvm-rc /V %p/Inputs/parser-versioninfo-wrong-fixed.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO1
218+
219+
; PVERSIONINFO1: llvm-rc: Error parsing file: expected fixed VERSIONINFO statement type, got WEIRDFIXED
220+
221+
222+
; RUN: not llvm-rc /V %p/Inputs/parser-versioninfo-named-main-block.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO2
223+
224+
; PVERSIONINFO2: llvm-rc: Error parsing file: expected fixed VERSIONINFO statement type, got BLOCK
225+
226+
227+
; RUN: not llvm-rc /V %p/Inputs/parser-versioninfo-unnamed-inner-block.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO3
228+
229+
; PVERSIONINFO3: llvm-rc: Error parsing file: expected string, got {
230+
231+
232+
; RUN: not llvm-rc /V %p/Inputs/parser-versioninfo-unnamed-value.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO4
233+
234+
; PVERSIONINFO4: llvm-rc: Error parsing file: expected string, got END
235+
236+
237+
; RUN: not llvm-rc /V %p/Inputs/parser-versioninfo-bad-type.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO5
238+
239+
; PVERSIONINFO5: llvm-rc: Error parsing file: expected BLOCK or VALUE, got INCORRECT
240+
241+
242+
; RUN: not llvm-rc /V %p/Inputs/parser-versioninfo-repeated-fixed.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO6
243+
244+
; PVERSIONINFO6: llvm-rc: Error parsing file: expected yet unread fixed VERSIONINFO statement type, got FILEVERSION

‎llvm/tools/llvm-rc/ResourceScriptParser.cpp

+75
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ RCParser::ParseType RCParser::parseSingleResource() {
7777
Result = parseHTMLResource();
7878
else if (TypeToken->equalsLower("MENU"))
7979
Result = parseMenuResource();
80+
else if (TypeToken->equalsLower("VERSIONINFO"))
81+
Result = parseVersionInfoResource();
8082
else
8183
return getExpectedError("resource type", /* IsAlreadyRead = */ true);
8284

@@ -322,6 +324,13 @@ RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
322324
return std::move(Dialog);
323325
}
324326

327+
RCParser::ParseType RCParser::parseVersionInfoResource() {
328+
ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
329+
ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
330+
return make_unique<VersionInfoResource>(std::move(**BlockResult),
331+
std::move(*FixedResult));
332+
}
333+
325334
Expected<Control> RCParser::parseControl() {
326335
// Each control definition (except CONTROL) follows one of the schemes below
327336
// depending on the control class:
@@ -446,6 +455,72 @@ RCParser::ParseType RCParser::parseStringTableResource() {
446455
return std::move(Table);
447456
}
448457

458+
Expected<std::unique_ptr<VersionInfoBlock>>
459+
RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
460+
RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
461+
462+
auto Contents = make_unique<VersionInfoBlock>(BlockName);
463+
464+
while (!isNextTokenKind(Kind::BlockEnd)) {
465+
ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
466+
Contents->addStmt(std::move(*Stmt));
467+
}
468+
469+
consume(); // Consume BlockEnd.
470+
471+
return std::move(Contents);
472+
}
473+
474+
Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
475+
// Expect either BLOCK or VALUE, then a name or a key (a string).
476+
ASSIGN_OR_RETURN(TypeResult, readIdentifier());
477+
478+
if (TypeResult->equals_lower("BLOCK")) {
479+
ASSIGN_OR_RETURN(NameResult, readString());
480+
return parseVersionInfoBlockContents(*NameResult);
481+
}
482+
483+
if (TypeResult->equals_lower("VALUE")) {
484+
ASSIGN_OR_RETURN(KeyResult, readString());
485+
// Read a (possibly empty) list of strings and/or ints, each preceded by
486+
// a comma.
487+
std::vector<IntOrString> Values;
488+
489+
while (consumeOptionalType(Kind::Comma)) {
490+
ASSIGN_OR_RETURN(ValueResult, readIntOrString());
491+
Values.push_back(*ValueResult);
492+
}
493+
return make_unique<VersionInfoValue>(*KeyResult, std::move(Values));
494+
}
495+
496+
return getExpectedError("BLOCK or VALUE", true);
497+
}
498+
499+
Expected<VersionInfoResource::VersionInfoFixed>
500+
RCParser::parseVersionInfoFixed() {
501+
using RetType = VersionInfoResource::VersionInfoFixed;
502+
RetType Result;
503+
504+
// Read until the beginning of the block.
505+
while (!isNextTokenKind(Kind::BlockBegin)) {
506+
ASSIGN_OR_RETURN(TypeResult, readIdentifier());
507+
auto FixedType = RetType::getFixedType(*TypeResult);
508+
509+
if (!RetType::isTypeSupported(FixedType))
510+
return getExpectedError("fixed VERSIONINFO statement type", true);
511+
if (Result.IsTypePresent[FixedType])
512+
return getExpectedError("yet unread fixed VERSIONINFO statement type",
513+
true);
514+
515+
// VERSION variations take multiple integers.
516+
size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
517+
ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts));
518+
Result.setValue(FixedType, *ArgsResult);
519+
}
520+
521+
return Result;
522+
}
523+
449524
RCParser::ParseOptionType RCParser::parseLanguageStmt() {
450525
ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
451526
return make_unique<LanguageResource>((*Args)[0], (*Args)[1]);

‎llvm/tools/llvm-rc/ResourceScriptParser.h

+10
Original file line numberDiff line numberDiff line change
@@ -133,13 +133,23 @@ class RCParser {
133133
ParseType parseHTMLResource();
134134
ParseType parseMenuResource();
135135
ParseType parseStringTableResource();
136+
ParseType parseVersionInfoResource();
136137

137138
// Helper DIALOG parser - a single control.
138139
Expected<Control> parseControl();
139140

140141
// Helper MENU parser.
141142
Expected<MenuDefinitionList> parseMenuItemsList();
142143

144+
// Helper VERSIONINFO parser - read the contents of a single BLOCK statement,
145+
// from BEGIN to END.
146+
Expected<std::unique_ptr<VersionInfoBlock>>
147+
parseVersionInfoBlockContents(StringRef BlockName);
148+
// Helper VERSIONINFO parser - read either VALUE or BLOCK statement.
149+
Expected<std::unique_ptr<VersionInfoStmt>> parseVersionInfoStmt();
150+
// Helper VERSIONINFO parser - read fixed VERSIONINFO statements.
151+
Expected<VersionInfoResource::VersionInfoFixed> parseVersionInfoFixed();
152+
143153
// Optional statement parsers.
144154
ParseOptionType parseLanguageStmt();
145155
ParseOptionType parseCharacteristicsStmt();

‎llvm/tools/llvm-rc/ResourceScriptStmt.cpp

+72
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,78 @@ raw_ostream &DialogResource::log(raw_ostream &OS) const {
142142
return OS;
143143
}
144144

145+
raw_ostream &VersionInfoBlock::log(raw_ostream &OS) const {
146+
OS << " Start of block (name: " << Name << ")\n";
147+
for (auto &Stmt : Stmts)
148+
Stmt->log(OS);
149+
return OS << " End of block\n";
150+
}
151+
152+
raw_ostream &VersionInfoValue::log(raw_ostream &OS) const {
153+
OS << " " << Key << " =>";
154+
for (auto &Value : Values)
155+
OS << " " << Value;
156+
return OS << "\n";
157+
}
158+
159+
using VersionInfoFixed = VersionInfoResource::VersionInfoFixed;
160+
using VersionInfoFixedType = VersionInfoFixed::VersionInfoFixedType;
161+
162+
const StringRef
163+
VersionInfoFixed::FixedFieldsNames[VersionInfoFixed::FtNumTypes] = {
164+
"", "FILEVERSION", "PRODUCTVERSION", "FILEFLAGSMASK",
165+
"FILEFLAGS", "FILEOS", "FILETYPE", "FILESUBTYPE"};
166+
167+
const StringMap<VersionInfoFixedType> VersionInfoFixed::FixedFieldsInfoMap = {
168+
{FixedFieldsNames[FtFileVersion], FtFileVersion},
169+
{FixedFieldsNames[FtProductVersion], FtProductVersion},
170+
{FixedFieldsNames[FtFileFlagsMask], FtFileFlagsMask},
171+
{FixedFieldsNames[FtFileFlags], FtFileFlags},
172+
{FixedFieldsNames[FtFileOS], FtFileOS},
173+
{FixedFieldsNames[FtFileType], FtFileType},
174+
{FixedFieldsNames[FtFileSubtype], FtFileSubtype}};
175+
176+
VersionInfoFixedType VersionInfoFixed::getFixedType(StringRef Type) {
177+
auto UpperType = Type.upper();
178+
auto Iter = FixedFieldsInfoMap.find(UpperType);
179+
if (Iter != FixedFieldsInfoMap.end())
180+
return Iter->getValue();
181+
return FtUnknown;
182+
}
183+
184+
bool VersionInfoFixed::isTypeSupported(VersionInfoFixedType Type) {
185+
return FtUnknown < Type && Type < FtNumTypes;
186+
}
187+
188+
bool VersionInfoFixed::isVersionType(VersionInfoFixedType Type) {
189+
switch (Type) {
190+
case FtFileVersion:
191+
case FtProductVersion:
192+
return true;
193+
194+
default:
195+
return false;
196+
}
197+
}
198+
199+
raw_ostream &VersionInfoFixed::log(raw_ostream &OS) const {
200+
for (int Type = FtUnknown; Type < FtNumTypes; ++Type) {
201+
if (!isTypeSupported((VersionInfoFixedType)Type))
202+
continue;
203+
OS << " Fixed: " << FixedFieldsNames[Type] << ":";
204+
for (uint32_t Val : FixedInfo[Type])
205+
OS << " " << Val;
206+
OS << "\n";
207+
}
208+
return OS;
209+
}
210+
211+
raw_ostream &VersionInfoResource::log(raw_ostream &OS) const {
212+
OS << "VersionInfo (" << ResName << "):\n";
213+
FixedData.log(OS);
214+
return MainBlock.log(OS);
215+
}
216+
145217
raw_ostream &CharacteristicsStmt::log(raw_ostream &OS) const {
146218
return OS << "Characteristics: " << Value << "\n";
147219
}

0 commit comments

Comments
 (0)
Please sign in to comment.