blob: 2ff7e82222f8b8af9288818a53b0e6673d9ce21c [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001# ex:ts=4:sw=4:sts=4:et
2# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3"""
4BitBake Utility Functions
5"""
6
7# Copyright (C) 2004 Michael Lauer
8#
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License version 2 as
11# published by the Free Software Foundation.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License along
19# with this program; if not, write to the Free Software Foundation, Inc.,
20# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
22import re, fcntl, os, string, stat, shutil, time
23import sys
24import errno
25import logging
26import bb
27import bb.msg
28import multiprocessing
29import fcntl
Patrick Williamsc0f7c042017-02-23 20:41:17 -060030import imp
31import itertools
Patrick Williamsc124f4f2015-09-15 14:41:29 -050032import subprocess
33import glob
34import fnmatch
35import traceback
36import errno
37import signal
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050038import ast
Patrick Williamsc0f7c042017-02-23 20:41:17 -060039import collections
40import copy
41from subprocess import getstatusoutput
Patrick Williamsc124f4f2015-09-15 14:41:29 -050042from contextlib import contextmanager
43from ctypes import cdll
44
Patrick Williamsc124f4f2015-09-15 14:41:29 -050045logger = logging.getLogger("BitBake.Util")
Patrick Williamsc0f7c042017-02-23 20:41:17 -060046python_extensions = [e for e, _, _ in imp.get_suffixes()]
47
Patrick Williamsc124f4f2015-09-15 14:41:29 -050048
49def clean_context():
50 return {
51 "os": os,
52 "bb": bb,
53 "time": time,
54 }
55
56def get_context():
57 return _context
58
59
60def set_context(ctx):
61 _context = ctx
62
63# Context used in better_exec, eval
64_context = clean_context()
65
66class VersionStringException(Exception):
67 """Exception raised when an invalid version specification is found"""
68
69def explode_version(s):
70 r = []
71 alpha_regexp = re.compile('^([a-zA-Z]+)(.*)$')
72 numeric_regexp = re.compile('^(\d+)(.*)$')
73 while (s != ''):
74 if s[0] in string.digits:
75 m = numeric_regexp.match(s)
76 r.append((0, int(m.group(1))))
77 s = m.group(2)
78 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -060079 if s[0] in string.ascii_letters:
Patrick Williamsc124f4f2015-09-15 14:41:29 -050080 m = alpha_regexp.match(s)
81 r.append((1, m.group(1)))
82 s = m.group(2)
83 continue
84 if s[0] == '~':
85 r.append((-1, s[0]))
86 else:
87 r.append((2, s[0]))
88 s = s[1:]
89 return r
90
91def split_version(s):
92 """Split a version string into its constituent parts (PE, PV, PR)"""
93 s = s.strip(" <>=")
94 e = 0
95 if s.count(':'):
96 e = int(s.split(":")[0])
97 s = s.split(":")[1]
98 r = ""
99 if s.count('-'):
100 r = s.rsplit("-", 1)[1]
101 s = s.rsplit("-", 1)[0]
102 v = s
103 return (e, v, r)
104
105def vercmp_part(a, b):
106 va = explode_version(a)
107 vb = explode_version(b)
108 while True:
109 if va == []:
110 (oa, ca) = (0, None)
111 else:
112 (oa, ca) = va.pop(0)
113 if vb == []:
114 (ob, cb) = (0, None)
115 else:
116 (ob, cb) = vb.pop(0)
117 if (oa, ca) == (0, None) and (ob, cb) == (0, None):
118 return 0
119 if oa < ob:
120 return -1
121 elif oa > ob:
122 return 1
123 elif ca < cb:
124 return -1
125 elif ca > cb:
126 return 1
127
128def vercmp(ta, tb):
129 (ea, va, ra) = ta
130 (eb, vb, rb) = tb
131
132 r = int(ea or 0) - int(eb or 0)
133 if (r == 0):
134 r = vercmp_part(va, vb)
135 if (r == 0):
136 r = vercmp_part(ra, rb)
137 return r
138
139def vercmp_string(a, b):
140 ta = split_version(a)
141 tb = split_version(b)
142 return vercmp(ta, tb)
143
144def vercmp_string_op(a, b, op):
145 """
146 Compare two versions and check if the specified comparison operator matches the result of the comparison.
147 This function is fairly liberal about what operators it will accept since there are a variety of styles
148 depending on the context.
149 """
150 res = vercmp_string(a, b)
151 if op in ('=', '=='):
152 return res == 0
153 elif op == '<=':
154 return res <= 0
155 elif op == '>=':
156 return res >= 0
157 elif op in ('>', '>>'):
158 return res > 0
159 elif op in ('<', '<<'):
160 return res < 0
161 elif op == '!=':
162 return res != 0
163 else:
164 raise VersionStringException('Unsupported comparison operator "%s"' % op)
165
166def explode_deps(s):
167 """
168 Take an RDEPENDS style string of format:
169 "DEPEND1 (optional version) DEPEND2 (optional version) ..."
170 and return a list of dependencies.
171 Version information is ignored.
172 """
173 r = []
174 l = s.split()
175 flag = False
176 for i in l:
177 if i[0] == '(':
178 flag = True
179 #j = []
180 if not flag:
181 r.append(i)
182 #else:
183 # j.append(i)
184 if flag and i.endswith(')'):
185 flag = False
186 # Ignore version
187 #r[-1] += ' ' + ' '.join(j)
188 return r
189
Brad Bishop316dfdd2018-06-25 12:45:53 -0400190def explode_dep_versions2(s, *, sort=True):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500191 """
192 Take an RDEPENDS style string of format:
193 "DEPEND1 (optional version) DEPEND2 (optional version) ..."
194 and return a dictionary of dependencies and versions.
195 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600196 r = collections.OrderedDict()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500197 l = s.replace(",", "").split()
198 lastdep = None
199 lastcmp = ""
200 lastver = ""
201 incmp = False
202 inversion = False
203 for i in l:
204 if i[0] == '(':
205 incmp = True
206 i = i[1:].strip()
207 if not i:
208 continue
209
210 if incmp:
211 incmp = False
212 inversion = True
213 # This list is based on behavior and supported comparisons from deb, opkg and rpm.
214 #
215 # Even though =<, <<, ==, !=, =>, and >> may not be supported,
216 # we list each possibly valid item.
217 # The build system is responsible for validation of what it supports.
218 if i.startswith(('<=', '=<', '<<', '==', '!=', '>=', '=>', '>>')):
219 lastcmp = i[0:2]
220 i = i[2:]
221 elif i.startswith(('<', '>', '=')):
222 lastcmp = i[0:1]
223 i = i[1:]
224 else:
225 # This is an unsupported case!
226 raise VersionStringException('Invalid version specification in "(%s" - invalid or missing operator' % i)
227 lastcmp = (i or "")
228 i = ""
229 i.strip()
230 if not i:
231 continue
232
233 if inversion:
234 if i.endswith(')'):
235 i = i[:-1] or ""
236 inversion = False
237 if lastver and i:
238 lastver += " "
239 if i:
240 lastver += i
241 if lastdep not in r:
242 r[lastdep] = []
243 r[lastdep].append(lastcmp + " " + lastver)
244 continue
245
246 #if not inversion:
247 lastdep = i
248 lastver = ""
249 lastcmp = ""
250 if not (i in r and r[i]):
251 r[lastdep] = []
252
Brad Bishop316dfdd2018-06-25 12:45:53 -0400253 if sort:
254 r = collections.OrderedDict(sorted(r.items(), key=lambda x: x[0]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500255 return r
256
257def explode_dep_versions(s):
258 r = explode_dep_versions2(s)
259 for d in r:
260 if not r[d]:
261 r[d] = None
262 continue
263 if len(r[d]) > 1:
264 bb.warn("explode_dep_versions(): Item %s appeared in dependency string '%s' multiple times with different values. explode_dep_versions cannot cope with this." % (d, s))
265 r[d] = r[d][0]
266 return r
267
268def join_deps(deps, commasep=True):
269 """
270 Take the result from explode_dep_versions and generate a dependency string
271 """
272 result = []
273 for dep in deps:
274 if deps[dep]:
275 if isinstance(deps[dep], list):
276 for v in deps[dep]:
277 result.append(dep + " (" + v + ")")
278 else:
279 result.append(dep + " (" + deps[dep] + ")")
280 else:
281 result.append(dep)
282 if commasep:
283 return ", ".join(result)
284 else:
285 return " ".join(result)
286
287def _print_trace(body, line):
288 """
289 Print the Environment of a Text Body
290 """
291 error = []
292 # print the environment of the method
293 min_line = max(1, line-4)
294 max_line = min(line + 4, len(body))
295 for i in range(min_line, max_line + 1):
296 if line == i:
297 error.append(' *** %.4d:%s' % (i, body[i-1].rstrip()))
298 else:
299 error.append(' %.4d:%s' % (i, body[i-1].rstrip()))
300 return error
301
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500302def better_compile(text, file, realfile, mode = "exec", lineno = 0):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500303 """
304 A better compile method. This method
305 will print the offending lines.
306 """
307 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500308 cache = bb.methodpool.compile_cache(text)
309 if cache:
310 return cache
311 # We can't add to the linenumbers for compile, we can pad to the correct number of blank lines though
312 text2 = "\n" * int(lineno) + text
313 code = compile(text2, realfile, mode)
314 bb.methodpool.compile_cache_add(text, code)
315 return code
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500316 except Exception as e:
317 error = []
318 # split the text into lines again
319 body = text.split('\n')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500320 error.append("Error in compiling python function in %s, line %s:\n" % (realfile, lineno))
321 if hasattr(e, "lineno"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500322 error.append("The code lines resulting in this error were:")
323 error.extend(_print_trace(body, e.lineno))
324 else:
325 error.append("The function causing this error was:")
326 for line in body:
327 error.append(line)
328 error.append("%s: %s" % (e.__class__.__name__, str(e)))
329
330 logger.error("\n".join(error))
331
332 e = bb.BBHandledException(e)
333 raise e
334
335def _print_exception(t, value, tb, realfile, text, context):
336 error = []
337 try:
338 exception = traceback.format_exception_only(t, value)
339 error.append('Error executing a python function in %s:\n' % realfile)
340
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500341 # Strip 'us' from the stack (better_exec call) unless that was where the
342 # error came from
343 if tb.tb_next is not None:
344 tb = tb.tb_next
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500345
346 textarray = text.split('\n')
347
348 linefailed = tb.tb_lineno
349
350 tbextract = traceback.extract_tb(tb)
351 tbformat = traceback.format_list(tbextract)
352 error.append("The stack trace of python calls that resulted in this exception/failure was:")
353 error.append("File: '%s', lineno: %s, function: %s" % (tbextract[0][0], tbextract[0][1], tbextract[0][2]))
354 error.extend(_print_trace(textarray, linefailed))
355
356 # See if this is a function we constructed and has calls back into other functions in
357 # "text". If so, try and improve the context of the error by diving down the trace
358 level = 0
359 nexttb = tb.tb_next
360 while nexttb is not None and (level+1) < len(tbextract):
361 error.append("File: '%s', lineno: %s, function: %s" % (tbextract[level+1][0], tbextract[level+1][1], tbextract[level+1][2]))
362 if tbextract[level][0] == tbextract[level+1][0] and tbextract[level+1][2] == tbextract[level][0]:
363 # The code was possibly in the string we compiled ourselves
364 error.extend(_print_trace(textarray, tbextract[level+1][1]))
365 elif tbextract[level+1][0].startswith("/"):
366 # The code looks like it might be in a file, try and load it
367 try:
368 with open(tbextract[level+1][0], "r") as f:
369 text = f.readlines()
370 error.extend(_print_trace(text, tbextract[level+1][1]))
371 except:
372 error.append(tbformat[level+1])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500373 else:
374 error.append(tbformat[level+1])
375 nexttb = tb.tb_next
376 level = level + 1
377
378 error.append("Exception: %s" % ''.join(exception))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600379
380 # If the exception is from spwaning a task, let's be helpful and display
381 # the output (which hopefully includes stderr).
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500382 if isinstance(value, subprocess.CalledProcessError) and value.output:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600383 error.append("Subprocess output:")
384 error.append(value.output.decode("utf-8", errors="ignore"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500385 finally:
386 logger.error("\n".join(error))
387
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500388def better_exec(code, context, text = None, realfile = "<code>", pythonexception=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500389 """
390 Similiar to better_compile, better_exec will
391 print the lines that are responsible for the
392 error.
393 """
394 import bb.parse
395 if not text:
396 text = code
397 if not hasattr(code, "co_filename"):
398 code = better_compile(code, realfile, realfile)
399 try:
400 exec(code, get_context(), context)
401 except (bb.BBHandledException, bb.parse.SkipRecipe, bb.build.FuncFailed, bb.data_smart.ExpansionError):
402 # Error already shown so passthrough, no need for traceback
403 raise
404 except Exception as e:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500405 if pythonexception:
406 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500407 (t, value, tb) = sys.exc_info()
408 try:
409 _print_exception(t, value, tb, realfile, text, context)
410 except Exception as e:
411 logger.error("Exception handler error: %s" % str(e))
412
413 e = bb.BBHandledException(e)
414 raise e
415
416def simple_exec(code, context):
417 exec(code, get_context(), context)
418
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600419def better_eval(source, locals, extraglobals = None):
420 ctx = get_context()
421 if extraglobals:
422 ctx = copy.copy(ctx)
423 for g in extraglobals:
424 ctx[g] = extraglobals[g]
425 return eval(source, ctx, locals)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500426
427@contextmanager
428def fileslocked(files):
429 """Context manager for locking and unlocking file locks."""
430 locks = []
431 if files:
432 for lockfile in files:
433 locks.append(bb.utils.lockfile(lockfile))
434
435 yield
436
437 for lock in locks:
438 bb.utils.unlockfile(lock)
439
440@contextmanager
441def timeout(seconds):
442 def timeout_handler(signum, frame):
443 pass
444
445 original_handler = signal.signal(signal.SIGALRM, timeout_handler)
446
447 try:
448 signal.alarm(seconds)
449 yield
450 finally:
451 signal.alarm(0)
452 signal.signal(signal.SIGALRM, original_handler)
453
454def lockfile(name, shared=False, retry=True, block=False):
455 """
456 Use the specified file as a lock file, return when the lock has
457 been acquired. Returns a variable to pass to unlockfile().
458 Parameters:
459 retry: True to re-try locking if it fails, False otherwise
460 block: True to block until the lock succeeds, False otherwise
461 The retry and block parameters are kind of equivalent unless you
462 consider the possibility of sending a signal to the process to break
463 out - at which point you want block=True rather than retry=True.
464 """
465 dirname = os.path.dirname(name)
466 mkdirhier(dirname)
467
468 if not os.access(dirname, os.W_OK):
469 logger.error("Unable to acquire lock '%s', directory is not writable",
470 name)
471 sys.exit(1)
472
473 op = fcntl.LOCK_EX
474 if shared:
475 op = fcntl.LOCK_SH
476 if not retry and not block:
477 op = op | fcntl.LOCK_NB
478
479 while True:
480 # If we leave the lockfiles lying around there is no problem
481 # but we should clean up after ourselves. This gives potential
482 # for races though. To work around this, when we acquire the lock
483 # we check the file we locked was still the lock file on disk.
484 # by comparing inode numbers. If they don't match or the lockfile
485 # no longer exists, we start again.
486
487 # This implementation is unfair since the last person to request the
488 # lock is the most likely to win it.
489
490 try:
491 lf = open(name, 'a+')
492 fileno = lf.fileno()
493 fcntl.flock(fileno, op)
494 statinfo = os.fstat(fileno)
495 if os.path.exists(lf.name):
496 statinfo2 = os.stat(lf.name)
497 if statinfo.st_ino == statinfo2.st_ino:
498 return lf
499 lf.close()
500 except Exception:
501 try:
502 lf.close()
503 except Exception:
504 pass
505 pass
506 if not retry:
507 return None
508
509def unlockfile(lf):
510 """
511 Unlock a file locked using lockfile()
512 """
513 try:
514 # If we had a shared lock, we need to promote to exclusive before
515 # removing the lockfile. Attempt this, ignore failures.
516 fcntl.flock(lf.fileno(), fcntl.LOCK_EX|fcntl.LOCK_NB)
517 os.unlink(lf.name)
518 except (IOError, OSError):
519 pass
520 fcntl.flock(lf.fileno(), fcntl.LOCK_UN)
521 lf.close()
522
523def md5_file(filename):
524 """
525 Return the hex string representation of the MD5 checksum of filename.
526 """
Brad Bishop220d5532018-08-14 00:59:39 +0100527 import hashlib, mmap
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500528
529 with open(filename, "rb") as f:
Brad Bishop220d5532018-08-14 00:59:39 +0100530 m = hashlib.md5()
531 try:
532 with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm:
533 for chunk in iter(lambda: mm.read(8192), b''):
534 m.update(chunk)
535 except ValueError:
536 # You can't mmap() an empty file so silence this exception
537 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500538 return m.hexdigest()
539
540def sha256_file(filename):
541 """
542 Return the hex string representation of the 256-bit SHA checksum of
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500543 filename.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500544 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500545 import hashlib
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500546
547 s = hashlib.sha256()
548 with open(filename, "rb") as f:
549 for line in f:
550 s.update(line)
551 return s.hexdigest()
552
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500553def sha1_file(filename):
554 """
555 Return the hex string representation of the SHA1 checksum of the filename
556 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500557 import hashlib
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500558
559 s = hashlib.sha1()
560 with open(filename, "rb") as f:
561 for line in f:
562 s.update(line)
563 return s.hexdigest()
564
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500565def preserved_envvars_exported():
566 """Variables which are taken from the environment and placed in and exported
567 from the metadata"""
568 return [
569 'BB_TASKHASH',
570 'HOME',
571 'LOGNAME',
572 'PATH',
573 'PWD',
574 'SHELL',
575 'TERM',
576 'USER',
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600577 'LC_ALL',
578 'BBSERVER',
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500579 ]
580
581def preserved_envvars():
582 """Variables which are taken from the environment and placed in the metadata"""
583 v = [
584 'BBPATH',
585 'BB_PRESERVE_ENV',
586 'BB_ENV_WHITELIST',
587 'BB_ENV_EXTRAWHITE',
588 ]
589 return v + preserved_envvars_exported()
590
591def filter_environment(good_vars):
592 """
593 Create a pristine environment for bitbake. This will remove variables that
594 are not known and may influence the build in a negative way.
595 """
596
597 removed_vars = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600598 for key in list(os.environ):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500599 if key in good_vars:
600 continue
601
602 removed_vars[key] = os.environ[key]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500603 del os.environ[key]
604
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600605 # If we spawn a python process, we need to have a UTF-8 locale, else python's file
606 # access methods will use ascii. You can't change that mode once the interpreter is
607 # started so we have to ensure a locale is set. Ideally we'd use C.UTF-8 but not all
608 # distros support that and we need to set something.
609 os.environ["LC_ALL"] = "en_US.UTF-8"
610
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500611 if removed_vars:
612 logger.debug(1, "Removed the following variables from the environment: %s", ", ".join(removed_vars.keys()))
613
614 return removed_vars
615
616def approved_variables():
617 """
618 Determine and return the list of whitelisted variables which are approved
619 to remain in the environment.
620 """
621 if 'BB_PRESERVE_ENV' in os.environ:
622 return os.environ.keys()
623 approved = []
624 if 'BB_ENV_WHITELIST' in os.environ:
625 approved = os.environ['BB_ENV_WHITELIST'].split()
626 approved.extend(['BB_ENV_WHITELIST'])
627 else:
628 approved = preserved_envvars()
629 if 'BB_ENV_EXTRAWHITE' in os.environ:
630 approved.extend(os.environ['BB_ENV_EXTRAWHITE'].split())
631 if 'BB_ENV_EXTRAWHITE' not in approved:
632 approved.extend(['BB_ENV_EXTRAWHITE'])
633 return approved
634
635def clean_environment():
636 """
637 Clean up any spurious environment variables. This will remove any
638 variables the user hasn't chosen to preserve.
639 """
640 if 'BB_PRESERVE_ENV' not in os.environ:
641 good_vars = approved_variables()
642 return filter_environment(good_vars)
643
644 return {}
645
646def empty_environment():
647 """
648 Remove all variables from the environment.
649 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600650 for s in list(os.environ.keys()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500651 os.unsetenv(s)
652 del os.environ[s]
653
654def build_environment(d):
655 """
656 Build an environment from all exported variables.
657 """
658 import bb.data
659 for var in bb.data.keys(d):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500660 export = d.getVarFlag(var, "export", False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500661 if export:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500662 os.environ[var] = d.getVar(var) or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500663
664def _check_unsafe_delete_path(path):
665 """
666 Basic safeguard against recursively deleting something we shouldn't. If it returns True,
667 the caller should raise an exception with an appropriate message.
668 NOTE: This is NOT meant to be a security mechanism - just a guard against silly mistakes
669 with potentially disastrous results.
670 """
671 extra = ''
672 # HOME might not be /home/something, so in case we can get it, check against it
673 homedir = os.environ.get('HOME', '')
674 if homedir:
675 extra = '|%s' % homedir
676 if re.match('(/|//|/home|/home/[^/]*%s)$' % extra, os.path.abspath(path)):
677 return True
678 return False
679
680def remove(path, recurse=False):
681 """Equivalent to rm -f or rm -rf"""
682 if not path:
683 return
684 if recurse:
685 for name in glob.glob(path):
686 if _check_unsafe_delete_path(path):
687 raise Exception('bb.utils.remove: called with dangerous path "%s" and recurse=True, refusing to delete!' % path)
688 # shutil.rmtree(name) would be ideal but its too slow
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500689 subprocess.check_call(['rm', '-rf'] + glob.glob(path))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500690 return
691 for name in glob.glob(path):
692 try:
693 os.unlink(name)
694 except OSError as exc:
695 if exc.errno != errno.ENOENT:
696 raise
697
698def prunedir(topdir):
699 # Delete everything reachable from the directory named in 'topdir'.
700 # CAUTION: This is dangerous!
701 if _check_unsafe_delete_path(topdir):
702 raise Exception('bb.utils.prunedir: called with dangerous path "%s", refusing to delete!' % topdir)
703 for root, dirs, files in os.walk(topdir, topdown = False):
704 for name in files:
705 os.remove(os.path.join(root, name))
706 for name in dirs:
707 if os.path.islink(os.path.join(root, name)):
708 os.remove(os.path.join(root, name))
709 else:
710 os.rmdir(os.path.join(root, name))
711 os.rmdir(topdir)
712
713#
714# Could also use return re.compile("(%s)" % "|".join(map(re.escape, suffixes))).sub(lambda mo: "", var)
715# but thats possibly insane and suffixes is probably going to be small
716#
717def prune_suffix(var, suffixes, d):
718 # See if var ends with any of the suffixes listed and
719 # remove it if found
720 for suffix in suffixes:
721 if var.endswith(suffix):
722 return var.replace(suffix, "")
723 return var
724
725def mkdirhier(directory):
726 """Create a directory like 'mkdir -p', but does not complain if
727 directory already exists like os.makedirs
728 """
729
730 try:
731 os.makedirs(directory)
732 except OSError as e:
733 if e.errno != errno.EEXIST:
734 raise e
735
736def movefile(src, dest, newmtime = None, sstat = None):
737 """Moves a file from src to dest, preserving all permissions and
738 attributes; mtime will be preserved even when moving across
739 filesystems. Returns true on success and false on failure. Move is
740 atomic.
741 """
742
743 #print "movefile(" + src + "," + dest + "," + str(newmtime) + "," + str(sstat) + ")"
744 try:
745 if not sstat:
746 sstat = os.lstat(src)
747 except Exception as e:
748 print("movefile: Stating source file failed...", e)
749 return None
750
751 destexists = 1
752 try:
753 dstat = os.lstat(dest)
754 except:
755 dstat = os.lstat(os.path.dirname(dest))
756 destexists = 0
757
758 if destexists:
759 if stat.S_ISLNK(dstat[stat.ST_MODE]):
760 try:
761 os.unlink(dest)
762 destexists = 0
763 except Exception as e:
764 pass
765
766 if stat.S_ISLNK(sstat[stat.ST_MODE]):
767 try:
768 target = os.readlink(src)
769 if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]):
770 os.unlink(dest)
771 os.symlink(target, dest)
772 #os.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
773 os.unlink(src)
774 return os.lstat(dest)
775 except Exception as e:
776 print("movefile: failed to properly create symlink:", dest, "->", target, e)
777 return None
778
779 renamefailed = 1
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500780 # os.rename needs to know the dest path ending with file name
781 # so append the file name to a path only if it's a dir specified
782 srcfname = os.path.basename(src)
783 destpath = os.path.join(dest, srcfname) if os.path.isdir(dest) \
784 else dest
785
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500786 if sstat[stat.ST_DEV] == dstat[stat.ST_DEV]:
787 try:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500788 os.rename(src, destpath)
789 renamefailed = 0
790 except Exception as e:
791 if e[0] != errno.EXDEV:
792 # Some random error.
793 print("movefile: Failed to move", src, "to", dest, e)
794 return None
795 # Invalid cross-device-link 'bind' mounted or actually Cross-Device
796
797 if renamefailed:
798 didcopy = 0
799 if stat.S_ISREG(sstat[stat.ST_MODE]):
800 try: # For safety copy then move it over.
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500801 shutil.copyfile(src, destpath + "#new")
802 os.rename(destpath + "#new", destpath)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500803 didcopy = 1
804 except Exception as e:
805 print('movefile: copy', src, '->', dest, 'failed.', e)
806 return None
807 else:
808 #we don't yet handle special, so we need to fall back to /bin/mv
809 a = getstatusoutput("/bin/mv -f " + "'" + src + "' '" + dest + "'")
810 if a[0] != 0:
811 print("movefile: Failed to move special file:" + src + "' to '" + dest + "'", a)
812 return None # failure
813 try:
814 if didcopy:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400815 os.lchown(destpath, sstat[stat.ST_UID], sstat[stat.ST_GID])
816 os.chmod(destpath, stat.S_IMODE(sstat[stat.ST_MODE])) # Sticky is reset on chown
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500817 os.unlink(src)
818 except Exception as e:
819 print("movefile: Failed to chown/chmod/unlink", dest, e)
820 return None
821
822 if newmtime:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500823 os.utime(destpath, (newmtime, newmtime))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500824 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500825 os.utime(destpath, (sstat[stat.ST_ATIME], sstat[stat.ST_MTIME]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500826 newmtime = sstat[stat.ST_MTIME]
827 return newmtime
828
829def copyfile(src, dest, newmtime = None, sstat = None):
830 """
831 Copies a file from src to dest, preserving all permissions and
832 attributes; mtime will be preserved even when moving across
833 filesystems. Returns true on success and false on failure.
834 """
835 #print "copyfile(" + src + "," + dest + "," + str(newmtime) + "," + str(sstat) + ")"
836 try:
837 if not sstat:
838 sstat = os.lstat(src)
839 except Exception as e:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600840 logger.warning("copyfile: stat of %s failed (%s)" % (src, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500841 return False
842
843 destexists = 1
844 try:
845 dstat = os.lstat(dest)
846 except:
847 dstat = os.lstat(os.path.dirname(dest))
848 destexists = 0
849
850 if destexists:
851 if stat.S_ISLNK(dstat[stat.ST_MODE]):
852 try:
853 os.unlink(dest)
854 destexists = 0
855 except Exception as e:
856 pass
857
858 if stat.S_ISLNK(sstat[stat.ST_MODE]):
859 try:
860 target = os.readlink(src)
861 if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]):
862 os.unlink(dest)
863 os.symlink(target, dest)
864 #os.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
865 return os.lstat(dest)
866 except Exception as e:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600867 logger.warning("copyfile: failed to create symlink %s to %s (%s)" % (dest, target, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500868 return False
869
870 if stat.S_ISREG(sstat[stat.ST_MODE]):
871 try:
872 srcchown = False
873 if not os.access(src, os.R_OK):
874 # Make sure we can read it
875 srcchown = True
876 os.chmod(src, sstat[stat.ST_MODE] | stat.S_IRUSR)
877
878 # For safety copy then move it over.
879 shutil.copyfile(src, dest + "#new")
880 os.rename(dest + "#new", dest)
881 except Exception as e:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600882 logger.warning("copyfile: copy %s to %s failed (%s)" % (src, dest, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500883 return False
884 finally:
885 if srcchown:
886 os.chmod(src, sstat[stat.ST_MODE])
887 os.utime(src, (sstat[stat.ST_ATIME], sstat[stat.ST_MTIME]))
888
889 else:
890 #we don't yet handle special, so we need to fall back to /bin/mv
891 a = getstatusoutput("/bin/cp -f " + "'" + src + "' '" + dest + "'")
892 if a[0] != 0:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600893 logger.warning("copyfile: failed to copy special file %s to %s (%s)" % (src, dest, a))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500894 return False # failure
895 try:
896 os.lchown(dest, sstat[stat.ST_UID], sstat[stat.ST_GID])
897 os.chmod(dest, stat.S_IMODE(sstat[stat.ST_MODE])) # Sticky is reset on chown
898 except Exception as e:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600899 logger.warning("copyfile: failed to chown/chmod %s (%s)" % (dest, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500900 return False
901
902 if newmtime:
903 os.utime(dest, (newmtime, newmtime))
904 else:
905 os.utime(dest, (sstat[stat.ST_ATIME], sstat[stat.ST_MTIME]))
906 newmtime = sstat[stat.ST_MTIME]
907 return newmtime
908
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500909def which(path, item, direction = 0, history = False, executable=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500910 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500911 Locate `item` in the list of paths `path` (colon separated string like $PATH).
912 If `direction` is non-zero then the list is reversed.
913 If `history` is True then the list of candidates also returned as result,history.
914 If `executable` is True then the candidate has to be an executable file,
915 otherwise the candidate simply has to exist.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500916 """
917
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500918 if executable:
919 is_candidate = lambda p: os.path.isfile(p) and os.access(p, os.X_OK)
920 else:
921 is_candidate = lambda p: os.path.exists(p)
922
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500923 hist = []
924 paths = (path or "").split(':')
925 if direction != 0:
926 paths.reverse()
927
928 for p in paths:
929 next = os.path.join(p, item)
930 hist.append(next)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500931 if is_candidate(next):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500932 if not os.path.isabs(next):
933 next = os.path.abspath(next)
934 if history:
935 return next, hist
936 return next
937
938 if history:
939 return "", hist
940 return ""
941
942def to_boolean(string, default=None):
943 if not string:
944 return default
945
946 normalized = string.lower()
947 if normalized in ("y", "yes", "1", "true"):
948 return True
949 elif normalized in ("n", "no", "0", "false"):
950 return False
951 else:
952 raise ValueError("Invalid value for to_boolean: %s" % string)
953
954def contains(variable, checkvalues, truevalue, falsevalue, d):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500955 """Check if a variable contains all the values specified.
956
957 Arguments:
958
959 variable -- the variable name. This will be fetched and expanded (using
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500960 d.getVar(variable)) and then split into a set().
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500961
962 checkvalues -- if this is a string it is split on whitespace into a set(),
963 otherwise coerced directly into a set().
964
965 truevalue -- the value to return if checkvalues is a subset of variable.
966
967 falsevalue -- the value to return if variable is empty or if checkvalues is
968 not a subset of variable.
969
970 d -- the data store.
971 """
972
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500973 val = d.getVar(variable)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500974 if not val:
975 return falsevalue
976 val = set(val.split())
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600977 if isinstance(checkvalues, str):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500978 checkvalues = set(checkvalues.split())
979 else:
980 checkvalues = set(checkvalues)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500981 if checkvalues.issubset(val):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500982 return truevalue
983 return falsevalue
984
985def contains_any(variable, checkvalues, truevalue, falsevalue, d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500986 val = d.getVar(variable)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500987 if not val:
988 return falsevalue
989 val = set(val.split())
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600990 if isinstance(checkvalues, str):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500991 checkvalues = set(checkvalues.split())
992 else:
993 checkvalues = set(checkvalues)
994 if checkvalues & val:
995 return truevalue
996 return falsevalue
997
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500998def filter(variable, checkvalues, d):
999 """Return all words in the variable that are present in the checkvalues.
1000
1001 Arguments:
1002
1003 variable -- the variable name. This will be fetched and expanded (using
1004 d.getVar(variable)) and then split into a set().
1005
1006 checkvalues -- if this is a string it is split on whitespace into a set(),
1007 otherwise coerced directly into a set().
1008
1009 d -- the data store.
1010 """
1011
1012 val = d.getVar(variable)
1013 if not val:
1014 return ''
1015 val = set(val.split())
1016 if isinstance(checkvalues, str):
1017 checkvalues = set(checkvalues.split())
1018 else:
1019 checkvalues = set(checkvalues)
1020 return ' '.join(sorted(checkvalues & val))
1021
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001022def cpu_count():
1023 return multiprocessing.cpu_count()
1024
1025def nonblockingfd(fd):
1026 fcntl.fcntl(fd, fcntl.F_SETFL, fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK)
1027
1028def process_profilelog(fn, pout = None):
1029 # Either call with a list of filenames and set pout or a filename and optionally pout.
1030 if not pout:
1031 pout = fn + '.processed'
1032 pout = open(pout, 'w')
1033
1034 import pstats
1035 if isinstance(fn, list):
1036 p = pstats.Stats(*fn, stream=pout)
1037 else:
1038 p = pstats.Stats(fn, stream=pout)
1039 p.sort_stats('time')
1040 p.print_stats()
1041 p.print_callers()
1042 p.sort_stats('cumulative')
1043 p.print_stats()
1044
1045 pout.flush()
1046 pout.close()
1047
1048#
1049# Was present to work around multiprocessing pool bugs in python < 2.7.3
1050#
1051def multiprocessingpool(*args, **kwargs):
1052
1053 import multiprocessing.pool
1054 #import multiprocessing.util
1055 #multiprocessing.util.log_to_stderr(10)
1056 # Deal with a multiprocessing bug where signals to the processes would be delayed until the work
1057 # completes. Putting in a timeout means the signals (like SIGINT/SIGTERM) get processed.
1058 def wrapper(func):
1059 def wrap(self, timeout=None):
1060 return func(self, timeout=timeout if timeout is not None else 1e100)
1061 return wrap
1062 multiprocessing.pool.IMapIterator.next = wrapper(multiprocessing.pool.IMapIterator.next)
1063
1064 return multiprocessing.Pool(*args, **kwargs)
1065
1066def exec_flat_python_func(func, *args, **kwargs):
1067 """Execute a flat python function (defined with def funcname(args):...)"""
1068 # Prepare a small piece of python code which calls the requested function
1069 # To do this we need to prepare two things - a set of variables we can use to pass
1070 # the values of arguments into the calling function, and the list of arguments for
1071 # the function being called
1072 context = {}
1073 funcargs = []
1074 # Handle unnamed arguments
1075 aidx = 1
1076 for arg in args:
1077 argname = 'arg_%s' % aidx
1078 context[argname] = arg
1079 funcargs.append(argname)
1080 aidx += 1
1081 # Handle keyword arguments
1082 context.update(kwargs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001083 funcargs.extend(['%s=%s' % (arg, arg) for arg in kwargs.keys()])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001084 code = 'retval = %s(%s)' % (func, ', '.join(funcargs))
1085 comp = bb.utils.better_compile(code, '<string>', '<string>')
1086 bb.utils.better_exec(comp, context, code, '<string>')
1087 return context['retval']
1088
1089def edit_metadata(meta_lines, variables, varfunc, match_overrides=False):
1090 """Edit lines from a recipe or config file and modify one or more
1091 specified variable values set in the file using a specified callback
1092 function. Lines are expected to have trailing newlines.
1093 Parameters:
1094 meta_lines: lines from the file; can be a list or an iterable
1095 (e.g. file pointer)
1096 variables: a list of variable names to look for. Functions
1097 may also be specified, but must be specified with '()' at
1098 the end of the name. Note that the function doesn't have
1099 any intrinsic understanding of _append, _prepend, _remove,
1100 or overrides, so these are considered as part of the name.
1101 These values go into a regular expression, so regular
1102 expression syntax is allowed.
1103 varfunc: callback function called for every variable matching
1104 one of the entries in the variables parameter. The function
1105 should take four arguments:
1106 varname: name of variable matched
1107 origvalue: current value in file
1108 op: the operator (e.g. '+=')
1109 newlines: list of lines up to this point. You can use
1110 this to prepend lines before this variable setting
1111 if you wish.
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001112 and should return a four-element tuple:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001113 newvalue: new value to substitute in, or None to drop
1114 the variable setting entirely. (If the removal
1115 results in two consecutive blank lines, one of the
1116 blank lines will also be dropped).
1117 newop: the operator to use - if you specify None here,
1118 the original operation will be used.
1119 indent: number of spaces to indent multi-line entries,
1120 or -1 to indent up to the level of the assignment
1121 and opening quote, or a string to use as the indent.
1122 minbreak: True to allow the first element of a
1123 multi-line value to continue on the same line as
1124 the assignment, False to indent before the first
1125 element.
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001126 To clarify, if you wish not to change the value, then you
1127 would return like this: return origvalue, None, 0, True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001128 match_overrides: True to match items with _overrides on the end,
1129 False otherwise
1130 Returns a tuple:
1131 updated:
1132 True if changes were made, False otherwise.
1133 newlines:
1134 Lines after processing
1135 """
1136
1137 var_res = {}
1138 if match_overrides:
1139 override_re = '(_[a-zA-Z0-9-_$(){}]+)?'
1140 else:
1141 override_re = ''
1142 for var in variables:
1143 if var.endswith('()'):
1144 var_res[var] = re.compile('^(%s%s)[ \\t]*\([ \\t]*\)[ \\t]*{' % (var[:-2].rstrip(), override_re))
1145 else:
1146 var_res[var] = re.compile('^(%s%s)[ \\t]*[?+:.]*=[+.]*[ \\t]*(["\'])' % (var, override_re))
1147
1148 updated = False
1149 varset_start = ''
1150 varlines = []
1151 newlines = []
1152 in_var = None
1153 full_value = ''
1154 var_end = ''
1155
1156 def handle_var_end():
1157 prerun_newlines = newlines[:]
1158 op = varset_start[len(in_var):].strip()
1159 (newvalue, newop, indent, minbreak) = varfunc(in_var, full_value, op, newlines)
1160 changed = (prerun_newlines != newlines)
1161
1162 if newvalue is None:
1163 # Drop the value
1164 return True
1165 elif newvalue != full_value or (newop not in [None, op]):
1166 if newop not in [None, op]:
1167 # Callback changed the operator
1168 varset_new = "%s %s" % (in_var, newop)
1169 else:
1170 varset_new = varset_start
1171
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001172 if isinstance(indent, int):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001173 if indent == -1:
1174 indentspc = ' ' * (len(varset_new) + 2)
1175 else:
1176 indentspc = ' ' * indent
1177 else:
1178 indentspc = indent
1179 if in_var.endswith('()'):
1180 # A function definition
1181 if isinstance(newvalue, list):
1182 newlines.append('%s {\n%s%s\n}\n' % (varset_new, indentspc, ('\n%s' % indentspc).join(newvalue)))
1183 else:
1184 if not newvalue.startswith('\n'):
1185 newvalue = '\n' + newvalue
1186 if not newvalue.endswith('\n'):
1187 newvalue = newvalue + '\n'
1188 newlines.append('%s {%s}\n' % (varset_new, newvalue))
1189 else:
1190 # Normal variable
1191 if isinstance(newvalue, list):
1192 if not newvalue:
1193 # Empty list -> empty string
1194 newlines.append('%s ""\n' % varset_new)
1195 elif minbreak:
1196 # First item on first line
1197 if len(newvalue) == 1:
1198 newlines.append('%s "%s"\n' % (varset_new, newvalue[0]))
1199 else:
1200 newlines.append('%s "%s \\\n' % (varset_new, newvalue[0]))
1201 for item in newvalue[1:]:
1202 newlines.append('%s%s \\\n' % (indentspc, item))
1203 newlines.append('%s"\n' % indentspc)
1204 else:
1205 # No item on first line
1206 newlines.append('%s " \\\n' % varset_new)
1207 for item in newvalue:
1208 newlines.append('%s%s \\\n' % (indentspc, item))
1209 newlines.append('%s"\n' % indentspc)
1210 else:
1211 newlines.append('%s "%s"\n' % (varset_new, newvalue))
1212 return True
1213 else:
1214 # Put the old lines back where they were
1215 newlines.extend(varlines)
1216 # If newlines was touched by the function, we'll need to return True
1217 return changed
1218
1219 checkspc = False
1220
1221 for line in meta_lines:
1222 if in_var:
1223 value = line.rstrip()
1224 varlines.append(line)
1225 if in_var.endswith('()'):
1226 full_value += '\n' + value
1227 else:
1228 full_value += value[:-1]
1229 if value.endswith(var_end):
1230 if in_var.endswith('()'):
1231 if full_value.count('{') - full_value.count('}') >= 0:
1232 continue
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001233 full_value = full_value[:-1]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001234 if handle_var_end():
1235 updated = True
1236 checkspc = True
1237 in_var = None
1238 else:
1239 skip = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001240 for (varname, var_re) in var_res.items():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001241 res = var_re.match(line)
1242 if res:
1243 isfunc = varname.endswith('()')
1244 if isfunc:
1245 splitvalue = line.split('{', 1)
1246 var_end = '}'
1247 else:
1248 var_end = res.groups()[-1]
1249 splitvalue = line.split(var_end, 1)
1250 varset_start = splitvalue[0].rstrip()
1251 value = splitvalue[1].rstrip()
1252 if not isfunc and value.endswith('\\'):
1253 value = value[:-1]
1254 full_value = value
1255 varlines = [line]
1256 in_var = res.group(1)
1257 if isfunc:
1258 in_var += '()'
1259 if value.endswith(var_end):
1260 full_value = full_value[:-1]
1261 if handle_var_end():
1262 updated = True
1263 checkspc = True
1264 in_var = None
1265 skip = True
1266 break
1267 if not skip:
1268 if checkspc:
1269 checkspc = False
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001270 if newlines and newlines[-1] == '\n' and line == '\n':
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001271 # Squash blank line if there are two consecutive blanks after a removal
1272 continue
1273 newlines.append(line)
1274 return (updated, newlines)
1275
1276
1277def edit_metadata_file(meta_file, variables, varfunc):
1278 """Edit a recipe or config file and modify one or more specified
1279 variable values set in the file using a specified callback function.
1280 The file is only written to if the value(s) actually change.
1281 This is basically the file version of edit_metadata(), see that
1282 function's description for parameter/usage information.
1283 Returns True if the file was written to, False otherwise.
1284 """
1285 with open(meta_file, 'r') as f:
1286 (updated, newlines) = edit_metadata(f, variables, varfunc)
1287 if updated:
1288 with open(meta_file, 'w') as f:
1289 f.writelines(newlines)
1290 return updated
1291
1292
1293def edit_bblayers_conf(bblayers_conf, add, remove):
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001294 """Edit bblayers.conf, adding and/or removing layers
1295 Parameters:
1296 bblayers_conf: path to bblayers.conf file to edit
1297 add: layer path (or list of layer paths) to add; None or empty
1298 list to add nothing
1299 remove: layer path (or list of layer paths) to remove; None or
1300 empty list to remove nothing
1301 Returns a tuple:
1302 notadded: list of layers specified to be added but weren't
1303 (because they were already in the list)
1304 notremoved: list of layers that were specified to be removed
1305 but weren't (because they weren't in the list)
1306 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001307
1308 import fnmatch
1309
1310 def remove_trailing_sep(pth):
1311 if pth and pth[-1] == os.sep:
1312 pth = pth[:-1]
1313 return pth
1314
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001315 approved = bb.utils.approved_variables()
1316 def canonicalise_path(pth):
1317 pth = remove_trailing_sep(pth)
1318 if 'HOME' in approved and '~' in pth:
1319 pth = os.path.expanduser(pth)
1320 return pth
1321
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001322 def layerlist_param(value):
1323 if not value:
1324 return []
1325 elif isinstance(value, list):
1326 return [remove_trailing_sep(x) for x in value]
1327 else:
1328 return [remove_trailing_sep(value)]
1329
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001330 addlayers = layerlist_param(add)
1331 removelayers = layerlist_param(remove)
1332
1333 # Need to use a list here because we can't set non-local variables from a callback in python 2.x
1334 bblayercalls = []
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001335 removed = []
1336 plusequals = False
1337 orig_bblayers = []
1338
1339 def handle_bblayers_firstpass(varname, origvalue, op, newlines):
1340 bblayercalls.append(op)
1341 if op == '=':
1342 del orig_bblayers[:]
1343 orig_bblayers.extend([canonicalise_path(x) for x in origvalue.split()])
1344 return (origvalue, None, 2, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001345
1346 def handle_bblayers(varname, origvalue, op, newlines):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001347 updated = False
1348 bblayers = [remove_trailing_sep(x) for x in origvalue.split()]
1349 if removelayers:
1350 for removelayer in removelayers:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001351 for layer in bblayers:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001352 if fnmatch.fnmatch(canonicalise_path(layer), canonicalise_path(removelayer)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001353 updated = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001354 bblayers.remove(layer)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001355 removed.append(removelayer)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001356 break
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001357 if addlayers and not plusequals:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001358 for addlayer in addlayers:
1359 if addlayer not in bblayers:
1360 updated = True
1361 bblayers.append(addlayer)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001362 del addlayers[:]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001363
1364 if updated:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001365 if op == '+=' and not bblayers:
1366 bblayers = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001367 return (bblayers, None, 2, False)
1368 else:
1369 return (origvalue, None, 2, False)
1370
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001371 with open(bblayers_conf, 'r') as f:
1372 (_, newlines) = edit_metadata(f, ['BBLAYERS'], handle_bblayers_firstpass)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001373
1374 if not bblayercalls:
1375 raise Exception('Unable to find BBLAYERS in %s' % bblayers_conf)
1376
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001377 # Try to do the "smart" thing depending on how the user has laid out
1378 # their bblayers.conf file
1379 if bblayercalls.count('+=') > 1:
1380 plusequals = True
1381
1382 removelayers_canon = [canonicalise_path(layer) for layer in removelayers]
1383 notadded = []
1384 for layer in addlayers:
1385 layer_canon = canonicalise_path(layer)
1386 if layer_canon in orig_bblayers and not layer_canon in removelayers_canon:
1387 notadded.append(layer)
1388 notadded_canon = [canonicalise_path(layer) for layer in notadded]
1389 addlayers[:] = [layer for layer in addlayers if canonicalise_path(layer) not in notadded_canon]
1390
1391 (updated, newlines) = edit_metadata(newlines, ['BBLAYERS'], handle_bblayers)
1392 if addlayers:
1393 # Still need to add these
1394 for addlayer in addlayers:
1395 newlines.append('BBLAYERS += "%s"\n' % addlayer)
1396 updated = True
1397
1398 if updated:
1399 with open(bblayers_conf, 'w') as f:
1400 f.writelines(newlines)
1401
1402 notremoved = list(set(removelayers) - set(removed))
1403
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001404 return (notadded, notremoved)
1405
1406
1407def get_file_layer(filename, d):
1408 """Determine the collection (as defined by a layer's layer.conf file) containing the specified file"""
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001409 collections = (d.getVar('BBFILE_COLLECTIONS') or '').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001410 collection_res = {}
1411 for collection in collections:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001412 collection_res[collection] = d.getVar('BBFILE_PATTERN_%s' % collection) or ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001413
1414 def path_to_layer(path):
1415 # Use longest path so we handle nested layers
1416 matchlen = 0
1417 match = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001418 for collection, regex in collection_res.items():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001419 if len(regex) > matchlen and re.match(regex, path):
1420 matchlen = len(regex)
1421 match = collection
1422 return match
1423
1424 result = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001425 bbfiles = (d.getVar('BBFILES') or '').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001426 bbfilesmatch = False
1427 for bbfilesentry in bbfiles:
1428 if fnmatch.fnmatch(filename, bbfilesentry):
1429 bbfilesmatch = True
1430 result = path_to_layer(bbfilesentry)
1431
1432 if not bbfilesmatch:
1433 # Probably a bbclass
1434 result = path_to_layer(filename)
1435
1436 return result
1437
1438
1439# Constant taken from http://linux.die.net/include/linux/prctl.h
1440PR_SET_PDEATHSIG = 1
1441
1442class PrCtlError(Exception):
1443 pass
1444
1445def signal_on_parent_exit(signame):
1446 """
1447 Trigger signame to be sent when the parent process dies
1448 """
1449 signum = getattr(signal, signame)
1450 # http://linux.die.net/man/2/prctl
1451 result = cdll['libc.so.6'].prctl(PR_SET_PDEATHSIG, signum)
1452 if result != 0:
1453 raise PrCtlError('prctl failed with error code %s' % result)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001454
1455#
1456# Manually call the ioprio syscall. We could depend on other libs like psutil
1457# however this gets us enough of what we need to bitbake for now without the
1458# dependency
1459#
1460_unamearch = os.uname()[4]
1461IOPRIO_WHO_PROCESS = 1
1462IOPRIO_CLASS_SHIFT = 13
1463
1464def ioprio_set(who, cls, value):
1465 NR_ioprio_set = None
1466 if _unamearch == "x86_64":
1467 NR_ioprio_set = 251
1468 elif _unamearch[0] == "i" and _unamearch[2:3] == "86":
1469 NR_ioprio_set = 289
1470
1471 if NR_ioprio_set:
1472 ioprio = value | (cls << IOPRIO_CLASS_SHIFT)
1473 rc = cdll['libc.so.6'].syscall(NR_ioprio_set, IOPRIO_WHO_PROCESS, who, ioprio)
1474 if rc != 0:
1475 raise ValueError("Unable to set ioprio, syscall returned %s" % rc)
1476 else:
1477 bb.warn("Unable to set IO Prio for arch %s" % _unamearch)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001478
1479def set_process_name(name):
1480 from ctypes import cdll, byref, create_string_buffer
1481 # This is nice to have for debugging, not essential
1482 try:
1483 libc = cdll.LoadLibrary('libc.so.6')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001484 buf = create_string_buffer(bytes(name, 'utf-8'))
1485 libc.prctl(15, byref(buf), 0, 0, 0)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001486 except:
1487 pass
1488
1489# export common proxies variables from datastore to environment
1490def export_proxies(d):
1491 import os
1492
1493 variables = ['http_proxy', 'HTTP_PROXY', 'https_proxy', 'HTTPS_PROXY',
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001494 'ftp_proxy', 'FTP_PROXY', 'no_proxy', 'NO_PROXY',
1495 'GIT_PROXY_COMMAND']
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001496 exported = False
1497
1498 for v in variables:
1499 if v in os.environ.keys():
1500 exported = True
1501 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001502 v_proxy = d.getVar(v)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001503 if v_proxy is not None:
1504 os.environ[v] = v_proxy
1505 exported = True
1506
1507 return exported
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001508
1509
1510def load_plugins(logger, plugins, pluginpath):
1511 def load_plugin(name):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001512 logger.debug(1, 'Loading plugin %s' % name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001513 fp, pathname, description = imp.find_module(name, [pluginpath])
1514 try:
1515 return imp.load_module(name, fp, pathname, description)
1516 finally:
1517 if fp:
1518 fp.close()
1519
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001520 logger.debug(1, 'Loading plugins from %s...' % pluginpath)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001521
1522 expanded = (glob.glob(os.path.join(pluginpath, '*' + ext))
1523 for ext in python_extensions)
1524 files = itertools.chain.from_iterable(expanded)
1525 names = set(os.path.splitext(os.path.basename(fn))[0] for fn in files)
1526 for name in names:
1527 if name != '__init__':
1528 plugin = load_plugin(name)
1529 if hasattr(plugin, 'plugin_init'):
1530 obj = plugin.plugin_init(plugins)
1531 plugins.append(obj or plugin)
1532 else:
1533 plugins.append(plugin)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001534
1535
1536class LogCatcher(logging.Handler):
1537 """Logging handler for collecting logged messages so you can check them later"""
1538 def __init__(self):
1539 self.messages = []
1540 logging.Handler.__init__(self, logging.WARNING)
1541 def emit(self, record):
1542 self.messages.append(bb.build.logformatter.format(record))
1543 def contains(self, message):
1544 return (message in self.messages)