1*4882a593Smuzhiyun# 2*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only 3*4882a593Smuzhiyun# 4*4882a593Smuzhiyun 5*4882a593Smuzhiyundef create_socket(url, d): 6*4882a593Smuzhiyun import urllib 7*4882a593Smuzhiyun from bb.utils import export_proxies 8*4882a593Smuzhiyun 9*4882a593Smuzhiyun export_proxies(d) 10*4882a593Smuzhiyun return urllib.request.urlopen(url) 11*4882a593Smuzhiyun 12*4882a593Smuzhiyundef get_links_from_url(url, d): 13*4882a593Smuzhiyun "Return all the href links found on the web location" 14*4882a593Smuzhiyun 15*4882a593Smuzhiyun from bs4 import BeautifulSoup, SoupStrainer 16*4882a593Smuzhiyun 17*4882a593Smuzhiyun soup = BeautifulSoup(create_socket(url,d), "html.parser", parse_only=SoupStrainer("a")) 18*4882a593Smuzhiyun hyperlinks = [] 19*4882a593Smuzhiyun for line in soup.find_all('a', href=True): 20*4882a593Smuzhiyun hyperlinks.append(line['href'].strip('/')) 21*4882a593Smuzhiyun return hyperlinks 22*4882a593Smuzhiyun 23*4882a593Smuzhiyundef find_latest_numeric_release(url, d): 24*4882a593Smuzhiyun "Find the latest listed numeric release on the given url" 25*4882a593Smuzhiyun max=0 26*4882a593Smuzhiyun maxstr="" 27*4882a593Smuzhiyun for link in get_links_from_url(url, d): 28*4882a593Smuzhiyun try: 29*4882a593Smuzhiyun # TODO use bb.utils.vercmp_string_op() 30*4882a593Smuzhiyun release = float(link) 31*4882a593Smuzhiyun except: 32*4882a593Smuzhiyun release = 0 33*4882a593Smuzhiyun if release > max: 34*4882a593Smuzhiyun max = release 35*4882a593Smuzhiyun maxstr = link 36*4882a593Smuzhiyun return maxstr 37*4882a593Smuzhiyun 38*4882a593Smuzhiyundef is_src_rpm(name): 39*4882a593Smuzhiyun "Check if the link is pointing to a src.rpm file" 40*4882a593Smuzhiyun return name.endswith(".src.rpm") 41*4882a593Smuzhiyun 42*4882a593Smuzhiyundef package_name_from_srpm(srpm): 43*4882a593Smuzhiyun "Strip out the package name from the src.rpm filename" 44*4882a593Smuzhiyun 45*4882a593Smuzhiyun # ca-certificates-2016.2.7-1.0.fc24.src.rpm 46*4882a593Smuzhiyun # ^name ^ver ^release^removed 47*4882a593Smuzhiyun (name, version, release) = srpm.replace(".src.rpm", "").rsplit("-", 2) 48*4882a593Smuzhiyun return name 49*4882a593Smuzhiyun 50*4882a593Smuzhiyundef get_source_package_list_from_url(url, section, d): 51*4882a593Smuzhiyun "Return a sectioned list of package names from a URL list" 52*4882a593Smuzhiyun 53*4882a593Smuzhiyun bb.note("Reading %s: %s" % (url, section)) 54*4882a593Smuzhiyun links = get_links_from_url(url, d) 55*4882a593Smuzhiyun srpms = filter(is_src_rpm, links) 56*4882a593Smuzhiyun names_list = map(package_name_from_srpm, srpms) 57*4882a593Smuzhiyun 58*4882a593Smuzhiyun new_pkgs = set() 59*4882a593Smuzhiyun for pkgs in names_list: 60*4882a593Smuzhiyun new_pkgs.add(pkgs + ":" + section) 61*4882a593Smuzhiyun return new_pkgs 62*4882a593Smuzhiyun 63*4882a593Smuzhiyundef get_source_package_list_from_url_by_letter(url, section, d): 64*4882a593Smuzhiyun import string 65*4882a593Smuzhiyun from urllib.error import HTTPError 66*4882a593Smuzhiyun packages = set() 67*4882a593Smuzhiyun for letter in (string.ascii_lowercase + string.digits): 68*4882a593Smuzhiyun # Not all subfolders may exist, so silently handle 404 69*4882a593Smuzhiyun try: 70*4882a593Smuzhiyun packages |= get_source_package_list_from_url(url + "/" + letter, section, d) 71*4882a593Smuzhiyun except HTTPError as e: 72*4882a593Smuzhiyun if e.code != 404: raise 73*4882a593Smuzhiyun return packages 74*4882a593Smuzhiyun 75*4882a593Smuzhiyundef get_latest_released_fedora_source_package_list(d): 76*4882a593Smuzhiyun "Returns list of all the name os packages in the latest fedora distro" 77*4882a593Smuzhiyun latest = find_latest_numeric_release("http://archive.fedoraproject.org/pub/fedora/linux/releases/", d) 78*4882a593Smuzhiyun package_names = get_source_package_list_from_url_by_letter("http://archive.fedoraproject.org/pub/fedora/linux/releases/%s/Everything/source/tree/Packages/" % latest, "main", d) 79*4882a593Smuzhiyun package_names |= get_source_package_list_from_url_by_letter("http://archive.fedoraproject.org/pub/fedora/linux/updates/%s/SRPMS/" % latest, "updates", d) 80*4882a593Smuzhiyun return latest, package_names 81*4882a593Smuzhiyun 82*4882a593Smuzhiyundef get_latest_released_opensuse_source_package_list(d): 83*4882a593Smuzhiyun "Returns list of all the name os packages in the latest opensuse distro" 84*4882a593Smuzhiyun latest = find_latest_numeric_release("http://download.opensuse.org/source/distribution/leap", d) 85*4882a593Smuzhiyun 86*4882a593Smuzhiyun package_names = get_source_package_list_from_url("http://download.opensuse.org/source/distribution/leap/%s/repo/oss/suse/src/" % latest, "main", d) 87*4882a593Smuzhiyun package_names |= get_source_package_list_from_url("http://download.opensuse.org/update/leap/%s/oss/src/" % latest, "updates", d) 88*4882a593Smuzhiyun return latest, package_names 89*4882a593Smuzhiyun 90*4882a593Smuzhiyundef get_latest_released_clear_source_package_list(d): 91*4882a593Smuzhiyun latest = find_latest_numeric_release("https://download.clearlinux.org/releases/", d) 92*4882a593Smuzhiyun package_names = get_source_package_list_from_url("https://download.clearlinux.org/releases/%s/clear/source/SRPMS/" % latest, "main", d) 93*4882a593Smuzhiyun return latest, package_names 94*4882a593Smuzhiyun 95*4882a593Smuzhiyundef find_latest_debian_release(url, d): 96*4882a593Smuzhiyun "Find the latest listed debian release on the given url" 97*4882a593Smuzhiyun 98*4882a593Smuzhiyun releases = [link.replace("Debian", "") 99*4882a593Smuzhiyun for link in get_links_from_url(url, d) 100*4882a593Smuzhiyun if link.startswith("Debian")] 101*4882a593Smuzhiyun releases.sort() 102*4882a593Smuzhiyun try: 103*4882a593Smuzhiyun return releases[-1] 104*4882a593Smuzhiyun except: 105*4882a593Smuzhiyun return "_NotFound_" 106*4882a593Smuzhiyun 107*4882a593Smuzhiyundef get_debian_style_source_package_list(url, section, d): 108*4882a593Smuzhiyun "Return the list of package-names stored in the debian style Sources.gz file" 109*4882a593Smuzhiyun import gzip 110*4882a593Smuzhiyun 111*4882a593Smuzhiyun package_names = set() 112*4882a593Smuzhiyun for line in gzip.open(create_socket(url, d), mode="rt"): 113*4882a593Smuzhiyun if line.startswith("Package:"): 114*4882a593Smuzhiyun pkg = line.split(":", 1)[1].strip() 115*4882a593Smuzhiyun package_names.add(pkg + ":" + section) 116*4882a593Smuzhiyun return package_names 117*4882a593Smuzhiyun 118*4882a593Smuzhiyundef get_latest_released_debian_source_package_list(d): 119*4882a593Smuzhiyun "Returns list of all the name of packages in the latest debian distro" 120*4882a593Smuzhiyun latest = find_latest_debian_release("http://ftp.debian.org/debian/dists/", d) 121*4882a593Smuzhiyun url = "http://ftp.debian.org/debian/dists/stable/main/source/Sources.gz" 122*4882a593Smuzhiyun package_names = get_debian_style_source_package_list(url, "main", d) 123*4882a593Smuzhiyun url = "http://ftp.debian.org/debian/dists/stable-proposed-updates/main/source/Sources.gz" 124*4882a593Smuzhiyun package_names |= get_debian_style_source_package_list(url, "updates", d) 125*4882a593Smuzhiyun return latest, package_names 126*4882a593Smuzhiyun 127*4882a593Smuzhiyundef find_latest_ubuntu_release(url, d): 128*4882a593Smuzhiyun """ 129*4882a593Smuzhiyun Find the latest listed Ubuntu release on the given ubuntu/dists/ URL. 130*4882a593Smuzhiyun 131*4882a593Smuzhiyun To avoid matching development releases look for distributions that have 132*4882a593Smuzhiyun updates, so the resulting distro could be any supported release. 133*4882a593Smuzhiyun """ 134*4882a593Smuzhiyun url += "?C=M;O=D" # Descending Sort by Last Modified 135*4882a593Smuzhiyun for link in get_links_from_url(url, d): 136*4882a593Smuzhiyun if "-updates" in link: 137*4882a593Smuzhiyun distro = link.replace("-updates", "") 138*4882a593Smuzhiyun return distro 139*4882a593Smuzhiyun return "_NotFound_" 140*4882a593Smuzhiyun 141*4882a593Smuzhiyundef get_latest_released_ubuntu_source_package_list(d): 142*4882a593Smuzhiyun "Returns list of all the name os packages in the latest ubuntu distro" 143*4882a593Smuzhiyun latest = find_latest_ubuntu_release("http://archive.ubuntu.com/ubuntu/dists/", d) 144*4882a593Smuzhiyun url = "http://archive.ubuntu.com/ubuntu/dists/%s/main/source/Sources.gz" % latest 145*4882a593Smuzhiyun package_names = get_debian_style_source_package_list(url, "main", d) 146*4882a593Smuzhiyun url = "http://archive.ubuntu.com/ubuntu/dists/%s-updates/main/source/Sources.gz" % latest 147*4882a593Smuzhiyun package_names |= get_debian_style_source_package_list(url, "updates", d) 148*4882a593Smuzhiyun return latest, package_names 149*4882a593Smuzhiyun 150*4882a593Smuzhiyundef create_distro_packages_list(distro_check_dir, d): 151*4882a593Smuzhiyun import shutil 152*4882a593Smuzhiyun 153*4882a593Smuzhiyun pkglst_dir = os.path.join(distro_check_dir, "package_lists") 154*4882a593Smuzhiyun bb.utils.remove(pkglst_dir, True) 155*4882a593Smuzhiyun bb.utils.mkdirhier(pkglst_dir) 156*4882a593Smuzhiyun 157*4882a593Smuzhiyun per_distro_functions = ( 158*4882a593Smuzhiyun ("Debian", get_latest_released_debian_source_package_list), 159*4882a593Smuzhiyun ("Ubuntu", get_latest_released_ubuntu_source_package_list), 160*4882a593Smuzhiyun ("Fedora", get_latest_released_fedora_source_package_list), 161*4882a593Smuzhiyun ("openSUSE", get_latest_released_opensuse_source_package_list), 162*4882a593Smuzhiyun ("Clear", get_latest_released_clear_source_package_list), 163*4882a593Smuzhiyun ) 164*4882a593Smuzhiyun 165*4882a593Smuzhiyun for name, fetcher_func in per_distro_functions: 166*4882a593Smuzhiyun try: 167*4882a593Smuzhiyun release, package_list = fetcher_func(d) 168*4882a593Smuzhiyun except Exception as e: 169*4882a593Smuzhiyun bb.warn("Cannot fetch packages for %s: %s" % (name, e)) 170*4882a593Smuzhiyun bb.note("Distro: %s, Latest Release: %s, # src packages: %d" % (name, release, len(package_list))) 171*4882a593Smuzhiyun if len(package_list) == 0: 172*4882a593Smuzhiyun bb.error("Didn't fetch any packages for %s %s" % (name, release)) 173*4882a593Smuzhiyun 174*4882a593Smuzhiyun package_list_file = os.path.join(pkglst_dir, name + "-" + release) 175*4882a593Smuzhiyun with open(package_list_file, 'w') as f: 176*4882a593Smuzhiyun for pkg in sorted(package_list): 177*4882a593Smuzhiyun f.write(pkg + "\n") 178*4882a593Smuzhiyun 179*4882a593Smuzhiyundef update_distro_data(distro_check_dir, datetime, d): 180*4882a593Smuzhiyun """ 181*4882a593Smuzhiyun If distro packages list data is old then rebuild it. 182*4882a593Smuzhiyun The operations has to be protected by a lock so that 183*4882a593Smuzhiyun only one thread performes it at a time. 184*4882a593Smuzhiyun """ 185*4882a593Smuzhiyun if not os.path.isdir (distro_check_dir): 186*4882a593Smuzhiyun try: 187*4882a593Smuzhiyun bb.note ("Making new directory: %s" % distro_check_dir) 188*4882a593Smuzhiyun os.makedirs (distro_check_dir) 189*4882a593Smuzhiyun except OSError: 190*4882a593Smuzhiyun raise Exception('Unable to create directory %s' % (distro_check_dir)) 191*4882a593Smuzhiyun 192*4882a593Smuzhiyun 193*4882a593Smuzhiyun datetime_file = os.path.join(distro_check_dir, "build_datetime") 194*4882a593Smuzhiyun saved_datetime = "_invalid_" 195*4882a593Smuzhiyun import fcntl 196*4882a593Smuzhiyun try: 197*4882a593Smuzhiyun if not os.path.exists(datetime_file): 198*4882a593Smuzhiyun open(datetime_file, 'w+').close() # touch the file so that the next open won't fail 199*4882a593Smuzhiyun 200*4882a593Smuzhiyun f = open(datetime_file, "r+") 201*4882a593Smuzhiyun fcntl.lockf(f, fcntl.LOCK_EX) 202*4882a593Smuzhiyun saved_datetime = f.read() 203*4882a593Smuzhiyun if saved_datetime[0:8] != datetime[0:8]: 204*4882a593Smuzhiyun bb.note("The build datetime did not match: saved:%s current:%s" % (saved_datetime, datetime)) 205*4882a593Smuzhiyun bb.note("Regenerating distro package lists") 206*4882a593Smuzhiyun create_distro_packages_list(distro_check_dir, d) 207*4882a593Smuzhiyun f.seek(0) 208*4882a593Smuzhiyun f.write(datetime) 209*4882a593Smuzhiyun 210*4882a593Smuzhiyun except OSError as e: 211*4882a593Smuzhiyun raise Exception('Unable to open timestamp: %s' % e) 212*4882a593Smuzhiyun finally: 213*4882a593Smuzhiyun fcntl.lockf(f, fcntl.LOCK_UN) 214*4882a593Smuzhiyun f.close() 215*4882a593Smuzhiyun 216*4882a593Smuzhiyundef compare_in_distro_packages_list(distro_check_dir, d): 217*4882a593Smuzhiyun if not os.path.isdir(distro_check_dir): 218*4882a593Smuzhiyun raise Exception("compare_in_distro_packages_list: invalid distro_check_dir passed") 219*4882a593Smuzhiyun 220*4882a593Smuzhiyun localdata = bb.data.createCopy(d) 221*4882a593Smuzhiyun pkglst_dir = os.path.join(distro_check_dir, "package_lists") 222*4882a593Smuzhiyun matching_distros = [] 223*4882a593Smuzhiyun pn = recipe_name = d.getVar('PN') 224*4882a593Smuzhiyun bb.note("Checking: %s" % pn) 225*4882a593Smuzhiyun 226*4882a593Smuzhiyun if pn.find("-native") != -1: 227*4882a593Smuzhiyun pnstripped = pn.split("-native") 228*4882a593Smuzhiyun localdata.setVar('OVERRIDES', "pn-" + pnstripped[0] + ":" + d.getVar('OVERRIDES')) 229*4882a593Smuzhiyun recipe_name = pnstripped[0] 230*4882a593Smuzhiyun 231*4882a593Smuzhiyun if pn.startswith("nativesdk-"): 232*4882a593Smuzhiyun pnstripped = pn.split("nativesdk-") 233*4882a593Smuzhiyun localdata.setVar('OVERRIDES', "pn-" + pnstripped[1] + ":" + d.getVar('OVERRIDES')) 234*4882a593Smuzhiyun recipe_name = pnstripped[1] 235*4882a593Smuzhiyun 236*4882a593Smuzhiyun if pn.find("-cross") != -1: 237*4882a593Smuzhiyun pnstripped = pn.split("-cross") 238*4882a593Smuzhiyun localdata.setVar('OVERRIDES', "pn-" + pnstripped[0] + ":" + d.getVar('OVERRIDES')) 239*4882a593Smuzhiyun recipe_name = pnstripped[0] 240*4882a593Smuzhiyun 241*4882a593Smuzhiyun if pn.find("-initial") != -1: 242*4882a593Smuzhiyun pnstripped = pn.split("-initial") 243*4882a593Smuzhiyun localdata.setVar('OVERRIDES', "pn-" + pnstripped[0] + ":" + d.getVar('OVERRIDES')) 244*4882a593Smuzhiyun recipe_name = pnstripped[0] 245*4882a593Smuzhiyun 246*4882a593Smuzhiyun bb.note("Recipe: %s" % recipe_name) 247*4882a593Smuzhiyun 248*4882a593Smuzhiyun distro_exceptions = dict({"OE-Core":'OE-Core', "OpenedHand":'OpenedHand', "Intel":'Intel', "Upstream":'Upstream', "Windriver":'Windriver', "OSPDT":'OSPDT Approved', "Poky":'poky'}) 249*4882a593Smuzhiyun tmp = localdata.getVar('DISTRO_PN_ALIAS') or "" 250*4882a593Smuzhiyun for str in tmp.split(): 251*4882a593Smuzhiyun if str and str.find("=") == -1 and distro_exceptions[str]: 252*4882a593Smuzhiyun matching_distros.append(str) 253*4882a593Smuzhiyun 254*4882a593Smuzhiyun distro_pn_aliases = {} 255*4882a593Smuzhiyun for str in tmp.split(): 256*4882a593Smuzhiyun if "=" in str: 257*4882a593Smuzhiyun (dist, pn_alias) = str.split('=') 258*4882a593Smuzhiyun distro_pn_aliases[dist.strip().lower()] = pn_alias.strip() 259*4882a593Smuzhiyun 260*4882a593Smuzhiyun for file in os.listdir(pkglst_dir): 261*4882a593Smuzhiyun (distro, distro_release) = file.split("-") 262*4882a593Smuzhiyun f = open(os.path.join(pkglst_dir, file), "r") 263*4882a593Smuzhiyun for line in f: 264*4882a593Smuzhiyun (pkg, section) = line.split(":") 265*4882a593Smuzhiyun if distro.lower() in distro_pn_aliases: 266*4882a593Smuzhiyun pn = distro_pn_aliases[distro.lower()] 267*4882a593Smuzhiyun else: 268*4882a593Smuzhiyun pn = recipe_name 269*4882a593Smuzhiyun if pn == pkg: 270*4882a593Smuzhiyun matching_distros.append(distro + "-" + section[:-1]) # strip the \n at the end 271*4882a593Smuzhiyun f.close() 272*4882a593Smuzhiyun break 273*4882a593Smuzhiyun f.close() 274*4882a593Smuzhiyun 275*4882a593Smuzhiyun for item in tmp.split(): 276*4882a593Smuzhiyun matching_distros.append(item) 277*4882a593Smuzhiyun bb.note("Matching: %s" % matching_distros) 278*4882a593Smuzhiyun return matching_distros 279*4882a593Smuzhiyun 280*4882a593Smuzhiyundef create_log_file(d, logname): 281*4882a593Smuzhiyun logpath = d.getVar('LOG_DIR') 282*4882a593Smuzhiyun bb.utils.mkdirhier(logpath) 283*4882a593Smuzhiyun logfn, logsuffix = os.path.splitext(logname) 284*4882a593Smuzhiyun logfile = os.path.join(logpath, "%s.%s%s" % (logfn, d.getVar('DATETIME'), logsuffix)) 285*4882a593Smuzhiyun if not os.path.exists(logfile): 286*4882a593Smuzhiyun slogfile = os.path.join(logpath, logname) 287*4882a593Smuzhiyun if os.path.exists(slogfile): 288*4882a593Smuzhiyun os.remove(slogfile) 289*4882a593Smuzhiyun open(logfile, 'w+').close() 290*4882a593Smuzhiyun os.symlink(logfile, slogfile) 291*4882a593Smuzhiyun d.setVar('LOG_FILE', logfile) 292*4882a593Smuzhiyun return logfile 293*4882a593Smuzhiyun 294*4882a593Smuzhiyun 295*4882a593Smuzhiyundef save_distro_check_result(result, datetime, result_file, d): 296*4882a593Smuzhiyun pn = d.getVar('PN') 297*4882a593Smuzhiyun logdir = d.getVar('LOG_DIR') 298*4882a593Smuzhiyun if not logdir: 299*4882a593Smuzhiyun bb.error("LOG_DIR variable is not defined, can't write the distro_check results") 300*4882a593Smuzhiyun return 301*4882a593Smuzhiyun bb.utils.mkdirhier(logdir) 302*4882a593Smuzhiyun 303*4882a593Smuzhiyun line = pn 304*4882a593Smuzhiyun for i in result: 305*4882a593Smuzhiyun line = line + "," + i 306*4882a593Smuzhiyun f = open(result_file, "a") 307*4882a593Smuzhiyun import fcntl 308*4882a593Smuzhiyun fcntl.lockf(f, fcntl.LOCK_EX) 309*4882a593Smuzhiyun f.seek(0, os.SEEK_END) # seek to the end of file 310*4882a593Smuzhiyun f.write(line + "\n") 311*4882a593Smuzhiyun fcntl.lockf(f, fcntl.LOCK_UN) 312*4882a593Smuzhiyun f.close() 313