summaryrefslogtreecommitdiff
path: root/tools/llvm-rc
diff options
context:
space:
mode:
authorMarek Sokolowski <mnbvmar@gmail.com>2017-09-28 22:41:38 +0000
committerMarek Sokolowski <mnbvmar@gmail.com>2017-09-28 22:41:38 +0000
commit86b6138b9a646cec82ce1b16c3de024c3e90bef8 (patch)
treef2b82ae2627cef3351cb1110f79aa5ab84d6499b /tools/llvm-rc
parentdfc0d5fadca20f596188ecd3d2e954a088a17b72 (diff)
[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 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@314468 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'tools/llvm-rc')
-rw-r--r--tools/llvm-rc/ResourceScriptParser.cpp75
-rw-r--r--tools/llvm-rc/ResourceScriptParser.h10
-rw-r--r--tools/llvm-rc/ResourceScriptStmt.cpp72
-rw-r--r--tools/llvm-rc/ResourceScriptStmt.h100
4 files changed, 257 insertions, 0 deletions
diff --git a/tools/llvm-rc/ResourceScriptParser.cpp b/tools/llvm-rc/ResourceScriptParser.cpp
index 41b11911b06..57a30d14371 100644
--- a/tools/llvm-rc/ResourceScriptParser.cpp
+++ b/tools/llvm-rc/ResourceScriptParser.cpp
@@ -77,6 +77,8 @@ RCParser::ParseType RCParser::parseSingleResource() {
Result = parseHTMLResource();
else if (TypeToken->equalsLower("MENU"))
Result = parseMenuResource();
+ else if (TypeToken->equalsLower("VERSIONINFO"))
+ Result = parseVersionInfoResource();
else
return getExpectedError("resource type", /* IsAlreadyRead = */ true);
@@ -322,6 +324,13 @@ RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
return std::move(Dialog);
}
+RCParser::ParseType RCParser::parseVersionInfoResource() {
+ ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
+ ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
+ return make_unique<VersionInfoResource>(std::move(**BlockResult),
+ std::move(*FixedResult));
+}
+
Expected<Control> RCParser::parseControl() {
// Each control definition (except CONTROL) follows one of the schemes below
// depending on the control class:
@@ -446,6 +455,72 @@ RCParser::ParseType RCParser::parseStringTableResource() {
return std::move(Table);
}
+Expected<std::unique_ptr<VersionInfoBlock>>
+RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
+ RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
+
+ auto Contents = make_unique<VersionInfoBlock>(BlockName);
+
+ while (!isNextTokenKind(Kind::BlockEnd)) {
+ ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
+ Contents->addStmt(std::move(*Stmt));
+ }
+
+ consume(); // Consume BlockEnd.
+
+ return std::move(Contents);
+}
+
+Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
+ // Expect either BLOCK or VALUE, then a name or a key (a string).
+ ASSIGN_OR_RETURN(TypeResult, readIdentifier());
+
+ if (TypeResult->equals_lower("BLOCK")) {
+ ASSIGN_OR_RETURN(NameResult, readString());
+ return parseVersionInfoBlockContents(*NameResult);
+ }
+
+ if (TypeResult->equals_lower("VALUE")) {
+ ASSIGN_OR_RETURN(KeyResult, readString());
+ // Read a (possibly empty) list of strings and/or ints, each preceded by
+ // a comma.
+ std::vector<IntOrString> Values;
+
+ while (consumeOptionalType(Kind::Comma)) {
+ ASSIGN_OR_RETURN(ValueResult, readIntOrString());
+ Values.push_back(*ValueResult);
+ }
+ return make_unique<VersionInfoValue>(*KeyResult, std::move(Values));
+ }
+
+ return getExpectedError("BLOCK or VALUE", true);
+}
+
+Expected<VersionInfoResource::VersionInfoFixed>
+RCParser::parseVersionInfoFixed() {
+ using RetType = VersionInfoResource::VersionInfoFixed;
+ RetType Result;
+
+ // Read until the beginning of the block.
+ while (!isNextTokenKind(Kind::BlockBegin)) {
+ ASSIGN_OR_RETURN(TypeResult, readIdentifier());
+ auto FixedType = RetType::getFixedType(*TypeResult);
+
+ if (!RetType::isTypeSupported(FixedType))
+ return getExpectedError("fixed VERSIONINFO statement type", true);
+ if (Result.IsTypePresent[FixedType])
+ return getExpectedError("yet unread fixed VERSIONINFO statement type",
+ true);
+
+ // VERSION variations take multiple integers.
+ size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
+ ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts));
+ Result.setValue(FixedType, *ArgsResult);
+ }
+
+ return Result;
+}
+
RCParser::ParseOptionType RCParser::parseLanguageStmt() {
ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
return make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
diff --git a/tools/llvm-rc/ResourceScriptParser.h b/tools/llvm-rc/ResourceScriptParser.h
index bce2e0b544e..132106f0c4e 100644
--- a/tools/llvm-rc/ResourceScriptParser.h
+++ b/tools/llvm-rc/ResourceScriptParser.h
@@ -133,6 +133,7 @@ private:
ParseType parseHTMLResource();
ParseType parseMenuResource();
ParseType parseStringTableResource();
+ ParseType parseVersionInfoResource();
// Helper DIALOG parser - a single control.
Expected<Control> parseControl();
@@ -140,6 +141,15 @@ private:
// Helper MENU parser.
Expected<MenuDefinitionList> parseMenuItemsList();
+ // Helper VERSIONINFO parser - read the contents of a single BLOCK statement,
+ // from BEGIN to END.
+ Expected<std::unique_ptr<VersionInfoBlock>>
+ parseVersionInfoBlockContents(StringRef BlockName);
+ // Helper VERSIONINFO parser - read either VALUE or BLOCK statement.
+ Expected<std::unique_ptr<VersionInfoStmt>> parseVersionInfoStmt();
+ // Helper VERSIONINFO parser - read fixed VERSIONINFO statements.
+ Expected<VersionInfoResource::VersionInfoFixed> parseVersionInfoFixed();
+
// Optional statement parsers.
ParseOptionType parseLanguageStmt();
ParseOptionType parseCharacteristicsStmt();
diff --git a/tools/llvm-rc/ResourceScriptStmt.cpp b/tools/llvm-rc/ResourceScriptStmt.cpp
index cfbd2f8f7a3..eb123e543f0 100644
--- a/tools/llvm-rc/ResourceScriptStmt.cpp
+++ b/tools/llvm-rc/ResourceScriptStmt.cpp
@@ -142,6 +142,78 @@ raw_ostream &DialogResource::log(raw_ostream &OS) const {
return OS;
}
+raw_ostream &VersionInfoBlock::log(raw_ostream &OS) const {
+ OS << " Start of block (name: " << Name << ")\n";
+ for (auto &Stmt : Stmts)
+ Stmt->log(OS);
+ return OS << " End of block\n";
+}
+
+raw_ostream &VersionInfoValue::log(raw_ostream &OS) const {
+ OS << " " << Key << " =>";
+ for (auto &Value : Values)
+ OS << " " << Value;
+ return OS << "\n";
+}
+
+using VersionInfoFixed = VersionInfoResource::VersionInfoFixed;
+using VersionInfoFixedType = VersionInfoFixed::VersionInfoFixedType;
+
+const StringRef
+ VersionInfoFixed::FixedFieldsNames[VersionInfoFixed::FtNumTypes] = {
+ "", "FILEVERSION", "PRODUCTVERSION", "FILEFLAGSMASK",
+ "FILEFLAGS", "FILEOS", "FILETYPE", "FILESUBTYPE"};
+
+const StringMap<VersionInfoFixedType> VersionInfoFixed::FixedFieldsInfoMap = {
+ {FixedFieldsNames[FtFileVersion], FtFileVersion},
+ {FixedFieldsNames[FtProductVersion], FtProductVersion},
+ {FixedFieldsNames[FtFileFlagsMask], FtFileFlagsMask},
+ {FixedFieldsNames[FtFileFlags], FtFileFlags},
+ {FixedFieldsNames[FtFileOS], FtFileOS},
+ {FixedFieldsNames[FtFileType], FtFileType},
+ {FixedFieldsNames[FtFileSubtype], FtFileSubtype}};
+
+VersionInfoFixedType VersionInfoFixed::getFixedType(StringRef Type) {
+ auto UpperType = Type.upper();
+ auto Iter = FixedFieldsInfoMap.find(UpperType);
+ if (Iter != FixedFieldsInfoMap.end())
+ return Iter->getValue();
+ return FtUnknown;
+}
+
+bool VersionInfoFixed::isTypeSupported(VersionInfoFixedType Type) {
+ return FtUnknown < Type && Type < FtNumTypes;
+}
+
+bool VersionInfoFixed::isVersionType(VersionInfoFixedType Type) {
+ switch (Type) {
+ case FtFileVersion:
+ case FtProductVersion:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+raw_ostream &VersionInfoFixed::log(raw_ostream &OS) const {
+ for (int Type = FtUnknown; Type < FtNumTypes; ++Type) {
+ if (!isTypeSupported((VersionInfoFixedType)Type))
+ continue;
+ OS << " Fixed: " << FixedFieldsNames[Type] << ":";
+ for (uint32_t Val : FixedInfo[Type])
+ OS << " " << Val;
+ OS << "\n";
+ }
+ return OS;
+}
+
+raw_ostream &VersionInfoResource::log(raw_ostream &OS) const {
+ OS << "VersionInfo (" << ResName << "):\n";
+ FixedData.log(OS);
+ return MainBlock.log(OS);
+}
+
raw_ostream &CharacteristicsStmt::log(raw_ostream &OS) const {
return OS << "Characteristics: " << Value << "\n";
}
diff --git a/tools/llvm-rc/ResourceScriptStmt.h b/tools/llvm-rc/ResourceScriptStmt.h
index 0812c263b98..b090be4b700 100644
--- a/tools/llvm-rc/ResourceScriptStmt.h
+++ b/tools/llvm-rc/ResourceScriptStmt.h
@@ -319,6 +319,106 @@ public:
raw_ostream &log(raw_ostream &) const override;
};
+// -- VERSIONINFO resource and its helper classes --
+//
+// This resource lists the version information on the executable/library.
+// The declaration consists of the following items:
+// * A number of fixed optional version statements (e.g. FILEVERSION, FILEOS)
+// * BEGIN
+// * A number of BLOCK and/or VALUE statements. BLOCK recursively defines
+// another block of version information, whereas VALUE defines a
+// key -> value correspondence. There might be more than one value
+// corresponding to the single key.
+// * END
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381058(v=vs.85).aspx
+
+// A single VERSIONINFO statement;
+class VersionInfoStmt {
+public:
+ virtual raw_ostream &log(raw_ostream &OS) const { return OS << "VI stmt\n"; }
+ virtual ~VersionInfoStmt() {}
+};
+
+// BLOCK definition; also the main VERSIONINFO declaration is considered a
+// BLOCK, although it has no name.
+// The correct top-level blocks are "VarFileInfo" and "StringFileInfo". We don't
+// care about them at the parsing phase.
+class VersionInfoBlock : public VersionInfoStmt {
+ std::vector<std::unique_ptr<VersionInfoStmt>> Stmts;
+ StringRef Name;
+
+public:
+ VersionInfoBlock(StringRef BlockName) : Name(BlockName) {}
+ void addStmt(std::unique_ptr<VersionInfoStmt> Stmt) {
+ Stmts.push_back(std::move(Stmt));
+ }
+ raw_ostream &log(raw_ostream &) const override;
+};
+
+class VersionInfoValue : public VersionInfoStmt {
+ StringRef Key;
+ std::vector<IntOrString> Values;
+
+public:
+ VersionInfoValue(StringRef InfoKey, std::vector<IntOrString> &&Vals)
+ : Key(InfoKey), Values(std::move(Vals)) {}
+ raw_ostream &log(raw_ostream &) const override;
+};
+
+class VersionInfoResource : public RCResource {
+public:
+ // A class listing fixed VERSIONINFO statements (occuring before main BEGIN).
+ // If any of these is not specified, it is assumed by the original tool to
+ // be equal to 0.
+ class VersionInfoFixed {
+ public:
+ enum VersionInfoFixedType {
+ FtUnknown,
+ FtFileVersion,
+ FtProductVersion,
+ FtFileFlagsMask,
+ FtFileFlags,
+ FtFileOS,
+ FtFileType,
+ FtFileSubtype,
+ FtNumTypes
+ };
+
+ private:
+ static const StringMap<VersionInfoFixedType> FixedFieldsInfoMap;
+ static const StringRef FixedFieldsNames[FtNumTypes];
+
+ public:
+ SmallVector<uint32_t, 4> FixedInfo[FtNumTypes];
+ SmallVector<bool, FtNumTypes> IsTypePresent;
+
+ static VersionInfoFixedType getFixedType(StringRef Type);
+ static bool isTypeSupported(VersionInfoFixedType Type);
+ static bool isVersionType(VersionInfoFixedType Type);
+
+ VersionInfoFixed() : IsTypePresent(FtNumTypes, false) {}
+
+ void setValue(VersionInfoFixedType Type, ArrayRef<uint32_t> Value) {
+ FixedInfo[Type] = SmallVector<uint32_t, 4>(Value.begin(), Value.end());
+ IsTypePresent[Type] = true;
+ }
+
+ raw_ostream &log(raw_ostream &) const;
+ };
+
+private:
+ VersionInfoBlock MainBlock;
+ VersionInfoFixed FixedData;
+
+public:
+ VersionInfoResource(VersionInfoBlock &&TopLevelBlock,
+ VersionInfoFixed &&FixedInfo)
+ : MainBlock(std::move(TopLevelBlock)), FixedData(std::move(FixedInfo)) {}
+
+ raw_ostream &log(raw_ostream &) const override;
+};
+
// CHARACTERISTICS optional statement.
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380872(v=vs.85).aspx