summaryrefslogtreecommitdiffstats
path: root/meta/lib/oe/utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'meta/lib/oe/utils.py')
-rw-r--r--meta/lib/oe/utils.py182
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
5import subprocess 7import subprocess
6import multiprocessing 8import multiprocessing
7import traceback 9import traceback
10import errno
8 11
9def read_file(filename): 12def 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
251def cpu_count(at_least=1): 254def 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
255def execute_pre_post_process(d, cmds): 258def 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
268def get_bb_number_threads(d):
269 return int(d.getVar("BB_NUMBER_THREADS") or os.cpu_count() or 1)
270
271def 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
268def multiprocess_launch(target, items, d, extraargs=None): 278def 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
347def format_pkg_list(pkg_dict, ret_format=None): 356def 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
378def 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
453from queue import Queue
454from threading import Thread
455
456class 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
487class 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
512def 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
523class ImageQAFailed(Exception): 485class 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):
536def sh_quote(string): 498def sh_quote(string):
537 import shlex 499 import shlex
538 return shlex.quote(string) 500 return shlex.quote(string)
501
502def 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
534def 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