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

Lines changed: 33 additions & 0 deletions
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
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
1 VERSIONINFO
2+
BEGIN
3+
INCORRECT "1", "2"
4+
END
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
1 VERSIONINFO
2+
BLOCK "hello"
3+
BEGIN
4+
END
Lines changed: 6 additions & 0 deletions
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
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
1 VERSIONINFO
2+
BEGIN
3+
BLOCK {}
4+
END
Lines changed: 7 additions & 0 deletions
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
Lines changed: 3 additions & 0 deletions
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

Lines changed: 58 additions & 0 deletions
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

Lines changed: 75 additions & 0 deletions
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

Lines changed: 10 additions & 0 deletions
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

Lines changed: 72 additions & 0 deletions
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
}

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

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,106 @@ class DialogResource : public RCResource {
319319
raw_ostream &log(raw_ostream &) const override;
320320
};
321321

322+
// -- VERSIONINFO resource and its helper classes --
323+
//
324+
// This resource lists the version information on the executable/library.
325+
// The declaration consists of the following items:
326+
// * A number of fixed optional version statements (e.g. FILEVERSION, FILEOS)
327+
// * BEGIN
328+
// * A number of BLOCK and/or VALUE statements. BLOCK recursively defines
329+
// another block of version information, whereas VALUE defines a
330+
// key -> value correspondence. There might be more than one value
331+
// corresponding to the single key.
332+
// * END
333+
//
334+
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381058(v=vs.85).aspx
335+
336+
// A single VERSIONINFO statement;
337+
class VersionInfoStmt {
338+
public:
339+
virtual raw_ostream &log(raw_ostream &OS) const { return OS << "VI stmt\n"; }
340+
virtual ~VersionInfoStmt() {}
341+
};
342+
343+
// BLOCK definition; also the main VERSIONINFO declaration is considered a
344+
// BLOCK, although it has no name.
345+
// The correct top-level blocks are "VarFileInfo" and "StringFileInfo". We don't
346+
// care about them at the parsing phase.
347+
class VersionInfoBlock : public VersionInfoStmt {
348+
std::vector<std::unique_ptr<VersionInfoStmt>> Stmts;
349+
StringRef Name;
350+
351+
public:
352+
VersionInfoBlock(StringRef BlockName) : Name(BlockName) {}
353+
void addStmt(std::unique_ptr<VersionInfoStmt> Stmt) {
354+
Stmts.push_back(std::move(Stmt));
355+
}
356+
raw_ostream &log(raw_ostream &) const override;
357+
};
358+
359+
class VersionInfoValue : public VersionInfoStmt {
360+
StringRef Key;
361+
std::vector<IntOrString> Values;
362+
363+
public:
364+
VersionInfoValue(StringRef InfoKey, std::vector<IntOrString> &&Vals)
365+
: Key(InfoKey), Values(std::move(Vals)) {}
366+
raw_ostream &log(raw_ostream &) const override;
367+
};
368+
369+
class VersionInfoResource : public RCResource {
370+
public:
371+
// A class listing fixed VERSIONINFO statements (occuring before main BEGIN).
372+
// If any of these is not specified, it is assumed by the original tool to
373+
// be equal to 0.
374+
class VersionInfoFixed {
375+
public:
376+
enum VersionInfoFixedType {
377+
FtUnknown,
378+
FtFileVersion,
379+
FtProductVersion,
380+
FtFileFlagsMask,
381+
FtFileFlags,
382+
FtFileOS,
383+
FtFileType,
384+
FtFileSubtype,
385+
FtNumTypes
386+
};
387+
388+
private:
389+
static const StringMap<VersionInfoFixedType> FixedFieldsInfoMap;
390+
static const StringRef FixedFieldsNames[FtNumTypes];
391+
392+
public:
393+
SmallVector<uint32_t, 4> FixedInfo[FtNumTypes];
394+
SmallVector<bool, FtNumTypes> IsTypePresent;
395+
396+
static VersionInfoFixedType getFixedType(StringRef Type);
397+
static bool isTypeSupported(VersionInfoFixedType Type);
398+
static bool isVersionType(VersionInfoFixedType Type);
399+
400+
VersionInfoFixed() : IsTypePresent(FtNumTypes, false) {}
401+
402+
void setValue(VersionInfoFixedType Type, ArrayRef<uint32_t> Value) {
403+
FixedInfo[Type] = SmallVector<uint32_t, 4>(Value.begin(), Value.end());
404+
IsTypePresent[Type] = true;
405+
}
406+
407+
raw_ostream &log(raw_ostream &) const;
408+
};
409+
410+
private:
411+
VersionInfoBlock MainBlock;
412+
VersionInfoFixed FixedData;
413+
414+
public:
415+
VersionInfoResource(VersionInfoBlock &&TopLevelBlock,
416+
VersionInfoFixed &&FixedInfo)
417+
: MainBlock(std::move(TopLevelBlock)), FixedData(std::move(FixedInfo)) {}
418+
419+
raw_ostream &log(raw_ostream &) const override;
420+
};
421+
322422
// CHARACTERISTICS optional statement.
323423
//
324424
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380872(v=vs.85).aspx

0 commit comments

Comments
 (0)
Please sign in to comment.