2021-01-25 00:24:31 +01:00
# include "./package.h"
# include "../parser/utils.h"
# include "reflection/package.h"
# include <c++utilities/conversion/stringbuilder.h>
# include <ostream>
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 < Dependency > & 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 > & package , DependencySet & destinationProvides , std : : unordered_set < std : : string > & 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 Package : : compareVersion ( const Package & 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 ( ; ; ) {
// determine current segments
part1End = firstNonAlphanumericCharacter ( part1Pos , part1End ) ;
part2End = firstNonAlphanumericCharacter ( part2Pos , part2End ) ;
// 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
2021-02-08 22:12:31 +01:00
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 ;
2021-01-25 00:24:31 +01:00
}
}
2021-02-08 22:12:31 +01:00
if ( ! * part1Pos ) {
// consider both parts equal if part 2 doesn't have explicit pkgrel and part 1 does
return PackageVersionPartComparison : : Equal ;
}
2021-01-25 00:24:31 +01:00
}
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 ) {
return string ( ) ;
}
if ( ! packageInfo - > fileName . empty ( ) ) {
return packageInfo - > fileName ;
}
return argsToString ( name , ' - ' , version , ' - ' , packageInfo - > arch , ' . ' , extension ) ;
}
string LibPkg : : Package : : computeRegularPackageName ( ) const
{
if ( name = = " mingw-w64-headers " | | name = = " mingw-w64-crt " ) {
return string ( ) ;
}
if ( startsWith ( name , " lib32- " ) ) {
return name . substr ( strlen ( " lib32- " ) ) ;
} else if ( startsWith ( name , " mingw-w64- " ) ) {
return name . substr ( strlen ( " mingw-w64- " ) ) ;
} else if ( startsWith ( name , " android-aarch64- " ) ) {
return name . substr ( strlen ( " android-aarch64- " ) ) ;
} else if ( startsWith ( name , " android-x86-64- " ) ) {
return name . substr ( strlen ( " android-x86-64- " ) ) ;
} else if ( startsWith ( name , " android-x86- " ) ) {
return name . substr ( strlen ( " android-x86- " ) ) ;
} else if ( startsWith ( name , " android-armv7a-eabi- " ) ) {
return name . substr ( strlen ( " android-armv7a-eabi- " ) ) ;
}
return string ( ) ;
}
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 = make_unique < PackageInfo > ( * ( 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 < std : : string_view > relevantDeps { " python " , " python2 " , " perl " } ;
std : : unordered_multimap < std : : string_view , const Dependency * > 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 ;
}
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 < Package > & relevantPackage )
{
auto iterator = findExact ( dependency ) ;
if ( iterator = = end ( ) ) {
iterator = insert ( 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 < Package > & relevantPackage )
{
auto iterator = findExact ( dependencyName , dependencyDetail ) ;
if ( iterator = = end ( ) ) {
iterator = insert ( 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 < Package > & 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