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-06-27 01:53:01 +02:00
parent 934980dcb1
commit 8f314372a8
14 changed files with 156 additions and 45 deletions

View File

@ -16,7 +16,7 @@ enum BPackageInfoAttributeID {
B_PACKAGE_INFO_VENDOR, // e.g. "Haiku Project"
B_PACKAGE_INFO_PACKAGER, // e-mail address preferred
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_LICENSES, // list
B_PACKAGE_INFO_PROVIDES, // list of resolvables this package provides,

View File

@ -25,20 +25,22 @@ public:
const BPackageVersionData& data);
BPackageVersion(const BString& major,
const BString& minor, const BString& micro,
uint8 release);
const BString& preRelease, uint8 release);
status_t InitCheck() const;
const BString& Major() const;
const BString& Minor() const;
const BString& Micro() const;
const BString& PreRelease() const;
// "alpha3", "beta2", "rc1" or "" if final
uint8 Release() const;
BString ToString() const;
void SetTo(const BString& major,
const BString& minor, const BString& micro,
uint8 release);
const BString& preRelease, uint8 release);
void Clear();
int Compare(const BPackageVersion& other) const;
@ -49,6 +51,7 @@ private:
BString fMajor;
BString fMinor;
BString fMicro;
BString fPreRelease;
uint8 fRelease;
};

View File

@ -110,6 +110,7 @@ enum BHPKGAttributeID {
B_HPKG_ATTRIBUTE_ID_PACKAGE_REPLACES = 37,
B_HPKG_ATTRIBUTE_ID_PACKAGE_RESOLVABLE_OPERATOR = 38,
B_HPKG_ATTRIBUTE_ID_PACKAGE_CHECKSUM = 39,
B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_PRE_RELEASE = 40,
//
B_HPKG_ATTRIBUTE_ID_ENUM_COUNT,
};

View File

@ -23,6 +23,7 @@ struct BPackageVersionData {
const char* major;
const char* minor;
const char* micro;
const char* preRelease;
uint8 release;
};

View File

@ -10,6 +10,7 @@
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <new>
#include <NaturalCompare.h>
@ -37,6 +38,7 @@ Version::Version()
fMajor(NULL),
fMinor(NULL),
fMicro(NULL),
fPreRelease(NULL),
fRelease(0)
{
}
@ -47,12 +49,13 @@ Version::~Version()
free(fMajor);
free(fMinor);
free(fMicro);
free(fPreRelease);
}
status_t
Version::Init(const char* major, const char* minor, const char* micro,
uint8 release)
const char* preRelease, uint8 release)
{
if (major != NULL) {
fMajor = strdup(major);
@ -72,6 +75,12 @@ Version::Init(const char* major, const char* minor, const char* micro,
return B_NO_MEMORY;
}
if (preRelease != NULL) {
fPreRelease = strdup(preRelease);
if (fPreRelease == NULL)
return B_NO_MEMORY;
}
fRelease = release;
return B_OK;
@ -80,13 +89,13 @@ Version::Init(const char* major, const char* minor, const char* micro,
/*static*/ status_t
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;
if (version == NULL)
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) {
delete version;
return error;
@ -112,6 +121,21 @@ Version::Compare(const Version& other) const
if (cmp != 0)
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;
}
@ -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,
// 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
// micro.
// avoids clashes, e.g. if one version defines major and minor and one only
// major and micro.
const char* major = fMajor;
const char* minor = fMinor;
const char* micro = fMicro;
@ -158,16 +182,28 @@ Version::ToString(char* buffer, size_t bufferSize) const
if (minor != NULL && major == NULL)
major = kVersionPartPlaceholder;
if (micro != NULL) {
return snprintf(buffer, bufferSize, "%s.%s.%s-%u", major, minor, micro,
fRelease);
size_t size = strlcpy(buffer, major, bufferSize);
if (minor != NULL) {
size_t offset = std::min(bufferSize, size);
size += snprintf(buffer + offset, bufferSize - offset, ".%s", minor);
}
if (minor != NULL)
return snprintf(buffer, bufferSize, "%s.%s-%u", major, minor, fRelease);
if (micro != NULL) {
size_t offset = std::min(bufferSize, size);
size += snprintf(buffer + offset, bufferSize - offset, ".%s", micro);
}
if (major != NULL)
return snprintf(buffer, bufferSize, "%s-%u", major, fRelease);
if (fPreRelease != NULL) {
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;
}

View File

@ -19,11 +19,12 @@ public:
~Version();
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,
const char* micro, uint8 release,
Version*& _version);
const char* micro, const char* preRelease,
uint8 release, Version*& _version);
int Compare(const Version& other) const;
bool Compare(BPackageResolvableOperator op,
@ -37,6 +38,7 @@ private:
char* fMajor;
char* fMinor;
char* fMicro;
char* fPreRelease;
uint8 fRelease;
};

View File

@ -294,7 +294,7 @@ struct Volume::PackageLoaderContentHandler : BPackageContentHandler {
Version* version;
status_t error = Version::Create(value.version.major,
value.version.minor, value.version.micro,
value.version.release, version);
value.version.preRelease, value.version.release, version);
if (error != B_OK)
RETURN_ERROR(error);
@ -312,7 +312,7 @@ struct Volume::PackageLoaderContentHandler : BPackageContentHandler {
= value.resolvable.version;
status_t error = Version::Create(versionInfo.major,
versionInfo.minor, versionInfo.micro,
versionInfo.release, version);
versionInfo.preRelease, versionInfo.release, version);
if (error != B_OK)
RETURN_ERROR(error);
}
@ -354,7 +354,7 @@ struct Volume::PackageLoaderContentHandler : BPackageContentHandler {
= value.resolvableExpression.version;
status_t error = Version::Create(versionInfo.major,
versionInfo.minor, versionInfo.micro,
versionInfo.release, version);
versionInfo.preRelease, versionInfo.release, version);
if (error != B_OK)
RETURN_ERROR(error);

