diff options
| -rw-r--r-- | meta-oe/classes/check-version-mismatch.bbclass | 399 | ||||
| -rw-r--r-- | meta-oe/conf/version-check.conf | 22 |
2 files changed, 421 insertions, 0 deletions
diff --git a/meta-oe/classes/check-version-mismatch.bbclass b/meta-oe/classes/check-version-mismatch.bbclass new file mode 100644 index 0000000000..7b46151b03 --- /dev/null +++ b/meta-oe/classes/check-version-mismatch.bbclass | |||
| @@ -0,0 +1,399 @@ | |||
| 1 | inherit qemu | ||
| 2 | |||
| 3 | ENABLE_VERSION_MISMATCH_CHECK ?= "${@'1' if bb.utils.contains('MACHINE_FEATURES', 'qemu-usermode', True, False, d) else '0'}" | ||
| 4 | DEBUG_VERSION_MISMATCH_CHECK ?= "1" | ||
| 5 | CHECK_VERSION_PV ?= "" | ||
| 6 | |||
| 7 | DEPENDS:append:class-target = "${@' qemu-native' if bb.utils.to_boolean(d.getVar('ENABLE_VERSION_MISMATCH_CHECK')) else ''}" | ||
| 8 | |||
| 9 | QEMU_EXEC ?= "${@qemu_wrapper_cmdline(d, '${STAGING_DIR_HOST}', ['${STAGING_DIR_HOST}${libdir}','${STAGING_DIR_HOST}${base_libdir}', '${PKGD}${libdir}', '${PKGD}${base_libdir}'])}" | ||
| 10 | |||
| 11 | python do_package_check_version_mismatch() { | ||
| 12 | import re | ||
| 13 | import subprocess | ||
| 14 | import shutil | ||
| 15 | import signal | ||
| 16 | |||
| 17 | classes_skip = ["nopackage", "image", "native", "cross", "crosssdk", "cross-canadian"] | ||
| 18 | for cs in classes_skip: | ||
| 19 | if bb.data.inherits_class(cs, d): | ||
| 20 | bb.note(f"Skip do_package_check_version_mismatch as {cs} is inherited.") | ||
| 21 | return | ||
| 22 | |||
| 23 | if not bb.utils.to_boolean(d.getVar('ENABLE_VERSION_MISMATCH_CHECK')): | ||
| 24 | bb.note("Skip do_package_check_version_mismatch as ENABLE_VERSION_MISMATCH_CHECK is disabled.") | ||
| 25 | return | ||
| 26 | |||
| 27 | __regexp_version_broad_match__ = re.compile(r"(?:\s|^|-|_|/|=| go|\()" + | ||
| 28 | r"(?P<version>v?[0-9][0-9.][0-9+.\-_~\(\)]*?|UNKNOWN)" + | ||
| 29 | r"(?:[+\-]release.*|[+\-]stable.*|)" + | ||
| 30 | r"(?P<extra>[+\-]unknown|[+\-]dirty|[+\-]rc?\d{1,3}|\+cargo-[0-9.]+|" + | ||
| 31 | r"[a-z]|-?[pP][0-9]{1,3}|-?beta[^\s]*|-?alpha[^\s]*|)" + | ||
| 32 | r"(?P<extra2>[+\-]dev|[+\-]devel|)" + | ||
| 33 | r"(?:,|:|\.|\)|-[0-9a-g]{6,42}|)" + | ||
| 34 | r"(?=\s|$)" | ||
| 35 | ) | ||
| 36 | __regexp_exclude_year__ = re.compile(r"^(19|20)[0-9]{2}$") | ||
| 37 | __regexp_single_number_ending_with_dot__ = re.compile(r"^\d\.$") | ||
| 38 | |||
| 39 | def is_shared_library(filepath): | ||
| 40 | return re.match(r'.*\.so(\.\d+)*$', filepath) is not None | ||
| 41 | |||
| 42 | def get_possible_versions(output_contents, full_cmd=None, max_lines=None): | ||
| 43 | # | ||
| 44 | # Algorithm: | ||
| 45 | # 1. Check version line by line. | ||
| 46 | # 2. Skip some lines which we know that do not contain version information, e.g., License, Copyright. | ||
| 47 | # 3. Do broad match, finding all possible versions. | ||
| 48 | # 4. If there's a version found by any match, do exclude match (e.g., exclude years) | ||
| 49 | # 5. If there's a valid version, do stripping and converting and then add to possible_versions. | ||
| 50 | # 6. Return possible_versions | ||
| 51 | # | ||
| 52 | possible_versions = [] | ||
| 53 | content_lines = output_contents.split("\n") | ||
| 54 | if max_lines: | ||
| 55 | content_lines = content_lines[0:max_lines] | ||
| 56 | if full_cmd: | ||
| 57 | base_cmd = os.path.basename(full_cmd) | ||
| 58 | __regex_help_format__ = re.compile(r"-[^\s].*") | ||
| 59 | for line in content_lines: | ||
| 60 | line = line.strip() | ||
| 61 | # skip help lines | ||
| 62 | if __regex_help_format__.match(line): | ||
| 63 | continue | ||
| 64 | # avoid command itself affecting output | ||
| 65 | if full_cmd: | ||
| 66 | if line.startswith(base_cmd): | ||
| 67 | line = line[len(base_cmd):] | ||
| 68 | elif line.startswith(full_cmd): | ||
| 69 | line = line[len(full_cmd):] | ||
| 70 | # skip specific lines | ||
| 71 | skip_keywords_start = ["copyright", "license", "compiled", "build", "built"] | ||
| 72 | skip_line = False | ||
| 73 | for sks in skip_keywords_start: | ||
| 74 | if line.lower().startswith(sks): | ||
| 75 | skip_line = True | ||
| 76 | break | ||
| 77 | if skip_line: | ||
| 78 | continue | ||
| 79 | |||
| 80 | # try broad match | ||
| 81 | for match in __regexp_version_broad_match__.finditer(line): | ||
| 82 | version = match.group("version") | ||
| 83 | #print(f"version = {version}") | ||
| 84 | # do exclude match | ||
| 85 | exclude_match = __regexp_exclude_year__.match(version) | ||
| 86 | if exclude_match: | ||
| 87 | continue | ||
| 88 | exclude_match = __regexp_single_number_ending_with_dot__.match(version) | ||
| 89 | if exclude_match: | ||
| 90 | continue | ||
| 91 | # do some stripping and converting | ||
| 92 | if version.startswith("("): | ||
| 93 | version = version[1:-1] | ||
| 94 | if version.startswith("v"): | ||
| 95 | version = version[1:] | ||
| 96 | if version.endswith(")") and "(" not in version: | ||
| 97 | version = version[:-1] | ||
| 98 | # handle extra version info | ||
| 99 | version = version + match.group("extra") + match.group("extra2") | ||
| 100 | possible_versions.append(version) | ||
| 101 | return possible_versions | ||
| 102 | |||
| 103 | def is_version_mismatch(rvs, pv): | ||
| 104 | got_match = False | ||
| 105 | if pv.startswith("git"): | ||
| 106 | return False | ||
| 107 | if "-pre" in pv: | ||
| 108 | pv = pv.split("-pre")[0] | ||
| 109 | if pv.startswith("v"): | ||
| 110 | pv = pv[1:] | ||
| 111 | for rv in rvs: | ||
| 112 | if rv == pv: | ||
| 113 | got_match = True | ||
| 114 | break | ||
| 115 | pv = pv.split("+git")[0] | ||
| 116 | # handle % character in pv which means matching any chars | ||
| 117 | if '%' in pv: | ||
| 118 | escaped_pv = re.escape(pv) | ||
| 119 | regex_pattern = escaped_pv.replace('%', '.*') | ||
| 120 | regex_pattern = f'^{regex_pattern}$' | ||
| 121 | if re.fullmatch(regex_pattern, rv): | ||
| 122 | got_match = True | ||
| 123 | break | ||
| 124 | else: | ||
| 125 | continue | ||
| 126 | # handle cases such as 2.36.0-r0 v.s. 2.36.0 | ||
| 127 | if "-r" in rv: | ||
| 128 | rv = rv.split("-r")[0] | ||
| 129 | chars_to_replace = ["-", "+", "_", "~"] | ||
| 130 | # convert to use "." as the version seperator | ||
| 131 | for cr in chars_to_replace: | ||
| 132 | rv = rv.replace(cr, ".") | ||
| 133 | pv = pv.replace(cr, ".") | ||
| 134 | if rv == pv: | ||
| 135 | got_match = True | ||
| 136 | break | ||
| 137 | # handle case such as 5.2.37(1) v.s. 5.2.37 | ||
| 138 | if "(" in rv: | ||
| 139 | rv = rv.split("(")[0] | ||
| 140 | if rv == pv: | ||
| 141 | got_match = True | ||
| 142 | break | ||
| 143 | # handle case such as 4.4.3p1 | ||
| 144 | if "p" in pv and "p" in rv.lower(): | ||
| 145 | pv = pv.lower().replace(".p", "p") | ||
| 146 | rv = rv.lower().replace(".p", "p") | ||
| 147 | if pv == rv: | ||
| 148 | got_match = True | ||
| 149 | break | ||
| 150 | # handle cases such as 6.00 v.s. 6.0 | ||
| 151 | if rv.startswith(pv): | ||
| 152 | if rv == pv + "0" or rv == pv + ".0": | ||
| 153 | got_match = True | ||
| 154 | break | ||
| 155 | elif pv.startswith(rv): | ||
| 156 | if pv == rv + "0" or pv == rv + ".0": | ||
| 157 | got_match = True | ||
| 158 | break | ||
| 159 | # handle cases such as 21306 v.s. 2.13.6 | ||
| 160 | if "." in pv and not "." in rv: | ||
| 161 | pv_components = pv.split(".") | ||
| 162 | if rv.startswith(pv_components[0]): | ||
| 163 | pv_num = 0 | ||
| 164 | for i in range(0, len(pv_components)): | ||
| 165 | pv_num = pv_num * 100 + int(pv_components[i]) | ||
| 166 | if pv_num == int(rv): | ||
| 167 | got_match = True | ||
| 168 | break | ||
| 169 | if got_match: | ||
| 170 | return False | ||
| 171 | else: | ||
| 172 | return True | ||
| 173 | |||
| 174 | # helper function to get PKGV, useful for recipes such as perf | ||
| 175 | def get_pkgv(pn): | ||
| 176 | pkgdestwork = d.getVar("PKGDESTWORK") | ||
| 177 | recipe_data_fn = pkgdestwork + "/" + pn | ||
| 178 | pn_data = oe.packagedata.read_pkgdatafile(recipe_data_fn) | ||
| 179 | if not "PACKAGES" in pn_data: | ||
| 180 | return d.getVar("PV") | ||
| 181 | packages = pn_data["PACKAGES"].split() | ||
| 182 | for pkg in packages: | ||
| 183 | pkg_fn = pkgdestwork + "/runtime/" + pkg | ||
| 184 | pkg_data = oe.packagedata.read_pkgdatafile(pkg_fn) | ||
| 185 | if "PKGV" in pkg_data: | ||
| 186 | return pkg_data["PKGV"] | ||
| 187 | |||
| 188 | # | ||
| 189 | # traverse PKGD, find executables and run them to get runtime version information and compare it with recipe version information | ||
| 190 | # | ||
| 191 | enable_debug = bb.utils.to_boolean(d.getVar("DEBUG_VERSION_MISMATCH_CHECK")) | ||
| 192 | pkgd = d.getVar("PKGD") | ||
| 193 | pn = d.getVar("PN") | ||
| 194 | pv = d.getVar("CHECK_VERSION_PV") | ||
| 195 | if not pv: | ||
| 196 | pv = get_pkgv(pn) | ||
| 197 | qemu_exec = d.getVar("QEMU_EXEC").strip() | ||
| 198 | executables = [] | ||
| 199 | possible_versions_all = [] | ||
| 200 | data_lines = [] | ||
| 201 | |||
| 202 | if enable_debug: | ||
| 203 | debug_directory = d.getVar("TMPDIR") + "/check-version-mismatch" | ||
| 204 | debug_data_file = debug_directory + "/" + pn | ||
| 205 | os.makedirs(debug_directory, exist_ok=True) | ||
| 206 | data_lines.append("pv: %s\n" % pv) | ||
| 207 | |||
| 208 | got_quick_match_result = False | ||
| 209 | # handle python3-xxx recipes quickly | ||
| 210 | __regex_python_module_version__ = re.compile(r"(?:^|.*:)Version: (?P<version>.*)$") | ||
| 211 | if "python3-" in pn: | ||
| 212 | version_check_cmd = "find %s -name 'METADATA' | xargs grep '^Version: '" % pkgd | ||
| 213 | try: | ||
| 214 | output = subprocess.check_output(version_check_cmd, shell=True).decode("utf-8") | ||
| 215 | data_lines.append("version_check_cmd: %s\n" % version_check_cmd) | ||
| 216 | data_lines.append("output:\n'''\n%s'''\n" % output) | ||
| 217 | possible_versions = [] | ||
| 218 | for line in output.split("\n"): | ||
| 219 | match = __regex_python_module_version__.match(line) | ||
| 220 | if match: | ||
| 221 | possible_versions.append(match.group("version")) | ||
| 222 | possible_versions = sorted(set(possible_versions)) | ||
| 223 | data_lines.append("possible versions: %s\n" % possible_versions) | ||
| 224 | if is_version_mismatch(possible_versions, pv): | ||
| 225 | data_lines.append("FINAL RESULT: MISMATCH (%s v.s. %s)\n\n" % (possible_versions, pv)) | ||
| 226 | bb.warn("Possible runtime versions %s do not match recipe version %s" % (possible_versions, pv)) | ||
| 227 | else: | ||
| 228 | data_lines.append("FINAL RESULT: MATCH (%s v.s. %s)\n\n" % (possible_versions, pv)) | ||
| 229 | got_quick_match_result = True | ||
| 230 | except: | ||
| 231 | data_lines.append("version_check_cmd: %s\n" % version_check_cmd) | ||
| 232 | data_lines.append("result: RUN_FAILED\n\n") | ||
| 233 | if got_quick_match_result: | ||
| 234 | if enable_debug: | ||
| 235 | with open(debug_data_file, "w") as f: | ||
| 236 | f.writelines(data_lines) | ||
| 237 | return | ||
| 238 | |||
| 239 | # handle .pc files | ||
| 240 | version_check_cmd = "find %s -name '*.pc' | xargs grep -i version" % pkgd | ||
| 241 | try: | ||
| 242 | output = subprocess.check_output(version_check_cmd, shell=True).decode("utf-8") | ||
| 243 | data_lines.append("version_check_cmd: %s\n" % version_check_cmd) | ||
| 244 | data_lines.append("output:\n'''\n%s'''\n" % output) | ||
| 245 | possible_versions = get_possible_versions(output) | ||
| 246 | possible_versions = sorted(set(possible_versions)) | ||
| 247 | data_lines.append("possible versions: %s\n" % possible_versions) | ||
| 248 | if is_version_mismatch(possible_versions, pv): | ||
| 249 | if pn.startswith("lib"): | ||
| 250 | data_lines.append("FINAL RESULT: MISMATCH (%s v.s. %s)\n\n" % (possible_versions, pv)) | ||
| 251 | bb.warn("Possible runtime versions %s do not match recipe version %s" % (possible_versions, pv)) | ||
| 252 | got_quick_match_result = True | ||
| 253 | else: | ||
| 254 | data_lines.append("result: MISMATCH (%s v.s. %s)\n\n" % (possible_versions, pv)) | ||
| 255 | else: | ||
| 256 | data_lines.append("FINAL RESULT: MATCH (%s v.s. %s)\n\n" % (possible_versions, pv)) | ||
| 257 | got_quick_match_result = True | ||
| 258 | except: | ||
| 259 | data_lines.append("version_check_cmd: %s\n" % version_check_cmd) | ||
| 260 | data_lines.append("result: RUN_FAILED\n\n") | ||
| 261 | if got_quick_match_result: | ||
| 262 | if enable_debug: | ||
| 263 | with open(debug_data_file, "w") as f: | ||
| 264 | f.writelines(data_lines) | ||
| 265 | return | ||
| 266 | |||
| 267 | skipped_directories = [".debug", "ptest", "installed-tests", "tests", "test", "__pycache__", "testcases"] | ||
| 268 | pkgd_libdir = pkgd + d.getVar("libdir") | ||
| 269 | pkgd_base_libdir = pkgd + d.getVar("base_libdir") | ||
| 270 | extra_exec_libdirs = [] | ||
| 271 | for root, dirs, files in os.walk(pkgd): | ||
| 272 | for dname in dirs: | ||
| 273 | fdir = os.path.join(root, dname) | ||
| 274 | if os.path.isdir(fdir) and fdir != pkgd_libdir and fdir != pkgd_base_libdir: | ||
| 275 | if fdir.startswith(pkgd_libdir) or fdir.startswith(pkgd_base_libdir): | ||
| 276 | for sd in skipped_directories: | ||
| 277 | if fdir.endswith("/" + sd) or ("/" + sd + "/") in fdir: | ||
| 278 | break | ||
| 279 | else: | ||
| 280 | extra_exec_libdirs.append(fdir) | ||
| 281 | for fname in files: | ||
| 282 | fpath = os.path.join(root, fname) | ||
| 283 | if os.path.isfile(fpath) and os.access(fpath, os.X_OK): | ||
| 284 | for sd in skipped_directories: | ||
| 285 | if ("/" + sd + "/") in fpath: | ||
| 286 | break | ||
| 287 | else: | ||
| 288 | if is_shared_library(fpath): | ||
| 289 | # we don't check shared libraries | ||
| 290 | continue | ||
| 291 | else: | ||
| 292 | executables.append(fpath) | ||
| 293 | if enable_debug: | ||
| 294 | data_lines.append("executables: %s\n" % executables) | ||
| 295 | |||
| 296 | found_match = False | ||
| 297 | some_cmd_succeed = False | ||
| 298 | if not executables: | ||
| 299 | bb.debug(1, "No executable found for %s" % pn) | ||
| 300 | data_lines.append("FINAL RESULT: NO_EXECUTABLE_FOUND\n\n") | ||
| 301 | else: | ||
| 302 | # first we extend qemu_exec to include library path if needed | ||
| 303 | if extra_exec_libdirs: | ||
| 304 | qemu_exec += ":" + ":".join(extra_exec_libdirs) | ||
| 305 | for fexec in executables: | ||
| 306 | for version_option in ["--version", "-V", "-v", "--help"]: | ||
| 307 | version_check_cmd_full = "%s %s %s" % (qemu_exec, fexec, version_option) | ||
| 308 | version_check_cmd = version_check_cmd_full | ||
| 309 | #version_check_cmd = "%s %s" % (os.path.relpath(fexec, pkgd), version_option) | ||
| 310 | |||
| 311 | try: | ||
| 312 | cwd_temp = d.getVar("TMPDIR") + "/check-version-mismatch/cwd-temp/" + pn | ||
| 313 | os.makedirs(cwd_temp, exist_ok=True) | ||
| 314 | # avoid pseudo to manage any file we create | ||
| 315 | sp_env = os.environ.copy() | ||
| 316 | sp_env["PSEUDO_UNLOAD"] = "1" | ||
| 317 | output = subprocess.check_output(version_check_cmd_full, | ||
| 318 | shell=True, | ||
| 319 | stderr=subprocess.STDOUT, | ||
| 320 | cwd=cwd_temp, | ||
| 321 | timeout=10, | ||
| 322 | env=sp_env).decode("utf-8") | ||
| 323 | some_cmd_succeed = True | ||
| 324 | data_lines.append("version_check_cmd: %s\n" % version_check_cmd) | ||
| 325 | data_lines.append("output:\n'''\n%s'''\n" % output) | ||
| 326 | if version_option == "--help": | ||
| 327 | max_lines = 5 | ||
| 328 | else: | ||
| 329 | max_lines = None | ||
| 330 | possible_versions = get_possible_versions(output, full_cmd=fexec, max_lines=max_lines) | ||
| 331 | if "." in pv: | ||
| 332 | possible_versions = [item for item in possible_versions if "." in item or item == "UNKNOWN"] | ||
| 333 | data_lines.append("possible versions: %s\n" % possible_versions) | ||
| 334 | if not possible_versions: | ||
| 335 | data_lines.append("result: NO_RUNTIME_VERSION_FOUND\n\n") | ||
| 336 | continue | ||
| 337 | possible_versions_all.extend(possible_versions) | ||
| 338 | possible_versions_all = sorted(set(possible_versions_all)) | ||
| 339 | if is_version_mismatch(possible_versions, pv): | ||
| 340 | data_lines.append("result: MISMATCH (%s v.s. %s)\n\n" % (possible_versions, pv)) | ||
| 341 | else: | ||
| 342 | found_match = True | ||
| 343 | data_lines.append("result: MATCH (%s v.s. %s)\n\n" % (possible_versions, pv)) | ||
| 344 | break | ||
| 345 | except: | ||
| 346 | data_lines.append("version_check_cmd: %s\n" % version_check_cmd) | ||
| 347 | data_lines.append("result: RUN_FAILED\n\n") | ||
| 348 | finally: | ||
| 349 | shutil.rmtree(cwd_temp) | ||
| 350 | if found_match: | ||
| 351 | break | ||
| 352 | if executables: | ||
| 353 | if found_match: | ||
| 354 | data_lines.append("FINAL RESULT: MATCH (%s v.s. %s)\n" % (possible_versions_all, pv)) | ||
| 355 | elif len(possible_versions_all) == 0: | ||
| 356 | if some_cmd_succeed: | ||
| 357 | bb.debug(1, "No valid runtime version found") | ||
| 358 | data_lines.append("FINAL RESULT: NO_VALID_RUNTIME_VERSION_FOUND\n") | ||
| 359 | else: | ||
| 360 | bb.debug(1, "All version check command failed") | ||
| 361 | data_lines.append("FINAL RESULT: RUN_FAILED\n") | ||
| 362 | else: | ||
| 363 | bb.warn("Possible runtime versions %s do not match recipe version %s" % (possible_versions_all, pv)) | ||
| 364 | data_lines.append("FINAL RESULT: MISMATCH (%s v.s. %s)\n" % (possible_versions_all, pv)) | ||
| 365 | |||
| 366 | if enable_debug: | ||
| 367 | with open(debug_data_file, "w") as f: | ||
| 368 | f.writelines(data_lines) | ||
| 369 | |||
| 370 | # clean up stale processes | ||
| 371 | process_name_common_prefix = "%s %s" % (' '.join(qemu_exec.split()[1:]), pkgd) | ||
| 372 | find_stale_process_cmd = "ps -e -o pid,args | grep -v grep | grep -F '%s'" % process_name_common_prefix | ||
| 373 | try: | ||
| 374 | stale_process_output = subprocess.check_output(find_stale_process_cmd, shell=True).decode("utf-8") | ||
| 375 | stale_process_pids = [] | ||
| 376 | for line in stale_process_output.split("\n"): | ||
| 377 | line = line.strip() | ||
| 378 | if not line: | ||
| 379 | continue | ||
| 380 | pid = line.split()[0] | ||
| 381 | stale_process_pids.append(pid) | ||
| 382 | for pid in stale_process_pids: | ||
| 383 | os.kill(int(pid), signal.SIGKILL) | ||
| 384 | except Exception as e: | ||
| 385 | bb.debug(1, "No stale process") | ||
| 386 | } | ||
| 387 | |||
| 388 | addtask do_package_check_version_mismatch after do_package before do_build | ||
| 389 | |||
| 390 | do_build[rdeptask] += "do_package_check_version_mismatch" | ||
| 391 | do_rootfs[recrdeptask] += "do_package_check_version_mismatch" | ||
| 392 | |||
| 393 | SSTATETASKS += "do_package_check_version_mismatch" | ||
| 394 | do_package_check_version_mismatch[sstate-inputdirs] = "" | ||
| 395 | do_package_check_version_mismatch[sstate-outputdirs] = "" | ||
| 396 | python do_package_check_version_mismatch_setscene () { | ||
| 397 | sstate_setscene(d) | ||
| 398 | } | ||
| 399 | addtask do_package_check_version_mismatch_setscene | ||
diff --git a/meta-oe/conf/version-check.conf b/meta-oe/conf/version-check.conf new file mode 100644 index 0000000000..c41df0d496 --- /dev/null +++ b/meta-oe/conf/version-check.conf | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | INHERIT += "check-version-mismatch" | ||
| 2 | # we need ps command to clean stale processes | ||
| 3 | HOSTTOOLS += "ps" | ||
| 4 | |||
| 5 | # Special cases that need to be handled. | ||
| 6 | # % has the same meaning as in bbappend files, that is, match any chars. | ||
| 7 | |||
| 8 | # oe-core | ||
| 9 | CHECK_VERSION_PV:pn-rust-llvm = "${LLVM_RELEASE}" | ||
| 10 | CHECK_VERSION_PV:pn-igt-gpu-tools = "${PV}-${PV}" | ||
| 11 | CHECK_VERSION_PV:pn-vim = "${@'.'.join(d.getVar('PV').split('.')[:-1])}" | ||
| 12 | CHECK_VERSION_PV:pn-vim-tiny = "${@'.'.join(d.getVar('PV').split('.')[:-1])}" | ||
| 13 | CHECK_VERSION_PV:pn-ncurses = "${PV}.%" | ||
| 14 | CHECK_VERSION_PV:pn-alsa-tools = "%" | ||
| 15 | CHECK_VERSION_PV:pn-gst-examples = "%" | ||
| 16 | CHECK_VERSION_PV:pn-libedit = "${@d.getVar('PV').split('-')[1]}" | ||
| 17 | |||
| 18 | # meta-oe | ||
| 19 | CHECK_VERSION_PV:pn-iozone3 = "3.${PV}" | ||
| 20 | CHECK_VERSION_PV:pn-can-utils = "%" | ||
| 21 | CHECK_VERSION_PV:pn-luajit = "${PV}.%" | ||
| 22 | CHECK_VERSION_PV:pn-sg3-utils = "%" | ||
