Introduce a pre-release version component

* The version string pattern is now:
  <major>[.<minor>[.<micro>]][-<pre>][-<release>]
* Introduce B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_PRE_RELEASE package
  attribute.
* Add "preRelease" field to BPackageVersionData.
* Add "preRelease" property to BPackageVersion and packagefs's Version.
* Adjust package reader and writer code accordingly.
This commit is contained in:
Ingo Weinhold
2011-11-25 06:18:24 +01:00
parent 934980dcb1
commit 8f314372a8
14 changed files with 156 additions and 45 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ enum BPackageInfoAttributeID {
B_PACKAGE_INFO_VENDOR, // e.g. "Haiku Project" B_PACKAGE_INFO_VENDOR, // e.g. "Haiku Project"
B_PACKAGE_INFO_PACKAGER, // e-mail address preferred B_PACKAGE_INFO_PACKAGER, // e-mail address preferred
B_PACKAGE_INFO_ARCHITECTURE, B_PACKAGE_INFO_ARCHITECTURE,
B_PACKAGE_INFO_VERSION, // <major>[.<minor>[.<micro>]] B_PACKAGE_INFO_VERSION, // <major>[.<minor>[.<micro>]][-<pre>]-<release>
B_PACKAGE_INFO_COPYRIGHTS, // list B_PACKAGE_INFO_COPYRIGHTS, // list
B_PACKAGE_INFO_LICENSES, // list B_PACKAGE_INFO_LICENSES, // list
B_PACKAGE_INFO_PROVIDES, // list of resolvables this package provides, B_PACKAGE_INFO_PROVIDES, // list of resolvables this package provides,
+5 -2
View File
@@ -25,20 +25,22 @@ public:
const BPackageVersionData& data); const BPackageVersionData& data);
BPackageVersion(const BString& major, BPackageVersion(const BString& major,
const BString& minor, const BString& micro, const BString& minor, const BString& micro,
uint8 release); const BString& preRelease, uint8 release);
status_t InitCheck() const; status_t InitCheck() const;
const BString& Major() const; const BString& Major() const;
const BString& Minor() const; const BString& Minor() const;
const BString& Micro() const; const BString& Micro() const;
const BString& PreRelease() const;
// "alpha3", "beta2", "rc1" or "" if final
uint8 Release() const; uint8 Release() const;
BString ToString() const; BString ToString() const;
void SetTo(const BString& major, void SetTo(const BString& major,
const BString& minor, const BString& micro, const BString& minor, const BString& micro,
uint8 release); const BString& preRelease, uint8 release);
void Clear(); void Clear();
int Compare(const BPackageVersion& other) const; int Compare(const BPackageVersion& other) const;
@@ -49,6 +51,7 @@ private:
BString fMajor; BString fMajor;
BString fMinor; BString fMinor;
BString fMicro; BString fMicro;
BString fPreRelease;
uint8 fRelease; uint8 fRelease;
}; };
+1
View File
@@ -110,6 +110,7 @@ enum BHPKGAttributeID {
B_HPKG_ATTRIBUTE_ID_PACKAGE_REPLACES = 37, B_HPKG_ATTRIBUTE_ID_PACKAGE_REPLACES = 37,
B_HPKG_ATTRIBUTE_ID_PACKAGE_RESOLVABLE_OPERATOR = 38, B_HPKG_ATTRIBUTE_ID_PACKAGE_RESOLVABLE_OPERATOR = 38,
B_HPKG_ATTRIBUTE_ID_PACKAGE_CHECKSUM = 39, B_HPKG_ATTRIBUTE_ID_PACKAGE_CHECKSUM = 39,
B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_PRE_RELEASE = 40,
// //
B_HPKG_ATTRIBUTE_ID_ENUM_COUNT, B_HPKG_ATTRIBUTE_ID_ENUM_COUNT,
}; };
@@ -23,6 +23,7 @@ struct BPackageVersionData {
const char* major; const char* major;
const char* minor; const char* minor;
const char* micro; const char* micro;
const char* preRelease;
uint8 release; uint8 release;
}; };
@@ -10,6 +10,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <algorithm>
#include <new> #include <new>
#include <NaturalCompare.h> #include <NaturalCompare.h>
@@ -37,6 +38,7 @@ Version::Version()
fMajor(NULL), fMajor(NULL),
fMinor(NULL), fMinor(NULL),
fMicro(NULL), fMicro(NULL),
fPreRelease(NULL),
fRelease(0) fRelease(0)
{ {
} }
@@ -47,12 +49,13 @@ Version::~Version()
free(fMajor); free(fMajor);
free(fMinor); free(fMinor);
free(fMicro); free(fMicro);
free(fPreRelease);
} }
status_t status_t
Version::Init(const char* major, const char* minor, const char* micro, Version::Init(const char* major, const char* minor, const char* micro,
uint8 release) const char* preRelease, uint8 release)
{ {
if (major != NULL) { if (major != NULL) {
fMajor = strdup(major); fMajor = strdup(major);
@@ -72,6 +75,12 @@ Version::Init(const char* major, const char* minor, const char* micro,
return B_NO_MEMORY; return B_NO_MEMORY;
} }
if (preRelease != NULL) {
fPreRelease = strdup(preRelease);
if (fPreRelease == NULL)
return B_NO_MEMORY;
}
fRelease = release; fRelease = release;
return B_OK; return B_OK;
@@ -80,13 +89,13 @@ Version::Init(const char* major, const char* minor, const char* micro,
/*static*/ status_t /*static*/ status_t
Version::Create(const char* major, const char* minor, const char* micro, Version::Create(const char* major, const char* minor, const char* micro,
uint8 release, Version*& _version) const char* preRelease, uint8 release, Version*& _version)
{ {
Version* version = new(std::nothrow) Version; Version* version = new(std::nothrow) Version;
if (version == NULL) if (version == NULL)
return B_NO_MEMORY; return B_NO_MEMORY;
status_t error = version->Init(major, minor, micro, release); status_t error = version->Init(major, minor, micro, preRelease, release);
if (error != B_OK) { if (error != B_OK) {
delete version; delete version;
return error; return error;
@@ -112,6 +121,21 @@ Version::Compare(const Version& other) const
if (cmp != 0) if (cmp != 0)
return cmp; return cmp;
// The pre-version works differently: The empty string is greater than any
// non-empty string (e.g. "R1" is newer than "R1-rc2"). So we catch the
// empty string cases first.
if (fPreRelease == NULL) {
if (other.fPreRelease != NULL)
return 1;
} else if (other.fPreRelease == NULL) {
return -1;
} else {
// both are non-null -- compare normally
cmp = BPrivate::NaturalCompare(fPreRelease, other.fPreRelease);
if (cmp != 0)
return cmp;
}
return (int)fRelease - other.fRelease; return (int)fRelease - other.fRelease;
} }
@@ -147,8 +171,8 @@ Version::ToString(char* buffer, size_t bufferSize) const
{ {
// We need to normalize the version string somewhat. If a subpart is given, // We need to normalize the version string somewhat. If a subpart is given,
// make sure that also the superparts are defined, using a placeholder. This // make sure that also the superparts are defined, using a placeholder. This
// avoids clashes, e.g. if one version defines only major and one only // avoids clashes, e.g. if one version defines major and minor and one only
// micro. // major and micro.
const char* major = fMajor; const char* major = fMajor;
const char* minor = fMinor; const char* minor = fMinor;
const char* micro = fMicro; const char* micro = fMicro;
@@ -158,16 +182,28 @@ Version::ToString(char* buffer, size_t bufferSize) const
if (minor != NULL && major == NULL) if (minor != NULL && major == NULL)
major = kVersionPartPlaceholder; major = kVersionPartPlaceholder;
if (micro != NULL) { size_t size = strlcpy(buffer, major, bufferSize);
return snprintf(buffer, bufferSize, "%s.%s.%s-%u", major, minor, micro,
fRelease); if (minor != NULL) {
size_t offset = std::min(bufferSize, size);
size += snprintf(buffer + offset, bufferSize - offset, ".%s", minor);
} }
if (minor != NULL) if (micro != NULL) {
return snprintf(buffer, bufferSize, "%s.%s-%u", major, minor, fRelease); size_t offset = std::min(bufferSize, size);
size += snprintf(buffer + offset, bufferSize - offset, ".%s", micro);
}
if (major != NULL) if (fPreRelease != NULL) {
return snprintf(buffer, bufferSize, "%s-%u", major, fRelease); size_t offset = std::min(bufferSize, size);
size += snprintf(buffer + offset, bufferSize - offset, "-%s",
fPreRelease);
}
return snprintf(buffer, bufferSize, "%u", fRelease); if (fRelease != 0) {
size_t offset = std::min(bufferSize, size);
size += snprintf(buffer + offset, bufferSize - offset, "-%u", fRelease);
}
return size;
} }
@@ -19,11 +19,12 @@ public:
~Version(); ~Version();
status_t Init(const char* major, const char* minor, status_t Init(const char* major, const char* minor,
const char* micro, uint8 release); const char* micro, const char* preRelease,
uint8 release);
static status_t Create(const char* major, const char* minor, static status_t Create(const char* major, const char* minor,
const char* micro, uint8 release, const char* micro, const char* preRelease,
Version*& _version); uint8 release, Version*& _version);
int Compare(const Version& other) const; int Compare(const Version& other) const;
bool Compare(BPackageResolvableOperator op, bool Compare(BPackageResolvableOperator op,
@@ -37,6 +38,7 @@ private:
char* fMajor; char* fMajor;
char* fMinor; char* fMinor;
char* fMicro; char* fMicro;
char* fPreRelease;
uint8 fRelease; uint8 fRelease;
}; };
@@ -294,7 +294,7 @@ struct Volume::PackageLoaderContentHandler : BPackageContentHandler {
Version* version; Version* version;
status_t error = Version::Create(value.version.major, status_t error = Version::Create(value.version.major,
value.version.minor, value.version.micro, value.version.minor, value.version.micro,
value.version.release, version); value.version.preRelease, value.version.release, version);
if (error != B_OK) if (error != B_OK)
RETURN_ERROR(error); RETURN_ERROR(error);
@@ -312,7 +312,7 @@ struct Volume::PackageLoaderContentHandler : BPackageContentHandler {
= value.resolvable.version; = value.resolvable.version;
status_t error = Version::Create(versionInfo.major, status_t error = Version::Create(versionInfo.major,
versionInfo.minor, versionInfo.micro, versionInfo.minor, versionInfo.micro,
versionInfo.release, version); versionInfo.preRelease, versionInfo.release, version);
if (error != B_OK) if (error != B_OK)
RETURN_ERROR(error); RETURN_ERROR(error);
} }
@@ -354,7 +354,7 @@ struct Volume::PackageLoaderContentHandler : BPackageContentHandler {
= value.resolvableExpression.version; = value.resolvableExpression.version;
status_t error = Version::Create(versionInfo.major, status_t error = Version::Create(versionInfo.major,
versionInfo.minor, versionInfo.micro, versionInfo.minor, versionInfo.micro,
versionInfo.release, version); versionInfo.preRelease, versionInfo.release, version);
if (error != B_OK) if (error != B_OK)
RETURN_ERROR(error); RETURN_ERROR(error);
+2
View File
@@ -253,6 +253,8 @@ private:
printf(".%s", version.minor); printf(".%s", version.minor);
if (version.micro != NULL && version.micro[0] != '\0') if (version.micro != NULL && version.micro[0] != '\0')
printf(".%s", version.micro); printf(".%s", version.micro);
if (version.preRelease != NULL && version.preRelease[0] != '\0')
printf("-%s", version.preRelease);
if (version.release > 0) if (version.release > 0)
printf("-%d", version.release); printf("-%d", version.release);
} }
+2
View File
@@ -226,6 +226,8 @@ private:
printf(".%s", version.minor); printf(".%s", version.minor);
if (version.micro != NULL && version.micro[0] != '\0') if (version.micro != NULL && version.micro[0] != '\0')
printf(".%s", version.micro); printf(".%s", version.micro);
if (version.preRelease != NULL && version.preRelease[0] != '\0')
printf("-%s", version.preRelease);
if (version.release > 0) if (version.release > 0)
printf("-%d", version.release); printf("-%d", version.release);
} }
+31 -11
View File
@@ -349,23 +349,43 @@ BPackageInfo::Parser::_ParseVersionValue(BPackageVersion* value,
if (word.type != TOKEN_WORD) if (word.type != TOKEN_WORD)
throw ParseError("expected word (a version)", word.pos); throw ParseError("expected word (a version)", word.pos);
// get the release number
uint8 release = 0; uint8 release = 0;
int32 lastDashPos = word.text.FindLast('-'); int32 lastDashPos = word.text.FindLast('-');
if (lastDashPos < 0) { if (lastDashPos >= 0) {
if (!releaseIsOptional) { // Might be either the release number or, if that is optional, a
throw ParseError("expected release number (-<number> suffix)", // pre-release. The former always is a number, the latter starts with a
// non-digit.
if (isdigit(word.text[lastDashPos + 1])) {
int number = atoi(word.text.String() + lastDashPos + 1);
if (number <= 0 || number > 99) {
throw ParseError("release number must be from 1-99",
word.pos + word.text.Length());
}
release = number;
word.text.Truncate(lastDashPos);
lastDashPos = word.text.FindLast('-');
}
}
if (release == 0 && !releaseIsOptional) {
throw ParseError("expected release number (-<number> suffix)",
word.pos + word.text.Length());
}
// get the pre-release string
BString preRelease;
if (lastDashPos >= 0) {
if (isdigit(word.text[lastDashPos + 1])) {
throw ParseError("pre-release number must not start with a digit",
word.pos + word.text.Length()); word.pos + word.text.Length());
} }
} else {
int number = atoi(word.text.String() + lastDashPos + 1); word.text.CopyInto(preRelease, lastDashPos + 1, word.text.Length());
if (number <= 0 || number > 99) {
throw ParseError("release number must be from 1-99",
word.pos + word.text.Length());
}
release = number;
word.text.Truncate(lastDashPos); word.text.Truncate(lastDashPos);
} }
// get major, minor, and micro strings
BString major; BString major;
BString minor; BString minor;
BString micro; BString micro;
@@ -387,7 +407,7 @@ BPackageInfo::Parser::_ParseVersionValue(BPackageVersion* value,
} }
} }
value->SetTo(major, minor, micro, release); value->SetTo(major, minor, micro, preRelease, release);
} }
+40 -11
View File
@@ -29,17 +29,19 @@ BPackageVersion::BPackageVersion(const BPackageVersionData& data)
fMajor(data.major), fMajor(data.major),
fMinor(data.minor), fMinor(data.minor),
fMicro(data.micro), fMicro(data.micro),
fPreRelease(data.preRelease),
fRelease(data.release) fRelease(data.release)
{ {
} }
BPackageVersion::BPackageVersion(const BString& major, const BString& minor, BPackageVersion::BPackageVersion(const BString& major, const BString& minor,
const BString& micro, uint8 release) const BString& micro, const BString& preRelease, uint8 release)
: :
fMajor(major), fMajor(major),
fMinor(minor), fMinor(minor),
fMicro(micro), fMicro(micro),
fPreRelease(preRelease),
fRelease(release) fRelease(release)
{ {
} }
@@ -73,6 +75,13 @@ BPackageVersion::Micro() const
} }
const BString&
BPackageVersion::PreRelease() const
{
return fPreRelease;
}
uint8 uint8
BPackageVersion::Release() const BPackageVersion::Release() const
{ {
@@ -83,17 +92,32 @@ BPackageVersion::Release() const
int int
BPackageVersion::Compare(const BPackageVersion& other) const BPackageVersion::Compare(const BPackageVersion& other) const
{ {
int majorDiff = NaturalCompare(fMajor.String(), other.fMajor.String()); int diff = NaturalCompare(fMajor.String(), other.fMajor.String());
if (majorDiff != 0) if (diff != 0)
return majorDiff; return diff;
int minorDiff = NaturalCompare(fMinor.String(), other.fMinor.String()); diff = NaturalCompare(fMinor.String(), other.fMinor.String());
if (minorDiff != 0) if (diff != 0)
return minorDiff; return diff;
int microDiff = NaturalCompare(fMicro.String(), other.fMicro.String()); diff = NaturalCompare(fMicro.String(), other.fMicro.String());
if (microDiff != 0) if (diff != 0)
return microDiff; return diff;
// The pre-version works differently: The empty string is greater than any
// non-empty string (e.g. "R1" is newer than "R1-rc2"). So we catch the
// empty string cases first.
if (fPreRelease.IsEmpty()) {
if (!other.fPreRelease.IsEmpty())
return 1;
} else if (other.fPreRelease.IsEmpty()) {
return -1;
} else {
// both are non-null -- compare normally
diff = NaturalCompare(fPreRelease.String(), other.fPreRelease.String());
if (diff != 0)
return diff;
}
return (int)fRelease - (int)other.fRelease; return (int)fRelease - (int)other.fRelease;
} }
@@ -110,6 +134,9 @@ BPackageVersion::ToString() const
string << '.' << fMicro; string << '.' << fMicro;
} }
if (!fPreRelease.IsEmpty())
string << '-' << fPreRelease;
if (fRelease > 0) if (fRelease > 0)
string << '-' << fRelease; string << '-' << fRelease;
@@ -119,11 +146,12 @@ BPackageVersion::ToString() const
void void
BPackageVersion::SetTo(const BString& major, const BString& minor, BPackageVersion::SetTo(const BString& major, const BString& minor,
const BString& micro, uint8 release) const BString& micro, const BString& preRelease, uint8 release)
{ {
fMajor = major; fMajor = major;
fMinor = minor; fMinor = minor;
fMicro = micro; fMicro = micro;
fPreRelease = preRelease;
fRelease = release; fRelease = release;
} }
@@ -134,6 +162,7 @@ BPackageVersion::Clear()
fMajor.Truncate(0); fMajor.Truncate(0);
fMinor.Truncate(0); fMinor.Truncate(0);
fMicro.Truncate(0); fMicro.Truncate(0);
fPreRelease.Truncate(0);
fRelease = 0; fRelease = 0;
} }
@@ -56,6 +56,7 @@ static const char* kAttributeNames[B_HPKG_ATTRIBUTE_ID_ENUM_COUNT + 1] = {
"package:replaces", "package:replaces",
"package:resolvable.operator", "package:resolvable.operator",
"package:checksum", "package:checksum",
"package:version.prerelease",
NULL NULL
}; };
+5 -1
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. * Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de> * Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
@@ -127,6 +127,10 @@ ReaderImplBase::PackageVersionAttributeHandler::HandleAttribute(
fPackageVersionData.micro = value.string; fPackageVersionData.micro = value.string;
break; break;
case B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_PRE_RELEASE:
fPackageVersionData.preRelease = value.string;
break;
case B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_RELEASE: case B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_RELEASE:
fPackageVersionData.release = value.unsignedInt; fPackageVersionData.release = value.unsignedInt;
break; break;
+10
View File
@@ -523,6 +523,16 @@ WriterImplBase::RegisterPackageVersion(PackageAttributeList& attributeList,
} }
} }
if (!version.PreRelease().IsEmpty()) {
PackageAttribute* preRelease = new PackageAttribute(
B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_PRE_RELEASE,
B_HPKG_ATTRIBUTE_TYPE_STRING,
B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
preRelease->string
= fPackageStringCache.Get(version.PreRelease().String());
versionMajor->children.Add(preRelease);
}
if (version.Release() != 0) { if (version.Release() != 0) {
PackageAttribute* versionRelease = new PackageAttribute( PackageAttribute* versionRelease = new PackageAttribute(
B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_RELEASE, B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_RELEASE,