diff options
Diffstat (limited to 'meta/lib/oe/utils.py')
-rw-r--r-- | meta/lib/oe/utils.py | 182 |
1 files changed, 93 insertions, 89 deletions
diff --git a/meta/lib/oe/utils.py b/meta/lib/oe/utils.py index 9a2187e36f..14a7d07ef0 100644 --- a/meta/lib/oe/utils.py +++ b/meta/lib/oe/utils.py | |||
@@ -1,10 +1,13 @@ | |||
1 | # | 1 | # |
2 | # Copyright OpenEmbedded Contributors | ||
3 | # | ||
2 | # SPDX-License-Identifier: GPL-2.0-only | 4 | # SPDX-License-Identifier: GPL-2.0-only |
3 | # | 5 | # |
4 | 6 | ||
5 | import subprocess | 7 | import subprocess |
6 | import multiprocessing | 8 | import multiprocessing |
7 | import traceback | 9 | import traceback |
10 | import errno | ||
8 | 11 | ||
9 | def read_file(filename): | 12 | def read_file(filename): |
10 | try: | 13 | try: |
@@ -221,12 +224,12 @@ def packages_filter_out_system(d): | |||
221 | PN-dbg PN-doc PN-locale-eb-gb removed. | 224 | PN-dbg PN-doc PN-locale-eb-gb removed. |
222 | """ | 225 | """ |
223 | pn = d.getVar('PN') | 226 | pn = d.getVar('PN') |
224 | blacklist = [pn + suffix for suffix in ('', '-dbg', '-dev', '-doc', '-locale', '-staticdev', '-src')] | 227 | pkgfilter = [pn + suffix for suffix in ('', '-dbg', '-dev', '-doc', '-locale', '-staticdev', '-src')] |
225 | localepkg = pn + "-locale-" | 228 | localepkg = pn + "-locale-" |
226 | pkgs = [] | 229 | pkgs = [] |
227 | 230 | ||
228 | for pkg in d.getVar('PACKAGES').split(): | 231 | for pkg in d.getVar('PACKAGES').split(): |
229 | if pkg not in blacklist and localepkg not in pkg: | 232 | if pkg not in pkgfilter and localepkg not in pkg: |
230 | pkgs.append(pkg) | 233 | pkgs.append(pkg) |
231 | return pkgs | 234 | return pkgs |
232 | 235 | ||
@@ -248,24 +251,31 @@ def trim_version(version, num_parts=2): | |||
248 | trimmed = ".".join(parts[:num_parts]) | 251 | trimmed = ".".join(parts[:num_parts]) |
249 | return trimmed | 252 | return trimmed |
250 | 253 | ||
251 | def cpu_count(at_least=1): | 254 | def cpu_count(at_least=1, at_most=64): |
252 | cpus = len(os.sched_getaffinity(0)) | 255 | cpus = len(os.sched_getaffinity(0)) |
253 | return max(cpus, at_least) | 256 | return max(min(cpus, at_most), at_least) |
254 | 257 | ||
255 | def execute_pre_post_process(d, cmds): | 258 | def execute_pre_post_process(d, cmds): |
256 | if cmds is None: | 259 | if cmds is None: |
257 | return | 260 | return |
258 | 261 | ||
259 | for cmd in cmds.strip().split(';'): | 262 | cmds = cmds.replace(";", " ") |
260 | cmd = cmd.strip() | 263 | |
261 | if cmd != '': | 264 | for cmd in cmds.split(): |
262 | bb.note("Executing %s ..." % cmd) | 265 | bb.note("Executing %s ..." % cmd) |
263 | bb.build.exec_func(cmd, d) | 266 | bb.build.exec_func(cmd, d) |
267 | |||
268 | def get_bb_number_threads(d): | ||
269 | return int(d.getVar("BB_NUMBER_THREADS") or os.cpu_count() or 1) | ||
270 | |||
271 | def multiprocess_launch(target, items, d, extraargs=None): | ||
272 | max_process = get_bb_number_threads(d) | ||
273 | return multiprocess_launch_mp(target, items, max_process, extraargs) | ||
264 | 274 | ||
265 | # For each item in items, call the function 'target' with item as the first | 275 | # For each item in items, call the function 'target' with item as the first |
266 | # argument, extraargs as the other arguments and handle any exceptions in the | 276 | # argument, extraargs as the other arguments and handle any exceptions in the |
267 | # parent thread | 277 | # parent thread |
268 | def multiprocess_launch(target, items, d, extraargs=None): | 278 | def multiprocess_launch_mp(target, items, max_process, extraargs=None): |
269 | 279 | ||
270 | class ProcessLaunch(multiprocessing.Process): | 280 | class ProcessLaunch(multiprocessing.Process): |
271 | def __init__(self, *args, **kwargs): | 281 | def __init__(self, *args, **kwargs): |
@@ -300,7 +310,6 @@ def multiprocess_launch(target, items, d, extraargs=None): | |||
300 | self.update() | 310 | self.update() |
301 | return self._result | 311 | return self._result |
302 | 312 | ||
303 | max_process = int(d.getVar("BB_NUMBER_THREADS") or os.cpu_count() or 1) | ||
304 | launched = [] | 313 | launched = [] |
305 | errors = [] | 314 | errors = [] |
306 | results = [] | 315 | results = [] |
@@ -344,7 +353,29 @@ def squashspaces(string): | |||
344 | import re | 353 | import re |
345 | return re.sub(r"\s+", " ", string).strip() | 354 | return re.sub(r"\s+", " ", string).strip() |
346 | 355 | ||
347 | def format_pkg_list(pkg_dict, ret_format=None): | 356 | def rprovides_map(pkgdata_dir, pkg_dict): |
357 | # Map file -> pkg provider | ||
358 | rprov_map = {} | ||
359 | |||
360 | for pkg in pkg_dict: | ||
361 | path_to_pkgfile = os.path.join(pkgdata_dir, 'runtime-reverse', pkg) | ||
362 | if not os.path.isfile(path_to_pkgfile): | ||
363 | continue | ||
364 | with open(path_to_pkgfile) as f: | ||
365 | for line in f: | ||
366 | if line.startswith('RPROVIDES') or line.startswith('FILERPROVIDES'): | ||
367 | # List all components provided by pkg. | ||
368 | # Exclude version strings, i.e. those starting with ( | ||
369 | provides = [x for x in line.split()[1:] if not x.startswith('(')] | ||
370 | for prov in provides: | ||
371 | if prov in rprov_map: | ||
372 | rprov_map[prov].append(pkg) | ||
373 | else: | ||
374 | rprov_map[prov] = [pkg] | ||
375 | |||
376 | return rprov_map | ||
377 | |||
378 | def format_pkg_list(pkg_dict, ret_format=None, pkgdata_dir=None): | ||
348 | output = [] | 379 | output = [] |
349 | 380 | ||
350 | if ret_format == "arch": | 381 | if ret_format == "arch": |
@@ -357,9 +388,15 @@ def format_pkg_list(pkg_dict, ret_format=None): | |||
357 | for pkg in sorted(pkg_dict): | 388 | for pkg in sorted(pkg_dict): |
358 | output.append("%s %s %s" % (pkg, pkg_dict[pkg]["arch"], pkg_dict[pkg]["ver"])) | 389 | output.append("%s %s %s" % (pkg, pkg_dict[pkg]["arch"], pkg_dict[pkg]["ver"])) |
359 | elif ret_format == "deps": | 390 | elif ret_format == "deps": |
391 | rprov_map = rprovides_map(pkgdata_dir, pkg_dict) | ||
360 | for pkg in sorted(pkg_dict): | 392 | for pkg in sorted(pkg_dict): |
361 | for dep in pkg_dict[pkg]["deps"]: | 393 | for dep in pkg_dict[pkg]["deps"]: |
362 | output.append("%s|%s" % (pkg, dep)) | 394 | if dep in rprov_map: |
395 | # There could be multiple providers within the image | ||
396 | for pkg_provider in rprov_map[dep]: | ||
397 | output.append("%s|%s * %s [RPROVIDES]" % (pkg, pkg_provider, dep)) | ||
398 | else: | ||
399 | output.append("%s|%s" % (pkg, dep)) | ||
363 | else: | 400 | else: |
364 | for pkg in sorted(pkg_dict): | 401 | for pkg in sorted(pkg_dict): |
365 | output.append(pkg) | 402 | output.append(pkg) |
@@ -445,81 +482,6 @@ def get_multilib_datastore(variant, d): | |||
445 | localdata.setVar("MLPREFIX", "") | 482 | localdata.setVar("MLPREFIX", "") |
446 | return localdata | 483 | return localdata |
447 | 484 | ||
448 | # | ||
449 | # Python 2.7 doesn't have threaded pools (just multiprocessing) | ||
450 | # so implement a version here | ||
451 | # | ||
452 | |||
453 | from queue import Queue | ||
454 | from threading import Thread | ||
455 | |||
456 | class ThreadedWorker(Thread): | ||
457 | """Thread executing tasks from a given tasks queue""" | ||
458 | def __init__(self, tasks, worker_init, worker_end): | ||
459 | Thread.__init__(self) | ||
460 | self.tasks = tasks | ||
461 | self.daemon = True | ||
462 | |||
463 | self.worker_init = worker_init | ||
464 | self.worker_end = worker_end | ||
465 | |||
466 | def run(self): | ||
467 | from queue import Empty | ||
468 | |||
469 | if self.worker_init is not None: | ||
470 | self.worker_init(self) | ||
471 | |||
472 | while True: | ||
473 | try: | ||
474 | func, args, kargs = self.tasks.get(block=False) | ||
475 | except Empty: | ||
476 | if self.worker_end is not None: | ||
477 | self.worker_end(self) | ||
478 | break | ||
479 | |||
480 | try: | ||
481 | func(self, *args, **kargs) | ||
482 | except Exception as e: | ||
483 | print(e) | ||
484 | finally: | ||
485 | self.tasks.task_done() | ||
486 | |||
487 | class ThreadedPool: | ||
488 | """Pool of threads consuming tasks from a queue""" | ||
489 | def __init__(self, num_workers, num_tasks, worker_init=None, | ||
490 | worker_end=None): | ||
491 | self.tasks = Queue(num_tasks) | ||
492 | self.workers = [] | ||
493 | |||
494 | for _ in range(num_workers): | ||
495 | worker = ThreadedWorker(self.tasks, worker_init, worker_end) | ||
496 | self.workers.append(worker) | ||
497 | |||
498 | def start(self): | ||
499 | for worker in self.workers: | ||
500 | worker.start() | ||
501 | |||
502 | def add_task(self, func, *args, **kargs): | ||
503 | """Add a task to the queue""" | ||
504 | self.tasks.put((func, args, kargs)) | ||
505 | |||
506 | def wait_completion(self): | ||
507 | """Wait for completion of all the tasks in the queue""" | ||
508 | self.tasks.join() | ||
509 | for worker in self.workers: | ||
510 | worker.join() | ||
511 | |||
512 | def write_ld_so_conf(d): | ||
513 | # Some utils like prelink may not have the correct target library paths | ||
514 | # so write an ld.so.conf to help them | ||
515 | ldsoconf = d.expand("${STAGING_DIR_TARGET}${sysconfdir}/ld.so.conf") | ||
516 | if os.path.exists(ldsoconf): | ||
517 | bb.utils.remove(ldsoconf) | ||
518 | bb.utils.mkdirhier(os.path.dirname(ldsoconf)) | ||
519 | with open(ldsoconf, "w") as f: | ||
520 | f.write(d.getVar("base_libdir") + '\n') | ||
521 | f.write(d.getVar("libdir") + '\n') | ||
522 | |||
523 | class ImageQAFailed(Exception): | 485 | class ImageQAFailed(Exception): |
524 | def __init__(self, description, name=None, logfile=None): | 486 | def __init__(self, description, name=None, logfile=None): |
525 | self.description = description | 487 | self.description = description |
@@ -536,3 +498,45 @@ class ImageQAFailed(Exception): | |||
536 | def sh_quote(string): | 498 | def sh_quote(string): |
537 | import shlex | 499 | import shlex |
538 | return shlex.quote(string) | 500 | return shlex.quote(string) |
501 | |||
502 | def directory_size(root, blocksize=4096): | ||
503 | """ | ||
504 | Calculate the size of the directory, taking into account hard links, | ||
505 | rounding up every size to multiples of the blocksize. | ||
506 | """ | ||
507 | def roundup(size): | ||
508 | """ | ||
509 | Round the size up to the nearest multiple of the block size. | ||
510 | """ | ||
511 | import math | ||
512 | return math.ceil(size / blocksize) * blocksize | ||
513 | |||
514 | def getsize(filename): | ||
515 | """ | ||
516 | Get the size of the filename, not following symlinks, taking into | ||
517 | account hard links. | ||
518 | """ | ||
519 | stat = os.lstat(filename) | ||
520 | if stat.st_ino not in inodes: | ||
521 | inodes.add(stat.st_ino) | ||
522 | return stat.st_size | ||
523 | else: | ||
524 | return 0 | ||
525 | |||
526 | inodes = set() | ||
527 | total = 0 | ||
528 | for root, dirs, files in os.walk(root): | ||
529 | total += sum(roundup(getsize(os.path.join(root, name))) for name in files) | ||
530 | total += roundup(getsize(root)) | ||
531 | return total | ||
532 | |||
533 | # Update the mtime of a file, skip if permission/read-only issues | ||
534 | def touch(filename): | ||
535 | try: | ||
536 | os.utime(filename, None) | ||
537 | except PermissionError: | ||
538 | pass | ||
539 | except OSError as e: | ||
540 | # Handle read-only file systems gracefully | ||
541 | if e.errno != errno.EROFS: | ||
542 | raise e | ||