aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2018-10-18 07:05:43 +0200
committerdec05eba <dec05eba@protonmail.com>2020-07-06 07:39:33 +0200
commit6bb79ef033c2a2e8f12c9da6409e3547af40417c (patch)
tree6fe6c5bea99cc0b38af92a7aa4714e5614de4868
parent4e38f2af2b97850ec5b395d4e0ea8310e664e52f (diff)
Use ranges for dependency version
-rw-r--r--CMakeLists.txt2
-rw-r--r--README.md22
-rw-r--r--backend/BackendUtils.cpp1
-rw-r--r--include/Conf.hpp4
-rw-r--r--include/Dependency.hpp3
-rw-r--r--include/FileUtil.hpp5
-rw-r--r--include/GlobalLib.hpp4
-rw-r--r--include/Package.hpp8
-rw-r--r--include/PkgConfig.hpp2
-rw-r--r--include/Version.hpp68
-rw-r--r--include/VersionParser.hpp51
-rw-r--r--project.conf2
-rw-r--r--src/CmakeModule.cpp1
-rw-r--r--src/Conf.cpp23
-rw-r--r--src/FileUtil.cpp27
-rw-r--r--src/GlobalLib.cpp40
-rw-r--r--src/Package.cpp65
-rw-r--r--src/PkgConfig.cpp48
-rw-r--r--src/Version.cpp86
-rw-r--r--src/VersionParser.cpp362
-rw-r--r--src/main.cpp2
-rw-r--r--tests/src/confTest/confTest.cpp4
-rw-r--r--tests/src/versionTest.cpp112
23 files changed, 860 insertions, 82 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 285f69d..1febc5e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -19,6 +19,8 @@ set(SOURCE_FILES
src/Package.cpp
src/GitRepository.cpp
src/Platform.cpp
+ src/Version.cpp
+ src/VersionParser.cpp
depends/libninja/src/Ninja.cpp)
diff --git a/README.md b/README.md
index d2159b9..2166be0 100644
--- a/README.md
+++ b/README.md
@@ -118,7 +118,7 @@ Required
### type
Required. Should be one of: "executable", "static", "dynamic", "library"
### version
-Required. Version string has to be in the format of "xxx.yyy.zzz" where xxx is major, yyy is minor and zzz is patch
+Required. Version string has to be in the format of "xxx.yyy.zzz" where xxx is major, yyy is minor and zzz is patch. Version format is based on [semver 2.0.0](https://semver.org/spec/v2.0.0.html)
### platforms
Required. A list of platforms the package supports. Can contain the following values: "any", "posix", "posix32", "posix64", linux", "linux32", "linux64", "win", "win32", "win64", "macos32", "macos64", "bsd", "openbsd", "openbsd32", "openbsd64", "haiku", "haiku32", "haiku64".
If platforms contains "any" then there is no need to specify other platforms
@@ -126,8 +126,24 @@ If platforms contains "any" then there is no need to specify other platforms
Optional. A list of authors
## dependencies
Optional. A list of dependencies which are specified in name-value pairs where the name is the name of the dependency, which should match the dependency name under the packages name specified in its project.conf file.
-Currently, the value is the version and has to be an exact match for the package version, which is specified in the dependencies project.conf file.
-This will later change and you should be able to choose minimum version and range of versions.
+The value should be a version string, which specified the range of versions that you want to accept as a dependency to only allow dependency version that has the features you need and the version which hasn't changed its interface.
+These are examples of the version string format:
+```
+# Version 1.0.0 or above and less than 2.0.0, same as >=1.0.0 and <2.0.0
+1.0.0
+# Version 1.0.0 or above
+>=1.0.0
+# Version above 1.0.0
+>1.0.0
+# Version exactly 1.0.0
+=1.0.0
+# Version less than 1.0.0
+<1.0.0
+# Version 1.0 or above but less than 2.0
+1.0 and <2.0
+# Version above 1.0 but less or equal to 1.3.2
+>1 and <=1.3.2
+```
Dependencies are automatically choosen from system (linux, mac) or if no package manager exists, then it's download from an url (see https://gitlab.com/DEC05EBA/sibs_packages).
The dependency can also be a git project, in which case it will have the fields 'git' and optionally 'branch' and 'revision'.
'git' specifies the url to the git repository, 'branch' is the git branch that should be used - defaults to 'master'.
diff --git a/backend/BackendUtils.cpp b/backend/BackendUtils.cpp
index b724f51..37a5002 100644
--- a/backend/BackendUtils.cpp
+++ b/backend/BackendUtils.cpp
@@ -122,6 +122,7 @@ namespace backend
collectSourceFiles(file->path, ninjaProject, sibsConfig, true);
}
}
+ return true;
});
}
}
diff --git a/include/Conf.hpp b/include/Conf.hpp
index a60bc36..ee61a21 100644
--- a/include/Conf.hpp
+++ b/include/Conf.hpp
@@ -8,6 +8,7 @@
#include "Dependency.hpp"
#include "Package.hpp"
#include "Platform.hpp"
+#include "Version.hpp"
#include <vector>
#include <unordered_map>
#include <cassert>
@@ -402,7 +403,8 @@ namespace sibs
bool zigTestAllFiles;
bool packaging;
bool bundling;
- std::string version;
+ std::string versionStr;
+ PackageVersion version;
Platform platform;
protected:
virtual void processObject(StringView name) override;
diff --git a/include/Dependency.hpp b/include/Dependency.hpp
index 7c8bbf1..0381625 100644
--- a/include/Dependency.hpp
+++ b/include/Dependency.hpp
@@ -3,6 +3,7 @@
#include <string>
#include <cassert>
+#include "Version.hpp";
namespace sibs
{
@@ -45,7 +46,7 @@ namespace sibs
}
std::string name;
- std::string version;
+ PackageVersionRange version;
};
class GitDependency : public Dependency
diff --git a/include/FileUtil.hpp b/include/FileUtil.hpp
index 3e2e302..0487227 100644
--- a/include/FileUtil.hpp
+++ b/include/FileUtil.hpp
@@ -36,7 +36,8 @@ namespace sibs
void replaceChar(FileString &input, wchar_t charToReplace, wchar_t charToReplaceWith);
#endif
- using FileWalkCallbackFunc = std::function<void(tinydir_file*)>;
+ // Return true if you want to continue iterating the remaining files, return false if you want to stop
+ using FileWalkCallbackFunc = std::function<bool(tinydir_file*)>;
enum class FileType
{
@@ -48,7 +49,7 @@ namespace sibs
FileType getFileType(const _tinydir_char_t *path);
void walkDir(const _tinydir_char_t *directory, FileWalkCallbackFunc callbackFunc);
void walkDirFiles(const _tinydir_char_t *directory, FileWalkCallbackFunc callbackFunc);
- void walkDirFilesRecursive(const _tinydir_char_t *directory, FileWalkCallbackFunc callbackFunc);
+ bool walkDirFilesRecursive(const _tinydir_char_t *directory, FileWalkCallbackFunc callbackFunc);
Result<StringView> getFileContent(const _tinydir_char_t *filepath);
Result<bool> fileWrite(const _tinydir_char_t *filepath, StringView data);
Result<bool> fileOverwrite(const _tinydir_char_t *filepath, StringView data);
diff --git a/include/GlobalLib.hpp b/include/GlobalLib.hpp
index 8647a65..6a6c72a 100644
--- a/include/GlobalLib.hpp
+++ b/include/GlobalLib.hpp
@@ -21,9 +21,9 @@ namespace sibs
static Result<bool> getLibs(const std::vector<PackageListDependency*> &libs, const SibsConfig &parentConfig, const FileString &globalLibRootDir, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback);
static Result<bool> validatePackageExists(const FileString &globalLibRootDir, const std::string &name);
- static Result<bool> getLibsLinkerFlags(const SibsConfig &parentConfig, const FileString &globalLibRootDir, const std::string &name, const std::string &version, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback);
+ static Result<bool> getLibsLinkerFlags(const SibsConfig &parentConfig, const FileString &globalLibRootDir, const std::string &name, const PackageVersionRange &versionRange, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback);
static Result<bool> getLibsLinkerFlags(const SibsConfig &parentConfig, const FileString &globalLibRootDir, GitDependency *gitDependency, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback);
- static Result<bool> downloadDependency(PackageListDependency *dependency);
+ static Result<bool> downloadDependency(PackageListDependency *dependency, Platform platform);
static Result<bool> downloadDependency(GitDependency *dependency);
private:
static Result<bool> getLibsLinkerFlagsCommon(const SibsConfig &parentConfig, const FileString &packageDir, const std::string &dependencyName, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback);
diff --git a/include/Package.hpp b/include/Package.hpp
index dbae5f2..72652be 100644
--- a/include/Package.hpp
+++ b/include/Package.hpp
@@ -4,6 +4,7 @@
#include "../external/rapidjson/document.h"
#include "Platform.hpp"
#include "Result.hpp"
+#include "Version.hpp"
#include <string>
#include <vector>
@@ -30,7 +31,7 @@ namespace sibs
struct PackageMetadata
{
std::string description;
- std::string version;
+ PackageVersion version;
std::vector<std::string> platforms;
std::vector<std::string> urls;
};
@@ -45,11 +46,10 @@ namespace sibs
static Result<rapidjson::Document*> getPackageList(const char *url);
/*
- * Return the first url in list.
+ * Return the package data for the package we can use
* TODO: If we fail to fetch package from first url, try other other ones in the list (or if the first url is too slow / takes too long to respond).
- * TODO: Add version matching with wildcard etc. If we specify "1.2.*", then it should get the latest version that matches; etc...
*/
- static Result<std::string> getPackageUrl(const char *packageName, const char *packageVersion, Platform platform);
+ static Result<PackageMetadata> getPackage(const char *packageName, const PackageVersionRange &versionRange, Platform platform);
};
}
diff --git a/include/PkgConfig.hpp b/include/PkgConfig.hpp
index 9181a2f..24aeb78 100644
--- a/include/PkgConfig.hpp
+++ b/include/PkgConfig.hpp
@@ -6,6 +6,7 @@
#include <string>
#include <vector>
#include "FileUtil.hpp"
+#include "Version.hpp"
namespace sibs
{
@@ -24,6 +25,7 @@ namespace sibs
static Result<bool> validatePkgConfigPackageVersionExists(PackageListDependency *dependency);
static Result<bool> validatePackageExists(const std::string &name);
static Result<bool> validatePackageVersionAtLeast(const std::string &name, const std::string &version);
+ static Result<PackageVersion> getPackageVersion(const std::string &name);
static Result<std::string> getDynamicLibsLinkerFlags(const std::vector<PackageListDependency*> &libs);
static Result<std::string> getDynamicLibsCflags(const std::vector<PackageListDependency*> &libs);
static Result<PkgConfigFlags> getDynamicLibsFlags(const std::vector<PackageListDependency*> &libs);
diff --git a/include/Version.hpp b/include/Version.hpp
new file mode 100644
index 0000000..915f622
--- /dev/null
+++ b/include/Version.hpp
@@ -0,0 +1,68 @@
+#pragma once
+
+#include <string>
+
+namespace sibs
+{
+ enum class VersionOperation
+ {
+ LESS,
+ LESS_EQUAL,
+ EQUAL,
+ GREATER,
+ GREATER_EQUAL
+ };
+
+ const char* asString(VersionOperation operation);
+
+ struct PackageVersion
+ {
+ int major;
+ int minor;
+ int patch;
+
+ bool operator < (const PackageVersion &other) const
+ {
+ if(major < other.major) return true;
+ if(major == other.major && minor < other.minor) return true;
+ if(major == other.major && minor == other.minor && patch < other.patch) return true;
+ return false;
+ }
+
+ bool operator == (const PackageVersion &other) const
+ {
+ return (major == other.major) && (minor == other.minor) && (patch == other.patch);
+ }
+
+ bool operator <= (const PackageVersion &other) const
+ {
+ return *this < other || *this == other;
+ }
+
+ std::string toString() const;
+ };
+ static_assert(sizeof(PackageVersion) == sizeof(int) * 3, "Expected PackageVersion to be the same size as 3 ints");
+
+ struct PackageVersionRange
+ {
+ PackageVersionRange()
+ {
+ start = { 0, 0, 0 };
+ end = { 0, 0, 0 };
+ startDefined = false;
+ endDefined = false;
+ startOperation = VersionOperation::LESS;
+ endOperation = VersionOperation::LESS;
+ }
+
+ bool isInRange(const PackageVersion &version) const;
+ std::string toString() const;
+
+ PackageVersion start;
+ PackageVersion end;
+ bool startDefined;
+ bool endDefined;
+ VersionOperation startOperation;
+ VersionOperation endOperation;
+ };
+} \ No newline at end of file
diff --git a/include/VersionParser.hpp b/include/VersionParser.hpp
new file mode 100644
index 0000000..eba4d33
--- /dev/null
+++ b/include/VersionParser.hpp
@@ -0,0 +1,51 @@
+#pragma once
+
+#include "types.hpp"
+#include "Version.hpp"
+#include "Result.hpp"
+#include "StringView.hpp"
+#include <string>
+
+namespace sibs
+{
+ Result<PackageVersion> parsePackageVersion(const StringView &versionStr, int *size);
+
+ enum class VersionToken
+ {
+ NONE,
+ END_OF_FILE,
+ INVALID,
+ OPERATION,
+ AND,
+ VERSION_NUMBER
+ };
+
+ struct VersionTokenizer
+ {
+ VersionTokenizer();
+ VersionTokenizer(const char *start, const usize size);
+ VersionTokenizer(const VersionTokenizer &other);
+ VersionToken next();
+
+
+ const char *start;
+ const char *code;
+ usize size;
+ usize index;
+ PackageVersion version;
+ VersionOperation operation;
+ StringView identifier;
+ std::string errMsg;
+ };
+
+ struct VersionParser
+ {
+ Result<PackageVersionRange> parse(const char *code, const usize size);
+
+ VersionTokenizer tokenizer;
+ PackageVersionRange versionRange;
+ private:
+ VersionToken parseStart();
+ VersionToken parseEnd();
+ };
+} \ No newline at end of file
diff --git a/project.conf b/project.conf
index f042daf..5d4e5cb 100644
--- a/project.conf
+++ b/project.conf
@@ -1,7 +1,7 @@
[package]
name = "sibs"
type = "executable"
-version = "0.1.5"
+version = "0.2.0"
authors = ["DEC05EBA <0xdec05eba@gmail.com>"]
platforms = ["any"]
diff --git a/src/CmakeModule.cpp b/src/CmakeModule.cpp
index 1e873cd..91c572d 100644
--- a/src/CmakeModule.cpp
+++ b/src/CmakeModule.cpp
@@ -239,6 +239,7 @@ namespace sibs
}
}
}
+ return true;
});
// TODO: Clean this up. The below code is indentical to code in Ninja.cpp.......
diff --git a/src/Conf.cpp b/src/Conf.cpp
index a204f13..7ae589e 100644
--- a/src/Conf.cpp
+++ b/src/Conf.cpp
@@ -1,5 +1,6 @@
#include "../include/Conf.hpp"
#include "../include/types.hpp"
+#include "../include/VersionParser.hpp"
#include "../external/utf8/unchecked.h"
#include <iostream>
@@ -482,7 +483,7 @@ namespace sibs
return Result<bool>::Err(errMsg);
}
- if(config.version.empty())
+ if(config.versionStr.empty())
{
string errMsg = "The project ";
errMsg += config.getPackageName();
@@ -670,6 +671,7 @@ namespace sibs
{
if(_tinydir_strcmp(file->extension, CONFIG_STATIC_LIB_FILE_EXTENSION) == 0)
outputFiles.push_back(toUtf8(file->path));
+ return true;
});
}
@@ -747,12 +749,17 @@ namespace sibs
else if(name.equals("version"))
{
if (value.isSingle())
- version = string(value.asSingle().data, value.asSingle().size);
+ versionStr = string(value.asSingle().data, value.asSingle().size);
else
throw ParserException("Expected package.version to be a single value, was a list");
- if(!isVersionStringValid(version))
- throw ParserException("package.version is in invalid format. Version string can only contain numbers and dots");
+ int versionSize = 0;
+ Result<PackageVersion> versionResult = parsePackageVersion(value.asSingle(), &versionSize);
+ if(!versionResult)
+ throw ParserException("package.version is in invalid format, error: " + versionResult.getErrMsg());
+ if(versionSize != (int)versionStr.size())
+ throw ParserException("package.version is in invalid format, expected to only contain numbers and dots");
+ version = versionResult.unwrap();
}
else if(name.equals("authors"))
{
@@ -1025,10 +1032,14 @@ namespace sibs
{
if(value.isSingle())
{
- // TODO: Validate version is number in correct format
+ VersionParser versionParser;
+ Result<PackageVersionRange> dependencyVersionResult = versionParser.parse(value.asSingle().data, value.asSingle().size);
+ if(!dependencyVersionResult)
+ throw ParserException("Dependency " + string(name.data, name.size) + " version is in invalid format, error: " + dependencyVersionResult.getErrMsg());
+
PackageListDependency *dependency = new PackageListDependency();
dependency->name = string(name.data, name.size);
- dependency->version = string(value.asSingle().data, value.asSingle().size);
+ dependency->version = dependencyVersionResult.unwrap();
packageListDependencies.emplace_back(dependency);
}
else if(value.isObject())
diff --git a/src/FileUtil.cpp b/src/FileUtil.cpp
index b41808b..d33c94d 100644
--- a/src/FileUtil.cpp
+++ b/src/FileUtil.cpp
@@ -175,7 +175,11 @@ namespace sibs
tinydir_file file;
tinydir_readfile(&dir, &file);
if(_tinydir_strncmp(file.name, TINYDIR_STRING("."), 1) != 0)
- callbackFunc(&file);
+ {
+ bool doContinue = callbackFunc(&file);
+ if(!doContinue)
+ break;
+ }
tinydir_next(&dir);
}
@@ -193,7 +197,11 @@ namespace sibs
tinydir_file file;
tinydir_readfile(&dir, &file);
if(file.is_reg)
- callbackFunc(&file);
+ {
+ bool doContinue = callbackFunc(&file);
+ if(!doContinue)
+ break;
+ }
tinydir_next(&dir);
}
@@ -201,7 +209,7 @@ namespace sibs
}
// TODO: Handle failure (directory doesn't exist, no permission etc)
- void walkDirFilesRecursive(const _tinydir_char_t *directory, FileWalkCallbackFunc callbackFunc)
+ bool walkDirFilesRecursive(const _tinydir_char_t *directory, FileWalkCallbackFunc callbackFunc)
{
tinydir_dir dir;
tinydir_open(&dir, directory);
@@ -211,13 +219,22 @@ namespace sibs
tinydir_file file;
tinydir_readfile(&dir, &file);
if(file.is_reg)
- callbackFunc(&file);
+ {
+ bool doContinue = callbackFunc(&file);
+ if(!doContinue)
+ return false;
+ }
else if(_tinydir_strncmp(file.name, TINYDIR_STRING("."), 1) != 0)
- walkDirFilesRecursive(file.path, callbackFunc);
+ {
+ bool doContinue = walkDirFilesRecursive(file.path, callbackFunc);
+ if(!doContinue)
+ return false;
+ }
tinydir_next(&dir);
}
tinydir_close(&dir);
+ return true;
}
Result<StringView> getFileContent(const _tinydir_char_t *filepath)
diff --git a/src/GlobalLib.cpp b/src/GlobalLib.cpp
index 242e621..61e6648 100644
--- a/src/GlobalLib.cpp
+++ b/src/GlobalLib.cpp
@@ -8,6 +8,7 @@
#include "../include/CmakeModule.hpp"
#include "../include/Dependency.hpp"
#include "../include/GitRepository.hpp"
+#include "../include/VersionParser.hpp"
using namespace std;
@@ -69,7 +70,7 @@ namespace sibs
// TODO: If return error is invalid url, then the message should be converted to
// invalid package name/version. A check should be done if it is the name or version
// that is invalid.
- Result<bool> downloadDependencyResult = GlobalLib::downloadDependency(globalLibDependency);
+ Result<bool> downloadDependencyResult = GlobalLib::downloadDependency(globalLibDependency, parentConfig.platform);
if(!downloadDependencyResult)
return downloadDependencyResult;
@@ -118,7 +119,7 @@ namespace sibs
return Result<bool>::Ok(true);
}
- Result<bool> GlobalLib::getLibsLinkerFlags(const SibsConfig &parentConfig, const FileString &globalLibRootDir, const std::string &name, const std::string &version, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback)
+ Result<bool> GlobalLib::getLibsLinkerFlags(const SibsConfig &parentConfig, const FileString &globalLibRootDir, const std::string &name, const PackageVersionRange &versionRange, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback)
{
Result<bool> packageExistsResult = validatePackageExists(globalLibRootDir, name);
if (packageExistsResult.isErr())
@@ -126,33 +127,34 @@ namespace sibs
#if OS_FAMILY == OS_FAMILY_POSIX
FileString namePlatformNative = name;
- FileString versionPlatformNative = version;
#else
FileString namePlatformNative = utf8To16(name);
- FileString versionPlatformNative = utf8To16(version);
#endif
FileString packageDir = globalLibRootDir + TINYDIR_STRING("/");
packageDir += namePlatformNative;
- // TODO: Instead of checking if version is exact match, check if package has same major version
- // and same or newer minor version
FileString foundVersion;
- walkDir(packageDir.c_str(), [&foundVersion, &versionPlatformNative](tinydir_file *file)
+ walkDir(packageDir.c_str(), [&foundVersion, &versionRange](tinydir_file *file)
{
if(file->is_dir)
{
- //printf("version: %s\n", file->name);
- if(_tinydir_strcmp(versionPlatformNative.c_str(), file->name) == 0)
+ string versionUtf8 = toUtf8(file->name);
+ Result<PackageVersion> versionResult = parsePackageVersion({ versionUtf8.data(), versionUtf8.size() }, nullptr);
+ if(versionResult && versionRange.isInRange(versionResult.unwrap()))
+ {
foundVersion = file->name;
+ return false;
+ }
}
+ return true;
});
if(foundVersion.empty())
- return Result<bool>::Err("Global lib dependency found, but version doesn't match dependency version", DependencyError::DEPENDENCY_VERSION_NO_MATCH);
+ return Result<bool>::Err("Global lib dependency found, but version isn't in range of version", DependencyError::DEPENDENCY_VERSION_NO_MATCH);
packageDir += TINYDIR_STRING("/");
- packageDir += versionPlatformNative;
+ packageDir += foundVersion;
return GlobalLib::getLibsLinkerFlagsCommon(parentConfig, packageDir, name, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback);
}
@@ -261,13 +263,13 @@ namespace sibs
}
}
- Result<bool> GlobalLib::downloadDependency(PackageListDependency *dependency)
+ Result<bool> GlobalLib::downloadDependency(PackageListDependency *dependency, Platform platform)
{
- Result<string> packageUrlResult = Package::getPackageUrl(dependency->name.c_str(), dependency->version.c_str(), SYSTEM_PLATFORM);
- if(!packageUrlResult)
- return Result<bool>::Err(packageUrlResult);
+ Result<PackageMetadata> packageResult = Package::getPackage(dependency->name.c_str(), dependency->version, platform);
+ if(!packageResult)
+ return Result<bool>::Err(packageResult);
- const string &url = packageUrlResult.unwrap();
+ const PackageMetadata &package = packageResult.unwrap();
Result<FileString> libPathResult = getHomeDir();
if (!libPathResult)
@@ -276,7 +278,7 @@ namespace sibs
libPath += TINYDIR_STRING("/.cache/sibs/lib/");
libPath += toFileString(dependency->name);
libPath += TINYDIR_STRING("/");
- libPath += toFileString(dependency->version);
+ libPath += package.version.toString();
FileString libArchivedFilePath = libPathResult.unwrap();
libArchivedFilePath += TINYDIR_STRING("/.cache/sibs/archive/");
@@ -286,8 +288,8 @@ namespace sibs
return createArchiveDirResult;
libArchivedFilePath += TINYDIR_STRING("/");
- libArchivedFilePath += toFileString(dependency->version);
- Result<bool> downloadResult = curl::downloadFile(url.c_str(), libArchivedFilePath.c_str());
+ libArchivedFilePath += package.version.toString();
+ Result<bool> downloadResult = curl::downloadFile(package.urls[0].c_str(), libArchivedFilePath.c_str());
if(!downloadResult)
return downloadResult;
diff --git a/src/Package.cpp b/src/Package.cpp
index eab0680..925de82 100644
--- a/src/Package.cpp
+++ b/src/Package.cpp
@@ -1,5 +1,6 @@
#include "../include/Package.hpp"
#include "../include/curl.hpp"
+#include "../include/VersionParser.hpp"
#include "../external/rapidjson/error/en.h"
#ifdef GetObject
@@ -37,7 +38,15 @@ namespace sibs
PackageMetadata packageMetadata;
packageMetadata.description.assign(description->value.GetString(), description->value.GetStringLength());
- packageMetadata.version.assign(version->value.GetString(), version->value.GetStringLength());
+
+ int versionStrSize = 0;
+ Result<PackageVersion> versionResult = parsePackageVersion({ version->value.GetString(), version->value.GetStringLength() }, &versionStrSize);
+ if(!versionResult)
+ return Result<PackageMetadata>::Err("package version is in wrong fromat, error: " + versionResult.getErrMsg());
+ if(versionStrSize != (int)version->value.GetStringLength())
+ return Result<PackageMetadata>::Err("package version is in wrong format, expected only numbers and dots (version: " + string(version->value.GetString(), version->value.GetStringLength()) + ")");
+
+ packageMetadata.version = versionResult.unwrap();
const auto &platformsArray = platforms->value.GetArray();
packageMetadata.platforms.reserve(platformsArray.Size());
@@ -50,6 +59,9 @@ namespace sibs
}
const auto &urlsArray = urls->value.GetArray();
+ if(urlsArray.Empty())
+ return Result<PackageMetadata>::Err("Expected url list to not be empty");
+
packageMetadata.urls.reserve(urlsArray.Size());
for(int i = 0; i < urlsArray.Size(); ++i)
{
@@ -62,16 +74,16 @@ namespace sibs
return Result<PackageMetadata>::Ok(packageMetadata);
}
- static Result<string> getPackageUrl(const PackageMetadata &packageMetadata, const char *packageName, const char *packageVersion, Platform platform)
+ static Result<bool> isPackageUsableForPlatform(const PackageMetadata &packageMetadata, const char *packageName, const PackageVersionRange &versionRange, Platform platform)
{
- if(strcmp(packageMetadata.version.c_str(), packageVersion) != 0)
+ if(!versionRange.isInRange(packageMetadata.version))
{
string errMsg = "Package \"";
errMsg += packageName;
- errMsg += "\" does not exist for version \"";
- errMsg += packageVersion;
+ errMsg += "\" does not exist for version range \"";
+ errMsg += versionRange.toString();
errMsg += "\"";
- return Result<string>::Err(errMsg);
+ return Result<bool>::Err(errMsg);
}
if(!containsPlatform(getPlatformsByNames(packageMetadata.platforms), platform))
@@ -79,14 +91,14 @@ namespace sibs
string errMsg = "Package \"";
errMsg += packageName;
errMsg += "\" with version \"";
- errMsg += packageVersion;
+ errMsg += packageMetadata.version.toString();
errMsg += "\" does not support platform \"";
errMsg += platform;
errMsg += "\"";
- return Result<string>::Err(errMsg);
+ return Result<bool>::Err(errMsg);
}
-
- return Result<string>::Ok(packageMetadata.urls[0]);
+
+ return Result<bool>::Ok(true);
}
// TODO: Always downloading is fine right now because the package list is small. This should later be modified to use local cache.
@@ -120,11 +132,11 @@ namespace sibs
return Result<Document*>::Ok(packageList);
}
- Result<string> Package::getPackageUrl(const char *packageName, const char *packageVersion, Platform platform)
+ Result<PackageMetadata> Package::getPackage(const char *packageName, const PackageVersionRange &versionRange, Platform platform)
{
Result<Document*> packageList = Package::getPackageList("https://gitlab.com/DEC05EBA/sibs_packages/raw/master/packages.json");
if(!packageList)
- return Result<string>::Err(packageList);
+ return Result<PackageMetadata>::Err(packageList);
const Document &packageDoc = *packageList.unwrap();
@@ -134,14 +146,18 @@ namespace sibs
string errMsg = "No package with the name \"";
errMsg += packageName;
errMsg += "\" was found";
- return Result<string>::Err(errMsg);
+ return Result<PackageMetadata>::Err(errMsg);
}
if(packageMetaDataJsonIt->value.IsObject())
{
Result<PackageMetadata> packageMetadataResult = getPackageMetadata(packageMetaDataJsonIt->value.GetObject());
- if(!packageMetadataResult) return Result<string>::Err(packageMetadataResult);
- return ::sibs::getPackageUrl(packageMetadataResult.unwrap(), packageName, packageVersion, platform);
+ if(!packageMetadataResult) return packageMetadataResult;
+ Result<bool> packageUsableResult = isPackageUsableForPlatform(packageMetadataResult.unwrap(), packageName, versionRange, platform);
+ if(packageUsableResult)
+ return Result<PackageMetadata>::Ok(packageMetadataResult.unwrap());
+ else
+ return Result<PackageMetadata>::Err(packageUsableResult);
}
else if(packageMetaDataJsonIt->value.IsArray())
{
@@ -155,35 +171,32 @@ namespace sibs
errMsg += "[";
errMsg += to_string(i);
errMsg += "] is not an object";
- return Result<string>::Err(errMsg);
+ return Result<PackageMetadata>::Err(errMsg);
}
Result<PackageMetadata> packageMetadataResult = getPackageMetadata(packageData.GetObject());
- if(packageMetadataResult)
- {
- Result<string> packageUrlResult = ::sibs::getPackageUrl(packageMetadataResult.unwrap(), packageName, packageVersion, platform);
- if(packageUrlResult)
- return packageUrlResult;
- }
+ if(!packageMetadataResult) return packageMetadataResult;
+ if(isPackageUsableForPlatform(packageMetadataResult.unwrap(), packageName, versionRange, platform))
+ return Result<PackageMetadata>::Ok(packageMetadataResult.unwrap());
++i;
}
string errMsg = "Package \"";
errMsg += packageName;
- errMsg += "\" with version \"";
- errMsg += packageVersion;
+ errMsg += "\" in version range \"";
+ errMsg += versionRange.toString();
errMsg += "\" does not exist or does not exist for platform \"";
errMsg += asString(platform);
errMsg += "\"";
- return Result<string>::Err(errMsg);
+ return Result<PackageMetadata>::Err(errMsg);
}
else
{
string errMsg = "No package with the name \"";
errMsg += packageName;
errMsg += "\" was found";
- return Result<string>::Err(errMsg);
+ return Result<PackageMetadata>::Err(errMsg);
}
}
}
diff --git a/src/PkgConfig.cpp b/src/PkgConfig.cpp
index e8f742c..89d3a44 100644
--- a/src/PkgConfig.cpp
+++ b/src/PkgConfig.cpp
@@ -1,6 +1,7 @@
#include "../include/PkgConfig.hpp"
#include "../include/Exec.hpp"
#include "../include/Dependency.hpp"
+#include "../include/VersionParser.hpp"
using namespace std;
@@ -32,9 +33,12 @@ namespace sibs
if(dependencyValidationResult.isErr())
return Result<bool>::Err(dependencyValidationResult.getErrMsg());
- Result<bool> dependencyVersionValidationResult = PkgConfig::validatePackageVersionAtLeast(dependency->name, dependency->version);
- if(dependencyVersionValidationResult.isErr())
- return Result<bool>::Err(dependencyVersionValidationResult.getErrMsg());
+ Result<PackageVersion> dependencyVersionResult = PkgConfig::getPackageVersion(dependency->name);
+ if(!dependencyVersionResult)
+ return Result<bool>::Err(dependencyVersionResult);
+
+ if(!dependency->version.isInRange(dependencyVersionResult.unwrap()))
+ return Result<bool>::Err("pkg-config package " + dependency->name + " exists but the version does not match our expected version range");
return Result<bool>::Ok(true);
}
@@ -70,11 +74,6 @@ namespace sibs
Result<bool> PkgConfig::validatePackageVersionAtLeast(const string &name, const string &version)
{
- // TODO: Instead of checking if package version is same or newer, check if package is same major version
- // and same or newer minor version
-
- // Use --modversion instead and check if the version returned is newer or equal to dependency version.
- // This way we can output installed version vs expected dependency version
FileString command = pkgConfigPath + TINYDIR_STRING(" '--atleast-version=");
command += toFileString(version);
command += TINYDIR_STRING("' '");
@@ -98,7 +97,7 @@ namespace sibs
}
else if(execResult.unwrap().exitCode != 0)
{
- string errMsg = "Failed to check dependency version, Unknown error, exit code: ";
+ string errMsg = "Failed to check pkg-config package version, Unknown error, exit code: ";
errMsg += to_string(execResult.unwrap().exitCode);
return Result<bool>::Err(errMsg);
}
@@ -106,6 +105,37 @@ namespace sibs
return Result<bool>::Ok(true);
}
+ Result<PackageVersion> PkgConfig::getPackageVersion(const std::string &name)
+ {
+ FileString command = pkgConfigPath + TINYDIR_STRING(" --modversion '");
+ command += toFileString(name);
+ command += TINYDIR_STRING("'");
+ Result<ExecResult> execResult = exec(command.c_str());
+ if(!execResult)
+ return Result<PackageVersion>::Err(execResult.getErrMsg());
+
+ if(execResult.unwrap().exitCode == 1)
+ {
+ string errMsg = "Dependency ";
+ errMsg += name;
+ errMsg += " not found in pkg-config";
+ return Result<PackageVersion>::Err(errMsg);
+ }
+ else if(execResult.unwrap().exitCode == 127)
+ {
+ return Result<PackageVersion>::Err("pkg-config is not installed");
+ }
+ else if(execResult.unwrap().exitCode != 0)
+ {
+ string errMsg = "Failed to get pkg-config package version, Unknown error, exit code: ";
+ errMsg += to_string(execResult.unwrap().exitCode);
+ return Result<PackageVersion>::Err(errMsg);
+ }
+
+ // Intentionally allow packages which have a version that contains more data after patch number, since some pkg-config packages are not in semver format
+ return parsePackageVersion({ execResult.unwrap().execStdout.data(), execResult.unwrap().execStdout.size() }, nullptr);
+ }
+
Result<string> PkgConfig::getDynamicLibsLinkerFlags(const vector<PackageListDependency*> &libs)
{
if(libs.empty()) return Result<string>::Ok("");
diff --git a/src/Version.cpp b/src/Version.cpp
new file mode 100644
index 0000000..26e5dbd
--- /dev/null
+++ b/src/Version.cpp
@@ -0,0 +1,86 @@
+#include "../include/Version.hpp"
+#include <cassert>
+
+namespace sibs
+{
+ const char* asString(VersionOperation operation)
+ {
+ switch(operation)
+ {
+ case VersionOperation::LESS: return "<";
+ case VersionOperation::LESS_EQUAL: return "<=";
+ case VersionOperation::EQUAL: return "=";
+ case VersionOperation::GREATER: return ">";
+ case VersionOperation::GREATER_EQUAL: return ">=";
+ default: return "";
+ }
+ }
+
+ std::string PackageVersion::toString() const
+ {
+ std::string result;
+ result += std::to_string(major);
+ result += ".";
+ result += std::to_string(minor);
+ result += ".";
+ result += std::to_string(patch);
+ return result;
+ }
+
+ std::string PackageVersionRange::toString() const
+ {
+ std::string result;
+ result += asString(startOperation);
+ result += start.toString();
+ if(endDefined)
+ {
+ result += " and ";
+ result += asString(endOperation);
+ result += end.toString();
+ }
+ return result;
+ }
+
+ static bool isInRangeOfEnd(const PackageVersionRange &versionRange, const PackageVersion &version)
+ {
+ if(!versionRange.endDefined)
+ return true;
+
+ switch(versionRange.endOperation)
+ {
+ case VersionOperation::LESS:
+ return version < versionRange.end;
+ case VersionOperation::LESS_EQUAL:
+ return version <= versionRange.end;
+ default:
+ assert(false);
+ return true;
+ }
+ }
+
+ bool PackageVersionRange::isInRange(const PackageVersion &version) const
+ {
+ switch(startOperation)
+ {
+ case VersionOperation::LESS:
+ return version < start;
+ case VersionOperation::LESS_EQUAL:
+ return version <= start;
+ case VersionOperation::EQUAL:
+ return version == start;
+ case VersionOperation::GREATER:
+ {
+ if(version <= start)
+ return false;
+ return isInRangeOfEnd(*this, version);
+ }
+ case VersionOperation::GREATER_EQUAL:
+ {
+ if(version < start)
+ return false;
+ return isInRangeOfEnd(*this, version);
+ }
+ }
+ return true;
+ }
+} \ No newline at end of file
diff --git a/src/VersionParser.cpp b/src/VersionParser.cpp
new file mode 100644
index 0000000..7b28cef
--- /dev/null
+++ b/src/VersionParser.cpp
@@ -0,0 +1,362 @@
+#include "../include/VersionParser.hpp"
+#include "../include/StringView.hpp"
+
+namespace sibs
+{
+ static int stringToIntNoVerify(const StringView &str)
+ {
+ int result = 0;
+ if(str.size > 0)
+ result += (str[0] - '0');
+
+ for(int i = 1; i < (int)str.size; ++i)
+ {
+ int num = str[i] - '0';
+ result += (10 * ((int)str.size - i) * num);
+ }
+ return result;
+ }
+
+ static bool isNum(char c)
+ {
+ return c >= '0' && c <= '9';
+ }
+
+ static bool isAlpha(char c)
+ {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+ }
+
+ Result<PackageVersion> parsePackageVersion(const StringView &versionStr, int *size)
+ {
+ PackageVersion result = { 0, 0, 0 };
+ int *versionPtr = &result.major;
+ int digitStart = 0;
+ int i = 0;
+
+ for(; i < (int)versionStr.size; ++i)
+ {
+ char c = versionStr[i];
+ if(isNum(c))
+ {
+ if(digitStart == -1)
+ digitStart = i;
+ }
+ else
+ {
+ int length = i - digitStart;
+ if(digitStart == -1 || length == 0)
+ return Result<PackageVersion>::Err("Package version string is in invalid format. Expected to be in format xxx.xxx.xxx where x is a number");
+
+ *versionPtr = stringToIntNoVerify({ versionStr.data + digitStart, (usize)(i - digitStart) });
+ bool endOfVersions = versionPtr == &result.patch;
+ ++versionPtr;
+ digitStart = -1;
+
+ if(c != '.' || endOfVersions)
+ break;
+ }
+ }
+
+ if(i == 0)
+ return Result<PackageVersion>::Err("version can't be empty");
+
+ if(digitStart != -1)
+ {
+ *versionPtr = stringToIntNoVerify({ versionStr.data + digitStart, (usize)(i - digitStart) });
+ ++versionPtr;
+ }
+
+ if(size)
+ *size = i;
+
+ if(versionPtr == &result.major)
+ return Result<PackageVersion>::Err("version can't be empty");
+
+ return Result<PackageVersion>::Ok(result);
+ }
+
+ VersionTokenizer::VersionTokenizer() :
+ start(nullptr),
+ code(nullptr),
+ size(0),
+ index(0)
+ {
+
+ }
+
+ VersionTokenizer::VersionTokenizer(const char *_start, const usize _size) :
+ start(_start),
+ code(_start),
+ size(_size),
+ index(0)
+ {
+
+ }
+
+ VersionTokenizer::VersionTokenizer(const VersionTokenizer &other)
+ {
+ start = other.start;
+ code = other.code;
+ size = other.size;
+ index = other.index;
+ }
+
+ VersionToken VersionTokenizer::next()
+ {
+ while(index < size)
+ {
+ char c = code[index];
+ if(c == ' ' || c == '\t' || c == '\n' || c == '\r')
+ ++index;
+ else
+ break;
+ }
+
+ if(index >= size)
+ return VersionToken::END_OF_FILE;
+
+ char c = code[index];
+ if(isNum(c))
+ {
+ int versionStrSize = 0;
+ identifier.data = code + index;
+ Result<PackageVersion> packageVersion = parsePackageVersion({ code + index, (usize)(size - index) }, &versionStrSize);
+ identifier.size = versionStrSize;
+ index += versionStrSize;
+ if(!packageVersion)
+ {
+ errMsg = packageVersion.getErrMsg();
+ return VersionToken::INVALID;
+ }
+ version = packageVersion.unwrap();
+ return VersionToken::VERSION_NUMBER;
+ }
+ else if(isAlpha(c))
+ {
+ usize identifierStart = index;
+ ++index;
+ while(index < size && isAlpha(code[index]))
+ {
+ ++index;
+ }
+ usize identifierEnd = index;
+ usize identifierLength = identifierEnd - identifierStart;
+
+ if(identifierLength == 3 && strncmp(code + identifierStart, "and", 3) == 0)
+ {
+ return VersionToken::AND;
+ }
+ else
+ {
+ errMsg = "Invalid identifier ";
+ errMsg += std::string(code + identifierStart, identifierLength);
+ return VersionToken::INVALID;
+ }
+ }
+ else if(c == '<')
+ {
+ ++index;
+ if(index < size && code[index] == '=')
+ {
+ ++index;
+ operation = VersionOperation::LESS_EQUAL;
+ return VersionToken::OPERATION;
+ }
+ operation = VersionOperation::LESS;
+ return VersionToken::OPERATION;
+ }
+ else if(c == '=')
+ {
+ ++index;
+ operation = VersionOperation::EQUAL;
+ return VersionToken::OPERATION;
+ }
+ else if(c == '>')
+ {
+ ++index;
+ if(index < size && code[index] == '=')
+ {
+ ++index;
+ operation = VersionOperation::GREATER_EQUAL;
+ return VersionToken::OPERATION;
+ }
+ operation = VersionOperation::GREATER;
+ return VersionToken::OPERATION;
+ }
+ else
+ {
+ errMsg = "Unexpected character ";
+ errMsg += c;
+ return VersionToken::INVALID;
+ }
+ }
+
+ Result<PackageVersionRange> VersionParser::parse(const char *code, const usize size)
+ {
+ versionRange = PackageVersionRange();
+ tokenizer = VersionTokenizer(code, size);
+ VersionToken token = parseStart();
+
+ if(token == VersionToken::END_OF_FILE)
+ {
+ if(!versionRange.startDefined)
+ return Result<PackageVersionRange>::Err("version can't be empty");
+ return Result<PackageVersionRange>::Ok(versionRange);
+ }
+ else if(token == VersionToken::INVALID)
+ return Result<PackageVersionRange>::Err(tokenizer.errMsg);
+ else
+ {
+ std::string errMsg = "Unexpected token '";
+ switch(token)
+ {
+ case VersionToken::NONE:
+ {
+ errMsg += "<none>";
+ break;
+ }
+ case VersionToken::OPERATION:
+ {
+ errMsg += "operation ";
+ errMsg += asString(tokenizer.operation);
+ break;
+ }
+ case VersionToken::AND:
+ {
+ errMsg += "and";
+ break;
+ }
+ case VersionToken::VERSION_NUMBER:
+ {
+ errMsg += "version ";
+ errMsg += std::string(tokenizer.identifier.data, tokenizer.identifier.size);
+ break;
+ }
+ default:
+ break;
+ }
+ errMsg += "'";
+ return Result<PackageVersionRange>::Err(errMsg);
+ }
+ }
+
+ VersionToken VersionParser::parseStart()
+ {
+ VersionToken token = tokenizer.next();
+ if(token == VersionToken::VERSION_NUMBER)
+ {
+ versionRange.startOperation = VersionOperation::GREATER_EQUAL;
+ versionRange.start = tokenizer.version;
+ versionRange.startDefined = true;
+ token = tokenizer.next();
+ if(token == VersionToken::AND)
+ {
+ token = VersionToken::INVALID;
+ tokenizer.errMsg = "Unexpected end version when start version does not have operation defined";
+ }
+ }
+ else if(token == VersionToken::OPERATION)
+ {
+ versionRange.startOperation = tokenizer.operation;
+ token = tokenizer.next();
+ if(token == VersionToken::VERSION_NUMBER)
+ {
+ versionRange.start = tokenizer.version;
+ versionRange.startDefined = true;
+ switch(versionRange.startOperation)
+ {
+ case VersionOperation::LESS:
+ {
+ token = VersionToken::INVALID;
+ tokenizer.errMsg = "Unexpected version end when start version is expected to less than ";
+ tokenizer.errMsg += versionRange.start.toString();
+ return token;
+ }
+ case VersionOperation::LESS_EQUAL:
+ {
+ token = VersionToken::INVALID;
+ tokenizer.errMsg = "Unexpected version end when start version is expected to be less or equal to ";
+ tokenizer.errMsg += versionRange.start.toString();
+ return token;
+ }
+ case VersionOperation::EQUAL:
+ {
+ token = VersionToken::INVALID;
+ tokenizer.errMsg = "Unexpected version end when start version is expected to be exactly ";
+ tokenizer.errMsg += versionRange.start.toString();
+ return token;
+ }
+ default:
+ break;
+ }
+
+ token = tokenizer.next();
+ if(token == VersionToken::AND)
+ {
+ return parseEnd();
+ }
+ }
+ else if(token == VersionToken::INVALID)
+ return token;
+ else
+ {
+ token = VersionToken::INVALID;
+ tokenizer.errMsg = "Expected version after operation";
+ }
+ }
+ return token;
+ }
+
+ VersionToken VersionParser::parseEnd()
+ {
+ VersionToken token = tokenizer.next();
+ if(token == VersionToken::OPERATION)
+ {
+ versionRange.endOperation = tokenizer.operation;
+ switch(versionRange.endOperation)
+ {
+ case VersionOperation::EQUAL:
+ case VersionOperation::GREATER:
+ case VersionOperation::GREATER_EQUAL:
+ {
+ token = VersionToken::INVALID;
+ tokenizer.errMsg = "End version can only have operations < and <=";
+ return token;
+ }
+ default:
+ break;
+ }
+
+ token = tokenizer.next();
+ if(token == VersionToken::VERSION_NUMBER)
+ {
+ versionRange.end = tokenizer.version;
+ versionRange.endDefined = true;
+ if(versionRange.end <= versionRange.start)
+ {
+ token = VersionToken::INVALID;
+ tokenizer.errMsg = "Expected version end to be greater than ";
+ tokenizer.errMsg += versionRange.start.toString();
+ tokenizer.errMsg += ", was ";
+ tokenizer.errMsg += versionRange.end.toString();
+ return token;
+ }
+ token = tokenizer.next();
+ }
+ else if(token == VersionToken::INVALID)
+ return token;
+ else
+ {
+ token = VersionToken::INVALID;
+ tokenizer.errMsg = "Expected version after operation";
+ }
+ }
+ else
+ {
+ token = VersionToken::INVALID;
+ tokenizer.errMsg = "Expected end version to have operation defined";
+ }
+ return token;
+ }
+}
diff --git a/src/main.cpp b/src/main.cpp
index 4bf840a..5d43679 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1173,7 +1173,7 @@ static int packageProject(int argc, const _tinydir_char_t **argv)
TINYDIR_STRING("\" \"") +
executablePath +
TINYDIR_STRING("\" \"") +
- toFileString(sibsConfig.version) +
+ toFileString(sibsConfig.version.toString()) +
TINYDIR_STRING("\" \"") +
packagePath +
TINYDIR_STRING("\" ") +
diff --git a/tests/src/confTest/confTest.cpp b/tests/src/confTest/confTest.cpp
index 8dab691..a86dbb3 100644
--- a/tests/src/confTest/confTest.cpp
+++ b/tests/src/confTest/confTest.cpp
@@ -26,11 +26,11 @@ TEST_CASE("parse config")
}
auto *xxhashDependency = sibsConfig.getPackageListDependencies()[0]->asPackageListDependency();
REQUIRE(xxhashDependency->name == "xxhash");
- REQUIRE(xxhashDependency->version == "0.1.0");
+ REQUIRE(xxhashDependency->version.toString() == ">=0.1.0");
const auto &catch2Dependency = sibsConfig.getPackageListDependencies()[1]->asPackageListDependency();
REQUIRE(catch2Dependency->name == "catch2");
- REQUIRE(catch2Dependency->version == "1.0.0");
+ REQUIRE(catch2Dependency->version.toString() == ">=1.0.0");
REQUIRE(sibsConfig.getGitDependencies().size() == 1);
for(auto *dep : sibsConfig.getGitDependencies())
diff --git a/tests/src/versionTest.cpp b/tests/src/versionTest.cpp
new file mode 100644
index 0000000..d5a492c
--- /dev/null
+++ b/tests/src/versionTest.cpp
@@ -0,0 +1,112 @@
+#include <catch.hpp>
+#include "../../include/VersionParser.hpp"
+
+using namespace sibs;
+
+TEST_CASE("parse package version")
+{
+ VersionParser parser;
+ {
+ Result<PackageVersionRange> parseResult = parser.parse("2.3.4", 5);
+ if(!parseResult)
+ FAIL(parseResult.getErrMsg());
+ PackageVersionRange versionRange = parseResult.unwrap();
+ REQUIRE(versionRange.startDefined);
+ REQUIRE(!versionRange.endDefined);
+ REQUIRE(versionRange.startOperation == VersionOperation::GREATER_EQUAL);
+ REQUIRE(versionRange.start.major == 2);
+ REQUIRE(versionRange.start.minor == 3);
+ REQUIRE(versionRange.start.patch == 4);
+ }
+ {
+ Result<PackageVersionRange> parseResult = parser.parse("2.3", 3);
+ if(!parseResult)
+ FAIL(parseResult.getErrMsg());
+ PackageVersionRange versionRange = parseResult.unwrap();
+ REQUIRE(versionRange.startDefined);
+ REQUIRE(!versionRange.endDefined);
+ REQUIRE(versionRange.startOperation == VersionOperation::GREATER_EQUAL);
+ REQUIRE(versionRange.start.major == 2);
+ REQUIRE(versionRange.start.minor == 3);
+ REQUIRE(versionRange.start.patch == 0);
+ }
+ {
+ Result<PackageVersionRange> parseResult = parser.parse("2", 1);
+ if(!parseResult)
+ FAIL(parseResult.getErrMsg());
+ PackageVersionRange versionRange = parseResult.unwrap();
+ REQUIRE(versionRange.startDefined);
+ REQUIRE(!versionRange.endDefined);
+ REQUIRE(versionRange.startOperation == VersionOperation::GREATER_EQUAL);
+ REQUIRE(versionRange.start.major == 2);
+ REQUIRE(versionRange.start.minor == 0);
+ REQUIRE(versionRange.start.patch == 0);
+ }
+ {
+ Result<PackageVersionRange> parseResult = parser.parse(">=2.3.4 and <5.6.7", 18);
+ if(!parseResult)
+ FAIL(parseResult.getErrMsg());
+ PackageVersionRange versionRange = parseResult.unwrap();
+ REQUIRE(versionRange.startDefined);
+ REQUIRE(versionRange.endDefined);
+ REQUIRE(versionRange.startOperation == VersionOperation::GREATER_EQUAL);
+ REQUIRE(versionRange.start.major == 2);
+ REQUIRE(versionRange.start.minor == 3);
+ REQUIRE(versionRange.start.patch == 4);
+ REQUIRE(versionRange.endOperation == VersionOperation::LESS);
+ REQUIRE(versionRange.end.major == 5);
+ REQUIRE(versionRange.end.minor == 6);
+ REQUIRE(versionRange.end.patch == 7);
+ }
+ {
+ Result<PackageVersionRange> parseResult = parser.parse(">2.3.4 and <=5.6.7", 18);
+ if(!parseResult)
+ FAIL(parseResult.getErrMsg());
+ PackageVersionRange versionRange = parseResult.unwrap();
+ REQUIRE(versionRange.startDefined);
+ REQUIRE(versionRange.endDefined);
+ REQUIRE(versionRange.startOperation == VersionOperation::GREATER);
+ REQUIRE(versionRange.start.major == 2);
+ REQUIRE(versionRange.start.minor == 3);
+ REQUIRE(versionRange.start.patch == 4);
+ REQUIRE(versionRange.endOperation == VersionOperation::LESS_EQUAL);
+ REQUIRE(versionRange.end.major == 5);
+ REQUIRE(versionRange.end.minor == 6);
+ REQUIRE(versionRange.end.patch == 7);
+ }
+ {
+ Result<PackageVersionRange> parseResult = parser.parse(">2.3.4 and <=5", 14);
+ if(!parseResult)
+ FAIL(parseResult.getErrMsg());
+ PackageVersionRange versionRange = parseResult.unwrap();
+ REQUIRE(versionRange.startDefined);
+ REQUIRE(versionRange.endDefined);
+ REQUIRE(versionRange.startOperation == VersionOperation::GREATER);
+ REQUIRE(versionRange.start.major == 2);
+ REQUIRE(versionRange.start.minor == 3);
+ REQUIRE(versionRange.start.patch == 4);
+ REQUIRE(versionRange.endOperation == VersionOperation::LESS_EQUAL);
+ REQUIRE(versionRange.end.major == 5);
+ REQUIRE(versionRange.end.minor == 0);
+ REQUIRE(versionRange.end.patch == 0);
+ }
+ {
+ REQUIRE(!parser.parse("", 0));
+ REQUIRE(!parser.parse(" ", 1));
+ REQUIRE(!parser.parse("2", 2));
+ REQUIRE(!parser.parse(".", 1));
+ REQUIRE(!parser.parse(".2", 2));
+ REQUIRE(!parser.parse("<", 1));
+ REQUIRE(!parser.parse("and", 3));
+ REQUIRE(!parser.parse("2 and", 5));
+ REQUIRE(!parser.parse("2 and 3", 7));
+ REQUIRE(!parser.parse("2 and =3", 8));
+ REQUIRE(!parser.parse("2 and >3", 8));
+ REQUIRE(!parser.parse(">=1.0.0 and 2.0.0", 17));
+ REQUIRE(!parser.parse("1.0.0 < 2.0.0", 13));
+ REQUIRE(!parser.parse("1.0.0 and <0.9.3", 16));
+ REQUIRE(!parser.parse("=1.0.0 and <2.0.0", 17));
+ REQUIRE(!parser.parse("1.0.0 and <2.0.0", 16));
+ REQUIRE(!parser.parse(">=1.0.0 and =2.0.0", 18));
+ }
+}