3131import glob
3232import http .client
3333import os
34+ import packaging .version
3435import pprint
3536import random
3637import re
@@ -214,7 +215,7 @@ def readCMakePackagingInformation() -> dict[str,Any]:
214215 cmakeFile : TextIO = open (packageInfo ['cmake_lists_name' ], 'r' , encoding = 'utf-8' )
215216 cmakeFileContents : Final [list [str ]] = cmakeFile .readlines ()
216217 for line in cmakeFileContents :
217- match = re_cmakefile_major .match (line )
218+ match : re . Match [ str ] | None = re_cmakefile_major .match (line )
218219 if match is not None :
219220 packageInfo ['cmake_version_major' ] = int (match .group (3 ))
220221 else :
@@ -267,14 +268,14 @@ def readAutoConfPackagingInformation() -> dict[str,Any]:
267268 break
268269 if os .path .isfile (autoconfConfigName ):
269270 re_autoconffile_version : Final [re .Pattern [str ]] = \
270- re .compile (r'^AC_INIT\([ \t]*\[(.*)\][ \t]*,[ \t]*\[(\d).(\d).(\d+)([~+][a-zA-Z0-9\.+]+|)\][ \t]*,[ \t]*\[(.*)\][ \t]*\)' )
271+ re .compile (r'^AC_INIT\([ \t]*\[(.*)\][ \t]*,[ \t]*\[(\d+ ).(\d+ ).(\d+)([~+][a-zA-Z0-9\.+]+|)\][ \t]*,[ \t]*\[(.*)\][ \t]*\)' )
271272
272273 packageInfo ['autoconf_config_name' ] = autoconfConfigName
273274 try :
274275 autoconfFile : TextIO = open (packageInfo ['autoconf_config_name' ], 'r' , encoding = 'utf-8' )
275276 autoconfFileContents : Final [list [str ]] = autoconfFile .readlines ()
276277 for line in autoconfFileContents :
277- match = re_autoconffile_version .match (line )
278+ match : re . Match [ str ] | None = re_autoconffile_version .match (line )
278279 if match is not None :
279280 packageInfo ['autoconf_package_name' ] = match .group (1 )
280281 packageInfo ['autoconf_version_major' ] = int (match .group (2 ))
@@ -296,6 +297,7 @@ def readAutoConfPackagingInformation() -> dict[str,Any]:
296297 if ( ( not 'autoconf_package_name' in packageInfo ) or
297298 ( not 'autoconf_version_string' in packageInfo ) ):
298299 sys .stderr .write ('ERROR: Cannot find required package versioning details in ' + packageInfo ['autoconf_config_name' ] + '!\n ' )
300+ print (packageInfo )
299301 sys .exit (1 )
300302
301303 return packageInfo
@@ -335,7 +337,7 @@ def readDebianPackagingInformation() -> dict[str,Any]:
335337 for line in debianChangeLogFileContents :
336338 n = n + 1
337339 if n == 1 :
338- match = re_debian_version .match (line )
340+ match : re . Match [ str ] | None = re_debian_version .match (line )
339341 if match is not None :
340342 packageInfo ['debian_package_name' ] = match .group (1 )
341343 packageInfo ['debian_version_prefix' ] = match .group (2 )
@@ -893,116 +895,99 @@ def fetchDebianChangelogAndControl(packageInfo : dict[str,Any],
893895 if not hasPackagingFor (packageInfo , 'debian' ):
894896 sys .stderr .write ('ERROR: Cannot find required Debian packaging information!\n ' )
895897 sys .exit (1 )
898+ packageName = packageInfo ['debian_package_name' ]
896899
900+ # ====== Set download directory URL ======================================
897901 assert DebianCodenames is not None
902+ downloadDirectoryURL : str
898903 if codename in DebianCodenames :
899- # Debian
900- statusPageURL : str = 'https://packages.debian.org/source/' + codename + '/' + packageInfo ['debian_package_name' ]
904+ # Debian:
905+ downloadDirectoryURL = \
906+ 'https://ftp.debian.org/debian/pool/main/' + \
907+ packageName [0 :1 ] + '/' + packageName + '/'
901908 else :
902- # Ubuntu
903- statusPageURL = 'https://packages.ubuntu.com/source/' + codename + '/' + packageInfo ['debian_package_name' ]
904-
905- debianNoSuchPackage : bool = False
906- debianVersion : str | None = None
907- debianLocation : str | None = None
908- debianArchive : str | None = None
909- debianArchiveFormat : str | None = None
910-
911- # ====== Get package status =================================================
912- # NOTE: https://packages.debian.org is well-known for being quite unreliable:
913- # HTTP 503 "No healthy backends"; see also
914- # https://www.reddit.com/r/debian/comments/1e0foqv/is_packagesdebianorg_inaccessible/
915- # => Work-around: Retry with random waiting time:
916- maxTrials : Final [int ] = 50
917- avgWaitingTime : Final [int ] = 15
918-
919- re_no_such_package : Final [re .Pattern [str ]] = \
920- re .compile (r'^.*<p>No such package.</p>.*$' )
921- re_debian_package : Final [re .Pattern [str ]] = \
922- re .compile (r'^.*Source Package: ' + packageInfo ['debian_package_name' ] + r' \(([0-9-+~\.a-z]+)\)' )
923- re_debian_archive : Final [re .Pattern [str ]] = \
924- re .compile (r'^.*href="((http|https)://[a-zA-Z0-9\./+-]+/' + \
925- packageInfo ['debian_package_name' ][0 :1 ] + '/' + \
926- packageInfo ['debian_package_name' ] + '/' + \
927- r')(' + packageInfo ['debian_package_name' ] + r'_[0-9-+~\.a-z]+\.debian\.tar\.[a-zA-Z]+)"' )
909+ # Ubuntu:
910+ downloadDirectoryURL = \
911+ 'https://archive.ubuntu.com/ubuntu/pool/universe/' + \
912+ packageName [0 :1 ] + '/' + packageName + '/'
913+
914+ # ====== Get package status ==============================================
915+ maxTrials : Final [int ] = 50
916+ avgWaitingTime : Final [int ] = 15
917+ re_debian_tarball : Final [re .Pattern [str ]] = \
918+ re .compile (r'(<tr>.*)(<a href=")(' + packageName + r')(_)([0-9+~\.a-z]+)(-)([0-9+~\.a-z]+)(\.debian\.tar\.[a-zA-Z0-9]+)(")' )
928919 httpHeders : Final [dict [str ,str ]] = {
929- 'User-Agent' : 'Build-Tool/0.2.0 ' ,
930- 'Accept' : '*/*'
931- }
920+ 'User-Agent' : 'Build-Tool/0.4.0 (AmigaOS; MC680x0) ' ,
921+ 'Accept' : '*/*'
922+ }
932923
933924 for trial in range (0 , maxTrials ):
934925 if trial > 0 :
935926 sys .stderr .write ('\n ' )
936- sys .stderr .write ('Looking for package status on ' + statusPageURL +
927+ sys .stderr .write ('Looking for package status on ' + downloadDirectoryURL +
937928 ' (trial ' + str (trial + 1 ) + '/' + str (maxTrials ) + ') ... ' )
938929 sys .stderr .flush ()
939930
931+ versions = [ ]
940932 try :
941- statusPageRequest : urllib .request .Request = urllib .request .Request (statusPageURL ,
933+ statusPageRequest : urllib .request .Request = urllib .request .Request (downloadDirectoryURL ,
942934 headers = httpHeders )
943935 statusPage : http .client .HTTPResponse = urllib .request .urlopen (statusPageRequest )
944936
945937 for statusPageLine in statusPage :
946- line = statusPageLine .decode ('utf-8' )
947-
948- match = re_no_such_package .match (line )
938+ line : str = statusPageLine .decode ('utf-8' )
939+ match : re .Match [str ] | None = re_debian_tarball .match (line )
949940 if match is not None :
950- debianNoSuchPackage = True
951- else :
952- match = re_debian_package .match (line )
953- if match is not None :
954- debianVersion = match .group (1 )
955- else :
956- match = re_debian_archive .match (line )
957- if match is not None :
958- debianLocation = match .group (1 )
959- debianArchive = match .group (3 )
941+ # Break the version into Python-compatible segments:
942+ version = ( packaging .version .parse (match .group (5 ).replace ('~' , '+' )),
943+ packaging .version .parse (re .sub (r'(\d)([a-zA-Z])' , r'\1+\2' , match .group (7 ))),
944+ match .group (5 ) + match .group (6 ) + match .group (7 ) + match .group (8 ) )
945+ versions .append (version )
960946
961947 statusPage .close ()
962-
963- # A status page has been downloaded successfully -> Done!
964- # NOTE: The status page may say "No such package", if there is no package.
965- # There is *no* HTTP 404 in this case!
966948 break
967949
968950 except urllib .error .HTTPError as e :
969- sys .stderr .write ('not found (HTTP ' + str (e .code ) + ')' )
951+ sys .stderr .write ('not found (HTTP ' + str (e .code ) + ') ... ' )
952+ if e .code == 404 :
953+ # Not found -> done!
954+ break
970955 if trial + 1 < maxTrials :
971956 time .sleep (random .uniform (0 , 2 * avgWaitingTime ))
972957
973- if debianNoSuchPackage == True :
974- sys .stderr .write ('not in Debian!\n ' )
975- return (None , None )
976-
977- if ( (debianVersion is None ) or (debianLocation is None ) or (debianArchive is None ) ):
978- sys .stderr .write ('ERROR: Unable to determinate package status in Debian (https://packages.ubuntu.com/ may be malfunctioning)!\n ' )
979- sys .exit (1 )
980-
981- sys .stderr .write ('Version in ' + codename + ' is ' + debianVersion + '\n ' )
958+ if len (versions ) == 0 :
959+ sys .stderr .write ('not found!\n ' )
960+ return ( None , None )
982961
962+ # ====== Obtain latest version ===========================================
963+ versions = sorted (versions , reverse = True )
964+ debianVersion : Final [str ] = versions [0 ][2 ]
965+ debianArchive : Final [str ] = packageName + '_' + debianVersion
966+ archiveFileURL : Final [str ] = downloadDirectoryURL + debianArchive
967+ sys .stderr .write ('latest version is ' + debianVersion + '\n ' )
983968
984969 # ====== Determine necessary compression option =============================
985970 debianArchiveFormat = debianArchive [len (debianArchive ) - 2 : len (debianArchive )]
986971 tarCompressionOption = TarOptions [debianArchiveFormat ]
987972
988-
989973 # ====== Fetch debian archive ===============================================
990- archiveFileURL : Final [str ] = debianLocation + debianArchive
991- result : tuple [list [str ] | None ,list [str ] | None ] = (None , None )
992974 sys .stderr .write ('Looking for \" debian\" archive at ' + archiveFileURL + ' ... ' )
993975 sys .stderr .flush ()
976+
977+ result : tuple [list [str ] | None ,list [str ] | None ] = ( None , None )
994978 try :
995979 archiveFileRequest : urllib .request .Request = urllib .request .Request (archiveFileURL ,
996980 headers = httpHeders )
997981 archiveFile : http .client .HTTPResponse = urllib .request .urlopen (archiveFileRequest )
998982 debianArchiveFile : tempfile ._TemporaryFileWrapper [bytes ] = \
999983 tempfile .NamedTemporaryFile (delete = False )
1000984
1001- shutil .copyfileobj (cast ( TextIO , archiveFile ) , debianArchiveFile )
985+ shutil .copyfileobj (archiveFile , debianArchiveFile )
1002986 debianArchiveFile .close ()
1003987 archiveFile .close ()
1004988 sys .stderr .write ('found!\n ' )
1005989
990+ # ====== Extract changelog and control ================================
1006991 debianChangelog : list [str ] = [ ]
1007992 debianControl : list [str ] = [ ]
1008993 try :
@@ -1021,7 +1006,7 @@ def fetchDebianChangelogAndControl(packageInfo : dict[str,Any],
10211006 debianControl = process .stdout .readlines ()
10221007
10231008 os .unlink (debianArchiveFile .name )
1024- result = (debianChangelog , debianControl )
1009+ result = ( debianChangelog , debianControl )
10251010
10261011 except Exception as e :
10271012 sys .stderr .write ('ERROR: Failed to extract debian/changelog from ' + debianArchiveFile .name + ': ' + str (e ) + '\n ' )
0 commit comments