blob: 80f0442d0aec9954135a38c9782596ae95239bda [file] [log] [blame]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001import subprocess
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002
3def read_file(filename):
4 try:
5 f = open( filename, "r" )
6 except IOError as reason:
7 return "" # WARNING: can't raise an error now because of the new RDEPENDS handling. This is a bit ugly. :M:
8 else:
9 data = f.read().strip()
10 f.close()
11 return data
12 return None
13
14def ifelse(condition, iftrue = True, iffalse = False):
15 if condition:
16 return iftrue
17 else:
18 return iffalse
19
20def conditional(variable, checkvalue, truevalue, falsevalue, d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -050021 if d.getVar(variable) == checkvalue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -050022 return truevalue
23 else:
24 return falsevalue
25
26def less_or_equal(variable, checkvalue, truevalue, falsevalue, d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -050027 if float(d.getVar(variable)) <= float(checkvalue):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050028 return truevalue
29 else:
30 return falsevalue
31
32def version_less_or_equal(variable, checkvalue, truevalue, falsevalue, d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -050033 result = bb.utils.vercmp_string(d.getVar(variable), checkvalue)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050034 if result <= 0:
35 return truevalue
36 else:
37 return falsevalue
38
39def both_contain(variable1, variable2, checkvalue, d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -050040 val1 = d.getVar(variable1)
41 val2 = d.getVar(variable2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050042 val1 = set(val1.split())
43 val2 = set(val2.split())
Patrick Williamsc0f7c042017-02-23 20:41:17 -060044 if isinstance(checkvalue, str):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050045 checkvalue = set(checkvalue.split())
46 else:
47 checkvalue = set(checkvalue)
48 if checkvalue.issubset(val1) and checkvalue.issubset(val2):
49 return " ".join(checkvalue)
50 else:
51 return ""
52
53def set_intersect(variable1, variable2, d):
54 """
55 Expand both variables, interpret them as lists of strings, and return the
56 intersection as a flattened string.
57
58 For example:
59 s1 = "a b c"
60 s2 = "b c d"
61 s3 = set_intersect(s1, s2)
62 => s3 = "b c"
63 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -050064 val1 = set(d.getVar(variable1).split())
65 val2 = set(d.getVar(variable2).split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -050066 return " ".join(val1 & val2)
67
68def prune_suffix(var, suffixes, d):
69 # See if var ends with any of the suffixes listed and
70 # remove it if found
71 for suffix in suffixes:
72 if var.endswith(suffix):
73 var = var.replace(suffix, "")
74
Brad Bishop6e60e8b2018-02-01 10:27:11 -050075 prefix = d.getVar("MLPREFIX")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050076 if prefix and var.startswith(prefix):
77 var = var.replace(prefix, "")
78
79 return var
80
81def str_filter(f, str, d):
82 from re import match
Patrick Williamsc0f7c042017-02-23 20:41:17 -060083 return " ".join([x for x in str.split() if match(f, x, 0)])
Patrick Williamsc124f4f2015-09-15 14:41:29 -050084
85def str_filter_out(f, str, d):
86 from re import match
Patrick Williamsc0f7c042017-02-23 20:41:17 -060087 return " ".join([x for x in str.split() if not match(f, x, 0)])
Patrick Williamsc124f4f2015-09-15 14:41:29 -050088
Brad Bishop6e60e8b2018-02-01 10:27:11 -050089def build_depends_string(depends, task):
90 """Append a taskname to a string of dependencies as used by the [depends] flag"""
91 return " ".join(dep + ":" + task for dep in depends.split())
92
Patrick Williamsc124f4f2015-09-15 14:41:29 -050093def inherits(d, *classes):
94 """Return True if the metadata inherits any of the specified classes"""
95 return any(bb.data.inherits_class(cls, d) for cls in classes)
96
97def features_backfill(var,d):
98 # This construct allows the addition of new features to variable specified
99 # as var
100 # Example for var = "DISTRO_FEATURES"
101 # This construct allows the addition of new features to DISTRO_FEATURES
102 # that if not present would disable existing functionality, without
103 # disturbing distributions that have already set DISTRO_FEATURES.
104 # Distributions wanting to elide a value in DISTRO_FEATURES_BACKFILL should
105 # add the feature to DISTRO_FEATURES_BACKFILL_CONSIDERED
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500106 features = (d.getVar(var) or "").split()
107 backfill = (d.getVar(var+"_BACKFILL") or "").split()
108 considered = (d.getVar(var+"_BACKFILL_CONSIDERED") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500109
110 addfeatures = []
111 for feature in backfill:
112 if feature not in features and feature not in considered:
113 addfeatures.append(feature)
114
115 if addfeatures:
116 d.appendVar(var, " " + " ".join(addfeatures))
117
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500118def all_distro_features(d, features, truevalue="1", falsevalue=""):
119 """
120 Returns truevalue if *all* given features are set in DISTRO_FEATURES,
121 else falsevalue. The features can be given as single string or anything
122 that can be turned into a set.
123
124 This is a shorter, more flexible version of
125 bb.utils.contains("DISTRO_FEATURES", features, truevalue, falsevalue, d).
126
127 Without explicit true/false values it can be used directly where
128 Python expects a boolean:
129 if oe.utils.all_distro_features(d, "foo bar"):
130 bb.fatal("foo and bar are mutually exclusive DISTRO_FEATURES")
131
132 With just a truevalue, it can be used to include files that are meant to be
133 used only when requested via DISTRO_FEATURES:
134 require ${@ oe.utils.all_distro_features(d, "foo bar", "foo-and-bar.inc")
135 """
136 return bb.utils.contains("DISTRO_FEATURES", features, truevalue, falsevalue, d)
137
138def any_distro_features(d, features, truevalue="1", falsevalue=""):
139 """
140 Returns truevalue if at least *one* of the given features is set in DISTRO_FEATURES,
141 else falsevalue. The features can be given as single string or anything
142 that can be turned into a set.
143
144 This is a shorter, more flexible version of
145 bb.utils.contains_any("DISTRO_FEATURES", features, truevalue, falsevalue, d).
146
147 Without explicit true/false values it can be used directly where
148 Python expects a boolean:
149 if not oe.utils.any_distro_features(d, "foo bar"):
150 bb.fatal("foo, bar or both must be set in DISTRO_FEATURES")
151
152 With just a truevalue, it can be used to include files that are meant to be
153 used only when requested via DISTRO_FEATURES:
154 require ${@ oe.utils.any_distro_features(d, "foo bar", "foo-or-bar.inc")
155
156 """
157 return bb.utils.contains_any("DISTRO_FEATURES", features, truevalue, falsevalue, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500158
Brad Bishop316dfdd2018-06-25 12:45:53 -0400159def parallel_make(d):
160 """
161 Return the integer value for the number of parallel threads to use when
162 building, scraped out of PARALLEL_MAKE. If no parallelization option is
163 found, returns None
164
165 e.g. if PARALLEL_MAKE = "-j 10", this will return 10 as an integer.
166 """
167 pm = (d.getVar('PARALLEL_MAKE') or '').split()
168 # look for '-j' and throw other options (e.g. '-l') away
169 while pm:
170 opt = pm.pop(0)
171 if opt == '-j':
172 v = pm.pop(0)
173 elif opt.startswith('-j'):
174 v = opt[2:].strip()
175 else:
176 continue
177
178 return int(v)
179
180 return None
181
182def parallel_make_argument(d, fmt, limit=None):
183 """
184 Helper utility to construct a parallel make argument from the number of
185 parallel threads specified in PARALLEL_MAKE.
186
187 Returns the input format string `fmt` where a single '%d' will be expanded
188 with the number of parallel threads to use. If `limit` is specified, the
189 number of parallel threads will be no larger than it. If no parallelization
190 option is found in PARALLEL_MAKE, returns an empty string
191
192 e.g. if PARALLEL_MAKE = "-j 10", parallel_make_argument(d, "-n %d") will return
193 "-n 10"
194 """
195 v = parallel_make(d)
196 if v:
197 if limit:
198 v = min(limit, v)
199 return fmt % v
200 return ''
201
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500202def packages_filter_out_system(d):
203 """
204 Return a list of packages from PACKAGES with the "system" packages such as
205 PN-dbg PN-doc PN-locale-eb-gb removed.
206 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500207 pn = d.getVar('PN')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600208 blacklist = [pn + suffix for suffix in ('', '-dbg', '-dev', '-doc', '-locale', '-staticdev')]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500209 localepkg = pn + "-locale-"
210 pkgs = []
211
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500212 for pkg in d.getVar('PACKAGES').split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500213 if pkg not in blacklist and localepkg not in pkg:
214 pkgs.append(pkg)
215 return pkgs
216
217def getstatusoutput(cmd):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500218 return subprocess.getstatusoutput(cmd)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500219
220
221def trim_version(version, num_parts=2):
222 """
223 Return just the first <num_parts> of <version>, split by periods. For
224 example, trim_version("1.2.3", 2) will return "1.2".
225 """
226 if type(version) is not str:
227 raise TypeError("Version should be a string")
228 if num_parts < 1:
229 raise ValueError("Cannot split to parts < 1")
230
231 parts = version.split(".")
232 trimmed = ".".join(parts[:num_parts])
233 return trimmed
234
235def cpu_count():
236 import multiprocessing
237 return multiprocessing.cpu_count()
238
239def execute_pre_post_process(d, cmds):
240 if cmds is None:
241 return
242
243 for cmd in cmds.strip().split(';'):
244 cmd = cmd.strip()
245 if cmd != '':
246 bb.note("Executing %s ..." % cmd)
247 bb.build.exec_func(cmd, d)
248
249def multiprocess_exec(commands, function):
250 import signal
251 import multiprocessing
252
253 if not commands:
254 return []
255
256 def init_worker():
257 signal.signal(signal.SIGINT, signal.SIG_IGN)
258
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500259 fails = []
260
261 def failures(res):
262 fails.append(res)
263
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500264 nproc = min(multiprocessing.cpu_count(), len(commands))
265 pool = bb.utils.multiprocessingpool(nproc, init_worker)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500266
267 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500268 mapresult = pool.map_async(function, commands, error_callback=failures)
269
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500270 pool.close()
271 pool.join()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500272 results = mapresult.get()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500273 except KeyboardInterrupt:
274 pool.terminate()
275 pool.join()
276 raise
277
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500278 if fails:
279 raise fails[0]
280
281 return results
282
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500283def squashspaces(string):
284 import re
285 return re.sub("\s+", " ", string).strip()
286
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500287def format_pkg_list(pkg_dict, ret_format=None):
288 output = []
289
290 if ret_format == "arch":
291 for pkg in sorted(pkg_dict):
292 output.append("%s %s" % (pkg, pkg_dict[pkg]["arch"]))
293 elif ret_format == "file":
294 for pkg in sorted(pkg_dict):
295 output.append("%s %s %s" % (pkg, pkg_dict[pkg]["filename"], pkg_dict[pkg]["arch"]))
296 elif ret_format == "ver":
297 for pkg in sorted(pkg_dict):
298 output.append("%s %s %s" % (pkg, pkg_dict[pkg]["arch"], pkg_dict[pkg]["ver"]))
299 elif ret_format == "deps":
300 for pkg in sorted(pkg_dict):
301 for dep in pkg_dict[pkg]["deps"]:
302 output.append("%s|%s" % (pkg, dep))
303 else:
304 for pkg in sorted(pkg_dict):
305 output.append(pkg)
306
307 return '\n'.join(output)
308
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500309def host_gcc_version(d):
310 import re, subprocess
311
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500312 compiler = d.getVar("BUILD_CC")
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500313 try:
314 env = os.environ.copy()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500315 env["PATH"] = d.getVar("PATH")
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500316 output = subprocess.check_output("%s --version" % compiler, shell=True, env=env).decode("utf-8")
317 except subprocess.CalledProcessError as e:
318 bb.fatal("Error running %s --version: %s" % (compiler, e.output.decode("utf-8")))
319
320 match = re.match(".* (\d\.\d)\.\d.*", output.split('\n')[0])
321 if not match:
322 bb.fatal("Can't get compiler version from %s --version output" % compiler)
323
324 version = match.group(1)
325 return "-%s" % version if version in ("4.8", "4.9") else ""
326
Brad Bishop316dfdd2018-06-25 12:45:53 -0400327
328def get_multilib_datastore(variant, d):
329 localdata = bb.data.createCopy(d)
330 overrides = localdata.getVar("OVERRIDES", False) + ":virtclass-multilib-" + variant
331 localdata.setVar("OVERRIDES", overrides)
332 localdata.setVar("MLPREFIX", variant + "-")
333 return localdata
334
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500335#
336# Python 2.7 doesn't have threaded pools (just multiprocessing)
337# so implement a version here
338#
339
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600340from queue import Queue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500341from threading import Thread
342
343class ThreadedWorker(Thread):
344 """Thread executing tasks from a given tasks queue"""
345 def __init__(self, tasks, worker_init, worker_end):
346 Thread.__init__(self)
347 self.tasks = tasks
348 self.daemon = True
349
350 self.worker_init = worker_init
351 self.worker_end = worker_end
352
353 def run(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600354 from queue import Empty
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500355
356 if self.worker_init is not None:
357 self.worker_init(self)
358
359 while True:
360 try:
361 func, args, kargs = self.tasks.get(block=False)
362 except Empty:
363 if self.worker_end is not None:
364 self.worker_end(self)
365 break
366
367 try:
368 func(self, *args, **kargs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600369 except Exception as e:
370 print(e)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500371 finally:
372 self.tasks.task_done()
373
374class ThreadedPool:
375 """Pool of threads consuming tasks from a queue"""
376 def __init__(self, num_workers, num_tasks, worker_init=None,
377 worker_end=None):
378 self.tasks = Queue(num_tasks)
379 self.workers = []
380
381 for _ in range(num_workers):
382 worker = ThreadedWorker(self.tasks, worker_init, worker_end)
383 self.workers.append(worker)
384
385 def start(self):
386 for worker in self.workers:
387 worker.start()
388
389 def add_task(self, func, *args, **kargs):
390 """Add a task to the queue"""
391 self.tasks.put((func, args, kargs))
392
393 def wait_completion(self):
394 """Wait for completion of all the tasks in the queue"""
395 self.tasks.join()
396 for worker in self.workers:
397 worker.join()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500398
399def write_ld_so_conf(d):
400 # Some utils like prelink may not have the correct target library paths
401 # so write an ld.so.conf to help them
402 ldsoconf = d.expand("${STAGING_DIR_TARGET}${sysconfdir}/ld.so.conf")
403 if os.path.exists(ldsoconf):
404 bb.utils.remove(ldsoconf)
405 bb.utils.mkdirhier(os.path.dirname(ldsoconf))
406 with open(ldsoconf, "w") as f:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500407 f.write(d.getVar("base_libdir") + '\n')
408 f.write(d.getVar("libdir") + '\n')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600409
410class ImageQAFailed(bb.build.FuncFailed):
411 def __init__(self, description, name=None, logfile=None):
412 self.description = description
413 self.name = name
414 self.logfile=logfile
415
416 def __str__(self):
417 msg = 'Function failed: %s' % self.name
418 if self.description:
419 msg = msg + ' (%s)' % self.description
420
421 return msg