diff options
Diffstat (limited to 'meta/classes/staging.bbclass')
-rw-r--r-- | meta/classes/staging.bbclass | 349 |
1 files changed, 347 insertions, 2 deletions
diff --git a/meta/classes/staging.bbclass b/meta/classes/staging.bbclass index 2512ae6f5d..1a4668e5d3 100644 --- a/meta/classes/staging.bbclass +++ b/meta/classes/staging.bbclass | |||
@@ -235,12 +235,357 @@ do_populate_sysroot[depends] += "${POPULATESYSROOTDEPS}" | |||
235 | SSTATETASKS += "do_populate_sysroot" | 235 | SSTATETASKS += "do_populate_sysroot" |
236 | do_populate_sysroot[cleandirs] = "${SYSROOT_DESTDIR}" | 236 | do_populate_sysroot[cleandirs] = "${SYSROOT_DESTDIR}" |
237 | do_populate_sysroot[sstate-inputdirs] = "${SYSROOT_DESTDIR}" | 237 | do_populate_sysroot[sstate-inputdirs] = "${SYSROOT_DESTDIR}" |
238 | do_populate_sysroot[sstate-outputdirs] = "${STAGING_DIR_HOST}/" | 238 | do_populate_sysroot[sstate-outputdirs] = "${STAGING_DIR}-components/${PACKAGE_ARCH}/${PN}" |
239 | do_populate_sysroot[stamp-extra-info] = "${MACHINE}" | 239 | do_populate_sysroot[sstate-fixmedir] = "${STAGING_DIR}-components/${PACKAGE_ARCH}/${PN}" |
240 | 240 | ||
241 | python do_populate_sysroot_setscene () { | 241 | python do_populate_sysroot_setscene () { |
242 | sstate_setscene(d) | 242 | sstate_setscene(d) |
243 | } | 243 | } |
244 | addtask do_populate_sysroot_setscene | 244 | addtask do_populate_sysroot_setscene |
245 | 245 | ||
246 | def staging_copyfile(c, target, fixme, postinsts, stagingdir): | ||
247 | import errno | ||
248 | |||
249 | if c.endswith("/fixmepath"): | ||
250 | fixme.append(c) | ||
251 | return None | ||
252 | if c.endswith("/fixmepath.cmd"): | ||
253 | return None | ||
254 | #bb.warn(c) | ||
255 | dest = c.replace(stagingdir, "") | ||
256 | dest = target + "/" + "/".join(dest.split("/")[3:]) | ||
257 | bb.utils.mkdirhier(os.path.dirname(dest)) | ||
258 | if "/usr/bin/postinst-" in c: | ||
259 | postinsts.append(dest) | ||
260 | if os.path.islink(c): | ||
261 | linkto = os.readlink(c) | ||
262 | if os.path.lexists(dest): | ||
263 | if os.readlink(dest) == linkto: | ||
264 | return dest | ||
265 | bb.fatal("Link %s already exists to a different location?" % dest) | ||
266 | os.symlink(linkto, dest) | ||
267 | #bb.warn(c) | ||
268 | else: | ||
269 | try: | ||
270 | os.link(c, dest) | ||
271 | except OSError as err: | ||
272 | if err.errno == errno.EXDEV: | ||
273 | bb.utils.copyfile(c, dest) | ||
274 | else: | ||
275 | raise | ||
276 | return dest | ||
277 | |||
278 | def staging_copydir(c, target, stagingdir): | ||
279 | dest = c.replace(stagingdir, "") | ||
280 | dest = target + "/" + "/".join(dest.split("/")[3:]) | ||
281 | bb.utils.mkdirhier(dest) | ||
282 | |||
283 | def staging_processfixme(fixme, target, recipesysroot, recipesysrootnative, d): | ||
284 | import subprocess | ||
285 | |||
286 | if not fixme: | ||
287 | return | ||
288 | cmd = "sed -e 's:^[^/]*/:%s/:g' %s | xargs sed -i -e 's:FIXMESTAGINGDIRTARGET:%s:g; s:FIXMESTAGINGDIRHOST:%s:g'" % (target, " ".join(fixme), recipesysroot, recipesysrootnative) | ||
289 | for fixmevar in ['PKGDATA_DIR']: | ||
290 | fixme_path = d.getVar(fixmevar) | ||
291 | cmd += " -e 's:FIXME_%s:%s:g'" % (fixmevar, fixme_path) | ||
292 | bb.note(cmd) | ||
293 | subprocess.check_call(cmd, shell=True) | ||
294 | |||
295 | |||
296 | def staging_populate_sysroot_dir(targetsysroot, nativesysroot, native, d): | ||
297 | import glob | ||
298 | import subprocess | ||
299 | |||
300 | fixme = [] | ||
301 | postinsts = [] | ||
302 | stagingdir = d.getVar("STAGING_DIR") | ||
303 | if native: | ||
304 | pkgarchs = ['${BUILD_ARCH}', '${BUILD_ARCH}_*'] | ||
305 | targetdir = nativesysroot | ||
306 | else: | ||
307 | pkgarchs = ['${MACHINE_ARCH}', '${TUNE_PKGARCH}', 'allarch'] | ||
308 | targetdir = targetsysroot | ||
309 | |||
310 | bb.utils.mkdirhier(targetdir) | ||
311 | for pkgarch in pkgarchs: | ||
312 | for manifest in glob.glob(d.expand("${SSTATE_MANIFESTS}/manifest-%s-*.populate_sysroot" % pkgarch)): | ||
313 | if manifest.endswith("-initial.populate_sysroot"): | ||
314 | # skip glibc-initial and libgcc-initial due to file overlap | ||
315 | continue | ||
316 | tmanifest = targetdir + "/" + os.path.basename(manifest) | ||
317 | if os.path.exists(tmanifest): | ||
318 | continue | ||
319 | try: | ||
320 | os.link(manifest, tmanifest) | ||
321 | except OSError as err: | ||
322 | if err.errno == errno.EXDEV: | ||
323 | bb.utils.copyfile(manifest, tmanifest) | ||
324 | else: | ||
325 | raise | ||
326 | with open(manifest, "r") as f: | ||
327 | for l in f: | ||
328 | l = l.strip() | ||
329 | if l.endswith("/"): | ||
330 | staging_copydir(l, targetdir, stagingdir) | ||
331 | continue | ||
332 | staging_copyfile(l, targetdir, fixme, postinsts, stagingdir) | ||
333 | |||
334 | staging_processfixme(fixme, targetdir, targetsysroot, nativesysroot, d) | ||
335 | for p in postinsts: | ||
336 | subprocess.check_call(p, shell=True) | ||
337 | |||
338 | # | ||
339 | # Manifests here are complicated. The main sysroot area has the unpacked sstate | ||
340 | # which us unrelocated and tracked by the main sstate manifests. Each recipe | ||
341 | # specific sysroot has manifests for each dependency that is installed there. | ||
342 | # The task hash is used to tell whether the data needs to be reinstalled. We | ||
343 | # use a symlink to point to the currently installed hash. There is also a | ||
344 | # "complete" stamp file which is used to mark if installation completed. If | ||
345 | # something fails (e.g. a postinst), this won't get written and we would | ||
346 | # remove and reinstall the dependency. This also means partially installed | ||
347 | # dependencies should get cleaned up correctly. | ||
348 | # | ||
349 | |||
350 | python extend_recipe_sysroot() { | ||
351 | import copy | ||
352 | import subprocess | ||
353 | |||
354 | taskdepdata = d.getVar("BB_TASKDEPDATA", False) | ||
355 | mytaskname = d.getVar("BB_RUNTASK") | ||
356 | #bb.warn(str(taskdepdata)) | ||
357 | pn = d.getVar("PN") | ||
358 | |||
359 | if mytaskname.endswith("_setscene"): | ||
360 | mytaskname = mytaskname.replace("_setscene", "") | ||
361 | |||
362 | start = None | ||
363 | configuredeps = [] | ||
364 | for dep in taskdepdata: | ||
365 | data = taskdepdata[dep] | ||
366 | if data[1] == mytaskname and data[0] == pn: | ||
367 | start = dep | ||
368 | break | ||
369 | if start is None: | ||
370 | bb.fatal("Couldn't find ourself in BB_TASKDEPDATA?") | ||
371 | |||
372 | # We need to figure out which sysroot files we need to expose to this task. | ||
373 | # This needs to match what would get restored from sstate, which is controlled | ||
374 | # ultimately by calls from bitbake to setscene_depvalid(). | ||
375 | # That function expects a setscene dependency tree. We build a dependency tree | ||
376 | # condensed to inter-sstate task dependencies, similar to that used by setscene | ||
377 | # tasks. We can then call into setscene_depvalid() and decide | ||
378 | # which dependencies we can "see" and should expose in the recipe specific sysroot. | ||
379 | setscenedeps = copy.deepcopy(taskdepdata) | ||
380 | |||
381 | start = set([start]) | ||
382 | |||
383 | sstatetasks = d.getVar("SSTATETASKS").split() | ||
384 | |||
385 | def print_dep_tree(deptree): | ||
386 | data = "" | ||
387 | for dep in deptree: | ||
388 | deps = " " + "\n ".join(deptree[dep][3]) + "\n" | ||
389 | data = "%s:\n %s\n %s\n%s %s\n %s\n" % (deptree[dep][0], deptree[dep][1], deptree[dep][2], deps, deptree[dep][4], deptree[dep][5]) | ||
390 | return data | ||
391 | |||
392 | #bb.note("Full dep tree is:\n%s" % print_dep_tree(taskdepdata)) | ||
393 | |||
394 | #bb.note(" start2 is %s" % str(start)) | ||
395 | |||
396 | # If start is an sstate task (like do_package) we need to add in its direct dependencies | ||
397 | # else the code below won't recurse into them. | ||
398 | for dep in set(start): | ||
399 | for dep2 in setscenedeps[dep][3]: | ||
400 | start.add(dep2) | ||
401 | start.remove(dep) | ||
402 | |||
403 | #bb.note(" start3 is %s" % str(start)) | ||
404 | |||
405 | # Create collapsed do_populate_sysroot -> do_populate_sysroot tree | ||
406 | for dep in taskdepdata: | ||
407 | data = setscenedeps[dep] | ||
408 | if data[1] not in sstatetasks: | ||
409 | for dep2 in setscenedeps: | ||
410 | data2 = setscenedeps[dep2] | ||
411 | if dep in data2[3]: | ||
412 | data2[3].update(setscenedeps[dep][3]) | ||
413 | data2[3].remove(dep) | ||
414 | if dep in start: | ||
415 | start.update(setscenedeps[dep][3]) | ||
416 | start.remove(dep) | ||
417 | del setscenedeps[dep] | ||
418 | |||
419 | # Remove circular references | ||
420 | for dep in setscenedeps: | ||
421 | if dep in setscenedeps[dep][3]: | ||
422 | setscenedeps[dep][3].remove(dep) | ||
423 | |||
424 | #bb.note("Computed dep tree is:\n%s" % print_dep_tree(setscenedeps)) | ||
425 | #bb.note(" start is %s" % str(start)) | ||
426 | |||
427 | # Direct dependencies should be present and can be depended upon | ||
428 | for dep in set(start): | ||
429 | if setscenedeps[dep][1] == "do_populate_sysroot": | ||
430 | if dep not in configuredeps: | ||
431 | configuredeps.append(dep) | ||
432 | bb.note("Direct dependencies are %s" % str(configuredeps)) | ||
433 | #bb.note(" or %s" % str(start)) | ||
434 | |||
435 | # Call into setscene_depvalid for each sub-dependency and only copy sysroot files | ||
436 | # for ones that would be restored from sstate. | ||
437 | done = list(start) | ||
438 | next = list(start) | ||
439 | while next: | ||
440 | new = [] | ||
441 | for dep in next: | ||
442 | data = setscenedeps[dep] | ||
443 | for datadep in data[3]: | ||
444 | if datadep in done: | ||
445 | continue | ||
446 | taskdeps = {} | ||
447 | taskdeps[dep] = setscenedeps[dep][:2] | ||
448 | taskdeps[datadep] = setscenedeps[datadep][:2] | ||
449 | retval = setscene_depvalid(datadep, taskdeps, [], d) | ||
450 | if retval: | ||
451 | bb.note("Skipping setscene dependency %s for installation into the sysroot" % datadep) | ||
452 | continue | ||
453 | done.append(datadep) | ||
454 | new.append(datadep) | ||
455 | if datadep not in configuredeps and setscenedeps[datadep][1] == "do_populate_sysroot": | ||
456 | configuredeps.append(datadep) | ||
457 | bb.note("Adding dependency on %s" % setscenedeps[datadep][0]) | ||
458 | else: | ||
459 | bb.note("Following dependency on %s" % setscenedeps[datadep][0]) | ||
460 | next = new | ||
461 | |||
462 | stagingdir = d.getVar("STAGING_DIR") | ||
463 | recipesysroot = d.getVar("RECIPE_SYSROOT") | ||
464 | recipesysrootnative = d.getVar("RECIPE_SYSROOT_NATIVE") | ||
465 | current_variant = d.getVar("BBEXTENDVARIANT") | ||
466 | |||
467 | # Detect bitbake -b usage | ||
468 | nodeps = d.getVar("BB_LIMITEDDEPS") or False | ||
469 | if nodeps: | ||
470 | lock = bb.utils.lockfile(recipesysroot + "/sysroot.lock") | ||
471 | staging_populate_sysroot_dir(recipesysroot, recipesysrootnative, True, d) | ||
472 | staging_populate_sysroot_dir(recipesysroot, recipesysrootnative, False, d) | ||
473 | bb.utils.unlockfile(lock) | ||
474 | |||
475 | depdir = recipesysrootnative + "/installeddeps" | ||
476 | bb.utils.mkdirhier(depdir) | ||
477 | |||
478 | lock = bb.utils.lockfile(recipesysroot + "/sysroot.lock") | ||
479 | |||
480 | fixme = {} | ||
481 | fixme[''] = [] | ||
482 | fixme['native'] = [] | ||
483 | postinsts = [] | ||
484 | multilibs = {} | ||
485 | |||
486 | for dep in configuredeps: | ||
487 | c = setscenedeps[dep][0] | ||
488 | taskhash = setscenedeps[dep][5] | ||
489 | taskmanifest = depdir + "/" + c + "." + taskhash | ||
490 | if mytaskname in ["do_sdk_depends", "do_populate_sdk_ext"] and c.endswith("-initial"): | ||
491 | bb.note("Skipping initial setscene dependency %s for installation into the sysroot" % c) | ||
492 | continue | ||
493 | if os.path.exists(depdir + "/" + c): | ||
494 | lnk = os.readlink(depdir + "/" + c) | ||
495 | if lnk == c + "." + taskhash and os.path.exists(depdir + "/" + c + ".complete"): | ||
496 | bb.note("%s exists in sysroot, skipping" % c) | ||
497 | continue | ||
498 | else: | ||
499 | bb.note("%s exists in sysroot, but is stale (%s vs. %s), removing." % (c, lnk, c + "." + taskhash)) | ||
500 | sstate_clean_manifest(depdir + "/" + lnk, d) | ||
501 | os.unlink(depdir + "/" + c) | ||
502 | elif os.path.lexists(depdir + "/" + c): | ||
503 | os.unlink(depdir + "/" + c) | ||
504 | |||
505 | os.symlink(c + "." + taskhash, depdir + "/" + c) | ||
506 | |||
507 | d2 = d | ||
508 | destsysroot = recipesysroot | ||
509 | variant = '' | ||
510 | if setscenedeps[dep][2].startswith("virtual:multilib"): | ||
511 | variant = setscenedeps[dep][2].split(":")[2] | ||
512 | if variant != current_variant: | ||
513 | if variant not in multilibs: | ||
514 | multilibs[variant] = get_multilib_datastore(variant, d) | ||
515 | d2 = multilibs[variant] | ||
516 | destsysroot = d2.getVar("RECIPE_SYSROOT") | ||
517 | |||
518 | native = False | ||
519 | if c.endswith("-native"): | ||
520 | manifest = d2.expand("${SSTATE_MANIFESTS}/manifest-${BUILD_ARCH}-%s.populate_sysroot" % c) | ||
521 | native = True | ||
522 | elif c.startswith("nativesdk-"): | ||
523 | manifest = d2.expand("${SSTATE_MANIFESTS}/manifest-${SDK_ARCH}_${SDK_OS}-%s.populate_sysroot" % c) | ||
524 | elif "-cross-" in c: | ||
525 | manifest = d2.expand("${SSTATE_MANIFESTS}/manifest-${BUILD_ARCH}_${TARGET_ARCH}-%s.populate_sysroot" % c) | ||
526 | native = True | ||
527 | elif "-crosssdk" in c: | ||
528 | manifest = d2.expand("${SSTATE_MANIFESTS}/manifest-${BUILD_ARCH}_${SDK_ARCH}_${SDK_OS}-%s.populate_sysroot" % c) | ||
529 | native = True | ||
530 | else: | ||
531 | manifest = d2.expand("${SSTATE_MANIFESTS}/manifest-${MACHINE_ARCH}-%s.populate_sysroot" % c) | ||
532 | if not os.path.exists(manifest): | ||
533 | manifest = d2.expand("${SSTATE_MANIFESTS}/manifest-${TUNE_PKGARCH}-%s.populate_sysroot" % c) | ||
534 | if not os.path.exists(manifest): | ||
535 | manifest = d2.expand("${SSTATE_MANIFESTS}/manifest-allarch-%s.populate_sysroot" % c) | ||
536 | if not os.path.exists(manifest): | ||
537 | bb.warn("Manifest %s not found?" % manifest) | ||
538 | else: | ||
539 | with open(manifest, "r") as f, open(taskmanifest, 'w') as m: | ||
540 | for l in f: | ||
541 | l = l.strip() | ||
542 | if l.endswith("/"): | ||
543 | if native: | ||
544 | dest = staging_copydir(l, recipesysrootnative, stagingdir) | ||
545 | else: | ||
546 | dest = staging_copydir(l, destsysroot, stagingdir) | ||
547 | continue | ||
548 | if native: | ||
549 | dest = staging_copyfile(l, recipesysrootnative, fixme['native'], postinsts, stagingdir) | ||
550 | else: | ||
551 | dest = staging_copyfile(l, destsysroot, fixme[''], postinsts, stagingdir) | ||
552 | if dest: | ||
553 | m.write(dest + "\n") | ||
554 | |||
555 | for f in fixme: | ||
556 | if f == '': | ||
557 | staging_processfixme(fixme[f], recipesysroot, recipesysroot, recipesysrootnative, d) | ||
558 | elif f == 'native': | ||
559 | staging_processfixme(fixme[f], recipesysrootnative, recipesysroot, recipesysrootnative, d) | ||
560 | else: | ||
561 | staging_processfixme(fixme[f], multilibs[f].getVar("RECIPE_SYSROOT"), recipesysroot, recipesysrootnative, d) | ||
562 | |||
563 | for p in postinsts: | ||
564 | subprocess.check_call(p, shell=True) | ||
565 | |||
566 | for dep in configuredeps: | ||
567 | c = setscenedeps[dep][0] | ||
568 | open(depdir + "/" + c + ".complete", "w").close() | ||
569 | |||
570 | bb.utils.unlockfile(lock) | ||
571 | } | ||
572 | extend_recipe_sysroot[vardepsexclude] += "MACHINE SDK_ARCH BUILD_ARCH SDK_OS BB_TASKDEPDATA" | ||
573 | |||
574 | python do_prepare_recipe_sysroot () { | ||
575 | bb.build.exec_func("extend_recipe_sysroot", d) | ||
576 | } | ||
577 | addtask do_prepare_recipe_sysroot before do_configure after do_fetch | ||
578 | |||
579 | # Clean out the recipe specific sysroots before do_fetch | ||
580 | do_fetch[cleandirs] += "${RECIPE_SYSROOT} ${RECIPE_SYSROOT_NATIVE}" | ||
581 | |||
582 | python staging_taskhandler() { | ||
583 | bbtasks = e.tasklist | ||
584 | for task in bbtasks: | ||
585 | deps = d.getVarFlag(task, "depends") | ||
586 | if deps and "populate_sysroot" in deps: | ||
587 | d.appendVarFlag(task, "prefuncs", " extend_recipe_sysroot") | ||
588 | } | ||
589 | staging_taskhandler[eventmask] = "bb.event.RecipeTaskPreProcess" | ||
590 | addhandler staging_taskhandler | ||
246 | 591 | ||