2018-06-19 14:11:46 +04:00
import collections
2019-01-17 13:25:22 +04:00
import logging
2018-06-22 14:40:38 +04:00
import os
2019-01-17 13:25:22 +04:00
import subprocess
2018-06-19 14:11:46 +04:00
try :
import rpm
except ImportError :
rpm = None
2020-09-18 13:43:54 +04:00
from port_stats import rpm_ffi
2018-06-19 14:11:46 +04:00
LOG = logging . getLogger ( __name__ )
2023-08-02 17:05:46 +04:00
def _as_str ( item ) :
if isinstance ( item , str ) :
return item
if isinstance ( item , bytes ) :
return item . decode ( ' utf-8 ' , errors = ' replace ' )
return str ( item )
2018-06-19 14:11:46 +04:00
class NEVR ( collections . namedtuple ( ' NEVR ' , [ ' name ' , ' epoch ' ,
' version ' , ' release ' ] ) ) :
if rpm :
@classmethod
def from_header ( cls , header ) :
2023-08-02 17:05:46 +04:00
return cls ( _as_str ( header [ rpm . RPMTAG_NAME ] ) ,
_as_str ( header [ rpm . RPMTAG_EPOCH ] ) ,
_as_str ( header [ rpm . RPMTAG_VERSION ] ) ,
_as_str ( header [ rpm . RPMTAG_RELEASE ] ) )
2018-06-19 14:11:46 +04:00
@classmethod
def from_tsv_line ( cls , line ) :
""" Creates a NEVR object from a tab-separated line.
The line should have the following format :
name \tepoch \tversion \trelease
"""
try :
2023-03-10 14:42:01 +04:00
n , e , v , r = line . split ( b ' \t ' )
2018-06-19 14:11:46 +04:00
except Exception :
LOG . error ( " Failed to parse line: %s " , line , exc_info = True )
return None
if e in ( ' ' , ' (none) ' , ' None ' ) :
e = None
else :
try :
e = int ( e )
except Exception :
LOG . error ( " Failed to parse epoch from line: %s " ,
line , exc_info = True )
return None
2023-08-02 17:05:46 +04:00
return cls ( _as_str ( n ) , e , _as_str ( v ) , _as_str ( r ) )
2018-06-19 14:11:46 +04:00
def format_evr ( self ) :
if self . epoch is None :
return ' %s - %s ' % ( self . version , self . release )
else :
2019-01-17 13:25:22 +04:00
return ' %s : %s - %s ' % ( self . epoch , self . version , self . release )
2018-06-19 14:11:46 +04:00
2018-06-26 13:03:42 +04:00
@property
def evr ( self ) :
return self [ 1 : ]
2018-06-19 14:11:46 +04:00
def format_evr ( nevr ) :
return nevr . format_evr ( ) if nevr else ' MISSING '
2018-06-20 15:53:53 +04:00
2018-08-10 18:39:27 +04:00
def read_pkglist_headers_rpm ( path ) :
2018-06-20 15:53:53 +04:00
LOG . info ( " Reading %s using python-module-rpm " , path )
2018-08-10 18:39:27 +04:00
if not rpm :
raise RuntimeError ( ' rpm module is not avalable ' )
2018-06-19 14:11:46 +04:00
if path . endswith ( ' .xz ' ) :
xz = subprocess . Popen ( [ ' xz ' , ' -dc ' , path ] , stdout = subprocess . PIPE )
input_file = xz . stdout
else :
input_file = open ( path , ' rb ' )
try :
2018-08-10 18:39:27 +04:00
return rpm . readHeaderListFromFD ( input_file . fileno ( ) )
2018-06-19 14:11:46 +04:00
finally :
input_file . close ( )
2018-08-10 18:39:27 +04:00
2021-08-12 15:19:08 +04:00
def read_pkglist_heders_for_repo ( repo_path , arches , components = None ) :
bin_headers = [ ]
src_headers = [ ]
for arch in arches :
basedir = os . path . join ( repo_path , arch , ' base ' )
for pkglist in os . listdir ( basedir ) :
parts = pkglist . split ( ' . ' , 3 )
if parts [ 0 ] not in ( ' pkglist ' , ' srclist ' ) :
continue
if components is not None and parts [ 1 ] not in components :
continue
( src_headers if parts [ 0 ] == ' srclist ' else bin_headers ) . extend (
read_pkglist_headers_rpm ( os . path . join ( basedir , pkglist ) ) )
return src_headers , bin_headers
2018-08-10 18:39:27 +04:00
def _read_pkglist_rpm ( path ) :
return ( NEVR . from_header ( h ) for h in read_pkglist_headers_rpm ( path ) )
2018-06-19 14:11:46 +04:00
2019-01-17 13:25:22 +04:00
_PKGLIST_QUERY_FORMAT = ' % {NAME} \t % {EPOCH} \t % {VERSION} \t % {RELEASE} \n '
2018-06-19 14:11:46 +04:00
def _read_pkglist_pkglist_query ( path ) :
2018-06-20 15:53:53 +04:00
LOG . info ( " Reading %s using pkglist-query " , path )
2018-06-19 14:11:46 +04:00
if path . endswith ( ' .xz ' ) :
xz = subprocess . Popen ( [ " xz " , ' -dc ' , path ] , stdout = subprocess . PIPE )
try :
query = subprocess . Popen (
[ " pkglist-query " , _PKGLIST_QUERY_FORMAT , ' - ' ] ,
stdin = xz . stdout , stdout = subprocess . PIPE )
finally :
xz . stdout . close ( ) # Allow xz to receive a SIGPIPE if p2 exits.
else :
2019-04-04 13:26:19 +04:00
query = subprocess . Popen (
[ " pkglist-query " , _PKGLIST_QUERY_FORMAT , path ] ,
stdout = subprocess . PIPE )
2021-09-08 12:43:17 +04:00
return ( NEVR . from_tsv_line ( line )
for line in query . communicate ( ) [ 0 ] . splitlines ( ) )
2018-06-19 14:11:46 +04:00
def read_pkglist ( path ) :
if rpm :
result = _read_pkglist_rpm ( path )
else :
result = _read_pkglist_pkglist_query ( path )
return [ r for r in result if r ]
2018-06-22 14:40:38 +04:00
2020-09-18 13:43:54 +04:00
def read_src_dot_list ( repo_path ) :
path = os . path . join ( repo_path , ' files/list/src.list.xz ' )
LOG . info ( " Reading src.list %s " , path )
xz = subprocess . Popen ( [ ' xz ' , ' -dc ' , path ] , stdout = subprocess . PIPE )
input_file = xz . stdout
result = [ ]
try :
for line in input_file :
try :
2023-03-10 14:42:01 +04:00
name , evr = line . split ( b ' \t ' , 2 ) [ : 2 ]
2020-09-18 13:43:54 +04:00
e , v , r = rpm_ffi . parse_evr ( evr )
2023-08-02 17:05:46 +04:00
result . append ( NEVR ( _as_str ( name ) , e , _as_str ( v ) , _as_str ( r ) ) )
2020-09-18 13:43:54 +04:00
except Exception :
LOG . warning ( ' Failed to parse line %r ' , line , exc_info = True )
finally :
input_file . close ( )
return frozenset ( result )
2018-08-13 12:56:07 +04:00
def read_srclists ( prefix , arches ) :
2018-08-10 18:39:27 +04:00
result = frozenset ( )
for arch in arches :
srclist = os . path . join ( prefix , arch , ' base ' , ' srclist.classic.xz ' )
result = result . union ( read_pkglist ( srclist ) )
2019-08-15 10:38:22 +04:00
if not result :
raise RuntimeError ( ' Empty lists at %s ' % prefix )
2018-08-10 18:39:27 +04:00
return result
2018-08-13 12:56:07 +04:00
2018-08-10 18:39:27 +04:00
def read_all_srclists ( repos ) :
2020-09-18 13:43:54 +04:00
return dict ( ( name , read_src_dot_list ( v [ ' path ' ] ) )
2018-08-10 18:39:27 +04:00
for name , v in repos . items ( ) )