View File

@ -253,6 +253,8 @@ private:
printf(".%s", version.minor);
if (version.micro != NULL && version.micro[0] != '\0')
printf(".%s", version.micro);
if (version.preRelease != NULL && version.preRelease[0] != '\0')
printf("-%s", version.preRelease);
if (version.release > 0)
printf("-%d", version.release);
}

View File

@ -226,6 +226,8 @@ private:
printf(".%s", version.minor);
if (version.micro != NULL && version.micro[0] != '\0')
printf(".%s", version.micro);
if (version.preRelease != NULL && version.preRelease[0] != '\0')
printf("-%s", version.preRelease);
if (version.release > 0)
printf("-%d", version.release);
}

View File

@ -349,23 +349,43 @@ BPackageInfo::Parser::_ParseVersionValue(BPackageVersion* value,
if (word.type != TOKEN_WORD)
throw ParseError("expected word (a version)", word.pos);
// get the release number
uint8 release = 0;
int32 lastDashPos = word.text.FindLast('-');
if (lastDashPos < 0) {
if (!releaseIsOptional) {
throw ParseError("expected release number (-<number> suffix)",
if (lastDashPos >= 0) {
// Might be either the release number or, if that is optional, a
// 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());
}
} else {
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.CopyInto(preRelease, lastDashPos + 1, word.text.Length());
word.text.Truncate(lastDashPos);
}
// get major, minor, and micro strings
BString major;
BString minor;
BString micro;
@ -387,7 +407,7 @@ BPackageInfo::Parser::_ParseVersionValue(BPackageVersion* value,
}
}
value->SetTo(major, minor, micro, release);
value->SetTo(major, minor, micro, preRelease, release);
}

View File

@ -29,17 +29,19 @@ BPackageVersion::BPackageVersion(const BPackageVersionData& data)
fMajor(data.major),
fMinor(data.minor),
fMicro(data.micro),
fPreRelease(data.preRelease),
fRelease(data.release)
{
}
BPackageVersion::BPackageVersion(const BString& major, const BString& minor,
const BString& micro, uint8 release)
const BString& micro, const BString& preRelease, uint8 release)
:
fMajor(major),
fMinor(minor),
fMicro(micro),
fPreRelease(preRelease),
fRelease(release)
{
}
@ -73,6 +75,13 @@ BPackageVersion::Micro() const
}
const BString&
BPackageVersion::PreRelease() const
{
return fPreRelease;
}
uint8
BPackageVersion::Release() const
{
@ -83,17 +92,32 @@ BPackageVersion::Release() const
int
BPackageVersion::Compare(const BPackageVersion& other) const
{
int majorDiff = NaturalCompare(fMajor.String(), other.fMajor.String());
if (majorDiff != 0)
return majorDiff;
int diff = NaturalCompare(fMajor.String(), other.fMajor.String());
if (diff != 0)
return diff;
int minorDiff = NaturalCompare(fMinor.String(), other.fMinor.String());
if (minorDiff != 0)
return minorDiff;
diff = NaturalCompare(fMinor.String(), other.fMinor.String());
if (diff != 0)
return diff;
int microDiff = NaturalCompare(fMicro.String(), other.fMicro.String());
if (microDiff != 0)
return microDiff;
diff = NaturalCompare(fMicro.String(), other.fMicro.String());
if (diff != 0)
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;
}
@ -110,6 +134,9 @@ BPackageVersion::ToString() const
string << '.' << fMicro;
}
if (!fPreRelease.IsEmpty())
string << '-' << fPreRelease;
if (fRelease > 0)
string << '-' << fRelease;
@ -119,11 +146,12 @@ BPackageVersion::ToString() const
void
BPackageVersion::SetTo(const BString& major, const BString& minor,
const BString& micro, uint8 release)
const BString& micro, const BString& preRelease, uint8 release)
{
fMajor = major;
fMinor = minor;
fMicro = micro;
fPreRelease = preRelease;
fRelease = release;
}
@ -134,6 +162,7 @@ BPackageVersion::Clear()
fMajor.Truncate(0);
fMinor.Truncate(0);
fMicro.Truncate(0);
fPreRelease.Truncate(0);
fRelease = 0;
}

View File

@ -56,6 +56,7 @@ static const char* kAttributeNames[B_HPKG_ATTRIBUTE_ID_ENUM_COUNT + 1] = {
"package:replaces",
"package:resolvable.operator",
"package:checksum",
"package:version.prerelease",
NULL
};

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>
* Distributed under the terms of the MIT License.
*/
@ -127,6 +127,10 @@ ReaderImplBase::PackageVersionAttributeHandler::HandleAttribute(
fPackageVersionData.micro = value.string;
break;
case B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_PRE_RELEASE:
fPackageVersionData.preRelease = value.string;
break;
case B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_RELEASE:
fPackageVersionData.release = value.unsignedInt;
break;

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) {
PackageAttribute* versionRelease = new PackageAttribute(
B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_RELEASE,