-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use AppsAndFeaturesEntries DisplayVersion info for installed package version mapping #2213
Changes from 1 commit
780c91c
b704d35
10c87ec
262915a
be5beb4
846c023
c598339
ca3cea8
5a4d4ec
0200aa5
e116fd1
54fb33a
16e925e
7b816e8
e1da15b
027c7dd
4206e2b
f3f21de
8c7e95c
b8a5f28
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -11,9 +11,10 @@ namespace AppInstaller::Utility | |||||
|
||||||
// Creates a comparable version object from a string. | ||||||
// Versions are parsed by: | ||||||
// 1. Splitting the string based on the given splitChars (or DefaultSplitChars) | ||||||
// 2. Parsing a leading, positive integer from each split part | ||||||
// 3. Saving any remaining, non-digits as a supplemental value | ||||||
// 1. Parse approximate comparator sign if applicable | ||||||
// 2. Splitting the string based on the given splitChars (or DefaultSplitChars) | ||||||
// 3. Parsing a leading, positive integer from each split part | ||||||
// 4. Saving any remaining, non-digits as a supplemental value | ||||||
// | ||||||
// Versions are compared by: | ||||||
// for each part in each version | ||||||
|
@@ -22,8 +23,20 @@ namespace AppInstaller::Utility | |||||
// else if integers not equal, return comparison of integers | ||||||
// else if only one side has a non-empty string part, it is less | ||||||
// else if string parts not equal, return comparison of strings | ||||||
// if all parts are same, use approximate comparator if applicable | ||||||
// | ||||||
// Note: approximate to another approximate version is invalid. | ||||||
// approximate to Unknown is invalid. | ||||||
struct Version | ||||||
{ | ||||||
// Used in approximate version to indicate the relation to the base version. | ||||||
enum class ApproximateComparator | ||||||
{ | ||||||
None, | ||||||
LessThan, | ||||||
GreaterThan, | ||||||
}; | ||||||
|
||||||
// The default characters to split a version string on. | ||||||
constexpr static std::string_view DefaultSplitChars = "."sv; | ||||||
|
||||||
|
@@ -33,6 +46,9 @@ namespace AppInstaller::Utility | |||||
Version(std::string(version), splitChars) {} | ||||||
Version(std::string&& version, std::string_view splitChars = DefaultSplitChars); | ||||||
|
||||||
// Constructing an approximate version from a base version. | ||||||
Version(const Version& baseVersion, ApproximateComparator approximateComparator); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
And |
||||||
|
||||||
// Resets the version's value to the input. | ||||||
void Assign(std::string&& version, std::string_view splitChars = DefaultSplitChars); | ||||||
|
||||||
|
@@ -72,24 +88,12 @@ namespace AppInstaller::Utility | |||||
std::string Other; | ||||||
}; | ||||||
|
||||||
// Used in approximate version to indicate the relation to the base version. | ||||||
enum class ApproximateComparator | ||||||
{ | ||||||
None, | ||||||
LessThan, | ||||||
GreaterThan, | ||||||
}; | ||||||
|
||||||
// Gets the part breakdown for a given version; used for tests. | ||||||
const std::vector<Part>& GetParts() const { return m_parts; } | ||||||
|
||||||
// Returns if the version is an approximate version. | ||||||
bool IsApproximateVersion() const { return m_approximateComparator != ApproximateComparator::None; } | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Why use many word, when few word do trick? |
||||||
|
||||||
// Static methods to create an approximate version from a base version. | ||||||
static Version CreateLessThanApproximateVersion(const Version& base); | ||||||
static Version CreateGreaterThanApproximateVersion(const Version& base); | ||||||
|
||||||
protected: | ||||||
|
||||||
bool IsBaseVersionLatest() const; | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -53,7 +53,7 @@ namespace AppInstaller::Manifest | |
const char* const ScopeNotSupported = "Scope is not supported for InstallerType portable."; | ||
const char* const ApproximateVersionNotAllowed = "Approximate version not allowed."; | ||
const char* const ArpVersionOverlapWithIndex = "DisplayVersion declared in the manifest has overlap with existing DisplayVersion range in the index. Existing DisplayVersion range in index: "; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This got me thinking - Are version ranges architecture specific, and/or should they be? I could see a scenario where a publisher uses the following - PackageVersion: 1.0-Alpha # This is their marketing version
Installers:
- Architecture: x86
AppsAndFeaturesEntries:
- DisplayVersion: 1.0.4.0
- Architecture: x64
AppsAndFeaturesEntries:
- DisplayVersion: 1.0.5.0 According to the spec, this would result in an Version Range of [1.0.4.0, 1.0.5.0] Suppose they release a new update - PackageVersion: 1.1-Alpha # This is their marketing version
Installers:
- Architecture: x86
AppsAndFeaturesEntries:
- DisplayVersion: 1.0.4.1
- Architecture: x64
AppsAndFeaturesEntries:
- DisplayVersion: 1.0.5.1 According to the logic, wouldn't 1.0.4.1 overlap with [1.0.4.0, 1.0.5.0]. since 1.0.4.0 =< 1.0.4.1 =< 1.0.5.0, thereby triggering a false overlap warning? #ByDesign There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct, this will be treated as unsupported case according to the spec previously written. Since there's no restriction on what is written to DisplayVersion entry, an installer could also write different values for different locale. The above example can be extended to scope, installertype, etc. Essentially this will end up being each installer is in its own set. |
||
const char* const ArpVersionValidationInternalError = "Encountered internal error during DisplayVersion validation against index."; | ||
const char* const ArpVersionValidationInternalError = "Internal error while validating DisplayVersion against index."; | ||
} | ||
|
||
struct ValidationError | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,26 @@ namespace AppInstaller::Utility | |
Assign(std::move(version), splitChars); | ||
} | ||
|
||
Version::Version(const Version& baseVersion, ApproximateComparator approximateComparator) : Version(baseVersion) | ||
{ | ||
if (approximateComparator == ApproximateComparator::None) | ||
{ | ||
return; | ||
} | ||
|
||
THROW_HR_IF(E_INVALIDARG, baseVersion.IsApproximateVersion() || baseVersion.IsUnknown()); | ||
|
||
m_approximateComparator = approximateComparator; | ||
if (approximateComparator == ApproximateComparator::LessThan) | ||
{ | ||
m_version = std::string{ s_Approximate_Less_Than } + m_version; | ||
} | ||
else if (approximateComparator == ApproximateComparator::GreaterThan) | ||
{ | ||
m_version = std::string{ s_Approximate_Greater_Than } + m_version; | ||
} | ||
} | ||
|
||
void Version::Assign(std::string&& version, std::string_view splitChars) | ||
{ | ||
m_version = std::move(version); | ||
|
@@ -28,12 +48,12 @@ namespace AppInstaller::Utility | |
if (CaseInsensitiveStartsWith(m_version, s_Approximate_Less_Than)) | ||
{ | ||
m_approximateComparator = ApproximateComparator::LessThan; | ||
baseVersion = m_version.substr(2, m_version.length() - 2); | ||
baseVersion = m_version.substr(s_Approximate_Less_Than.length(), m_version.length() - s_Approximate_Less_Than.length()); | ||
} | ||
else if (CaseInsensitiveStartsWith(m_version, s_Approximate_Greater_Than)) | ||
{ | ||
m_approximateComparator = ApproximateComparator::GreaterThan; | ||
baseVersion = m_version.substr(2, m_version.length() - 2); | ||
baseVersion = m_version.substr(s_Approximate_Greater_Than.length(), m_version.length() - s_Approximate_Greater_Than.length()); | ||
} | ||
|
||
// Then parse the base version | ||
|
@@ -62,24 +82,12 @@ namespace AppInstaller::Utility | |
break; | ||
} | ||
} | ||
|
||
THROW_HR_IF(E_INVALIDARG, m_approximateComparator != ApproximateComparator::None && IsBaseVersionUnknown()); | ||
} | ||
|
||
bool Version::operator<(const Version& other) const | ||
{ | ||
// If base versions are same, result is based on approximate comparator | ||
Version thisBase = *this; | ||
thisBase.m_approximateComparator = ApproximateComparator::None; | ||
Version otherBase = other; | ||
otherBase.m_approximateComparator = ApproximateComparator::None; | ||
if (thisBase == otherBase) | ||
{ | ||
// Only true if this is less than, other is not, OR this is none, other is greater than | ||
return (m_approximateComparator == ApproximateComparator::LessThan && other.m_approximateComparator != ApproximateComparator::LessThan) || | ||
(m_approximateComparator == ApproximateComparator::None && other.m_approximateComparator == ApproximateComparator::GreaterThan); | ||
} | ||
|
||
// The approximate comparator can be ignored now, just compare the base version. | ||
|
||
// Sort Latest higher than any other values | ||
bool thisIsLatest = IsBaseVersionLatest(); | ||
bool otherIsLatest = other.IsBaseVersionLatest(); | ||
|
@@ -122,8 +130,19 @@ namespace AppInstaller::Utility | |
// else parts are equal, so continue to next part | ||
} | ||
|
||
// All parts tested were equal, so this is only less if there are more parts in other. | ||
return m_parts.size() < other.m_parts.size(); | ||
// All parts tested were equal | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You have to change the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yep, and unit tests failed for this... rushing to get a commit run in the pipeline before running tests locally |
||
if (m_parts.size() == other.m_parts.size()) | ||
{ | ||
// Only true if this is less than, other is not, OR this is none, other is greater than | ||
return (m_approximateComparator == ApproximateComparator::LessThan && other.m_approximateComparator != ApproximateComparator::LessThan) || | ||
(m_approximateComparator == ApproximateComparator::None && other.m_approximateComparator == ApproximateComparator::GreaterThan); | ||
} | ||
else | ||
{ | ||
// Else this is only less if there are more parts in other. | ||
return m_parts.size() < other.m_parts.size(); | ||
} | ||
|
||
} | ||
|
||
bool Version::operator>(const Version& other) const | ||
|
@@ -177,7 +196,7 @@ namespace AppInstaller::Utility | |
|
||
bool Version::IsLatest() const | ||
{ | ||
return (m_approximateComparator == ApproximateComparator::None && IsBaseVersionLatest()); | ||
return (m_approximateComparator != ApproximateComparator::LessThan && IsBaseVersionLatest()); | ||
} | ||
|
||
Version Version::CreateLatest() | ||
|
@@ -190,7 +209,7 @@ namespace AppInstaller::Utility | |
|
||
bool Version::IsUnknown() const | ||
{ | ||
return (m_approximateComparator == ApproximateComparator::None && IsBaseVersionUnknown()); | ||
return IsBaseVersionUnknown(); | ||
} | ||
|
||
Version Version::CreateUnknown() | ||
|
@@ -201,18 +220,6 @@ namespace AppInstaller::Utility | |
return result; | ||
} | ||
|
||
Version Version::CreateLessThanApproximateVersion(const Version& base) | ||
{ | ||
THROW_HR_IF(E_INVALIDARG, base.IsApproximateVersion()); | ||
return Version{ std::string{ s_Approximate_Less_Than } + base.ToString() }; | ||
} | ||
|
||
Version Version::CreateGreaterThanApproximateVersion(const Version& base) | ||
{ | ||
THROW_HR_IF(E_INVALIDARG, base.IsApproximateVersion()); | ||
return Version{ std::string{ s_Approximate_Greater_Than } + base.ToString() }; | ||
} | ||
|
||
bool Version::IsBaseVersionLatest() const | ||
{ | ||
return (m_parts.size() == 1 && m_parts[0].Integer == 0 && Utility::CaseInsensitiveEquals(m_parts[0].Other, s_Version_Part_Latest)); | ||
|
@@ -350,8 +357,7 @@ namespace AppInstaller::Utility | |
return false; | ||
} | ||
|
||
return (m_minVersion >= other.m_minVersion && m_minVersion <= other.m_maxVersion) || | ||
(m_maxVersion >= other.m_minVersion && m_maxVersion <= other.m_maxVersion); | ||
return m_minVersion <= other.m_maxVersion && m_maxVersion >= other.m_minVersion; | ||
} | ||
|
||
bool VersionRange::IsSameAsSingleVersion(const Version& version) const | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like this could be an operator overload of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't feel like doing |
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any advantage to using
||
's here, instead of a switch statement? I would think that when compiled, a switch statement would be slightly more efficient, given that it compiles as a jump table rather than a sequential evaluationThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just copied from above similar functions. I guess it's more readable. In the end, this function will be invoked at most once for each installed package so I think I'll leave it for consistency for now.