#include "./package.h" #include "../parser/utils.h" #include "reflection/package.h" #include #include using namespace std; using namespace CppUtilities; namespace LibPkg { bool Dependency::matches(const DependencyMode mode, const string &version1, const string &version2) { switch (mode) { case DependencyMode::Any: return true; case DependencyMode::Equal: switch (PackageVersion::compareParts(version2, version1, true)) { case PackageVersionPartComparison::Equal: return true; default:; } break; case DependencyMode::GreatherEqual: switch (PackageVersion::compareParts(version2, version1, true)) { case PackageVersionPartComparison::Equal: case PackageVersionPartComparison::Newer: return true; default:; } break; case DependencyMode::GreatherThan: switch (PackageVersion::compareParts(version2, version1, true)) { case PackageVersionPartComparison::Newer: return true; default:; } break; case DependencyMode::LessEqual: switch (PackageVersion::compareParts(version2, version1, true)) { case PackageVersionPartComparison::Equal: case PackageVersionPartComparison::Older: return true; default:; } break; case DependencyMode::LessThan: switch (PackageVersion::compareParts(version2, version1, true)) { case PackageVersionPartComparison::Older: return true; default:; } break; } return false; } ostream &operator<<(ostream &o, const DependencyMode &mode) { switch (mode) { case DependencyMode::Any: break; case DependencyMode::Equal: o << '='; break; case DependencyMode::GreatherEqual: o << '>' << '='; break; case DependencyMode::GreatherThan: o << '>'; break; case DependencyMode::LessEqual: o << '<' << '='; break; case DependencyMode::LessThan: o << '<'; break; } return o; } ostream &operator<<(ostream &o, const Dependency &dependency) { o << dependency.name; if (dependency.mode != DependencyMode::Any) { o << dependency.mode << dependency.version; } if (!dependency.description.empty()) { o << ':' << ' ' << dependency.description; } return o; } ostream &operator<<(ostream &o, const std::vector &dependencies) { for (const auto &dependency : dependencies) { o << dependency << '\n'; } return o; } bool Package::providesDependency(const Dependency &dependency) const { if (name == dependency.name) { if (Dependency::matches(dependency.mode, dependency.version, version)) { return true; } } for (const Dependency &provide : provides) { if (provide.matches(dependency)) { return true; } } return false; } void Package::exportProvides( const std::shared_ptr &package, DependencySet &destinationProvides, std::unordered_set &destinationLibProvides) { destinationProvides.add(LibPkg::Dependency(package->name, package->version), package); for (const auto &provide : package->provides) { destinationProvides.add(provide, package); } destinationLibProvides.insert(package->libprovides.begin(), package->libprovides.end()); } ostream &operator<<(ostream &o, const PackageVersionComparison &res) { switch (res) { case PackageVersionComparison::Equal: o << "equal"; break; case PackageVersionComparison::SoftwareUpgrade: o << "software upgrade"; break; case PackageVersionComparison::PackageUpgradeOnly: o << "package upgrade"; break; case PackageVersionComparison::NewerThanSyncVersion: o << "newer than sync version"; break; } return o; } ostream &operator<<(ostream &o, const PackageVersionPartComparison &res) { switch (res) { case PackageVersionPartComparison::Equal: o << "equal"; break; case PackageVersionPartComparison::Newer: o << "newer"; break; case PackageVersionPartComparison::Older: o << "older"; break; } return o; } PackageVersionComparison PackageBase::compareVersion(const PackageBase &other) const { return PackageVersion::fromString(version).compare(PackageVersion::fromString(other.version)); } /*! * \brief Compares the specified version segments which are supposed to only contain alphanumeric characters. */ PackageVersionPartComparison compareSegments(const char *segment1, const char *segment1End, const char *segment2, const char *segment2End) { // trim leading zeros for (; segment1 != segment1End && *segment1 == '0'; ++segment1) ; for (; segment2 != segment2End && *segment2 == '0'; ++segment2) ; // calculate segment sizes const auto segment1Size = segment1End - segment1; const auto segment2Size = segment2End - segment2; if (segment1Size > segment2Size) { // segment 1 has more digits and hence is newer return PackageVersionPartComparison::Newer; } else if (segment2Size > segment1Size) { // segment 2 has more digits and hence is newer return PackageVersionPartComparison::Older; } else { for (; segment1 != segment1End; ++segment1, ++segment2) { if (*segment1 > *segment2) { // segment 1 digit has higher value and hence is newer return PackageVersionPartComparison::Newer; } else if (*segment2 > *segment1) { // segment 2 digit has higher value and hence is newer return PackageVersionPartComparison::Older; } } } return PackageVersionPartComparison::Equal; } /*! * \brief Compares the specified version parts (which are either epoch, pkgver or pkgrel). * \returns Returns whether \a part1 is newer or older than \a part2 or whether both are equal. * \remarks \a part1 and \a part2 are considered null-terminated. So processing them is stopped * when the first null-byte occurs even if std::string::size() denotes that string is longer. */ PackageVersionPartComparison PackageVersion::compareParts(const string &part1, const string &part2, bool allowImplicitPkgRel) { const char *part1Pos = part1.data(), *part2Pos = part2.data(); const char *part1End = part1.data() + part1.size(), *part2End = part2.data() + part2.size(); for (int i = 0;; ++i) { // determine current segments part1End = firstNonAlphanumericCharacter(part1Pos, part1End); part2End = firstNonAlphanumericCharacter(part2Pos, part2End); // check whether one of the segments is the epoch if (!i) { const auto part1IsEpoch = *part1End == ':'; const auto part2IsEpoch = *part2End == ':'; // if one one has an epoch, consider the one with epoch newer if (part1IsEpoch && !part2IsEpoch) { return PackageVersionPartComparison::Newer; } else if (part2IsEpoch && !part1IsEpoch) { return PackageVersionPartComparison::Older; } } // compare segments const auto segmentComparsion = compareSegments(part1Pos, part1End, part2Pos, part2End); if (segmentComparsion != PackageVersionPartComparison::Equal) { return segmentComparsion; } // the segments are equal, check next segment if (*part1End && *part2End) { // there is another segment in both parts part1Pos = part1End + 1; part2Pos = part2End + 1; } else if (*part1End) { // only part 1 has another segment -> it is more specific and hence considered newer if (allowImplicitPkgRel && *part1End == '-') { // check whether the only further segment in part 1 is pkgrel (starts with '-' and only contains alphanumeric chars and '.') for (part1Pos = part1End + 1; part1Pos != part1End; ++part1Pos) { const char c = *part1Pos; if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '.'))) { break; } } if (!*part1Pos) { // consider both parts equal if part 2 doesn't have explicit pkgrel and part 1 does return PackageVersionPartComparison::Equal; } } return PackageVersionPartComparison::Newer; } else if (*part2End) { // only part 2 has another segment -> it is more specific and hence considered newer return PackageVersionPartComparison::Older; } else { // none of the parts has another segment -> parts are equal return PackageVersionPartComparison::Equal; } } } string PackageVersion::trimPackageVersion(const string &versionString) { const auto lastDash = versionString.rfind('-'); if (lastDash == string::npos) { return versionString; } return versionString.substr(0, lastDash); } /*! * \brief Compares the current instance with another version. * \returns Returns whether the current instance is newer or older than \a other or whether both are equal. */ PackageVersionComparison PackageVersion::compare(const PackageVersion &other) const { // check whether epoch differs if (!epoch.empty() || !other.epoch.empty()) { switch (compareParts(other.epoch, epoch)) { case PackageVersionPartComparison::Newer: return PackageVersionComparison::SoftwareUpgrade; case PackageVersionPartComparison::Older: return PackageVersionComparison::NewerThanSyncVersion; case PackageVersionPartComparison::Equal:; } } // check whether upstream version differs switch (compareParts(other.upstream, upstream)) { case PackageVersionPartComparison::Newer: return PackageVersionComparison::SoftwareUpgrade; case PackageVersionPartComparison::Older: return PackageVersionComparison::NewerThanSyncVersion; case PackageVersionPartComparison::Equal:; } // check whether package version differs if (!package.empty() && !other.package.empty()) { // only consider package release if both versions specify it (otherwise consider packages equal) switch (compareParts(other.package, package)) { case PackageVersionPartComparison::Newer: return PackageVersionComparison::PackageUpgradeOnly; case PackageVersionPartComparison::Older: return PackageVersionComparison::NewerThanSyncVersion; case PackageVersionPartComparison::Equal:; } } // no difference -> equal return PackageVersionComparison::Equal; } std::string PackageVersion::toString() const { return epoch.empty() ? upstream % '-' + package : epoch % ':' % upstream % '-' + package; } /*! * \brief Returns the file of the binary package file. * \remarks This value is computed from name, version and arch if unknown. * \param extension Specifies the extension to be used when computing the value. */ string LibPkg::Package::computeFileName(const char *extension) const { if (packageInfo && !packageInfo->fileName.empty()) { return packageInfo->fileName; } return argsToString(name, '-', version, '-', arch, '.', extension); } std::string_view LibPkg::PackageBase::computeRegularPackageName() const { if (name == "mingw-w64-headers" || name == "mingw-w64-crt") { return std::string_view(); } static constexpr std::string_view prefixes[] = { "lib32-", "mingw-w64-", "android-aarch64-", "android-x86-64-", "android-x86-", "android-armv7a-eabi-", "static-compat-", }; for (const auto prefix : prefixes) { if (name.starts_with(prefix)) { return std::string_view(name).substr(prefix.size()); } } static constexpr std::string_view suffixes[] = { "-static-compat" }; for (const auto suffix : suffixes) { if (name.ends_with(suffix)) { return std::string_view(name.data(), name.size() - suffix.size()); } } return std::string_view(); } void PackageBase::clear() { origin = PackageOrigin::Default; timestamp = buildDate = DateTime(); name.clear(); version.clear(); arch.clear(); archs.clear(); description.clear(); } bool Package::addDepsAndProvidesFromOtherPackage(const Package &otherPackage, bool force) { if (&otherPackage == this) { return false; } // skip if otherPackage does not match the current instance (only version and build date are considered) or if otherPackage has no info from package contents if (!force && ((otherPackage.origin != PackageOrigin::PackageContents && otherPackage.origin != PackageOrigin::CustomSource) || version != otherPackage.version || !(!packageInfo || packageInfo->buildDate.isNull() || (otherPackage.packageInfo && packageInfo->buildDate == otherPackage.packageInfo->buildDate)))) { return false; } // add package info from other package if this package has none at all if (!packageInfo && otherPackage.packageInfo) { packageInfo = std::make_optional(*(otherPackage.packageInfo)); } // add source info from other package; at least add missing make and check dependencies if (!sourceInfo) { sourceInfo = otherPackage.sourceInfo; } else if (otherPackage.sourceInfo) { if (sourceInfo->checkDependencies.empty()) { sourceInfo->checkDependencies = otherPackage.sourceInfo->checkDependencies; } if (sourceInfo->makeDependencies.empty()) { sourceInfo->makeDependencies = otherPackage.sourceInfo->makeDependencies; } } // retain libdepends and libprovides added in Package::addDepsAndProvidesFromContents() libdepends = otherPackage.libdepends; libprovides = otherPackage.libprovides; // replace python/perl dependencies with ones added in Package::addDepsAndProvidesFromContents() const static std::unordered_set relevantDeps{ "python", "python2", "perl" }; std::unordered_multimap foundDeps; for (const auto &dependency : otherPackage.dependencies) { const auto relevantDep = relevantDeps.find(dependency.name); if (relevantDep != relevantDeps.end()) { foundDeps.insert(std::make_pair(*relevantDep, &dependency)); } } dependencies.erase(std::remove_if(dependencies.begin(), dependencies.end(), [&foundDeps](const Dependency &dependency) { return foundDeps.find(dependency.name) != foundDeps.end(); }), dependencies.end()); const auto requiredCapacity = dependencies.size() + foundDeps.size(); if (dependencies.capacity() < requiredCapacity) { dependencies.reserve(requiredCapacity); } for (const auto &[dependencyName, dependency] : foundDeps) { dependencies.emplace_back(*dependency); } // consider this package as good as if Package::addDepsAndProvidesFromContents() was called origin = PackageOrigin::PackageContents; if (otherPackage.timestamp > timestamp) { timestamp = otherPackage.timestamp; } return true; } bool Package::isArchAny() const { const auto &a = archs.empty() && sourceInfo ? sourceInfo->archs : archs; if (a.empty()) { return false; } for (const auto &arch : a) { if (arch != "any") { return false; } } return true; } DependencySetBase::iterator DependencySet::find(const Dependency &dependency) { for (auto range = equal_range(dependency.name); range.first != range.second; ++range.first) { if (Dependency::matches(dependency.mode, dependency.version, range.first->second.version)) { return range.first; } } return end(); } // FIXME: maybe remove again DependencySetBase::iterator DependencySet::find(const std::string &dependencyName, const DependencyDetail &dependencyDetail) { for (auto range = equal_range(dependencyName); range.first != range.second; ++range.first) { if (Dependency::matches(dependencyDetail.mode, dependencyDetail.version, range.first->second.version)) { return range.first; } } return end(); } DependencySetBase::iterator DependencySet::findExact(const Dependency &dependency) { for (auto range = equal_range(dependency.name); range.first != range.second; ++range.first) { if (dependency.version == range.first->second.version) { return range.first; } } return end(); } // FIXME: maybe remove again DependencySetBase::iterator DependencySet::findExact(const std::string &dependencyName, const DependencyDetail &dependencyDetail) { for (auto range = equal_range(dependencyName); range.first != range.second; ++range.first) { if (dependencyDetail.version == range.first->second.version) { return range.first; } } return end(); } bool DependencySet::provides(const Dependency &dependency) const { for (auto range = equal_range(dependency.name); range.first != range.second; ++range.first) { if (Dependency::matches(dependency.mode, dependency.version, range.first->second.version)) { return true; } } return false; } bool DependencySet::provides(const string &dependencyName, const DependencyDetail &dependencyDetail) const { for (auto range = equal_range(dependencyName); range.first != range.second; ++range.first) { if (Dependency::matches(dependencyDetail.mode, dependencyDetail.version, range.first->second.version)) { return true; } } return false; } DependencySetBase::iterator DependencySet::add(const Dependency &dependency, const std::shared_ptr &relevantPackage) { auto iterator = findExact(dependency); if (iterator == end()) { iterator = insert(std::make_pair(dependency.name, DependencyDetail(dependency.version, dependency.mode, { relevantPackage }))); } else { iterator->second.relevantPackages.emplace(relevantPackage); } return iterator; } // FIXME: maybe remove again DependencySetBase::iterator DependencySet::add( const string &dependencyName, const DependencyDetail &dependencyDetail, const std::shared_ptr &relevantPackage) { auto iterator = findExact(dependencyName, dependencyDetail); if (iterator == end()) { iterator = insert(std::make_pair(dependencyName, DependencyDetail{ dependencyDetail.version, dependencyDetail.mode, { relevantPackage } })); } else { iterator->second.relevantPackages.emplace(relevantPackage); } return iterator; } DependencySetBase::iterator DependencySet::add(string &&dependencyName, DependencyDetail &&dependencyDetail) { auto iterator = find(dependencyName, dependencyDetail); if (iterator == end() || iterator->second.version != dependencyDetail.version) { iterator = insert(std::pair(std::move(dependencyName), std::move(dependencyDetail))); } return iterator; } void DependencySet::remove(const Dependency &dependency, const std::shared_ptr &relevantPackage) { for (auto range = equal_range(dependency.name); range.first != range.second;) { auto &relevantPackages = range.first->second.relevantPackages; relevantPackages.erase(relevantPackage); if (relevantPackages.empty()) { erase(range.first); range = equal_range(dependency.name); } else { ++range.first; } } } void DependencySet::remove(const string &name) { for (auto range = equal_range(name); range.first != range.second;) { erase(range.first); range = equal_range(name); } } } // namespace LibPkg namespace ReflectiveRapidJSON { namespace JsonReflector { template > * = nullptr> static void internalPush( const PackageSpecType &reflectable, ::RAPIDJSON_NAMESPACE::Value::Object &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator) { // just serialize the package (and ignore the ID) if (reflectable.pkg) { push(*reflectable.pkg, value, allocator); } } template > * = nullptr> static void internalPull(PackageSpecType &reflectable, const ::RAPIDJSON_NAMESPACE::GenericValue<::RAPIDJSON_NAMESPACE::UTF8>::ConstObject &value, JsonDeserializationErrors *errors) { // find member if (const auto pkg = value.FindMember("pkg"); pkg != value.MemberEnd()) { reflectable.pkg = std::make_shared(); pull(*reflectable.pkg, pkg->value, errors); if (const auto id = value.FindMember("id"); id != value.MemberEnd()) { pull(reflectable.id, id->value, errors); } } else { reflectable.pkg = std::make_shared(); pull(*reflectable.pkg, value, errors); } } template <> LIBPKG_EXPORT void push( const LibPkg::PackageSpec &reflectable, ::RAPIDJSON_NAMESPACE::Value::Object &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator) { internalPush(reflectable, value, allocator); } template <> LIBPKG_EXPORT void push>(const LibPkg::GenericPackageSpec &reflectable, ::RAPIDJSON_NAMESPACE::Value::Object &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator) { internalPush(reflectable, value, allocator); } template <> LIBPKG_EXPORT void pull(LibPkg::PackageSpec &reflectable, const ::RAPIDJSON_NAMESPACE::GenericValue<::RAPIDJSON_NAMESPACE::UTF8>::ConstObject &value, JsonDeserializationErrors *errors) { internalPull(reflectable, value, errors); } template <> LIBPKG_EXPORT void pull>(LibPkg::GenericPackageSpec &reflectable, const ::RAPIDJSON_NAMESPACE::GenericValue<::RAPIDJSON_NAMESPACE::UTF8>::ConstObject &value, JsonDeserializationErrors *errors) { internalPull(reflectable, value, errors); } } // namespace JsonReflector namespace BinaryReflector { template > * = nullptr> BinaryVersion internalReadCustomType(BinaryDeserializer &deserializer, PackageSpecType &reflectable, BinaryVersion version) { // read version using V = Versioning<::ReflectiveRapidJSON::BinarySerializable>; if constexpr (V::enabled) { V::assertVersion(version = deserializer.readVariableLengthUIntBE(), "LibPkg::GenericPackageSpec"); } // read members deserializer.read(reflectable.id, version); deserializer.read(reflectable.pkg, version); return version; } template > * = nullptr> void internalWriteCustomType(BinarySerializer &serializer, const PackageSpecType &reflectable, BinaryVersion version) { // write version using V = Versioning<::ReflectiveRapidJSON::BinarySerializable>; if constexpr (V::enabled) { serializer.writeVariableLengthUIntBE(V::applyDefault(version)); } // write members serializer.write(reflectable.id, version); serializer.write(reflectable.pkg, version); } template <> LIBPKG_EXPORT BinaryVersion readCustomType( BinaryDeserializer &deserializer, LibPkg::PackageSpec &reflectable, BinaryVersion version) { return internalReadCustomType(deserializer, reflectable, version); } template <> LIBPKG_EXPORT BinaryVersion readCustomType>( BinaryDeserializer &deserializer, LibPkg::GenericPackageSpec &reflectable, BinaryVersion version) { return internalReadCustomType(deserializer, reflectable, version); } template <> LIBPKG_EXPORT void writeCustomType(BinarySerializer &serializer, const LibPkg::PackageSpec &reflectable, BinaryVersion version) { internalWriteCustomType(serializer, reflectable, version); } template <> LIBPKG_EXPORT void writeCustomType>( BinarySerializer &serializer, const LibPkg::GenericPackageSpec &reflectable, BinaryVersion version) { internalWriteCustomType(serializer, reflectable, version); } } // namespace BinaryReflector } // namespace ReflectiveRapidJSON