* [PATCH v5 00/13] Add sstate-cache
@ 2021-11-04 11:51 Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 01/13] oe imports in central location Adriaan Schmidt
` (12 more replies)
0 siblings, 13 replies; 14+ messages in thread
From: Adriaan Schmidt @ 2021-11-04 11:51 UTC (permalink / raw)
To: isar-users; +Cc: Adriaan Schmidt
This series adds the sstate-cache feature from OE to Isar. The cache holds
the results of bootstrapping, rootfs generation (buildchroot, image rootfs),
and deb package generation.
To use the cache, the only configuration neccessary is setting SSTATE_DIR.
The contents of that directory need to be preserved across bitbake invocations.
One known weakness is that the package lists of cached rootfs's can run
out of sync with upstream ("apt-get update" only happens at bootstrap time).
But this also happens with an "old" local build dir, and is something that
may be addressed elsewhere. For now, the recommendation is to frequently
clear the cache (in one of our projects we run a nightly "clear&populate
cache" CI job).
Patches 1..5 fix (unrelated) issues that would otherwise block sstate caching,
patch 6 copies files from OE, and patches 7..10 add caching to Isar.
Patches 11..12 make minor adjustments to the tests, and patch 13 adds
a test case for the new feature.
---
Changes sinve v4:
- rebase on next
- add test case
Changes since v3:
- rebase on next
- make sstate understand that images and initramfss are MACHINE specific
(fixes a false-sharing bug that occured while testing with multiconfig)
Changes since v2:
- rebase on next
- add the explicit isar-apt dependency to all images (not only wic as previously).
- don't move existing code in isar-bootstrap.inc
- add `do_deploy_deb[deptask] = "do_deploy_deb"` to dpkg-base. This is so that
packages depend recursively on their dependencies, even when build results
are taken from cache.
- generally improve commit messages
Changes since v1:
- fix copy/paste typo in rootfs.bbclass
- add mounting trick to tar rootfs (because --one-file-system
does not stop at bind mounts)
- have install_imager_deps also depend explicitly on isar-apt
*** BLURB HERE ***
Adriaan Schmidt (13):
oe imports in central location
images: create deploy dir
rootfs: recursively depend on packages
base: remove unneeded "before do_build" task dependencies
dpkg: add explicit dependency to isar-apt
meta: add sstate feature from oe
sstate: configure
sstate: add caching to isar-bootstrap
sstate: add caching to rootfs
sstate: add caching to debian packages
test: pass absolute path for build_dir
test: make bitbake_args a list
sstate: add test case
meta/classes/base.bbclass | 32 +-
meta/classes/container-img.bbclass | 1 +
meta/classes/cpiogz-img.bbclass | 1 +
meta/classes/dpkg-base.bbclass | 34 +-
meta/classes/dpkg.bbclass | 1 +
meta/classes/ext4-img.bbclass | 1 +
meta/classes/fit-img.bbclass | 1 +
meta/classes/image-tools-extension.bbclass | 2 +-
meta/classes/image.bbclass | 3 +
meta/classes/initramfs.bbclass | 3 +
meta/classes/patch.bbclass | 5 -
meta/classes/rootfs.bbclass | 29 +-
meta/classes/sstate.bbclass | 1311 +++++++++++++++++
meta/classes/targz-img.bbclass | 1 +
meta/classes/ubi-img.bbclass | 1 +
meta/classes/ubifs-img.bbclass | 1 +
meta/classes/wic-img.bbclass | 1 +
meta/conf/bitbake.conf | 10 +-
meta/lib/oe/gpg_sign.py | 130 ++
meta/lib/oe/sstatesig.py | 603 ++++++++
.../isar-bootstrap/isar-bootstrap.inc | 24 +
scripts/ci_build.sh | 2 +-
testsuite/build_test/build_test.py | 101 +-
testsuite/build_test/cibase.py | 20 +-
testsuite/build_test/cibuilder.py | 2 +-
25 files changed, 2284 insertions(+), 36 deletions(-)
create mode 100644 meta/classes/sstate.bbclass
create mode 100644 meta/lib/oe/gpg_sign.py
create mode 100644 meta/lib/oe/sstatesig.py
--
2.30.2
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v5 01/13] oe imports in central location
2021-11-04 11:51 [PATCH v5 00/13] Add sstate-cache Adriaan Schmidt
@ 2021-11-04 11:51 ` Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 02/13] images: create deploy dir Adriaan Schmidt
` (11 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Adriaan Schmidt @ 2021-11-04 11:51 UTC (permalink / raw)
To: isar-users; +Cc: Adriaan Schmidt
Code taken from OE. Allows to import OE modules
without first setting python paths manually.
The reason is that we'd like to take sstate code
unmodified from OE, and here it's expected that
imports of oe and bb modules "just works".
Signed-off-by: Adriaan Schmidt <adriaan.schmidt@siemens.com>
---
meta/classes/base.bbclass | 28 ++++++++++++++++++++++++++++
meta/classes/dpkg-base.bbclass | 5 -----
meta/classes/patch.bbclass | 5 -----
3 files changed, 28 insertions(+), 10 deletions(-)
diff --git a/meta/classes/base.bbclass b/meta/classes/base.bbclass
index 72d4cc0..13134ff 100644
--- a/meta/classes/base.bbclass
+++ b/meta/classes/base.bbclass
@@ -21,6 +21,34 @@
THISDIR = "${@os.path.dirname(d.getVar('FILE', True))}"
FILESPATH = "${@base_set_filespath(["${FILE_DIRNAME}/${PF}", "${FILE_DIRNAME}/${P}", "${FILE_DIRNAME}/${PN}", "${FILE_DIRNAME}/files", "${FILE_DIRNAME}"], d)}"
+OE_IMPORTS += "os sys time oe.path oe.patch"
+OE_IMPORTS[type] = "list"
+
+def oe_import(d):
+ import sys
+
+ bbpath = d.getVar("BBPATH").split(":")
+ sys.path[0:0] = [os.path.join(dir, "lib") for dir in bbpath]
+
+ def inject(name, value):
+ """Make a python object accessible from the metadata"""
+ if hasattr(bb.utils, "_context"):
+ bb.utils._context[name] = value
+ else:
+ __builtins__[name] = value
+
+ import oe.data
+ for toimport in oe.data.typed_value("OE_IMPORTS", d):
+ try:
+ imported = __import__(toimport)
+ inject(toimport.split(".", 1)[0], imported)
+ except AttributeError as e:
+ bb.error("Error importing OE modules: %s" % str(e))
+ return ""
+
+# We need the oe module name space early (before INHERITs get added)
+OE_IMPORTED := "${@oe_import(d)}"
+
def get_deb_host_arch():
import subprocess
host_arch = subprocess.check_output(
diff --git a/meta/classes/dpkg-base.bbclass b/meta/classes/dpkg-base.bbclass
index 83500da..8a39a6d 100644
--- a/meta/classes/dpkg-base.bbclass
+++ b/meta/classes/dpkg-base.bbclass
@@ -248,11 +248,6 @@ do_deploy_deb[lockfiles] = "${REPO_ISAR_DIR}/isar.lock"
do_deploy_deb[dirs] = "${S}"
python do_devshell() {
- import sys
-
- oe_lib_path = os.path.join(d.getVar('LAYERDIR_core'), 'lib')
- sys.path.insert(0, oe_lib_path)
-
bb.build.exec_func('dpkg_do_mounts', d)
isar_export_proxies(d)
diff --git a/meta/classes/patch.bbclass b/meta/classes/patch.bbclass
index 3060755..2337693 100644
--- a/meta/classes/patch.bbclass
+++ b/meta/classes/patch.bbclass
@@ -92,11 +92,6 @@ def should_apply(parm, d):
should_apply[vardepsexclude] = "DATE SRCDATE"
python patch_do_patch() {
- import sys
-
- oe_lib_path = os.path.join(d.getVar('LAYERDIR_core'), 'lib')
- sys.path.insert(0, oe_lib_path)
-
import oe.patch
patchsetmap = {
--
2.30.2
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v5 02/13] images: create deploy dir
2021-11-04 11:51 [PATCH v5 00/13] Add sstate-cache Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 01/13] oe imports in central location Adriaan Schmidt
@ 2021-11-04 11:51 ` Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 03/13] rootfs: recursively depend on packages Adriaan Schmidt
` (10 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Adriaan Schmidt @ 2021-11-04 11:51 UTC (permalink / raw)
To: isar-users; +Cc: Adriaan Schmidt
I ran into several cases where the image deploy dir was not created
when results of previous tasks were taken from cache.
This makes the dependency explicit.
Signed-off-by: Adriaan Schmidt <adriaan.schmidt@siemens.com>
---
meta/classes/container-img.bbclass | 1 +
meta/classes/cpiogz-img.bbclass | 1 +
meta/classes/ext4-img.bbclass | 1 +
meta/classes/fit-img.bbclass | 1 +
meta/classes/targz-img.bbclass | 1 +
meta/classes/ubi-img.bbclass | 1 +
meta/classes/ubifs-img.bbclass | 1 +
meta/classes/wic-img.bbclass | 1 +
8 files changed, 8 insertions(+)
diff --git a/meta/classes/container-img.bbclass b/meta/classes/container-img.bbclass
index 79ef3e8..8fef52a 100644
--- a/meta/classes/container-img.bbclass
+++ b/meta/classes/container-img.bbclass
@@ -6,6 +6,7 @@
# This class provides the task 'containerize_rootfs'
# to create container images containing the target rootfs.
+do_container_image[dirs] = "${DEPLOY_DIR_IMAGE}"
do_container_image[stamp-extra-info] = "${DISTRO}-${MACHINE}"
do_container_image[vardeps] += "CONTAINER_FORMATS"
do_container_image(){
diff --git a/meta/classes/cpiogz-img.bbclass b/meta/classes/cpiogz-img.bbclass
index 940e2fb..2a49456 100644
--- a/meta/classes/cpiogz-img.bbclass
+++ b/meta/classes/cpiogz-img.bbclass
@@ -19,3 +19,4 @@ do_cpiogz_image() {
}
addtask cpiogz_image before do_image after do_image_tools
+do_cpiogz_image[dirs] = "${DEPLOY_DIR_IMAGE}"
diff --git a/meta/classes/ext4-img.bbclass b/meta/classes/ext4-img.bbclass
index 334dc64..5085afc 100644
--- a/meta/classes/ext4-img.bbclass
+++ b/meta/classes/ext4-img.bbclass
@@ -21,3 +21,4 @@ do_ext4_image() {
addtask ext4_image before do_image after do_image_tools
do_ext4_image[prefuncs] = 'set_image_size'
+do_ext4_image[dirs] = "${DEPLOY_DIR_IMAGE}"
diff --git a/meta/classes/fit-img.bbclass b/meta/classes/fit-img.bbclass
index 82b96d8..1ad0c5b 100644
--- a/meta/classes/fit-img.bbclass
+++ b/meta/classes/fit-img.bbclass
@@ -26,3 +26,4 @@ do_fit_image() {
sudo chown $(id -u):$(id -g) '${DEPLOY_DIR_IMAGE}/${FIT_IMAGE_FILE}'
}
addtask fit_image before do_image after do_image_tools do_transform_template
+do_fit_image[dirs] = "${DEPLOY_DIR_IMAGE}"
diff --git a/meta/classes/targz-img.bbclass b/meta/classes/targz-img.bbclass
index af12ae1..bf94af0 100644
--- a/meta/classes/targz-img.bbclass
+++ b/meta/classes/targz-img.bbclass
@@ -12,3 +12,4 @@ do_targz_image() {
}
addtask targz_image before do_image after do_image_tools
+do_targz_image[dirs] = "${DEPLOY_DIR_IMAGE}"
diff --git a/meta/classes/ubi-img.bbclass b/meta/classes/ubi-img.bbclass
index c69ac4d..efaf058 100644
--- a/meta/classes/ubi-img.bbclass
+++ b/meta/classes/ubi-img.bbclass
@@ -29,3 +29,4 @@ do_ubi_image() {
sudo chown $(id -u):$(id -g) '${DEPLOY_DIR_IMAGE}/${UBI_IMAGE_FILE}'
}
addtask ubi_image before do_image after do_image_tools do_transform_template
+do_ubi_image[dirs] = "${DEPLOY_DIR_IMAGE}"
diff --git a/meta/classes/ubifs-img.bbclass b/meta/classes/ubifs-img.bbclass
index 5d48c1d..229eb3e 100644
--- a/meta/classes/ubifs-img.bbclass
+++ b/meta/classes/ubifs-img.bbclass
@@ -29,3 +29,4 @@ do_ubifs_image() {
}
addtask ubifs_image before do_image after do_image_tools
+do_ubifs_image[dirs] = "${DEPLOY_DIR_IMAGE}"
diff --git a/meta/classes/wic-img.bbclass b/meta/classes/wic-img.bbclass
index caad1b1..399f7ee 100644
--- a/meta/classes/wic-img.bbclass
+++ b/meta/classes/wic-img.bbclass
@@ -137,6 +137,7 @@ python check_for_wic_warnings() {
}
do_wic_image[file-checksums] += "${WKS_FILE_CHECKSUM}"
+do_wic_image[dirs] = "${DEPLOY_DIR_IMAGE}"
python do_wic_image() {
cmds = ['wic_do_mounts', 'generate_wic_image', 'check_for_wic_warnings']
weights = [5, 90, 5]
--
2.30.2
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v5 03/13] rootfs: recursively depend on packages
2021-11-04 11:51 [PATCH v5 00/13] Add sstate-cache Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 01/13] oe imports in central location Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 02/13] images: create deploy dir Adriaan Schmidt
@ 2021-11-04 11:51 ` Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 04/13] base: remove unneeded "before do_build" task dependencies Adriaan Schmidt
` (9 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Adriaan Schmidt @ 2021-11-04 11:51 UTC (permalink / raw)
To: isar-users; +Cc: Adriaan Schmidt
This specifies dependencies more precisely, which becomes
important when we introduce caching.
The current definition of deptask can lead to errors, e.g.:
If rootfs includes A which depends on B, then currently rootfs
depends on A's do_deploy_deb, but only A's do_prepare_build
depends on B's do_deploy_deb. So if we can produce A from cache
without running its do_prepare_build, B might not be there.
This is of course never observed when building from scratch.
Setting recrdeptask instead fixes this, which is also how it's
done in OE.
The same thing can happen when building packages. Unfortunately
setting `do_prepare_build[recrdeptask] = "do_deploy_deb"`
makes a recipes do_prepare_build depend on its own do_deploy_deb,
creating a dependency loop. This is solved by making do_deploy_deb
depend on the do_deploy_deb task of dependent recipes. Not precisely
what we want, but should have the same effect.
Signed-off-by: Adriaan Schmidt <adriaan.schmidt@siemens.com>
---
meta/classes/dpkg-base.bbclass | 1 +
meta/classes/rootfs.bbclass | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/meta/classes/dpkg-base.bbclass b/meta/classes/dpkg-base.bbclass
index 8a39a6d..2005707 100644
--- a/meta/classes/dpkg-base.bbclass
+++ b/meta/classes/dpkg-base.bbclass
@@ -244,6 +244,7 @@ do_deploy_deb() {
}
addtask deploy_deb after do_dpkg_build before do_build
+do_deploy_deb[deptask] = "do_deploy_deb"
do_deploy_deb[lockfiles] = "${REPO_ISAR_DIR}/isar.lock"
do_deploy_deb[dirs] = "${S}"
diff --git a/meta/classes/rootfs.bbclass b/meta/classes/rootfs.bbclass
index 20ccb00..b38de66 100644
--- a/meta/classes/rootfs.bbclass
+++ b/meta/classes/rootfs.bbclass
@@ -153,7 +153,7 @@ rootfs_install_pkgs_install() {
do_rootfs_install[root_cleandirs] = "${ROOTFSDIR}"
do_rootfs_install[vardeps] += "${ROOTFS_CONFIGURE_COMMAND} ${ROOTFS_INSTALL_COMMAND}"
do_rootfs_install[depends] = "isar-bootstrap-${@'target' if d.getVar('ROOTFS_ARCH') == d.getVar('DISTRO_ARCH') else 'host'}:do_build"
-do_rootfs_install[deptask] = "do_deploy_deb"
+do_rootfs_install[recrdeptask] = "do_deploy_deb"
python do_rootfs_install() {
configure_cmds = (d.getVar("ROOTFS_CONFIGURE_COMMAND", True) or "").split()
install_cmds = (d.getVar("ROOTFS_INSTALL_COMMAND", True) or "").split()
--
2.30.2
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v5 04/13] base: remove unneeded "before do_build" task dependencies
2021-11-04 11:51 [PATCH v5 00/13] Add sstate-cache Adriaan Schmidt
` (2 preceding siblings ...)
2021-11-04 11:51 ` [PATCH v5 03/13] rootfs: recursively depend on packages Adriaan Schmidt
@ 2021-11-04 11:51 ` Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 05/13] dpkg: add explicit dependency to isar-apt Adriaan Schmidt
` (8 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Adriaan Schmidt @ 2021-11-04 11:51 UTC (permalink / raw)
To: isar-users; +Cc: Adriaan Schmidt
This makes dependencies more precise, in preparation for
the introduction of caching.
Currently the dependency graph is overspecified, and do_build has
too many explicit dependencies. What happens then is that even
if a package (the output of do_dpkg_build) can be taken from
cache, there are still dependencies that cause the execution of
do_fetch, do_patch etc.
Signed-off-by: Adriaan Schmidt <adriaan.schmidt@siemens.com>
---
meta/classes/base.bbclass | 4 ++--
meta/classes/dpkg-base.bbclass | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/meta/classes/base.bbclass b/meta/classes/base.bbclass
index 13134ff..fe5bcb0 100644
--- a/meta/classes/base.bbclass
+++ b/meta/classes/base.bbclass
@@ -183,7 +183,7 @@ python do_fetch() {
bb.fatal(str(e))
}
-addtask fetch before do_build
+addtask fetch
do_unpack[dirs] = "${WORKDIR}"
@@ -202,7 +202,7 @@ python do_unpack() {
bb.fatal(str(e))
}
-addtask unpack after do_fetch before do_build
+addtask unpack after do_fetch
do_build[noexec] = "1"
do_build () {
diff --git a/meta/classes/dpkg-base.bbclass b/meta/classes/dpkg-base.bbclass
index 2005707..eddf643 100644
--- a/meta/classes/dpkg-base.bbclass
+++ b/meta/classes/dpkg-base.bbclass
@@ -212,7 +212,7 @@ python do_dpkg_build() {
bb.utils.unlockfile(lock)
}
-addtask dpkg_build before do_build
+addtask dpkg_build
KEEP_INSTALLED_ON_CLEAN ?= "0"
--
2.30.2
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v5 05/13] dpkg: add explicit dependency to isar-apt
2021-11-04 11:51 [PATCH v5 00/13] Add sstate-cache Adriaan Schmidt
` (3 preceding siblings ...)
2021-11-04 11:51 ` [PATCH v5 04/13] base: remove unneeded "before do_build" task dependencies Adriaan Schmidt
@ 2021-11-04 11:51 ` Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 06/13] meta: add sstate feature from oe Adriaan Schmidt
` (7 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Adriaan Schmidt @ 2021-11-04 11:51 UTC (permalink / raw)
To: isar-users; +Cc: Adriaan Schmidt
Again, make dependencies more precise and explicit.
The tasks do_install_builddeps and do_deploy_deb need
isar-apt, which is usually "just there" because other early tasks
pull this in.
When using caching, one example of what can happen is:
We run with empty builddir and warm cache, and request the
build of a single package. With a cache hit, the only task
that will run is the packages do_deploy_deb. So this needs
an explicit dependency on isar-apt.
Same for install_builddeps and install_imager_deps.
Signed-off-by: Adriaan Schmidt <adriaan.schmidt@siemens.com>
---
meta/classes/dpkg-base.bbclass | 1 +
meta/classes/dpkg.bbclass | 1 +
meta/classes/image-tools-extension.bbclass | 2 +-
3 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/meta/classes/dpkg-base.bbclass b/meta/classes/dpkg-base.bbclass
index eddf643..7e372de 100644
--- a/meta/classes/dpkg-base.bbclass
+++ b/meta/classes/dpkg-base.bbclass
@@ -245,6 +245,7 @@ do_deploy_deb() {
addtask deploy_deb after do_dpkg_build before do_build
do_deploy_deb[deptask] = "do_deploy_deb"
+do_deploy_deb[depends] += "isar-apt:do_cache_config"
do_deploy_deb[lockfiles] = "${REPO_ISAR_DIR}/isar.lock"
do_deploy_deb[dirs] = "${S}"
diff --git a/meta/classes/dpkg.bbclass b/meta/classes/dpkg.bbclass
index 4e7c2f7..ef668f6 100644
--- a/meta/classes/dpkg.bbclass
+++ b/meta/classes/dpkg.bbclass
@@ -23,6 +23,7 @@ do_install_builddeps() {
}
addtask install_builddeps after do_prepare_build before do_dpkg_build
+do_install_builddeps[depends] += "isar-apt:do_cache_config"
# apt and reprepro may not run in parallel, acquire the Isar lock
do_install_builddeps[lockfiles] += "${REPO_ISAR_DIR}/isar.lock"
diff --git a/meta/classes/image-tools-extension.bbclass b/meta/classes/image-tools-extension.bbclass
index 9f28800..b996813 100644
--- a/meta/classes/image-tools-extension.bbclass
+++ b/meta/classes/image-tools-extension.bbclass
@@ -14,7 +14,7 @@ IMAGER_INSTALL ??= ""
IMAGER_BUILD_DEPS ??= ""
DEPENDS += "${IMAGER_BUILD_DEPS}"
-do_install_imager_deps[depends] = "${BUILDCHROOT_DEP}"
+do_install_imager_deps[depends] = "${BUILDCHROOT_DEP} isar-apt:do_cache_config"
do_install_imager_deps[deptask] = "do_deploy_deb"
do_install_imager_deps[lockfiles] += "${REPO_ISAR_DIR}/isar.lock"
do_install_imager_deps() {
--
2.30.2
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v5 06/13] meta: add sstate feature from oe
2021-11-04 11:51 [PATCH v5 00/13] Add sstate-cache Adriaan Schmidt
` (4 preceding siblings ...)
2021-11-04 11:51 ` [PATCH v5 05/13] dpkg: add explicit dependency to isar-apt Adriaan Schmidt
@ 2021-11-04 11:51 ` Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 07/13] sstate: configure Adriaan Schmidt
` (6 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Adriaan Schmidt @ 2021-11-04 11:51 UTC (permalink / raw)
To: isar-users; +Cc: Adriaan Schmidt
Copied unmodified from poky:
* tag yocto-3.3.2
* commit 31c639eb8664059eb4ed711be9173c223b4cc940
Signed-off-by: Adriaan Schmidt <adriaan.schmidt@siemens.com>
---
meta/classes/sstate.bbclass | 1311 +++++++++++++++++++++++++++++++++++
meta/lib/oe/gpg_sign.py | 130 ++++
meta/lib/oe/sstatesig.py | 603 ++++++++++++++++
3 files changed, 2044 insertions(+)
create mode 100644 meta/classes/sstate.bbclass
create mode 100644 meta/lib/oe/gpg_sign.py
create mode 100644 meta/lib/oe/sstatesig.py
diff --git a/meta/classes/sstate.bbclass b/meta/classes/sstate.bbclass
new file mode 100644
index 0000000..24dcff2
--- /dev/null
+++ b/meta/classes/sstate.bbclass
@@ -0,0 +1,1311 @@
+SSTATE_VERSION = "3"
+
+SSTATE_MANIFESTS ?= "${TMPDIR}/sstate-control"
+SSTATE_MANFILEPREFIX = "${SSTATE_MANIFESTS}/manifest-${SSTATE_MANMACH}-${PN}"
+
+def generate_sstatefn(spec, hash, taskname, siginfo, d):
+ if taskname is None:
+ return ""
+ extension = ".tgz"
+ # 8 chars reserved for siginfo
+ limit = 254 - 8
+ if siginfo:
+ limit = 254
+ extension = ".tgz.siginfo"
+ if not hash:
+ hash = "INVALID"
+ fn = spec + hash + "_" + taskname + extension
+ # If the filename is too long, attempt to reduce it
+ if len(fn) > limit:
+ components = spec.split(":")
+ # Fields 0,5,6 are mandatory, 1 is most useful, 2,3,4 are just for information
+ # 7 is for the separators
+ avail = (254 - len(hash + "_" + taskname + extension) - len(components[0]) - len(components[1]) - len(components[5]) - len(components[6]) - 7) // 3
+ components[2] = components[2][:avail]
+ components[3] = components[3][:avail]
+ components[4] = components[4][:avail]
+ spec = ":".join(components)
+ fn = spec + hash + "_" + taskname + extension
+ if len(fn) > limit:
+ bb.fatal("Unable to reduce sstate name to less than 255 chararacters")
+ return hash[:2] + "/" + hash[2:4] + "/" + fn
+
+SSTATE_PKGARCH = "${PACKAGE_ARCH}"
+SSTATE_PKGSPEC = "sstate:${PN}:${PACKAGE_ARCH}${TARGET_VENDOR}-${TARGET_OS}:${PV}:${PR}:${SSTATE_PKGARCH}:${SSTATE_VERSION}:"
+SSTATE_SWSPEC = "sstate:${PN}::${PV}:${PR}::${SSTATE_VERSION}:"
+SSTATE_PKGNAME = "${SSTATE_EXTRAPATH}${@generate_sstatefn(d.getVar('SSTATE_PKGSPEC'), d.getVar('BB_UNIHASH'), d.getVar('SSTATE_CURRTASK'), False, d)}"
+SSTATE_PKG = "${SSTATE_DIR}/${SSTATE_PKGNAME}"
+SSTATE_EXTRAPATH = ""
+SSTATE_EXTRAPATHWILDCARD = ""
+SSTATE_PATHSPEC = "${SSTATE_DIR}/${SSTATE_EXTRAPATHWILDCARD}*/*/${SSTATE_PKGSPEC}*_${SSTATE_PATH_CURRTASK}.tgz*"
+
+# explicitly make PV to depend on evaluated value of PV variable
+PV[vardepvalue] = "${PV}"
+
+# We don't want the sstate to depend on things like the distro string
+# of the system, we let the sstate paths take care of this.
+SSTATE_EXTRAPATH[vardepvalue] = ""
+SSTATE_EXTRAPATHWILDCARD[vardepvalue] = ""
+
+# For multilib rpm the allarch packagegroup files can overwrite (in theory they're identical)
+SSTATE_DUPWHITELIST = "${DEPLOY_DIR}/licenses/"
+# Avoid docbook/sgml catalog warnings for now
+SSTATE_DUPWHITELIST += "${STAGING_ETCDIR_NATIVE}/sgml ${STAGING_DATADIR_NATIVE}/sgml"
+# sdk-provides-dummy-nativesdk and nativesdk-buildtools-perl-dummy overlap for different SDKMACHINE
+SSTATE_DUPWHITELIST += "${DEPLOY_DIR_RPM}/sdk_provides_dummy_nativesdk/ ${DEPLOY_DIR_IPK}/sdk-provides-dummy-nativesdk/"
+SSTATE_DUPWHITELIST += "${DEPLOY_DIR_RPM}/buildtools_dummy_nativesdk/ ${DEPLOY_DIR_IPK}/buildtools-dummy-nativesdk/"
+# target-sdk-provides-dummy overlaps that allarch is disabled when multilib is used
+SSTATE_DUPWHITELIST += "${COMPONENTS_DIR}/sdk-provides-dummy-target/ ${DEPLOY_DIR_RPM}/sdk_provides_dummy_target/ ${DEPLOY_DIR_IPK}/sdk-provides-dummy-target/"
+# Archive the sources for many architectures in one deploy folder
+SSTATE_DUPWHITELIST += "${DEPLOY_DIR_SRC}"
+# ovmf/grub-efi/systemd-boot/intel-microcode multilib recipes can generate identical overlapping files
+SSTATE_DUPWHITELIST += "${DEPLOY_DIR_IMAGE}/ovmf"
+SSTATE_DUPWHITELIST += "${DEPLOY_DIR_IMAGE}/grub-efi"
+SSTATE_DUPWHITELIST += "${DEPLOY_DIR_IMAGE}/systemd-boot"
+SSTATE_DUPWHITELIST += "${DEPLOY_DIR_IMAGE}/microcode"
+
+SSTATE_SCAN_FILES ?= "*.la *-config *_config postinst-*"
+SSTATE_SCAN_CMD ??= 'find ${SSTATE_BUILDDIR} \( -name "${@"\" -o -name \"".join(d.getVar("SSTATE_SCAN_FILES").split())}" \) -type f'
+SSTATE_SCAN_CMD_NATIVE ??= 'grep -Irl -e ${RECIPE_SYSROOT} -e ${RECIPE_SYSROOT_NATIVE} -e ${HOSTTOOLS_DIR} ${SSTATE_BUILDDIR}'
+
+BB_HASHFILENAME = "False ${SSTATE_PKGSPEC} ${SSTATE_SWSPEC}"
+
+SSTATE_ARCHS = " \
+ ${BUILD_ARCH} \
+ ${BUILD_ARCH}_${ORIGNATIVELSBSTRING} \
+ ${BUILD_ARCH}_${SDK_ARCH}_${SDK_OS} \
+ ${BUILD_ARCH}_${TARGET_ARCH} \
+ ${SDK_ARCH}_${SDK_OS} \
+ ${SDK_ARCH}_${PACKAGE_ARCH} \
+ allarch \
+ ${PACKAGE_ARCH} \
+ ${PACKAGE_EXTRA_ARCHS} \
+ ${MACHINE_ARCH}"
+SSTATE_ARCHS[vardepsexclude] = "ORIGNATIVELSBSTRING"
+
+SSTATE_MANMACH ?= "${SSTATE_PKGARCH}"
+
+SSTATECREATEFUNCS = "sstate_hardcode_path"
+SSTATECREATEFUNCS[vardeps] = "SSTATE_SCAN_FILES"
+SSTATEPOSTCREATEFUNCS = ""
+SSTATEPREINSTFUNCS = ""
+SSTATEPOSTUNPACKFUNCS = "sstate_hardcode_path_unpack"
+SSTATEPOSTINSTFUNCS = ""
+EXTRA_STAGING_FIXMES ?= "HOSTTOOLS_DIR"
+
+# Check whether sstate exists for tasks that support sstate and are in the
+# locked signatures file.
+SIGGEN_LOCKEDSIGS_SSTATE_EXISTS_CHECK ?= 'error'
+
+# Check whether the task's computed hash matches the task's hash in the
+# locked signatures file.
+SIGGEN_LOCKEDSIGS_TASKSIG_CHECK ?= "error"
+
+# The GnuPG key ID and passphrase to use to sign sstate archives (or unset to
+# not sign)
+SSTATE_SIG_KEY ?= ""
+SSTATE_SIG_PASSPHRASE ?= ""
+# Whether to verify the GnUPG signatures when extracting sstate archives
+SSTATE_VERIFY_SIG ?= "0"
+
+SSTATE_HASHEQUIV_METHOD ?= "oe.sstatesig.OEOuthashBasic"
+SSTATE_HASHEQUIV_METHOD[doc] = "The fully-qualified function used to calculate \
+ the output hash for a task, which in turn is used to determine equivalency. \
+ "
+
+SSTATE_HASHEQUIV_REPORT_TASKDATA ?= "0"
+SSTATE_HASHEQUIV_REPORT_TASKDATA[doc] = "Report additional useful data to the \
+ hash equivalency server, such as PN, PV, taskname, etc. This information \
+ is very useful for developers looking at task data, but may leak sensitive \
+ data if the equivalence server is public. \
+ "
+
+python () {
+ if bb.data.inherits_class('native', d):
+ d.setVar('SSTATE_PKGARCH', d.getVar('BUILD_ARCH', False))
+ if d.getVar("PN") == "pseudo-native":
+ d.appendVar('SSTATE_PKGARCH', '_${ORIGNATIVELSBSTRING}')
+ elif bb.data.inherits_class('crosssdk', d):
+ d.setVar('SSTATE_PKGARCH', d.expand("${BUILD_ARCH}_${SDK_ARCH}_${SDK_OS}"))
+ elif bb.data.inherits_class('cross', d):
+ d.setVar('SSTATE_PKGARCH', d.expand("${BUILD_ARCH}_${TARGET_ARCH}"))
+ elif bb.data.inherits_class('nativesdk', d):
+ d.setVar('SSTATE_PKGARCH', d.expand("${SDK_ARCH}_${SDK_OS}"))
+ elif bb.data.inherits_class('cross-canadian', d):
+ d.setVar('SSTATE_PKGARCH', d.expand("${SDK_ARCH}_${PACKAGE_ARCH}"))
+ elif bb.data.inherits_class('allarch', d) and d.getVar("PACKAGE_ARCH") == "all":
+ d.setVar('SSTATE_PKGARCH', "allarch")
+ else:
+ d.setVar('SSTATE_MANMACH', d.expand("${PACKAGE_ARCH}"))
+
+ if bb.data.inherits_class('native', d) or bb.data.inherits_class('crosssdk', d) or bb.data.inherits_class('cross', d):
+ d.setVar('SSTATE_EXTRAPATH', "${NATIVELSBSTRING}/")
+ d.setVar('BB_HASHFILENAME', "True ${SSTATE_PKGSPEC} ${SSTATE_SWSPEC}")
+ d.setVar('SSTATE_EXTRAPATHWILDCARD', "${NATIVELSBSTRING}/")
+
+ unique_tasks = sorted(set((d.getVar('SSTATETASKS') or "").split()))
+ d.setVar('SSTATETASKS', " ".join(unique_tasks))
+ for task in unique_tasks:
+ d.prependVarFlag(task, 'prefuncs', "sstate_task_prefunc ")
+ d.appendVarFlag(task, 'postfuncs', " sstate_task_postfunc")
+}
+
+def sstate_init(task, d):
+ ss = {}
+ ss['task'] = task
+ ss['dirs'] = []
+ ss['plaindirs'] = []
+ ss['lockfiles'] = []
+ ss['lockfiles-shared'] = []
+ return ss
+
+def sstate_state_fromvars(d, task = None):
+ if task is None:
+ task = d.getVar('BB_CURRENTTASK')
+ if not task:
+ bb.fatal("sstate code running without task context?!")
+ task = task.replace("_setscene", "")
+
+ if task.startswith("do_"):
+ task = task[3:]
+ inputs = (d.getVarFlag("do_" + task, 'sstate-inputdirs') or "").split()
+ outputs = (d.getVarFlag("do_" + task, 'sstate-outputdirs') or "").split()
+ plaindirs = (d.getVarFlag("do_" + task, 'sstate-plaindirs') or "").split()
+ lockfiles = (d.getVarFlag("do_" + task, 'sstate-lockfile') or "").split()
+ lockfilesshared = (d.getVarFlag("do_" + task, 'sstate-lockfile-shared') or "").split()
+ interceptfuncs = (d.getVarFlag("do_" + task, 'sstate-interceptfuncs') or "").split()
+ fixmedir = d.getVarFlag("do_" + task, 'sstate-fixmedir') or ""
+ if not task or len(inputs) != len(outputs):
+ bb.fatal("sstate variables not setup correctly?!")
+
+ if task == "populate_lic":
+ d.setVar("SSTATE_PKGSPEC", "${SSTATE_SWSPEC}")
+ d.setVar("SSTATE_EXTRAPATH", "")
+ d.setVar('SSTATE_EXTRAPATHWILDCARD', "")
+
+ ss = sstate_init(task, d)
+ for i in range(len(inputs)):
+ sstate_add(ss, inputs[i], outputs[i], d)
+ ss['lockfiles'] = lockfiles
+ ss['lockfiles-shared'] = lockfilesshared
+ ss['plaindirs'] = plaindirs
+ ss['interceptfuncs'] = interceptfuncs
+ ss['fixmedir'] = fixmedir
+ return ss
+
+def sstate_add(ss, source, dest, d):
+ if not source.endswith("/"):
+ source = source + "/"
+ if not dest.endswith("/"):
+ dest = dest + "/"
+ source = os.path.normpath(source)
+ dest = os.path.normpath(dest)
+ srcbase = os.path.basename(source)
+ ss['dirs'].append([srcbase, source, dest])
+ return ss
+
+def sstate_install(ss, d):
+ import oe.path
+ import oe.sstatesig
+ import subprocess
+
+ sharedfiles = []
+ shareddirs = []
+ bb.utils.mkdirhier(d.expand("${SSTATE_MANIFESTS}"))
+
+ sstateinst = d.expand("${WORKDIR}/sstate-install-%s/" % ss['task'])
+
+ manifest, d2 = oe.sstatesig.sstate_get_manifest_filename(ss['task'], d)
+
+ if os.access(manifest, os.R_OK):
+ bb.fatal("Package already staged (%s)?!" % manifest)
+
+ d.setVar("SSTATE_INST_POSTRM", manifest + ".postrm")
+
+ locks = []
+ for lock in ss['lockfiles-shared']:
+ locks.append(bb.utils.lockfile(lock, True))
+ for lock in ss['lockfiles']:
+ locks.append(bb.utils.lockfile(lock))
+
+ for state in ss['dirs']:
+ bb.debug(2, "Staging files from %s to %s" % (state[1], state[2]))
+ for walkroot, dirs, files in os.walk(state[1]):
+ for file in files:
+ srcpath = os.path.join(walkroot, file)
+ dstpath = srcpath.replace(state[1], state[2])
+ #bb.debug(2, "Staging %s to %s" % (srcpath, dstpath))
+ sharedfiles.append(dstpath)
+ for dir in dirs:
+ srcdir = os.path.join(walkroot, dir)
+ dstdir = srcdir.replace(state[1], state[2])
+ #bb.debug(2, "Staging %s to %s" % (srcdir, dstdir))
+ if os.path.islink(srcdir):
+ sharedfiles.append(dstdir)
+ continue
+ if not dstdir.endswith("/"):
+ dstdir = dstdir + "/"
+ shareddirs.append(dstdir)
+
+ # Check the file list for conflicts against files which already exist
+ whitelist = (d.getVar("SSTATE_DUPWHITELIST") or "").split()
+ match = []
+ for f in sharedfiles:
+ if os.path.exists(f) and not os.path.islink(f):
+ f = os.path.normpath(f)
+ realmatch = True
+ for w in whitelist:
+ w = os.path.normpath(w)
+ if f.startswith(w):
+ realmatch = False
+ break
+ if realmatch:
+ match.append(f)
+ sstate_search_cmd = "grep -rlF '%s' %s --exclude=master.list | sed -e 's:^.*/::'" % (f, d.expand("${SSTATE_MANIFESTS}"))
+ search_output = subprocess.Popen(sstate_search_cmd, shell=True, stdout=subprocess.PIPE).communicate()[0]
+ if search_output:
+ match.append(" (matched in %s)" % search_output.decode('utf-8').rstrip())
+ else:
+ match.append(" (not matched to any task)")
+ if match:
+ bb.error("The recipe %s is trying to install files into a shared " \
+ "area when those files already exist. Those files and their manifest " \
+ "location are:\n %s\nPlease verify which recipe should provide the " \
+ "above files.\n\nThe build has stopped, as continuing in this scenario WILL " \
+ "break things - if not now, possibly in the future (we've seen builds fail " \
+ "several months later). If the system knew how to recover from this " \
+ "automatically it would, however there are several different scenarios " \
+ "which can result in this and we don't know which one this is. It may be " \
+ "you have switched providers of something like virtual/kernel (e.g. from " \
+ "linux-yocto to linux-yocto-dev), in that case you need to execute the " \
+ "clean task for both recipes and it will resolve this error. It may be " \
+ "you changed DISTRO_FEATURES from systemd to udev or vice versa. Cleaning " \
+ "those recipes should again resolve this error, however switching " \
+ "DISTRO_FEATURES on an existing build directory is not supported - you " \
+ "should really clean out tmp and rebuild (reusing sstate should be safe). " \
+ "It could be the overlapping files detected are harmless in which case " \
+ "adding them to SSTATE_DUPWHITELIST may be the correct solution. It could " \
+ "also be your build is including two different conflicting versions of " \
+ "things (e.g. bluez 4 and bluez 5 and the correct solution for that would " \
+ "be to resolve the conflict. If in doubt, please ask on the mailing list, " \
+ "sharing the error and filelist above." % \
+ (d.getVar('PN'), "\n ".join(match)))
+ bb.fatal("If the above message is too much, the simpler version is you're advised to wipe out tmp and rebuild (reusing sstate is fine). That will likely fix things in most (but not all) cases.")
+
+ if ss['fixmedir'] and os.path.exists(ss['fixmedir'] + "/fixmepath.cmd"):
+ sharedfiles.append(ss['fixmedir'] + "/fixmepath.cmd")
+ sharedfiles.append(ss['fixmedir'] + "/fixmepath")
+
+ # Write out the manifest
+ f = open(manifest, "w")
+ for file in sharedfiles:
+ f.write(file + "\n")
+
+ # We want to ensure that directories appear at the end of the manifest
+ # so that when we test to see if they should be deleted any contents
+ # added by the task will have been removed first.
+ dirs = sorted(shareddirs, key=len)
+ # Must remove children first, which will have a longer path than the parent
+ for di in reversed(dirs):
+ f.write(di + "\n")
+ f.close()
+
+ # Append to the list of manifests for this PACKAGE_ARCH
+
+ i = d2.expand("${SSTATE_MANIFESTS}/index-${SSTATE_MANMACH}")
+ l = bb.utils.lockfile(i + ".lock")
+ filedata = d.getVar("STAMP") + " " + d2.getVar("SSTATE_MANFILEPREFIX") + " " + d.getVar("WORKDIR") + "\n"
+ manifests = []
+ if os.path.exists(i):
+ with open(i, "r") as f:
+ manifests = f.readlines()
+ # We append new entries, we don't remove older entries which may have the same
+ # manifest name but different versions from stamp/workdir. See below.
+ if filedata not in manifests:
+ with open(i, "a+") as f:
+ f.write(filedata)
+ bb.utils.unlockfile(l)
+
+ # Run the actual file install
+ for state in ss['dirs']:
+ if os.path.exists(state[1]):
+ oe.path.copyhardlinktree(state[1], state[2])
+
+ for postinst in (d.getVar('SSTATEPOSTINSTFUNCS') or '').split():
+ # All hooks should run in the SSTATE_INSTDIR
+ bb.build.exec_func(postinst, d, (sstateinst,))
+
+ for lock in locks:
+ bb.utils.unlockfile(lock)
+
+sstate_install[vardepsexclude] += "SSTATE_DUPWHITELIST STATE_MANMACH SSTATE_MANFILEPREFIX"
+sstate_install[vardeps] += "${SSTATEPOSTINSTFUNCS}"
+
+def sstate_installpkg(ss, d):
+ from oe.gpg_sign import get_signer
+
+ sstateinst = d.expand("${WORKDIR}/sstate-install-%s/" % ss['task'])
+ d.setVar("SSTATE_CURRTASK", ss['task'])
+ sstatefetch = d.getVar('SSTATE_PKGNAME')
+ sstatepkg = d.getVar('SSTATE_PKG')
+
+ if not os.path.exists(sstatepkg):
+ pstaging_fetch(sstatefetch, d)
+
+ if not os.path.isfile(sstatepkg):
+ bb.note("Sstate package %s does not exist" % sstatepkg)
+ return False
+
+ sstate_clean(ss, d)
+
+ d.setVar('SSTATE_INSTDIR', sstateinst)
+
+ if bb.utils.to_boolean(d.getVar("SSTATE_VERIFY_SIG"), False):
+ if not os.path.isfile(sstatepkg + '.sig'):
+ bb.warn("No signature file for sstate package %s, skipping acceleration..." % sstatepkg)
+ return False
+ signer = get_signer(d, 'local')
+ if not signer.verify(sstatepkg + '.sig'):
+ bb.warn("Cannot verify signature on sstate package %s, skipping acceleration..." % sstatepkg)
+ return False
+
+ # Empty sstateinst directory, ensure its clean
+ if os.path.exists(sstateinst):
+ oe.path.remove(sstateinst)
+ bb.utils.mkdirhier(sstateinst)
+
+ sstateinst = d.getVar("SSTATE_INSTDIR")
+ d.setVar('SSTATE_FIXMEDIR', ss['fixmedir'])
+
+ for f in (d.getVar('SSTATEPREINSTFUNCS') or '').split() + ['sstate_unpack_package']:
+ # All hooks should run in the SSTATE_INSTDIR
+ bb.build.exec_func(f, d, (sstateinst,))
+
+ return sstate_installpkgdir(ss, d)
+
+def sstate_installpkgdir(ss, d):
+ import oe.path
+ import subprocess
+
+ sstateinst = d.getVar("SSTATE_INSTDIR")
+ d.setVar('SSTATE_FIXMEDIR', ss['fixmedir'])
+
+ for f in (d.getVar('SSTATEPOSTUNPACKFUNCS') or '').split():
+ # All hooks should run in the SSTATE_INSTDIR
+ bb.build.exec_func(f, d, (sstateinst,))
+
+ def prepdir(dir):
+ # remove dir if it exists, ensure any parent directories do exist
+ if os.path.exists(dir):
+ oe.path.remove(dir)
+ bb.utils.mkdirhier(dir)
+ oe.path.remove(dir)
+
+ for state in ss['dirs']:
+ prepdir(state[1])
+ os.rename(sstateinst + state[0], state[1])
+ sstate_install(ss, d)
+
+ for plain in ss['plaindirs']:
+ workdir = d.getVar('WORKDIR')
+ sharedworkdir = os.path.join(d.getVar('TMPDIR'), "work-shared")
+ src = sstateinst + "/" + plain.replace(workdir, '')
+ if sharedworkdir in plain:
+ src = sstateinst + "/" + plain.replace(sharedworkdir, '')
+ dest = plain
+ bb.utils.mkdirhier(src)
+ prepdir(dest)
+ os.rename(src, dest)
+
+ return True
+
+python sstate_hardcode_path_unpack () {
+ # Fixup hardcoded paths
+ #
+ # Note: The logic below must match the reverse logic in
+ # sstate_hardcode_path(d)
+ import subprocess
+
+ sstateinst = d.getVar('SSTATE_INSTDIR')
+ sstatefixmedir = d.getVar('SSTATE_FIXMEDIR')
+ fixmefn = sstateinst + "fixmepath"
+ if os.path.isfile(fixmefn):
+ staging_target = d.getVar('RECIPE_SYSROOT')
+ staging_host = d.getVar('RECIPE_SYSROOT_NATIVE')
+
+ if bb.data.inherits_class('native', d) or bb.data.inherits_class('cross-canadian', d):
+ sstate_sed_cmd = "sed -i -e 's:FIXMESTAGINGDIRHOST:%s:g'" % (staging_host)
+ elif bb.data.inherits_class('cross', d) or bb.data.inherits_class('crosssdk', d):
+ sstate_sed_cmd = "sed -i -e 's:FIXMESTAGINGDIRTARGET:%s:g; s:FIXMESTAGINGDIRHOST:%s:g'" % (staging_target, staging_host)
+ else:
+ sstate_sed_cmd = "sed -i -e 's:FIXMESTAGINGDIRTARGET:%s:g'" % (staging_target)
+
+ extra_staging_fixmes = d.getVar('EXTRA_STAGING_FIXMES') or ''
+ for fixmevar in extra_staging_fixmes.split():
+ fixme_path = d.getVar(fixmevar)
+ sstate_sed_cmd += " -e 's:FIXME_%s:%s:g'" % (fixmevar, fixme_path)
+
+ # Add sstateinst to each filename in fixmepath, use xargs to efficiently call sed
+ sstate_hardcode_cmd = "sed -e 's:^:%s:g' %s | xargs %s" % (sstateinst, fixmefn, sstate_sed_cmd)
+
+ # Defer do_populate_sysroot relocation command
+ if sstatefixmedir:
+ bb.utils.mkdirhier(sstatefixmedir)
+ with open(sstatefixmedir + "/fixmepath.cmd", "w") as f:
+ sstate_hardcode_cmd = sstate_hardcode_cmd.replace(fixmefn, sstatefixmedir + "/fixmepath")
+ sstate_hardcode_cmd = sstate_hardcode_cmd.replace(sstateinst, "FIXMEFINALSSTATEINST")
+ sstate_hardcode_cmd = sstate_hardcode_cmd.replace(staging_host, "FIXMEFINALSSTATEHOST")
+ sstate_hardcode_cmd = sstate_hardcode_cmd.replace(staging_target, "FIXMEFINALSSTATETARGET")
+ f.write(sstate_hardcode_cmd)
+ bb.utils.copyfile(fixmefn, sstatefixmedir + "/fixmepath")
+ return
+
+ bb.note("Replacing fixme paths in sstate package: %s" % (sstate_hardcode_cmd))
+ subprocess.check_call(sstate_hardcode_cmd, shell=True)
+
+ # Need to remove this or we'd copy it into the target directory and may
+ # conflict with another writer
+ os.remove(fixmefn)
+}
+
+def sstate_clean_cachefile(ss, d):
+ import oe.path
+
+ if d.getVarFlag('do_%s' % ss['task'], 'task'):
+ d.setVar("SSTATE_PATH_CURRTASK", ss['task'])
+ sstatepkgfile = d.getVar('SSTATE_PATHSPEC')
+ bb.note("Removing %s" % sstatepkgfile)
+ oe.path.remove(sstatepkgfile)
+
+def sstate_clean_cachefiles(d):
+ for task in (d.getVar('SSTATETASKS') or "").split():
+ ld = d.createCopy()
+ ss = sstate_state_fromvars(ld, task)
+ sstate_clean_cachefile(ss, ld)
+
+def sstate_clean_manifest(manifest, d, canrace=False, prefix=None):
+ import oe.path
+
+ mfile = open(manifest)
+ entries = mfile.readlines()
+ mfile.close()
+
+ for entry in entries:
+ entry = entry.strip()
+ if prefix and not entry.startswith("/"):
+ entry = prefix + "/" + entry
+ bb.debug(2, "Removing manifest: %s" % entry)
+ # We can race against another package populating directories as we're removing them
+ # so we ignore errors here.
+ try:
+ if entry.endswith("/"):
+ if os.path.islink(entry[:-1]):
+ os.remove(entry[:-1])
+ elif os.path.exists(entry) and len(os.listdir(entry)) == 0 and not canrace:
+ # Removing directories whilst builds are in progress exposes a race. Only
+ # do it in contexts where it is safe to do so.
+ os.rmdir(entry[:-1])
+ else:
+ os.remove(entry)
+ except OSError:
+ pass
+
+ postrm = manifest + ".postrm"
+ if os.path.exists(manifest + ".postrm"):
+ import subprocess
+ os.chmod(postrm, 0o755)
+ subprocess.check_call(postrm, shell=True)
+ oe.path.remove(postrm)
+
+ oe.path.remove(manifest)
+
+def sstate_clean(ss, d):
+ import oe.path
+ import glob
+
+ d2 = d.createCopy()
+ stamp_clean = d.getVar("STAMPCLEAN")
+ extrainf = d.getVarFlag("do_" + ss['task'], 'stamp-extra-info')
+ if extrainf:
+ d2.setVar("SSTATE_MANMACH", extrainf)
+ wildcard_stfile = "%s.do_%s*.%s" % (stamp_clean, ss['task'], extrainf)
+ else:
+ wildcard_stfile = "%s.do_%s*" % (stamp_clean, ss['task'])
+
+ manifest = d2.expand("${SSTATE_MANFILEPREFIX}.%s" % ss['task'])
+
+ if os.path.exists(manifest):
+ locks = []
+ for lock in ss['lockfiles-shared']:
+ locks.append(bb.utils.lockfile(lock))
+ for lock in ss['lockfiles']:
+ locks.append(bb.utils.lockfile(lock))
+
+ sstate_clean_manifest(manifest, d, canrace=True)
+
+ for lock in locks:
+ bb.utils.unlockfile(lock)
+
+ # Remove the current and previous stamps, but keep the sigdata.
+ #
+ # The glob() matches do_task* which may match multiple tasks, for
+ # example: do_package and do_package_write_ipk, so we need to
+ # exactly match *.do_task.* and *.do_task_setscene.*
+ rm_stamp = '.do_%s.' % ss['task']
+ rm_setscene = '.do_%s_setscene.' % ss['task']
+ # For BB_SIGNATURE_HANDLER = "noop"
+ rm_nohash = ".do_%s" % ss['task']
+ for stfile in glob.glob(wildcard_stfile):
+ # Keep the sigdata
+ if ".sigdata." in stfile or ".sigbasedata." in stfile:
+ continue
+ # Preserve taint files in the stamps directory
+ if stfile.endswith('.taint'):
+ continue
+ if rm_stamp in stfile or rm_setscene in stfile or \
+ stfile.endswith(rm_nohash):
+ oe.path.remove(stfile)
+
+sstate_clean[vardepsexclude] = "SSTATE_MANFILEPREFIX"
+
+CLEANFUNCS += "sstate_cleanall"
+
+python sstate_cleanall() {
+ bb.note("Removing shared state for package %s" % d.getVar('PN'))
+
+ manifest_dir = d.getVar('SSTATE_MANIFESTS')
+ if not os.path.exists(manifest_dir):
+ return
+
+ tasks = d.getVar('SSTATETASKS').split()
+ for name in tasks:
+ ld = d.createCopy()
+ shared_state = sstate_state_fromvars(ld, name)
+ sstate_clean(shared_state, ld)
+}
+
+python sstate_hardcode_path () {
+ import subprocess, platform
+
+ # Need to remove hardcoded paths and fix these when we install the
+ # staging packages.
+ #
+ # Note: the logic in this function needs to match the reverse logic
+ # in sstate_installpkg(ss, d)
+
+ staging_target = d.getVar('RECIPE_SYSROOT')
+ staging_host = d.getVar('RECIPE_SYSROOT_NATIVE')
+ sstate_builddir = d.getVar('SSTATE_BUILDDIR')
+
+ sstate_sed_cmd = "sed -i -e 's:%s:FIXMESTAGINGDIRHOST:g'" % staging_host
+ if bb.data.inherits_class('native', d) or bb.data.inherits_class('cross-canadian', d):
+ sstate_grep_cmd = "grep -l -e '%s'" % (staging_host)
+ elif bb.data.inherits_class('cross', d) or bb.data.inherits_class('crosssdk', d):
+ sstate_grep_cmd = "grep -l -e '%s' -e '%s'" % (staging_target, staging_host)
+ sstate_sed_cmd += " -e 's:%s:FIXMESTAGINGDIRTARGET:g'" % staging_target
+ else:
+ sstate_grep_cmd = "grep -l -e '%s' -e '%s'" % (staging_target, staging_host)
+ sstate_sed_cmd += " -e 's:%s:FIXMESTAGINGDIRTARGET:g'" % staging_target
+
+ extra_staging_fixmes = d.getVar('EXTRA_STAGING_FIXMES') or ''
+ for fixmevar in extra_staging_fixmes.split():
+ fixme_path = d.getVar(fixmevar)
+ sstate_sed_cmd += " -e 's:%s:FIXME_%s:g'" % (fixme_path, fixmevar)
+ sstate_grep_cmd += " -e '%s'" % (fixme_path)
+
+ fixmefn = sstate_builddir + "fixmepath"
+
+ sstate_scan_cmd = d.getVar('SSTATE_SCAN_CMD')
+ sstate_filelist_cmd = "tee %s" % (fixmefn)
+
+ # fixmepath file needs relative paths, drop sstate_builddir prefix
+ sstate_filelist_relative_cmd = "sed -i -e 's:^%s::g' %s" % (sstate_builddir, fixmefn)
+
+ xargs_no_empty_run_cmd = '--no-run-if-empty'
+ if platform.system() == 'Darwin':
+ xargs_no_empty_run_cmd = ''
+
+ # Limit the fixpaths and sed operations based on the initial grep search
+ # This has the side effect of making sure the vfs cache is hot
+ sstate_hardcode_cmd = "%s | xargs %s | %s | xargs %s %s" % (sstate_scan_cmd, sstate_grep_cmd, sstate_filelist_cmd, xargs_no_empty_run_cmd, sstate_sed_cmd)
+
+ bb.note("Removing hardcoded paths from sstate package: '%s'" % (sstate_hardcode_cmd))
+ subprocess.check_output(sstate_hardcode_cmd, shell=True, cwd=sstate_builddir)
+
+ # If the fixmefn is empty, remove it..
+ if os.stat(fixmefn).st_size == 0:
+ os.remove(fixmefn)
+ else:
+ bb.note("Replacing absolute paths in fixmepath file: '%s'" % (sstate_filelist_relative_cmd))
+ subprocess.check_output(sstate_filelist_relative_cmd, shell=True)
+}
+
+def sstate_package(ss, d):
+ import oe.path
+
+ tmpdir = d.getVar('TMPDIR')
+
+ sstatebuild = d.expand("${WORKDIR}/sstate-build-%s/" % ss['task'])
+ d.setVar("SSTATE_CURRTASK", ss['task'])
+ bb.utils.remove(sstatebuild, recurse=True)
+ bb.utils.mkdirhier(sstatebuild)
+ for state in ss['dirs']:
+ if not os.path.exists(state[1]):
+ continue
+ srcbase = state[0].rstrip("/").rsplit('/', 1)[0]
+ # Find and error for absolute symlinks. We could attempt to relocate but its not
+ # clear where the symlink is relative to in this context. We could add that markup
+ # to sstate tasks but there aren't many of these so better just avoid them entirely.
+ for walkroot, dirs, files in os.walk(state[1]):
+ for file in files + dirs:
+ srcpath = os.path.join(walkroot, file)
+ if not os.path.islink(srcpath):
+ continue
+ link = os.readlink(srcpath)
+ if not os.path.isabs(link):
+ continue
+ if not link.startswith(tmpdir):
+ continue
+ bb.error("sstate found an absolute path symlink %s pointing at %s. Please replace this with a relative link." % (srcpath, link))
+ bb.debug(2, "Preparing tree %s for packaging at %s" % (state[1], sstatebuild + state[0]))
+ os.rename(state[1], sstatebuild + state[0])
+
+ workdir = d.getVar('WORKDIR')
+ sharedworkdir = os.path.join(d.getVar('TMPDIR'), "work-shared")
+ for plain in ss['plaindirs']:
+ pdir = plain.replace(workdir, sstatebuild)
+ if sharedworkdir in plain:
+ pdir = plain.replace(sharedworkdir, sstatebuild)
+ bb.utils.mkdirhier(plain)
+ bb.utils.mkdirhier(pdir)
+ os.rename(plain, pdir)
+
+ d.setVar('SSTATE_BUILDDIR', sstatebuild)
+ d.setVar('SSTATE_INSTDIR', sstatebuild)
+
+ if d.getVar('SSTATE_SKIP_CREATION') == '1':
+ return
+
+ sstate_create_package = ['sstate_report_unihash', 'sstate_create_package']
+ if d.getVar('SSTATE_SIG_KEY'):
+ sstate_create_package.append('sstate_sign_package')
+
+ for f in (d.getVar('SSTATECREATEFUNCS') or '').split() + \
+ sstate_create_package + \
+ (d.getVar('SSTATEPOSTCREATEFUNCS') or '').split():
+ # All hooks should run in SSTATE_BUILDDIR.
+ bb.build.exec_func(f, d, (sstatebuild,))
+
+ # SSTATE_PKG may have been changed by sstate_report_unihash
+ siginfo = d.getVar('SSTATE_PKG') + ".siginfo"
+ if not os.path.exists(siginfo):
+ bb.siggen.dump_this_task(siginfo, d)
+ else:
+ try:
+ os.utime(siginfo, None)
+ except PermissionError:
+ pass
+ except OSError as e:
+ # Handle read-only file systems gracefully
+ if e.errno != errno.EROFS:
+ raise e
+
+ return
+
+sstate_package[vardepsexclude] += "SSTATE_SIG_KEY"
+
+def pstaging_fetch(sstatefetch, d):
+ import bb.fetch2
+
+ # Only try and fetch if the user has configured a mirror
+ mirrors = d.getVar('SSTATE_MIRRORS')
+ if not mirrors:
+ return
+
+ # Copy the data object and override DL_DIR and SRC_URI
+ localdata = bb.data.createCopy(d)
+
+ dldir = localdata.expand("${SSTATE_DIR}")
+ bb.utils.mkdirhier(dldir)
+
+ localdata.delVar('MIRRORS')
+ localdata.setVar('FILESPATH', dldir)
+ localdata.setVar('DL_DIR', dldir)
+ localdata.setVar('PREMIRRORS', mirrors)
+
+ # if BB_NO_NETWORK is set but we also have SSTATE_MIRROR_ALLOW_NETWORK,
+ # we'll want to allow network access for the current set of fetches.
+ if bb.utils.to_boolean(localdata.getVar('BB_NO_NETWORK')) and \
+ bb.utils.to_boolean(localdata.getVar('SSTATE_MIRROR_ALLOW_NETWORK')):
+ localdata.delVar('BB_NO_NETWORK')
+
+ # Try a fetch from the sstate mirror, if it fails just return and
+ # we will build the package
+ uris = ['file://{0};downloadfilename={0}'.format(sstatefetch),
+ 'file://{0}.siginfo;downloadfilename={0}.siginfo'.format(sstatefetch)]
+ if bb.utils.to_boolean(d.getVar("SSTATE_VERIFY_SIG"), False):
+ uris += ['file://{0}.sig;downloadfilename={0}.sig'.format(sstatefetch)]
+
+ for srcuri in uris:
+ localdata.setVar('SRC_URI', srcuri)
+ try:
+ fetcher = bb.fetch2.Fetch([srcuri], localdata, cache=False)
+ fetcher.checkstatus()
+ fetcher.download()
+
+ except bb.fetch2.BBFetchException:
+ pass
+
+def sstate_setscene(d):
+ shared_state = sstate_state_fromvars(d)
+ accelerate = sstate_installpkg(shared_state, d)
+ if not accelerate:
+ bb.fatal("No suitable staging package found")
+
+python sstate_task_prefunc () {
+ shared_state = sstate_state_fromvars(d)
+ sstate_clean(shared_state, d)
+}
+sstate_task_prefunc[dirs] = "${WORKDIR}"
+
+python sstate_task_postfunc () {
+ shared_state = sstate_state_fromvars(d)
+
+ for intercept in shared_state['interceptfuncs']:
+ bb.build.exec_func(intercept, d, (d.getVar("WORKDIR"),))
+
+ omask = os.umask(0o002)
+ if omask != 0o002:
+ bb.note("Using umask 0o002 (not %0o) for sstate packaging" % omask)
+ sstate_package(shared_state, d)
+ os.umask(omask)
+
+ sstateinst = d.getVar("SSTATE_INSTDIR")
+ d.setVar('SSTATE_FIXMEDIR', shared_state['fixmedir'])
+
+ sstate_installpkgdir(shared_state, d)
+
+ bb.utils.remove(d.getVar("SSTATE_BUILDDIR"), recurse=True)
+}
+sstate_task_postfunc[dirs] = "${WORKDIR}"
+
+
+#
+# Shell function to generate a sstate package from a directory
+# set as SSTATE_BUILDDIR. Will be run from within SSTATE_BUILDDIR.
+#
+sstate_create_package () {
+ # Exit early if it already exists
+ if [ -e ${SSTATE_PKG} ]; then
+ [ ! -w ${SSTATE_PKG} ] || touch ${SSTATE_PKG}
+ return
+ fi
+
+ mkdir --mode=0775 -p `dirname ${SSTATE_PKG}`
+ TFILE=`mktemp ${SSTATE_PKG}.XXXXXXXX`
+
+ # Use pigz if available
+ OPT="-czS"
+ if [ -x "$(command -v pigz)" ]; then
+ OPT="-I pigz -cS"
+ fi
+
+ # Need to handle empty directories
+ if [ "$(ls -A)" ]; then
+ set +e
+ tar $OPT -f $TFILE *
+ ret=$?
+ if [ $ret -ne 0 ] && [ $ret -ne 1 ]; then
+ exit 1
+ fi
+ set -e
+ else
+ tar $OPT --file=$TFILE --files-from=/dev/null
+ fi
+ chmod 0664 $TFILE
+ # Skip if it was already created by some other process
+ if [ ! -e ${SSTATE_PKG} ]; then
+ # Move into place using ln to attempt an atomic op.
+ # Abort if it already exists
+ ln $TFILE ${SSTATE_PKG} && rm $TFILE
+ else
+ rm $TFILE
+ fi
+ [ ! -w ${SSTATE_PKG} ] || touch ${SSTATE_PKG}
+}
+
+python sstate_sign_package () {
+ from oe.gpg_sign import get_signer
+
+
+ signer = get_signer(d, 'local')
+ sstate_pkg = d.getVar('SSTATE_PKG')
+ if os.path.exists(sstate_pkg + '.sig'):
+ os.unlink(sstate_pkg + '.sig')
+ signer.detach_sign(sstate_pkg, d.getVar('SSTATE_SIG_KEY', False), None,
+ d.getVar('SSTATE_SIG_PASSPHRASE'), armor=False)
+}
+
+python sstate_report_unihash() {
+ report_unihash = getattr(bb.parse.siggen, 'report_unihash', None)
+
+ if report_unihash:
+ ss = sstate_state_fromvars(d)
+ report_unihash(os.getcwd(), ss['task'], d)
+}
+
+#
+# Shell function to decompress and prepare a package for installation
+# Will be run from within SSTATE_INSTDIR.
+#
+sstate_unpack_package () {
+ tar -xvzf ${SSTATE_PKG}
+ # update .siginfo atime on local/NFS mirror
+ [ -O ${SSTATE_PKG}.siginfo ] && [ -w ${SSTATE_PKG}.siginfo ] && [ -h ${SSTATE_PKG}.siginfo ] && touch -a ${SSTATE_PKG}.siginfo
+ # Use "! -w ||" to return true for read only files
+ [ ! -w ${SSTATE_PKG} ] || touch --no-dereference ${SSTATE_PKG}
+ [ ! -w ${SSTATE_PKG}.sig ] || [ ! -e ${SSTATE_PKG}.sig ] || touch --no-dereference ${SSTATE_PKG}.sig
+ [ ! -w ${SSTATE_PKG}.siginfo ] || [ ! -e ${SSTATE_PKG}.siginfo ] || touch --no-dereference ${SSTATE_PKG}.siginfo
+}
+
+BB_HASHCHECK_FUNCTION = "sstate_checkhashes"
+
+def sstate_checkhashes(sq_data, d, siginfo=False, currentcount=0, summary=True, **kwargs):
+ found = set()
+ foundLocal = set()
+ foundNet = set()
+ missed = set()
+
+ def gethash(task):
+ return sq_data['unihash'][task]
+
+ def getpathcomponents(task, d):
+ # Magic data from BB_HASHFILENAME
+ splithashfn = sq_data['hashfn'][task].split(" ")
+ spec = splithashfn[1]
+ if splithashfn[0] == "True":
+ extrapath = d.getVar("NATIVELSBSTRING") + "/"
+ else:
+ extrapath = ""
+
+ tname = bb.runqueue.taskname_from_tid(task)[3:]
+
+ if tname in ["fetch", "unpack", "patch", "populate_lic", "preconfigure"] and splithashfn[2]:
+ spec = splithashfn[2]
+ extrapath = ""
+
+ return spec, extrapath, tname
+
+
+ for tid in sq_data['hash']:
+
+ spec, extrapath, tname = getpathcomponents(tid, d)
+
+ sstatefile = d.expand("${SSTATE_DIR}/" + extrapath + generate_sstatefn(spec, gethash(tid), tname, siginfo, d))
+
+ if os.path.exists(sstatefile):
+ bb.debug(2, "SState: Found valid sstate file %s" % sstatefile)
+ found.add(tid)
+ foundLocal.add(tid)
+ continue
+ else:
+ missed.add(tid)
+ bb.debug(2, "SState: Looked for but didn't find file %s" % sstatefile)
+
+ mirrors = d.getVar("SSTATE_MIRRORS")
+ if mirrors:
+ # Copy the data object and override DL_DIR and SRC_URI
+ localdata = bb.data.createCopy(d)
+
+ dldir = localdata.expand("${SSTATE_DIR}")
+ localdata.delVar('MIRRORS')
+ localdata.setVar('FILESPATH', dldir)
+ localdata.setVar('DL_DIR', dldir)
+ localdata.setVar('PREMIRRORS', mirrors)
+
+ bb.debug(2, "SState using premirror of: %s" % mirrors)
+
+ # if BB_NO_NETWORK is set but we also have SSTATE_MIRROR_ALLOW_NETWORK,
+ # we'll want to allow network access for the current set of fetches.
+ if bb.utils.to_boolean(localdata.getVar('BB_NO_NETWORK')) and \
+ bb.utils.to_boolean(localdata.getVar('SSTATE_MIRROR_ALLOW_NETWORK')):
+ localdata.delVar('BB_NO_NETWORK')
+
+ from bb.fetch2 import FetchConnectionCache
+ def checkstatus_init(thread_worker):
+ thread_worker.connection_cache = FetchConnectionCache()
+
+ def checkstatus_end(thread_worker):
+ thread_worker.connection_cache.close_connections()
+
+ def checkstatus(thread_worker, arg):
+ (tid, sstatefile) = arg
+
+ localdata2 = bb.data.createCopy(localdata)
+ srcuri = "file://" + sstatefile
+ localdata.setVar('SRC_URI', srcuri)
+ bb.debug(2, "SState: Attempting to fetch %s" % srcuri)
+
+ try:
+ fetcher = bb.fetch2.Fetch(srcuri.split(), localdata2,
+ connection_cache=thread_worker.connection_cache)
+ fetcher.checkstatus()
+ bb.debug(2, "SState: Successful fetch test for %s" % srcuri)
+ found.add(tid)
+ foundNet.add(tid)
+ if tid in missed:
+ missed.remove(tid)
+ except:
+ missed.add(tid)
+ bb.debug(2, "SState: Unsuccessful fetch test for %s" % srcuri)
+ pass
+ if len(tasklist) >= min_tasks:
+ bb.event.fire(bb.event.ProcessProgress(msg, len(tasklist) - thread_worker.tasks.qsize()), d)
+
+ tasklist = []
+ min_tasks = 100
+ for tid in sq_data['hash']:
+ if tid in found:
+ continue
+ spec, extrapath, tname = getpathcomponents(tid, d)
+ sstatefile = d.expand(extrapath + generate_sstatefn(spec, gethash(tid), tname, siginfo, d))
+ tasklist.append((tid, sstatefile))
+
+ if tasklist:
+ if len(tasklist) >= min_tasks:
+ msg = "Checking sstate mirror object availability"
+ bb.event.fire(bb.event.ProcessStarted(msg, len(tasklist)), d)
+
+ import multiprocessing
+ nproc = min(multiprocessing.cpu_count(), len(tasklist))
+
+ bb.event.enable_threadlock()
+ pool = oe.utils.ThreadedPool(nproc, len(tasklist),
+ worker_init=checkstatus_init, worker_end=checkstatus_end)
+ for t in tasklist:
+ pool.add_task(checkstatus, t)
+ pool.start()
+ pool.wait_completion()
+ bb.event.disable_threadlock()
+
+ if len(tasklist) >= min_tasks:
+ bb.event.fire(bb.event.ProcessFinished(msg), d)
+
+ inheritlist = d.getVar("INHERIT")
+ if "toaster" in inheritlist:
+ evdata = {'missed': [], 'found': []};
+ for tid in missed:
+ spec, extrapath, tname = getpathcomponents(tid, d)
+ sstatefile = d.expand(extrapath + generate_sstatefn(spec, gethash(tid), tname, False, d))
+ evdata['missed'].append((bb.runqueue.fn_from_tid(tid), bb.runqueue.taskname_from_tid(tid), gethash(tid), sstatefile ) )
+ for tid in found:
+ spec, extrapath, tname = getpathcomponents(tid, d)
+ sstatefile = d.expand(extrapath + generate_sstatefn(spec, gethash(tid), tname, False, d))
+ evdata['found'].append((bb.runqueue.fn_from_tid(tid), bb.runqueue.taskname_from_tid(tid), gethash(tid), sstatefile ) )
+ bb.event.fire(bb.event.MetadataEvent("MissedSstate", evdata), d)
+
+ if summary:
+ # Print some summary statistics about the current task completion and how much sstate
+ # reuse there was. Avoid divide by zero errors.
+ total = len(sq_data['hash'])
+ complete = 0
+ if currentcount:
+ complete = (len(found) + currentcount) / (total + currentcount) * 100
+ match = 0
+ if total:
+ match = len(found) / total * 100
+ bb.plain("Sstate summary: Wanted %d Local %d Network %d Missed %d Current %d (%d%% match, %d%% complete)" % (total, len(foundLocal), len(foundNet),len(missed), currentcount, match, complete))
+
+ if hasattr(bb.parse.siggen, "checkhashes"):
+ bb.parse.siggen.checkhashes(sq_data, missed, found, d)
+
+ return found
+
+BB_SETSCENE_DEPVALID = "setscene_depvalid"
+
+def setscene_depvalid(task, taskdependees, notneeded, d, log=None):
+ # taskdependees is a dict of tasks which depend on task, each being a 3 item list of [PN, TASKNAME, FILENAME]
+ # task is included in taskdependees too
+ # Return - False - We need this dependency
+ # - True - We can skip this dependency
+ import re
+
+ def logit(msg, log):
+ if log is not None:
+ log.append(msg)
+ else:
+ bb.debug(2, msg)
+
+ logit("Considering setscene task: %s" % (str(taskdependees[task])), log)
+
+ def isNativeCross(x):
+ return x.endswith("-native") or "-cross-" in x or "-crosssdk" in x or x.endswith("-cross")
+
+ # We only need to trigger populate_lic through direct dependencies
+ if taskdependees[task][1] == "do_populate_lic":
+ return True
+
+ # stash_locale and gcc_stash_builddir are never needed as a dependency for built objects
+ if taskdependees[task][1] == "do_stash_locale" or taskdependees[task][1] == "do_gcc_stash_builddir":
+ return True
+
+ # We only need to trigger packagedata through direct dependencies
+ # but need to preserve packagedata on packagedata links
+ if taskdependees[task][1] == "do_packagedata":
+ for dep in taskdependees:
+ if taskdependees[dep][1] == "do_packagedata":
+ return False
+ return True
+
+ for dep in taskdependees:
+ logit(" considering dependency: %s" % (str(taskdependees[dep])), log)
+ if task == dep:
+ continue
+ if dep in notneeded:
+ continue
+ # do_package_write_* and do_package doesn't need do_package
+ if taskdependees[task][1] == "do_package" and taskdependees[dep][1] in ['do_package', 'do_package_write_deb', 'do_package_write_ipk', 'do_package_write_rpm', 'do_packagedata', 'do_package_qa']:
+ continue
+ # do_package_write_* need do_populate_sysroot as they're mainly postinstall dependencies
+ if taskdependees[task][1] == "do_populate_sysroot" and taskdependees[dep][1] in ['do_package_write_deb', 'do_package_write_ipk', 'do_package_write_rpm']:
+ return False
+ # do_package/packagedata/package_qa don't need do_populate_sysroot
+ if taskdependees[task][1] == "do_populate_sysroot" and taskdependees[dep][1] in ['do_package', 'do_packagedata', 'do_package_qa']:
+ continue
+ # Native/Cross packages don't exist and are noexec anyway
+ if isNativeCross(taskdependees[dep][0]) and taskdependees[dep][1] in ['do_package_write_deb', 'do_package_write_ipk', 'do_package_write_rpm', 'do_packagedata', 'do_package', 'do_package_qa']:
+ continue
+
+ # This is due to the [depends] in useradd.bbclass complicating matters
+ # The logic *is* reversed here due to the way hard setscene dependencies are injected
+ if (taskdependees[task][1] == 'do_package' or taskdependees[task][1] == 'do_populate_sysroot') and taskdependees[dep][0].endswith(('shadow-native', 'shadow-sysroot', 'base-passwd', 'pseudo-native')) and taskdependees[dep][1] == 'do_populate_sysroot':
+ continue
+
+ # Consider sysroot depending on sysroot tasks
+ if taskdependees[task][1] == 'do_populate_sysroot' and taskdependees[dep][1] == 'do_populate_sysroot':
+ # Allow excluding certain recursive dependencies. If a recipe needs it should add a
+ # specific dependency itself, rather than relying on one of its dependees to pull
+ # them in.
+ # See also http://lists.openembedded.org/pipermail/openembedded-core/2018-January/146324.html
+ not_needed = False
+ excludedeps = d.getVar('_SSTATE_EXCLUDEDEPS_SYSROOT')
+ if excludedeps is None:
+ # Cache the regular expressions for speed
+ excludedeps = []
+ for excl in (d.getVar('SSTATE_EXCLUDEDEPS_SYSROOT') or "").split():
+ excludedeps.append((re.compile(excl.split('->', 1)[0]), re.compile(excl.split('->', 1)[1])))
+ d.setVar('_SSTATE_EXCLUDEDEPS_SYSROOT', excludedeps)
+ for excl in excludedeps:
+ if excl[0].match(taskdependees[dep][0]):
+ if excl[1].match(taskdependees[task][0]):
+ not_needed = True
+ break
+ if not_needed:
+ continue
+ # For meta-extsdk-toolchain we want all sysroot dependencies
+ if taskdependees[dep][0] == 'meta-extsdk-toolchain':
+ return False
+ # Native/Cross populate_sysroot need their dependencies
+ if isNativeCross(taskdependees[task][0]) and isNativeCross(taskdependees[dep][0]):
+ return False
+ # Target populate_sysroot depended on by cross tools need to be installed
+ if isNativeCross(taskdependees[dep][0]):
+ return False
+ # Native/cross tools depended upon by target sysroot are not needed
+ # Add an exception for shadow-native as required by useradd.bbclass
+ if isNativeCross(taskdependees[task][0]) and taskdependees[task][0] != 'shadow-native':
+ continue
+ # Target populate_sysroot need their dependencies
+ return False
+
+ if taskdependees[task][1] == 'do_shared_workdir':
+ continue
+
+ if taskdependees[dep][1] == "do_populate_lic":
+ continue
+
+
+ # Safe fallthrough default
+ logit(" Default setscene dependency fall through due to dependency: %s" % (str(taskdependees[dep])), log)
+ return False
+ return True
+
+addhandler sstate_eventhandler
+sstate_eventhandler[eventmask] = "bb.build.TaskSucceeded"
+python sstate_eventhandler() {
+ d = e.data
+ writtensstate = d.getVar('SSTATE_CURRTASK')
+ if not writtensstate:
+ taskname = d.getVar("BB_RUNTASK")[3:]
+ spec = d.getVar('SSTATE_PKGSPEC')
+ swspec = d.getVar('SSTATE_SWSPEC')
+ if taskname in ["fetch", "unpack", "patch", "populate_lic", "preconfigure"] and swspec:
+ d.setVar("SSTATE_PKGSPEC", "${SSTATE_SWSPEC}")
+ d.setVar("SSTATE_EXTRAPATH", "")
+ d.setVar("SSTATE_CURRTASK", taskname)
+ siginfo = d.getVar('SSTATE_PKG') + ".siginfo"
+ if not os.path.exists(siginfo):
+ bb.siggen.dump_this_task(siginfo, d)
+ else:
+ try:
+ os.utime(siginfo, None)
+ except PermissionError:
+ pass
+ except OSError as e:
+ # Handle read-only file systems gracefully
+ if e.errno != errno.EROFS:
+ raise e
+
+}
+
+SSTATE_PRUNE_OBSOLETEWORKDIR ?= "1"
+
+#
+# Event handler which removes manifests and stamps file for recipes which are no
+# longer 'reachable' in a build where they once were. 'Reachable' refers to
+# whether a recipe is parsed so recipes in a layer which was removed would no
+# longer be reachable. Switching between systemd and sysvinit where recipes
+# became skipped would be another example.
+#
+# Also optionally removes the workdir of those tasks/recipes
+#
+addhandler sstate_eventhandler_reachablestamps
+sstate_eventhandler_reachablestamps[eventmask] = "bb.event.ReachableStamps"
+python sstate_eventhandler_reachablestamps() {
+ import glob
+ d = e.data
+ stamps = e.stamps.values()
+ removeworkdir = (d.getVar("SSTATE_PRUNE_OBSOLETEWORKDIR", False) == "1")
+ preservestampfile = d.expand('${SSTATE_MANIFESTS}/preserve-stamps')
+ preservestamps = []
+ if os.path.exists(preservestampfile):
+ with open(preservestampfile, 'r') as f:
+ preservestamps = f.readlines()
+ seen = []
+
+ # The machine index contains all the stamps this machine has ever seen in this build directory.
+ # We should only remove things which this machine once accessed but no longer does.
+ machineindex = set()
+ bb.utils.mkdirhier(d.expand("${SSTATE_MANIFESTS}"))
+ mi = d.expand("${SSTATE_MANIFESTS}/index-machine-${MACHINE}")
+ if os.path.exists(mi):
+ with open(mi, "r") as f:
+ machineindex = set(line.strip() for line in f.readlines())
+
+ for a in sorted(list(set(d.getVar("SSTATE_ARCHS").split()))):
+ toremove = []
+ i = d.expand("${SSTATE_MANIFESTS}/index-" + a)
+ if not os.path.exists(i):
+ continue
+ manseen = set()
+ ignore = []
+ with open(i, "r") as f:
+ lines = f.readlines()
+ for l in reversed(lines):
+ try:
+ (stamp, manifest, workdir) = l.split()
+ # The index may have multiple entries for the same manifest as the code above only appends
+ # new entries and there may be an entry with matching manifest but differing version in stamp/workdir.
+ # The last entry in the list is the valid one, any earlier entries with matching manifests
+ # should be ignored.
+ if manifest in manseen:
+ ignore.append(l)
+ continue
+ manseen.add(manifest)
+ if stamp not in stamps and stamp not in preservestamps and stamp in machineindex:
+ toremove.append(l)
+ if stamp not in seen:
+ bb.debug(2, "Stamp %s is not reachable, removing related manifests" % stamp)
+ seen.append(stamp)
+ except ValueError:
+ bb.fatal("Invalid line '%s' in sstate manifest '%s'" % (l, i))
+
+ if toremove:
+ msg = "Removing %d recipes from the %s sysroot" % (len(toremove), a)
+ bb.event.fire(bb.event.ProcessStarted(msg, len(toremove)), d)
+
+ removed = 0
+ for r in toremove:
+ (stamp, manifest, workdir) = r.split()
+ for m in glob.glob(manifest + ".*"):
+ if m.endswith(".postrm"):
+ continue
+ sstate_clean_manifest(m, d)
+ bb.utils.remove(stamp + "*")
+ if removeworkdir:
+ bb.utils.remove(workdir, recurse = True)
+ lines.remove(r)
+ removed = removed + 1
+ bb.event.fire(bb.event.ProcessProgress(msg, removed), d)
+
+ bb.event.fire(bb.event.ProcessFinished(msg), d)
+
+ with open(i, "w") as f:
+ for l in lines:
+ if l in ignore:
+ continue
+ f.write(l)
+ machineindex |= set(stamps)
+ with open(mi, "w") as f:
+ for l in machineindex:
+ f.write(l + "\n")
+
+ if preservestamps:
+ os.remove(preservestampfile)
+}
+
+
+#
+# Bitbake can generate an event showing which setscene tasks are 'stale',
+# i.e. which ones will be rerun. These are ones where a stamp file is present but
+# it is stable (e.g. taskhash doesn't match). With that list we can go through
+# the manifests for matching tasks and "uninstall" those manifests now. We do
+# this now rather than mid build since the distribution of files between sstate
+# objects may have changed, new tasks may run first and if those new tasks overlap
+# with the stale tasks, we'd see overlapping files messages and failures. Thankfully
+# removing these files is fast.
+#
+addhandler sstate_eventhandler_stalesstate
+sstate_eventhandler_stalesstate[eventmask] = "bb.event.StaleSetSceneTasks"
+python sstate_eventhandler_stalesstate() {
+ d = e.data
+ tasks = e.tasks
+
+ bb.utils.mkdirhier(d.expand("${SSTATE_MANIFESTS}"))
+
+ for a in list(set(d.getVar("SSTATE_ARCHS").split())):
+ toremove = []
+ i = d.expand("${SSTATE_MANIFESTS}/index-" + a)
+ if not os.path.exists(i):
+ continue
+ with open(i, "r") as f:
+ lines = f.readlines()
+ for l in lines:
+ try:
+ (stamp, manifest, workdir) = l.split()
+ for tid in tasks:
+ for s in tasks[tid]:
+ if s.startswith(stamp):
+ taskname = bb.runqueue.taskname_from_tid(tid)[3:]
+ manname = manifest + "." + taskname
+ if os.path.exists(manname):
+ bb.debug(2, "Sstate for %s is stale, removing related manifest %s" % (tid, manname))
+ toremove.append((manname, tid, tasks[tid]))
+ break
+ except ValueError:
+ bb.fatal("Invalid line '%s' in sstate manifest '%s'" % (l, i))
+
+ if toremove:
+ msg = "Removing %d stale sstate objects for arch %s" % (len(toremove), a)
+ bb.event.fire(bb.event.ProcessStarted(msg, len(toremove)), d)
+
+ removed = 0
+ for (manname, tid, stamps) in toremove:
+ sstate_clean_manifest(manname, d)
+ for stamp in stamps:
+ bb.utils.remove(stamp)
+ removed = removed + 1
+ bb.event.fire(bb.event.ProcessProgress(msg, removed), d)
+
+ bb.event.fire(bb.event.ProcessFinished(msg), d)
+}
diff --git a/meta/lib/oe/gpg_sign.py b/meta/lib/oe/gpg_sign.py
new file mode 100644
index 0000000..492f096
--- /dev/null
+++ b/meta/lib/oe/gpg_sign.py
@@ -0,0 +1,130 @@
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+
+"""Helper module for GPG signing"""
+import os
+
+import bb
+import subprocess
+import shlex
+
+class LocalSigner(object):
+ """Class for handling local (on the build host) signing"""
+ def __init__(self, d):
+ self.gpg_bin = d.getVar('GPG_BIN') or \
+ bb.utils.which(os.getenv('PATH'), 'gpg')
+ self.gpg_cmd = [self.gpg_bin]
+ self.gpg_agent_bin = bb.utils.which(os.getenv('PATH'), "gpg-agent")
+ # Without this we see "Cannot allocate memory" errors when running processes in parallel
+ # It needs to be set for any gpg command since any agent launched can stick around in memory
+ # and this parameter must be set.
+ if self.gpg_agent_bin:
+ self.gpg_cmd += ["--agent-program=%s|--auto-expand-secmem" % (self.gpg_agent_bin)]
+ self.gpg_path = d.getVar('GPG_PATH')
+ self.rpm_bin = bb.utils.which(os.getenv('PATH'), "rpmsign")
+ self.gpg_version = self.get_gpg_version()
+
+
+ def export_pubkey(self, output_file, keyid, armor=True):
+ """Export GPG public key to a file"""
+ cmd = self.gpg_cmd + ["--no-permission-warning", "--batch", "--yes", "--export", "-o", output_file]
+ if self.gpg_path:
+ cmd += ["--homedir", self.gpg_path]
+ if armor:
+ cmd += ["--armor"]
+ cmd += [keyid]
+ subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+
+ def sign_rpms(self, files, keyid, passphrase, digest, sign_chunk, fsk=None, fsk_password=None):
+ """Sign RPM files"""
+
+ cmd = self.rpm_bin + " --addsign --define '_gpg_name %s' " % keyid
+ gpg_args = '--no-permission-warning --batch --passphrase=%s --agent-program=%s|--auto-expand-secmem' % (passphrase, self.gpg_agent_bin)
+ if self.gpg_version > (2,1,):
+ gpg_args += ' --pinentry-mode=loopback'
+ cmd += "--define '_gpg_sign_cmd_extra_args %s' " % gpg_args
+ cmd += "--define '_binary_filedigest_algorithm %s' " % digest
+ if self.gpg_bin:
+ cmd += "--define '__gpg %s' " % self.gpg_bin
+ if self.gpg_path:
+ cmd += "--define '_gpg_path %s' " % self.gpg_path
+ if fsk:
+ cmd += "--signfiles --fskpath %s " % fsk
+ if fsk_password:
+ cmd += "--define '_file_signing_key_password %s' " % fsk_password
+
+ # Sign in chunks
+ for i in range(0, len(files), sign_chunk):
+ subprocess.check_output(shlex.split(cmd + ' '.join(files[i:i+sign_chunk])), stderr=subprocess.STDOUT)
+
+ def detach_sign(self, input_file, keyid, passphrase_file, passphrase=None, armor=True):
+ """Create a detached signature of a file"""
+
+ if passphrase_file and passphrase:
+ raise Exception("You should use either passphrase_file of passphrase, not both")
+
+ cmd = self.gpg_cmd + ['--detach-sign', '--no-permission-warning', '--batch',
+ '--no-tty', '--yes', '--passphrase-fd', '0', '-u', keyid]
+
+ if self.gpg_path:
+ cmd += ['--homedir', self.gpg_path]
+ if armor:
+ cmd += ['--armor']
+
+ #gpg > 2.1 supports password pipes only through the loopback interface
+ #gpg < 2.1 errors out if given unknown parameters
+ if self.gpg_version > (2,1,):
+ cmd += ['--pinentry-mode', 'loopback']
+
+ cmd += [input_file]
+
+ try:
+ if passphrase_file:
+ with open(passphrase_file) as fobj:
+ passphrase = fobj.readline();
+
+ job = subprocess.Popen(cmd, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
+ (_, stderr) = job.communicate(passphrase.encode("utf-8"))
+
+ if job.returncode:
+ bb.fatal("GPG exited with code %d: %s" % (job.returncode, stderr.decode("utf-8")))
+
+ except IOError as e:
+ bb.error("IO error (%s): %s" % (e.errno, e.strerror))
+ raise Exception("Failed to sign '%s'" % input_file)
+
+ except OSError as e:
+ bb.error("OS error (%s): %s" % (e.errno, e.strerror))
+ raise Exception("Failed to sign '%s" % input_file)
+
+
+ def get_gpg_version(self):
+ """Return the gpg version as a tuple of ints"""
+ try:
+ cmd = self.gpg_cmd + ["--version", "--no-permission-warning"]
+ ver_str = subprocess.check_output(cmd).split()[2].decode("utf-8")
+ return tuple([int(i) for i in ver_str.split("-")[0].split('.')])
+ except subprocess.CalledProcessError as e:
+ bb.fatal("Could not get gpg version: %s" % e)
+
+
+ def verify(self, sig_file):
+ """Verify signature"""
+ cmd = self.gpg_cmd + ["--verify", "--no-permission-warning"]
+ if self.gpg_path:
+ cmd += ["--homedir", self.gpg_path]
+
+ cmd += [sig_file]
+ status = subprocess.call(cmd)
+ ret = False if status else True
+ return ret
+
+
+def get_signer(d, backend):
+ """Get signer object for the specified backend"""
+ # Use local signing by default
+ if backend == 'local':
+ return LocalSigner(d)
+ else:
+ bb.fatal("Unsupported signing backend '%s'" % backend)
diff --git a/meta/lib/oe/sstatesig.py b/meta/lib/oe/sstatesig.py
new file mode 100644
index 0000000..1a526db
--- /dev/null
+++ b/meta/lib/oe/sstatesig.py
@@ -0,0 +1,603 @@
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+import bb.siggen
+import bb.runqueue
+import oe
+
+def sstate_rundepfilter(siggen, fn, recipename, task, dep, depname, dataCaches):
+ # Return True if we should keep the dependency, False to drop it
+ def isNative(x):
+ return x.endswith("-native")
+ def isCross(x):
+ return "-cross-" in x
+ def isNativeSDK(x):
+ return x.startswith("nativesdk-")
+ def isKernel(mc, fn):
+ inherits = " ".join(dataCaches[mc].inherits[fn])
+ return inherits.find("/module-base.bbclass") != -1 or inherits.find("/linux-kernel-base.bbclass") != -1
+ def isPackageGroup(mc, fn):
+ inherits = " ".join(dataCaches[mc].inherits[fn])
+ return "/packagegroup.bbclass" in inherits
+ def isAllArch(mc, fn):
+ inherits = " ".join(dataCaches[mc].inherits[fn])
+ return "/allarch.bbclass" in inherits
+ def isImage(mc, fn):
+ return "/image.bbclass" in " ".join(dataCaches[mc].inherits[fn])
+
+ depmc, _, deptaskname, depmcfn = bb.runqueue.split_tid_mcfn(dep)
+ mc, _ = bb.runqueue.split_mc(fn)
+
+ # (Almost) always include our own inter-task dependencies (unless it comes
+ # from a mcdepends). The exception is the special
+ # do_kernel_configme->do_unpack_and_patch dependency from archiver.bbclass.
+ if recipename == depname and depmc == mc:
+ if task == "do_kernel_configme" and deptaskname == "do_unpack_and_patch":
+ return False
+ return True
+
+ # Exclude well defined recipe->dependency
+ if "%s->%s" % (recipename, depname) in siggen.saferecipedeps:
+ return False
+
+ # Check for special wildcard
+ if "*->%s" % depname in siggen.saferecipedeps and recipename != depname:
+ return False
+
+ # Don't change native/cross/nativesdk recipe dependencies any further
+ if isNative(recipename) or isCross(recipename) or isNativeSDK(recipename):
+ return True
+
+ # Only target packages beyond here
+
+ # allarch packagegroups are assumed to have well behaved names which don't change between architecures/tunes
+ if isPackageGroup(mc, fn) and isAllArch(mc, fn) and not isNative(depname):
+ return False
+
+ # Exclude well defined machine specific configurations which don't change ABI
+ if depname in siggen.abisaferecipes and not isImage(mc, fn):
+ return False
+
+ # Kernel modules are well namespaced. We don't want to depend on the kernel's checksum
+ # if we're just doing an RRECOMMENDS_xxx = "kernel-module-*", not least because the checksum
+ # is machine specific.
+ # Therefore if we're not a kernel or a module recipe (inheriting the kernel classes)
+ # and we reccomend a kernel-module, we exclude the dependency.
+ if dataCaches and isKernel(depmc, depmcfn) and not isKernel(mc, fn):
+ for pkg in dataCaches[mc].runrecs[fn]:
+ if " ".join(dataCaches[mc].runrecs[fn][pkg]).find("kernel-module-") != -1:
+ return False
+
+ # Default to keep dependencies
+ return True
+
+def sstate_lockedsigs(d):
+ sigs = {}
+ types = (d.getVar("SIGGEN_LOCKEDSIGS_TYPES") or "").split()
+ for t in types:
+ siggen_lockedsigs_var = "SIGGEN_LOCKEDSIGS_%s" % t
+ lockedsigs = (d.getVar(siggen_lockedsigs_var) or "").split()
+ for ls in lockedsigs:
+ pn, task, h = ls.split(":", 2)
+ if pn not in sigs:
+ sigs[pn] = {}
+ sigs[pn][task] = [h, siggen_lockedsigs_var]
+ return sigs
+
+class SignatureGeneratorOEBasic(bb.siggen.SignatureGeneratorBasic):
+ name = "OEBasic"
+ def init_rundepcheck(self, data):
+ self.abisaferecipes = (data.getVar("SIGGEN_EXCLUDERECIPES_ABISAFE") or "").split()
+ self.saferecipedeps = (data.getVar("SIGGEN_EXCLUDE_SAFE_RECIPE_DEPS") or "").split()
+ pass
+ def rundep_check(self, fn, recipename, task, dep, depname, dataCaches = None):
+ return sstate_rundepfilter(self, fn, recipename, task, dep, depname, dataCaches)
+
+class SignatureGeneratorOEBasicHashMixIn(object):
+ supports_multiconfig_datacaches = True
+
+ def init_rundepcheck(self, data):
+ self.abisaferecipes = (data.getVar("SIGGEN_EXCLUDERECIPES_ABISAFE") or "").split()
+ self.saferecipedeps = (data.getVar("SIGGEN_EXCLUDE_SAFE_RECIPE_DEPS") or "").split()
+ self.lockedsigs = sstate_lockedsigs(data)
+ self.lockedhashes = {}
+ self.lockedpnmap = {}
+ self.lockedhashfn = {}
+ self.machine = data.getVar("MACHINE")
+ self.mismatch_msgs = []
+ self.unlockedrecipes = (data.getVar("SIGGEN_UNLOCKED_RECIPES") or
+ "").split()
+ self.unlockedrecipes = { k: "" for k in self.unlockedrecipes }
+ self.buildarch = data.getVar('BUILD_ARCH')
+ self._internal = False
+ pass
+
+ def tasks_resolved(self, virtmap, virtpnmap, dataCache):
+ # Translate virtual/xxx entries to PN values
+ newabisafe = []
+ for a in self.abisaferecipes:
+ if a in virtpnmap:
+ newabisafe.append(virtpnmap[a])
+ else:
+ newabisafe.append(a)
+ self.abisaferecipes = newabisafe
+ newsafedeps = []
+ for a in self.saferecipedeps:
+ a1, a2 = a.split("->")
+ if a1 in virtpnmap:
+ a1 = virtpnmap[a1]
+ if a2 in virtpnmap:
+ a2 = virtpnmap[a2]
+ newsafedeps.append(a1 + "->" + a2)
+ self.saferecipedeps = newsafedeps
+
+ def rundep_check(self, fn, recipename, task, dep, depname, dataCaches = None):
+ return sstate_rundepfilter(self, fn, recipename, task, dep, depname, dataCaches)
+
+ def get_taskdata(self):
+ return (self.lockedpnmap, self.lockedhashfn, self.lockedhashes) + super().get_taskdata()
+
+ def set_taskdata(self, data):
+ self.lockedpnmap, self.lockedhashfn, self.lockedhashes = data[:3]
+ super().set_taskdata(data[3:])
+
+ def dump_sigs(self, dataCache, options):
+ sigfile = os.getcwd() + "/locked-sigs.inc"
+ bb.plain("Writing locked sigs to %s" % sigfile)
+ self.dump_lockedsigs(sigfile)
+ return super(bb.siggen.SignatureGeneratorBasicHash, self).dump_sigs(dataCache, options)
+
+ def prep_taskhash(self, tid, deps, dataCaches):
+ super().prep_taskhash(tid, deps, dataCaches)
+ if hasattr(self, "extramethod"):
+ (mc, _, _, fn) = bb.runqueue.split_tid_mcfn(tid)
+ inherits = " ".join(dataCaches[mc].inherits[fn])
+ if inherits.find("/native.bbclass") != -1 or inherits.find("/cross.bbclass") != -1:
+ self.extramethod[tid] = ":" + self.buildarch
+
+ def get_taskhash(self, tid, deps, dataCaches):
+ if tid in self.lockedhashes:
+ if self.lockedhashes[tid]:
+ return self.lockedhashes[tid]
+ else:
+ return super().get_taskhash(tid, deps, dataCaches)
+
+ h = super().get_taskhash(tid, deps, dataCaches)
+
+ (mc, _, task, fn) = bb.runqueue.split_tid_mcfn(tid)
+
+ recipename = dataCaches[mc].pkg_fn[fn]
+ self.lockedpnmap[fn] = recipename
+ self.lockedhashfn[fn] = dataCaches[mc].hashfn[fn]
+
+ unlocked = False
+ if recipename in self.unlockedrecipes:
+ unlocked = True
+ else:
+ def recipename_from_dep(dep):
+ (depmc, _, _, depfn) = bb.runqueue.split_tid_mcfn(dep)
+ return dataCaches[depmc].pkg_fn[depfn]
+
+ # If any unlocked recipe is in the direct dependencies then the
+ # current recipe should be unlocked as well.
+ depnames = [ recipename_from_dep(x) for x in deps if mc == bb.runqueue.mc_from_tid(x)]
+ if any(x in y for y in depnames for x in self.unlockedrecipes):
+ self.unlockedrecipes[recipename] = ''
+ unlocked = True
+
+ if not unlocked and recipename in self.lockedsigs:
+ if task in self.lockedsigs[recipename]:
+ h_locked = self.lockedsigs[recipename][task][0]
+ var = self.lockedsigs[recipename][task][1]
+ self.lockedhashes[tid] = h_locked
+ self._internal = True
+ unihash = self.get_unihash(tid)
+ self._internal = False
+ #bb.warn("Using %s %s %s" % (recipename, task, h))
+
+ if h != h_locked and h_locked != unihash:
+ self.mismatch_msgs.append('The %s:%s sig is computed to be %s, but the sig is locked to %s in %s'
+ % (recipename, task, h, h_locked, var))
+
+ return h_locked
+
+ self.lockedhashes[tid] = False
+ #bb.warn("%s %s %s" % (recipename, task, h))
+ return h
+
+ def get_stampfile_hash(self, tid):
+ if tid in self.lockedhashes and self.lockedhashes[tid]:
+ return self.lockedhashes[tid]
+ return super().get_stampfile_hash(tid)
+
+ def get_unihash(self, tid):
+ if tid in self.lockedhashes and self.lockedhashes[tid] and not self._internal:
+ return self.lockedhashes[tid]
+ return super().get_unihash(tid)
+
+ def dump_sigtask(self, fn, task, stampbase, runtime):
+ tid = fn + ":" + task
+ if tid in self.lockedhashes and self.lockedhashes[tid]:
+ return
+ super(bb.siggen.SignatureGeneratorBasicHash, self).dump_sigtask(fn, task, stampbase, runtime)
+
+ def dump_lockedsigs(self, sigfile, taskfilter=None):
+ types = {}
+ for tid in self.runtaskdeps:
+ if taskfilter:
+ if not tid in taskfilter:
+ continue
+ fn = bb.runqueue.fn_from_tid(tid)
+ t = self.lockedhashfn[fn].split(" ")[1].split(":")[5]
+ t = 't-' + t.replace('_', '-')
+ if t not in types:
+ types[t] = []
+ types[t].append(tid)
+
+ with open(sigfile, "w") as f:
+ l = sorted(types)
+ for t in l:
+ f.write('SIGGEN_LOCKEDSIGS_%s = "\\\n' % t)
+ types[t].sort()
+ sortedtid = sorted(types[t], key=lambda tid: self.lockedpnmap[bb.runqueue.fn_from_tid(tid)])
+ for tid in sortedtid:
+ (_, _, task, fn) = bb.runqueue.split_tid_mcfn(tid)
+ if tid not in self.taskhash:
+ continue
+ f.write(" " + self.lockedpnmap[fn] + ":" + task + ":" + self.get_unihash(tid) + " \\\n")
+ f.write(' "\n')
+ f.write('SIGGEN_LOCKEDSIGS_TYPES_%s = "%s"' % (self.machine, " ".join(l)))
+
+ def dump_siglist(self, sigfile):
+ with open(sigfile, "w") as f:
+ tasks = []
+ for taskitem in self.taskhash:
+ (fn, task) = taskitem.rsplit(":", 1)
+ pn = self.lockedpnmap[fn]
+ tasks.append((pn, task, fn, self.taskhash[taskitem]))
+ for (pn, task, fn, taskhash) in sorted(tasks):
+ f.write('%s:%s %s %s\n' % (pn, task, fn, taskhash))
+
+ def checkhashes(self, sq_data, missed, found, d):
+ warn_msgs = []
+ error_msgs = []
+ sstate_missing_msgs = []
+
+ for tid in sq_data['hash']:
+ if tid not in found:
+ for pn in self.lockedsigs:
+ taskname = bb.runqueue.taskname_from_tid(tid)
+ if sq_data['hash'][tid] in iter(self.lockedsigs[pn].values()):
+ if taskname == 'do_shared_workdir':
+ continue
+ sstate_missing_msgs.append("Locked sig is set for %s:%s (%s) yet not in sstate cache?"
+ % (pn, taskname, sq_data['hash'][tid]))
+
+ checklevel = d.getVar("SIGGEN_LOCKEDSIGS_TASKSIG_CHECK")
+ if checklevel == 'warn':
+ warn_msgs += self.mismatch_msgs
+ elif checklevel == 'error':
+ error_msgs += self.mismatch_msgs
+
+ checklevel = d.getVar("SIGGEN_LOCKEDSIGS_SSTATE_EXISTS_CHECK")
+ if checklevel == 'warn':
+ warn_msgs += sstate_missing_msgs
+ elif checklevel == 'error':
+ error_msgs += sstate_missing_msgs
+
+ if warn_msgs:
+ bb.warn("\n".join(warn_msgs))
+ if error_msgs:
+ bb.fatal("\n".join(error_msgs))
+
+class SignatureGeneratorOEBasicHash(SignatureGeneratorOEBasicHashMixIn, bb.siggen.SignatureGeneratorBasicHash):
+ name = "OEBasicHash"
+
+class SignatureGeneratorOEEquivHash(SignatureGeneratorOEBasicHashMixIn, bb.siggen.SignatureGeneratorUniHashMixIn, bb.siggen.SignatureGeneratorBasicHash):
+ name = "OEEquivHash"
+
+ def init_rundepcheck(self, data):
+ super().init_rundepcheck(data)
+ self.server = data.getVar('BB_HASHSERVE')
+ if not self.server:
+ bb.fatal("OEEquivHash requires BB_HASHSERVE to be set")
+ self.method = data.getVar('SSTATE_HASHEQUIV_METHOD')
+ if not self.method:
+ bb.fatal("OEEquivHash requires SSTATE_HASHEQUIV_METHOD to be set")
+
+# Insert these classes into siggen's namespace so it can see and select them
+bb.siggen.SignatureGeneratorOEBasic = SignatureGeneratorOEBasic
+bb.siggen.SignatureGeneratorOEBasicHash = SignatureGeneratorOEBasicHash
+bb.siggen.SignatureGeneratorOEEquivHash = SignatureGeneratorOEEquivHash
+
+
+def find_siginfo(pn, taskname, taskhashlist, d):
+ """ Find signature data files for comparison purposes """
+
+ import fnmatch
+ import glob
+
+ if not taskname:
+ # We have to derive pn and taskname
+ key = pn
+ splitit = key.split('.bb:')
+ taskname = splitit[1]
+ pn = os.path.basename(splitit[0]).split('_')[0]
+ if key.startswith('virtual:native:'):
+ pn = pn + '-native'
+
+ hashfiles = {}
+ filedates = {}
+
+ def get_hashval(siginfo):
+ if siginfo.endswith('.siginfo'):
+ return siginfo.rpartition(':')[2].partition('_')[0]
+ else:
+ return siginfo.rpartition('.')[2]
+
+ # First search in stamps dir
+ localdata = d.createCopy()
+ localdata.setVar('MULTIMACH_TARGET_SYS', '*')
+ localdata.setVar('PN', pn)
+ localdata.setVar('PV', '*')
+ localdata.setVar('PR', '*')
+ localdata.setVar('EXTENDPE', '')
+ stamp = localdata.getVar('STAMP')
+ if pn.startswith("gcc-source"):
+ # gcc-source shared workdir is a special case :(
+ stamp = localdata.expand("${STAMPS_DIR}/work-shared/gcc-${PV}-${PR}")
+
+ filespec = '%s.%s.sigdata.*' % (stamp, taskname)
+ foundall = False
+ import glob
+ for fullpath in glob.glob(filespec):
+ match = False
+ if taskhashlist:
+ for taskhash in taskhashlist:
+ if fullpath.endswith('.%s' % taskhash):
+ hashfiles[taskhash] = fullpath
+ if len(hashfiles) == len(taskhashlist):
+ foundall = True
+ break
+ else:
+ try:
+ filedates[fullpath] = os.stat(fullpath).st_mtime
+ except OSError:
+ continue
+ hashval = get_hashval(fullpath)
+ hashfiles[hashval] = fullpath
+
+ if not taskhashlist or (len(filedates) < 2 and not foundall):
+ # That didn't work, look in sstate-cache
+ hashes = taskhashlist or ['?' * 64]
+ localdata = bb.data.createCopy(d)
+ for hashval in hashes:
+ localdata.setVar('PACKAGE_ARCH', '*')
+ localdata.setVar('TARGET_VENDOR', '*')
+ localdata.setVar('TARGET_OS', '*')
+ localdata.setVar('PN', pn)
+ localdata.setVar('PV', '*')
+ localdata.setVar('PR', '*')
+ localdata.setVar('BB_TASKHASH', hashval)
+ swspec = localdata.getVar('SSTATE_SWSPEC')
+ if taskname in ['do_fetch', 'do_unpack', 'do_patch', 'do_populate_lic', 'do_preconfigure'] and swspec:
+ localdata.setVar('SSTATE_PKGSPEC', '${SSTATE_SWSPEC}')
+ elif pn.endswith('-native') or "-cross-" in pn or "-crosssdk-" in pn:
+ localdata.setVar('SSTATE_EXTRAPATH', "${NATIVELSBSTRING}/")
+ sstatename = taskname[3:]
+ filespec = '%s_%s.*.siginfo' % (localdata.getVar('SSTATE_PKG'), sstatename)
+
+ matchedfiles = glob.glob(filespec)
+ for fullpath in matchedfiles:
+ actual_hashval = get_hashval(fullpath)
+ if actual_hashval in hashfiles:
+ continue
+ hashfiles[hashval] = fullpath
+ if not taskhashlist:
+ try:
+ filedates[fullpath] = os.stat(fullpath).st_mtime
+ except:
+ continue
+
+ if taskhashlist:
+ return hashfiles
+ else:
+ return filedates
+
+bb.siggen.find_siginfo = find_siginfo
+
+
+def sstate_get_manifest_filename(task, d):
+ """
+ Return the sstate manifest file path for a particular task.
+ Also returns the datastore that can be used to query related variables.
+ """
+ d2 = d.createCopy()
+ extrainf = d.getVarFlag("do_" + task, 'stamp-extra-info')
+ if extrainf:
+ d2.setVar("SSTATE_MANMACH", extrainf)
+ return (d2.expand("${SSTATE_MANFILEPREFIX}.%s" % task), d2)
+
+def find_sstate_manifest(taskdata, taskdata2, taskname, d, multilibcache):
+ d2 = d
+ variant = ''
+ curr_variant = ''
+ if d.getVar("BBEXTENDCURR") == "multilib":
+ curr_variant = d.getVar("BBEXTENDVARIANT")
+ if "virtclass-multilib" not in d.getVar("OVERRIDES"):
+ curr_variant = "invalid"
+ if taskdata2.startswith("virtual:multilib"):
+ variant = taskdata2.split(":")[2]
+ if curr_variant != variant:
+ if variant not in multilibcache:
+ multilibcache[variant] = oe.utils.get_multilib_datastore(variant, d)
+ d2 = multilibcache[variant]
+
+ if taskdata.endswith("-native"):
+ pkgarchs = ["${BUILD_ARCH}", "${BUILD_ARCH}_${ORIGNATIVELSBSTRING}"]
+ elif taskdata.startswith("nativesdk-"):
+ pkgarchs = ["${SDK_ARCH}_${SDK_OS}", "allarch"]
+ elif "-cross-canadian" in taskdata:
+ pkgarchs = ["${SDK_ARCH}_${SDK_ARCH}-${SDKPKGSUFFIX}"]
+ elif "-cross-" in taskdata:
+ pkgarchs = ["${BUILD_ARCH}_${TARGET_ARCH}"]
+ elif "-crosssdk" in taskdata:
+ pkgarchs = ["${BUILD_ARCH}_${SDK_ARCH}_${SDK_OS}"]
+ else:
+ pkgarchs = ['${MACHINE_ARCH}']
+ pkgarchs = pkgarchs + list(reversed(d2.getVar("PACKAGE_EXTRA_ARCHS").split()))
+ pkgarchs.append('allarch')
+ pkgarchs.append('${SDK_ARCH}_${SDK_ARCH}-${SDKPKGSUFFIX}')
+
+ for pkgarch in pkgarchs:
+ manifest = d2.expand("${SSTATE_MANIFESTS}/manifest-%s-%s.%s" % (pkgarch, taskdata, taskname))
+ if os.path.exists(manifest):
+ return manifest, d2
+ bb.error("Manifest %s not found in %s (variant '%s')?" % (manifest, d2.expand(" ".join(pkgarchs)), variant))
+ return None, d2
+
+def OEOuthashBasic(path, sigfile, task, d):
+ """
+ Basic output hash function
+
+ Calculates the output hash of a task by hashing all output file metadata,
+ and file contents.
+ """
+ import hashlib
+ import stat
+ import pwd
+ import grp
+
+ def update_hash(s):
+ s = s.encode('utf-8')
+ h.update(s)
+ if sigfile:
+ sigfile.write(s)
+
+ h = hashlib.sha256()
+ prev_dir = os.getcwd()
+ include_owners = os.environ.get('PSEUDO_DISABLED') == '0'
+ if "package_write_" in task or task == "package_qa":
+ include_owners = False
+ include_timestamps = False
+ if task == "package":
+ include_timestamps = d.getVar('BUILD_REPRODUCIBLE_BINARIES') == '1'
+ extra_content = d.getVar('HASHEQUIV_HASH_VERSION')
+
+ try:
+ os.chdir(path)
+
+ update_hash("OEOuthashBasic\n")
+ if extra_content:
+ update_hash(extra_content + "\n")
+
+ # It is only currently useful to get equivalent hashes for things that
+ # can be restored from sstate. Since the sstate object is named using
+ # SSTATE_PKGSPEC and the task name, those should be included in the
+ # output hash calculation.
+ update_hash("SSTATE_PKGSPEC=%s\n" % d.getVar('SSTATE_PKGSPEC'))
+ update_hash("task=%s\n" % task)
+
+ for root, dirs, files in os.walk('.', topdown=True):
+ # Sort directories to ensure consistent ordering when recursing
+ dirs.sort()
+ files.sort()
+
+ def process(path):
+ s = os.lstat(path)
+
+ if stat.S_ISDIR(s.st_mode):
+ update_hash('d')
+ elif stat.S_ISCHR(s.st_mode):
+ update_hash('c')
+ elif stat.S_ISBLK(s.st_mode):
+ update_hash('b')
+ elif stat.S_ISSOCK(s.st_mode):
+ update_hash('s')
+ elif stat.S_ISLNK(s.st_mode):
+ update_hash('l')
+ elif stat.S_ISFIFO(s.st_mode):
+ update_hash('p')
+ else:
+ update_hash('-')
+
+ def add_perm(mask, on, off='-'):
+ if mask & s.st_mode:
+ update_hash(on)
+ else:
+ update_hash(off)
+
+ add_perm(stat.S_IRUSR, 'r')
+ add_perm(stat.S_IWUSR, 'w')
+ if stat.S_ISUID & s.st_mode:
+ add_perm(stat.S_IXUSR, 's', 'S')
+ else:
+ add_perm(stat.S_IXUSR, 'x')
+
+ add_perm(stat.S_IRGRP, 'r')
+ add_perm(stat.S_IWGRP, 'w')
+ if stat.S_ISGID & s.st_mode:
+ add_perm(stat.S_IXGRP, 's', 'S')
+ else:
+ add_perm(stat.S_IXGRP, 'x')
+
+ add_perm(stat.S_IROTH, 'r')
+ add_perm(stat.S_IWOTH, 'w')
+ if stat.S_ISVTX & s.st_mode:
+ update_hash('t')
+ else:
+ add_perm(stat.S_IXOTH, 'x')
+
+ if include_owners:
+ try:
+ update_hash(" %10s" % pwd.getpwuid(s.st_uid).pw_name)
+ update_hash(" %10s" % grp.getgrgid(s.st_gid).gr_name)
+ except KeyError as e:
+ bb.warn("KeyError in %s" % path)
+ msg = ("KeyError: %s\nPath %s is owned by uid %d, gid %d, which doesn't match "
+ "any user/group on target. This may be due to host contamination." % (e, path, s.st_uid, s.st_gid))
+ raise Exception(msg).with_traceback(e.__traceback__)
+
+ if include_timestamps:
+ update_hash(" %10d" % s.st_mtime)
+
+ update_hash(" ")
+ if stat.S_ISBLK(s.st_mode) or stat.S_ISCHR(s.st_mode):
+ update_hash("%9s" % ("%d.%d" % (os.major(s.st_rdev), os.minor(s.st_rdev))))
+ else:
+ update_hash(" " * 9)
+
+ update_hash(" ")
+ if stat.S_ISREG(s.st_mode):
+ update_hash("%10d" % s.st_size)
+ else:
+ update_hash(" " * 10)
+
+ update_hash(" ")
+ fh = hashlib.sha256()
+ if stat.S_ISREG(s.st_mode):
+ # Hash file contents
+ with open(path, 'rb') as d:
+ for chunk in iter(lambda: d.read(4096), b""):
+ fh.update(chunk)
+ update_hash(fh.hexdigest())
+ else:
+ update_hash(" " * len(fh.hexdigest()))
+
+ update_hash(" %s" % path)
+
+ if stat.S_ISLNK(s.st_mode):
+ update_hash(" -> %s" % os.readlink(path))
+
+ update_hash("\n")
+
+ # Process this directory and all its child files
+ process(root)
+ for f in files:
+ if f == 'fixmepath':
+ continue
+ process(os.path.join(root, f))
+ finally:
+ os.chdir(prev_dir)
+
+ return h.hexdigest()
--
2.30.2
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v5 07/13] sstate: configure
2021-11-04 11:51 [PATCH v5 00/13] Add sstate-cache Adriaan Schmidt
` (5 preceding siblings ...)
2021-11-04 11:51 ` [PATCH v5 06/13] meta: add sstate feature from oe Adriaan Schmidt
@ 2021-11-04 11:51 ` Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 08/13] sstate: add caching to isar-bootstrap Adriaan Schmidt
` (5 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Adriaan Schmidt @ 2021-11-04 11:51 UTC (permalink / raw)
To: isar-users; +Cc: Adriaan Schmidt
Basic (global) configuration of the sstate-cache.
Signed-off-by: Adriaan Schmidt <adriaan.schmidt@siemens.com>
---
meta/classes/base.bbclass | 2 +-
meta/conf/bitbake.conf | 10 ++++++++--
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/meta/classes/base.bbclass b/meta/classes/base.bbclass
index fe5bcb0..2d8805c 100644
--- a/meta/classes/base.bbclass
+++ b/meta/classes/base.bbclass
@@ -21,7 +21,7 @@
THISDIR = "${@os.path.dirname(d.getVar('FILE', True))}"
FILESPATH = "${@base_set_filespath(["${FILE_DIRNAME}/${PF}", "${FILE_DIRNAME}/${P}", "${FILE_DIRNAME}/${PN}", "${FILE_DIRNAME}/files", "${FILE_DIRNAME}"], d)}"
-OE_IMPORTS += "os sys time oe.path oe.patch"
+OE_IMPORTS += "os sys time oe.path oe.patch oe.sstatesig"
OE_IMPORTS[type] = "list"
def oe_import(d):
diff --git a/meta/conf/bitbake.conf b/meta/conf/bitbake.conf
index 7f5901d..db7eb2f 100644
--- a/meta/conf/bitbake.conf
+++ b/meta/conf/bitbake.conf
@@ -54,7 +54,8 @@ DEPLOY_DIR_BUILDCHROOT = "${DEPLOY_DIR}/buildchroot"
DEPLOY_DIR_SDKCHROOT = "${DEPLOY_DIR}/sdkchroot"
DEPLOY_DIR_IMAGE = "${DEPLOY_DIR}/images/${MACHINE}"
DL_DIR ?= "${TOPDIR}/downloads"
-SSTATE_DIR ?= "${TMPDIR}/sstate-cache"
+SSTATE_DIR ?= "${TOPDIR}/sstate-cache"
+SSTATE_MANIFESTS = "${TMPDIR}/sstate-control/${DISTRO}-${DISTRO_ARCH}"
BUILDCHROOT_HOST_DIR = "${DEPLOY_DIR_BUILDCHROOT}-host/${HOST_DISTRO}-${HOST_ARCH}_${DISTRO}-${DISTRO_ARCH}"
BUILDCHROOT_TARGET_DIR = "${DEPLOY_DIR_BUILDCHROOT}-target/${DISTRO}-${DISTRO_ARCH}"
SDKCHROOT_DIR = "${DEPLOY_DIR_SDKCHROOT}/${DISTRO}-${DISTRO_ARCH}"
@@ -79,6 +80,11 @@ QEMU_ARCH_riscv64 = "riscv64"
# Codename of the repository created by the caching class
DEBDISTRONAME ?= "isar"
+# Strings used in sstate signature files
+TARGET_VENDOR = ""
+TARGET_OS = "isar"
+PACKAGE_ARCH ?= "${DISTRO_ARCH}"
+
# Isar apt repository paths
REPO_ISAR_DIR = "${DEPLOY_DIR}/isar-apt/${DISTRO}-${DISTRO_ARCH}/apt"
REPO_ISAR_DB_DIR = "${DEPLOY_DIR}/isar-apt/${DISTRO}-${DISTRO_ARCH}/db"
@@ -113,7 +119,7 @@ PARALLEL_MAKE ?= "-j ${@bb.utils.cpu_count()}"
BBINCLUDELOGS ??= "yes"
# Add event handlers for bitbake
-INHERIT += "isar-events"
+INHERIT += "isar-events sstate"
include conf/local.conf
include conf/multiconfig/${BB_CURRENT_MC}.conf
--
2.30.2
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v5 08/13] sstate: add caching to isar-bootstrap
2021-11-04 11:51 [PATCH v5 00/13] Add sstate-cache Adriaan Schmidt
` (6 preceding siblings ...)
2021-11-04 11:51 ` [PATCH v5 07/13] sstate: configure Adriaan Schmidt
@ 2021-11-04 11:51 ` Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 09/13] sstate: add caching to rootfs Adriaan Schmidt
` (4 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Adriaan Schmidt @ 2021-11-04 11:51 UTC (permalink / raw)
To: isar-users; +Cc: Adriaan Schmidt
We cache the output of do_bootstrap. This needs "sudo tar" to preserve
ownerships and permissions.
Signed-off-by: Adriaan Schmidt <adriaan.schmidt@siemens.com>
---
.../isar-bootstrap/isar-bootstrap.inc | 24 +++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/meta/recipes-core/isar-bootstrap/isar-bootstrap.inc b/meta/recipes-core/isar-bootstrap/isar-bootstrap.inc
index e9f9291..7e2c588 100644
--- a/meta/recipes-core/isar-bootstrap/isar-bootstrap.inc
+++ b/meta/recipes-core/isar-bootstrap/isar-bootstrap.inc
@@ -398,6 +398,30 @@ EOSUDO
addtask bootstrap before do_build after do_generate_keyrings
+SSTATETASKS += "do_bootstrap"
+BOOTSTRAP_SSTATE = "${WORKDIR}/bootstrap-sstate"
+do_bootstrap[dirs] += "${BOOTSTRAP_SSTATE}"
+do_bootstrap[cleandirs] += "${BOOTSTRAP_SSTATE}"
+do_bootstrap[sstate-plaindirs] = "${BOOTSTRAP_SSTATE}"
+do_bootstrap[sstate-interceptfuncs] = "bootstrap_sstate_prepare"
+
+bootstrap_sstate_prepare() {
+ sudo tar -C $(dirname "${ROOTFSDIR}") -cpf ${BOOTSTRAP_SSTATE}/bootstrap.tar --one-file-system $(basename "${ROOTFSDIR}")
+}
+
+bootstrap_sstate_finalize() {
+ sudo tar -C $(dirname "${ROOTFSDIR}") -xpf ${BOOTSTRAP_SSTATE}/bootstrap.tar
+ sudo ln -Tfsr "${ROOTFSDIR}" "${DEPLOY_ISAR_BOOTSTRAP}"
+}
+
+python do_bootstrap_setscene() {
+ sstate_setscene(d)
+ bb.build.exec_func('bootstrap_sstate_finalize', d)
+}
+
+addtask do_bootstrap_setscene
+do_bootstrap_setscene[dirs] = "${DEPLOY_DIR_BOOTSTRAP}"
+
CLEANFUNCS = "clean_deploy"
clean_deploy() {
rm -f "${DEPLOY_ISAR_BOOTSTRAP}"
--
2.30.2
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v5 09/13] sstate: add caching to rootfs
2021-11-04 11:51 [PATCH v5 00/13] Add sstate-cache Adriaan Schmidt
` (7 preceding siblings ...)
2021-11-04 11:51 ` [PATCH v5 08/13] sstate: add caching to isar-bootstrap Adriaan Schmidt
@ 2021-11-04 11:51 ` Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 10/13] sstate: add caching to debian packages Adriaan Schmidt
` (3 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Adriaan Schmidt @ 2021-11-04 11:51 UTC (permalink / raw)
To: isar-users; +Cc: Adriaan Schmidt
We cache the output of do_rootfs. Again "sudo tar" to preserve
ownerships and permissions. In addition, it can happen that there are
active mounts while tar'ing the rootfs (there shouldn't be, but I've
seen cases where deb-src is mounted). Unfortunately --one-file-system
does not stop at bind mounts, so we use a trick with a temporary mount
to ensure that we don't package more than we want.
Special care needs to be taken with image and initramfs, which are
MACHINE specific. So they adapt sstate manifest location to ensure
that there is no false sharing.
Signed-off-by: Adriaan Schmidt <adriaan.schmidt@siemens.com>
---
meta/classes/image.bbclass | 3 +++
meta/classes/initramfs.bbclass | 3 +++
meta/classes/rootfs.bbclass | 27 +++++++++++++++++++++++++++
3 files changed, 33 insertions(+)
diff --git a/meta/classes/image.bbclass b/meta/classes/image.bbclass
index 5a0f32e..eedc160 100644
--- a/meta/classes/image.bbclass
+++ b/meta/classes/image.bbclass
@@ -6,6 +6,9 @@ WORKDIR = "${TMPDIR}/work/${DISTRO}-${DISTRO_ARCH}/${PN}-${MACHINE}-${IMAGE_TYPE
STAMP = "${STAMPS_DIR}/${DISTRO}-${DISTRO_ARCH}/${PN}-${MACHINE}-${IMAGE_TYPE}/${PV}-${PR}"
STAMPCLEAN = "${STAMPS_DIR}/${DISTRO}-${DISTRO_ARCH}/${PN}-${MACHINE}-${IMAGE_TYPE}/*-*"
+# Sstate also needs to be machine-specific
+SSTATE_MANIFESTS = "${TMPDIR}/sstate-control/${MACHINE}-${DISTRO}-${DISTRO_ARCH}"
+
IMAGE_INSTALL ?= ""
IMAGE_TYPE ?= "ext4-img"
IMAGE_ROOTFS ?= "${WORKDIR}/rootfs"
diff --git a/meta/classes/initramfs.bbclass b/meta/classes/initramfs.bbclass
index 10a642b..2cec85d 100644
--- a/meta/classes/initramfs.bbclass
+++ b/meta/classes/initramfs.bbclass
@@ -5,6 +5,9 @@ WORKDIR = "${TMPDIR}/work/${DISTRO}-${DISTRO_ARCH}/${PN}-${MACHINE}/${PV}-${PR}"
STAMP = "${STAMPS_DIR}/${DISTRO}-${DISTRO_ARCH}/${PN}-${MACHINE}/${PV}-${PR}"
STAMPCLEAN = "${STAMPS_DIR}/${DISTRO}-${DISTRO_ARCH}/${PN}-${MACHINE}/*-*"
+# Sstate also needs to be machine-specific
+SSTATE_MANIFESTS = "${TMPDIR}/sstate-control/${MACHINE}-${DISTRO}-${DISTRO_ARCH}"
+
INITRAMFS_INSTALL ?= ""
INITRAMFS_PREINSTALL ?= ""
INITRAMFS_ROOTFS ?= "${WORKDIR}/rootfs"
diff --git a/meta/classes/rootfs.bbclass b/meta/classes/rootfs.bbclass
index b38de66..e0604e0 100644
--- a/meta/classes/rootfs.bbclass
+++ b/meta/classes/rootfs.bbclass
@@ -265,3 +265,30 @@ python do_rootfs() {
pass
}
addtask rootfs before do_build
+
+do_rootfs[depends] = "base-apt:do_cache isar-apt:do_cache_config"
+
+SSTATETASKS += "do_rootfs"
+ROOTFS_SSTATE = "${WORKDIR}/rootfs-sstate"
+do_rootfs[dirs] += "${ROOTFS_SSTATE} ${WORKDIR}/mnt/rootfs"
+do_rootfs[cleandirs] += "${ROOTFS_SSTATE}"
+do_rootfs[sstate-plaindirs] = "${ROOTFS_SSTATE}"
+do_rootfs[sstate-interceptfuncs] = "rootfs_sstate_prepare"
+
+# the buildchroot is owned by root, so we need some sudoing to pack and unpack
+rootfs_sstate_prepare() {
+ sudo mount --bind ${WORKDIR}/rootfs ${WORKDIR}/mnt/rootfs -o ro
+ sudo tar -C ${WORKDIR}/mnt -cpf ${ROOTFS_SSTATE}/rootfs.tar --one-file-system rootfs
+ sudo umount ${WORKDIR}/mnt/rootfs
+}
+do_rootfs_sstate_prepare[lockfiles] = "${REPO_ISAR_DIR}/isar.lock"
+
+rootfs_sstate_finalize() {
+ sudo tar -C ${WORKDIR} -xpf ${ROOTFS_SSTATE}/rootfs.tar
+}
+
+python do_rootfs_setscene() {
+ sstate_setscene(d)
+ bb.build.exec_func('rootfs_sstate_finalize', d)
+}
+addtask do_rootfs_setscene
--
2.30.2
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v5 10/13] sstate: add caching to debian packages
2021-11-04 11:51 [PATCH v5 00/13] Add sstate-cache Adriaan Schmidt
` (8 preceding siblings ...)
2021-11-04 11:51 ` [PATCH v5 09/13] sstate: add caching to rootfs Adriaan Schmidt
@ 2021-11-04 11:51 ` Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 11/13] test: pass absolute path for build_dir Adriaan Schmidt
` (2 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Adriaan Schmidt @ 2021-11-04 11:51 UTC (permalink / raw)
To: isar-users; +Cc: Adriaan Schmidt
We cache *.deb packages at the end of dpkg_build.
Signed-off-by: Adriaan Schmidt <adriaan.schmidt@siemens.com>
---
meta/classes/dpkg-base.bbclass | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/meta/classes/dpkg-base.bbclass b/meta/classes/dpkg-base.bbclass
index 7e372de..eb57e35 100644
--- a/meta/classes/dpkg-base.bbclass
+++ b/meta/classes/dpkg-base.bbclass
@@ -214,6 +214,31 @@ python do_dpkg_build() {
addtask dpkg_build
+SSTATETASKS += "do_dpkg_build"
+DPKG_SSTATE = "${WORKDIR}/dpkg-sstate"
+do_dpkg_build[dirs] += "${DPKG_SSTATE} ${S}/.."
+do_dpkg_build[cleandirs] += "${DPKG_SSTATE}"
+do_dpkg_build[sstate-plaindirs] = "${DPKG_SSTATE}"
+do_dpkg_build[sstate-interceptfuncs] = "dpkg_build_sstate_prepare"
+
+dpkg_build_sstate_prepare() {
+ test -n "$(find ${S}/.. -maxdepth 1 -name '*.deb' -print -quit)" &&
+ ln -f ${S}/../*.deb -t ${DPKG_SSTATE}
+}
+
+dpkg_build_sstate_finalize() {
+ test -n "$(find ${DPKG_SSTATE} -maxdepth 1 -name '*.deb' -print -quit)" &&
+ ln -f ${DPKG_SSTATE}/*.deb -t ${S}/..
+}
+
+python do_dpkg_build_setscene() {
+ sstate_setscene(d)
+ bb.build.exec_func('dpkg_build_sstate_finalize', d)
+}
+
+addtask dpkg_build_setscene
+do_dpkg_build_setscene[dirs] += "${S}/.."
+
KEEP_INSTALLED_ON_CLEAN ?= "0"
CLEANFUNCS += "deb_clean"
--
2.30.2
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v5 11/13] test: pass absolute path for build_dir
2021-11-04 11:51 [PATCH v5 00/13] Add sstate-cache Adriaan Schmidt
` (9 preceding siblings ...)
2021-11-04 11:51 ` [PATCH v5 10/13] sstate: add caching to debian packages Adriaan Schmidt
@ 2021-11-04 11:51 ` Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 12/13] test: make bitbake_args a list Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 13/13] sstate: add test case Adriaan Schmidt
12 siblings, 0 replies; 14+ messages in thread
From: Adriaan Schmidt @ 2021-11-04 11:51 UTC (permalink / raw)
To: isar-users; +Cc: Adriaan Schmidt
When having multiple calls to `perform_*_test` in one test case (which
is what a future test for the sstate cache will use), `build_dir`
gets mixed up if it's passed as a relative path. I observe that
the first bitbake has the correct build dir ($isar_root/build), but
the second one has $isar_root/build/build, and then fails.
I briefly tried fixing this in the testsuite itself, but did not
manage, so here's a workaround.
Signed-off-by: Adriaan Schmidt <adriaan.schmidt@siemens.com>
---
scripts/ci_build.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/ci_build.sh b/scripts/ci_build.sh
index 5ff455e..4ebd1a5 100755
--- a/scripts/ci_build.sh
+++ b/scripts/ci_build.sh
@@ -28,7 +28,7 @@ fi
BUILD_TEST_DIR="$(pwd)/testsuite/build_test"
# Start build in Isar tree by default
-BUILD_DIR=./build
+BUILD_DIR="$(pwd)/build"
# Check dependencies
DEPENDENCIES="umoci skopeo"
--
2.30.2
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v5 12/13] test: make bitbake_args a list
2021-11-04 11:51 [PATCH v5 00/13] Add sstate-cache Adriaan Schmidt
` (10 preceding siblings ...)
2021-11-04 11:51 ` [PATCH v5 11/13] test: pass absolute path for build_dir Adriaan Schmidt
@ 2021-11-04 11:51 ` Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 13/13] sstate: add test case Adriaan Schmidt
12 siblings, 0 replies; 14+ messages in thread
From: Adriaan Schmidt @ 2021-11-04 11:51 UTC (permalink / raw)
To: isar-users; +Cc: Adriaan Schmidt
The existing code composes the bitbake command as a list, but composes
bitbake args as string. Currently that doesn't matter because there is
at most one arg (-v). But when adding tests for sstate cache, there will
be another one. This patch makes the code somewhat more consistent
and extensible by also handling the args in a list.
Signed-off-by: Adriaan Schmidt <adriaan.schmidt@siemens.com>
---
testsuite/build_test/cibase.py | 6 +++---
testsuite/build_test/cibuilder.py | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/testsuite/build_test/cibase.py b/testsuite/build_test/cibase.py
index 78d7bdb..0bff7e4 100644
--- a/testsuite/build_test/cibase.py
+++ b/testsuite/build_test/cibase.py
@@ -15,10 +15,10 @@ class CIBaseTest(CIBuilder):
build_dir = self.params.get('build_dir', default=isar_root + '/build')
build_dir = os.path.realpath(build_dir)
quiet = int(self.params.get('quiet', default=0))
- bitbake_args = '-v'
+ bitbake_args = []
- if quiet:
- bitbake_args = ''
+ if not quiet:
+ bitbake_args.append('-v')
self.log.info('===================================================')
self.log.info('Running ' + testname + ' test for:')
diff --git a/testsuite/build_test/cibuilder.py b/testsuite/build_test/cibuilder.py
index baa7185..b224479 100644
--- a/testsuite/build_test/cibuilder.py
+++ b/testsuite/build_test/cibuilder.py
@@ -74,7 +74,7 @@ class CIBuilder(Test):
os.chdir(build_dir)
cmdline = ['bitbake']
if args:
- cmdline.append(args)
+ cmdline.extend(args)
if cmd:
cmdline.append('-c')
cmdline.append(cmd)
--
2.30.2
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v5 13/13] sstate: add test case
2021-11-04 11:51 [PATCH v5 00/13] Add sstate-cache Adriaan Schmidt
` (11 preceding siblings ...)
2021-11-04 11:51 ` [PATCH v5 12/13] test: make bitbake_args a list Adriaan Schmidt
@ 2021-11-04 11:51 ` Adriaan Schmidt
12 siblings, 0 replies; 14+ messages in thread
From: Adriaan Schmidt @ 2021-11-04 11:51 UTC (permalink / raw)
To: isar-users; +Cc: Adriaan Schmidt
This also adds a parameter `sstate` to the build function, which is
only passed as `1` for the actual sstate test. None of the other tests
uses sstate caching.
Signed-off-by: Adriaan Schmidt <adriaan.schmidt@siemens.com>
---
testsuite/build_test/build_test.py | 101 ++++++++++++++++++++++++++---
testsuite/build_test/cibase.py | 14 ++--
2 files changed, 101 insertions(+), 14 deletions(-)
diff --git a/testsuite/build_test/build_test.py b/testsuite/build_test/build_test.py
index d39c10c..817f4ce 100644
--- a/testsuite/build_test/build_test.py
+++ b/testsuite/build_test/build_test.py
@@ -1,9 +1,10 @@
#!/usr/bin/env python3
+import glob
import os
from avocado import skipUnless
-from avocado.utils import path
+from avocado.utils import path, process
from cibase import CIBaseTest
UMOCI_AVAILABLE = True
@@ -58,7 +59,7 @@ class CrossTest(CIBaseTest):
'mc:rpi-stretch:isar-image-base'
]
- self.perform_build_test(targets, 1, None)
+ self.perform_build_test(targets, 1, 0, None)
def test_cross_ubuntu(self):
targets = [
@@ -66,7 +67,7 @@ class CrossTest(CIBaseTest):
]
try:
- self.perform_build_test(targets, 1, None)
+ self.perform_build_test(targets, 1, 0, None)
except:
self.cancel('KFAIL')
@@ -76,7 +77,7 @@ class CrossTest(CIBaseTest):
]
try:
- self.perform_build_test(targets, 1, None)
+ self.perform_build_test(targets, 1, 0, None)
except:
self.cancel('KFAIL')
@@ -90,7 +91,7 @@ class SdkTest(CIBaseTest):
def test_sdk(self):
targets = ['mc:qemuarm-stretch:isar-image-base']
- self.perform_build_test(targets, 1, 'do_populate_sdk')
+ self.perform_build_test(targets, 1, 0, 'do_populate_sdk')
class NoCrossTest(CIBaseTest):
@@ -123,7 +124,7 @@ class NoCrossTest(CIBaseTest):
self.deletetmp(self.params.get('build_dir',
default=os.path.dirname(__file__) + '/../../build'))
- self.perform_build_test(targets, 0, None)
+ self.perform_build_test(targets, 0, 0, None)
def test_nocross_bullseye(self):
targets = [
@@ -134,7 +135,7 @@ class NoCrossTest(CIBaseTest):
]
try:
- self.perform_build_test(targets, 0, None)
+ self.perform_build_test(targets, 0, 0, None)
except:
self.cancel('KFAIL')
@@ -158,7 +159,7 @@ class RebuildTest(CIBaseTest):
try:
self.perform_build_test('mc:qemuamd64-stretch:isar-image-base',
- is_cross_build, None)
+ is_cross_build, 0, None)
finally:
self.restorefile(dpkgbase_file)
@@ -206,3 +207,87 @@ class ContainerSdkTest(CIBaseTest):
targets = ['mc:container-amd64-stretch:isar-image-base']
self.perform_container_test(targets, 'do_populate_sdk')
+
+class SstateTest(CIBaseTest):
+
+ """
+ Test builds with artifacts taken from sstate cache
+
+ :avocado: tags=sstate,fast,full
+ """
+ def check_executed_tasks(self, build_dir, target, expected):
+ taskorder_file = glob.glob(f'{build_dir}/tmp/work/{self.distro_distroarch}/{target}/*/temp/log.task_order')
+ try:
+ with open(taskorder_file[0], 'r') as f:
+ tasks = [l.split()[0] for l in f.readlines()]
+ except (FileNotFoundError, IndexError):
+ tasks = []
+ if expected is None:
+ # require that no tasks were executed
+ return len(tasks) == 0
+ for e in expected:
+ should_run = True
+ if e.startswith('!'):
+ should_run = False
+ e = e[1:]
+ if should_run != (e in tasks):
+ self.log.error(f"{target}: executed tasks {str(tasks)} did not match expected {str(expected)}")
+ return False
+ return True
+
+ def test_sstate(self):
+ build_dir, bb_args = self.prep('Sstate', '', 0, 1, 1)
+ image_target = 'mc:qemuamd64-bullseye:isar-image-base'
+ package_target = 'mc:qemuamd64-bullseye:hello'
+ self.distro_distroarch = 'debian-bullseye-amd64'
+
+ # Cleanup sstate and tmp before test
+ process.run(f'rm -rf {build_dir}/sstate-cache', sudo=True)
+ self.deletetmp(build_dir)
+
+ # Populate cache
+ self.perform_build_test(image_target, 0, 1, None)
+
+ # Rebuild image
+ self.deletetmp(build_dir)
+ self.perform_build_test(image_target, 0, 1, None)
+ if not all([
+ self.check_executed_tasks(build_dir, 'isar-bootstrap-target',
+ ['do_bootstrap_setscene', '!do_bootstrap']),
+ self.check_executed_tasks(build_dir, 'buildchroot-target',
+ ['do_rootfs_setscene', '!do_rootfs']),
+ self.check_executed_tasks(build_dir, 'isar-image-base-*-wic-img',
+ ['do_rootfs_setscene', '!do_rootfs'])
+ ]):
+ self.fail("Failed rebuild image")
+
+ # Rebuild single package
+ self.deletetmp(build_dir)
+ self.perform_build_test(package_target, 0, 1, None)
+ if not all([
+ self.check_executed_tasks(build_dir, 'isar-bootstrap-target',
+ ['do_bootstrap_setscene']),
+ self.check_executed_tasks(build_dir, 'buildchroot-target',
+ ['!do_buildchroot_deploy']),
+ self.check_executed_tasks(build_dir, 'hello',
+ ['do_dpkg_build_setscene', 'do_deploy_deb', '!do_dpkg_build'])
+ ]):
+ self.fail("Failed rebuild single package")
+
+ # Rebuild package and image
+ self.deletetmp(build_dir)
+ process.run(f'find {build_dir}/sstate-cache/ -name sstate:hello:* -delete')
+ self.perform_build_test(image_target, 0, 1, None)
+ if not all([
+ self.check_executed_tasks(build_dir, 'isar-bootstrap-target',
+ ['do_bootstrap_setscene', '!do_bootstrap']),
+ self.check_executed_tasks(build_dir, 'buildchroot-target',
+ ['do_rootfs_setscene', '!do_rootfs']),
+ self.check_executed_tasks(build_dir, 'hello',
+ ['do_fetch', 'do_dpkg_build']),
+ # TODO: if we actually make a change to hello, then we could test that do_rootfs is executed.
+ # currently, hello is rebuilt, but its sstate sig/hash does not change.
+ self.check_executed_tasks(build_dir, 'isar-image-base-*-wic-img',
+ ['do_rootfs_setscene', '!do_rootfs'])
+ ]):
+ self.fail("Failed rebuild package and image")
diff --git a/testsuite/build_test/cibase.py b/testsuite/build_test/cibase.py
index 0bff7e4..3cba412 100644
--- a/testsuite/build_test/cibase.py
+++ b/testsuite/build_test/cibase.py
@@ -11,7 +11,7 @@ isar_root = os.path.dirname(__file__) + '/../..'
class CIBaseTest(CIBuilder):
- def prep(self, testname, targets, cross, debsrc_cache):
+ def prep(self, testname, targets, cross, debsrc_cache, sstate):
build_dir = self.params.get('build_dir', default=isar_root + '/build')
build_dir = os.path.realpath(build_dir)
quiet = int(self.params.get('quiet', default=0))
@@ -19,6 +19,8 @@ class CIBaseTest(CIBuilder):
if not quiet:
bitbake_args.append('-v')
+ if not sstate:
+ bitbake_args.append('--no-setscene')
self.log.info('===================================================')
self.log.info('Running ' + testname + ' test for:')
@@ -31,8 +33,8 @@ class CIBaseTest(CIBuilder):
return build_dir, bitbake_args;
- def perform_build_test(self, targets, cross, bitbake_cmd):
- build_dir, bb_args = self.prep('Isar build', targets, cross, 1)
+ def perform_build_test(self, targets, cross, sstate, bitbake_cmd):
+ build_dir, bb_args = self.prep('Isar build', targets, cross, 1, sstate)
self.log.info('Starting build...')
@@ -40,7 +42,7 @@ class CIBaseTest(CIBuilder):
def perform_repro_test(self, targets, signed):
cross = int(self.params.get('cross', default=0))
- build_dir, bb_args = self.prep('repro Isar build', targets, cross, 0)
+ build_dir, bb_args = self.prep('repro Isar build', targets, cross, 0, 0)
gpg_pub_key = os.path.dirname(__file__) + '/../base-apt/test_pub.key'
gpg_priv_key = os.path.dirname(__file__) + '/../base-apt/test_priv.key'
@@ -79,7 +81,7 @@ class CIBaseTest(CIBuilder):
def perform_wic_test(self, targets, wks_path, wic_path):
cross = int(self.params.get('cross', default=0))
- build_dir, bb_args = self.prep('WIC exclude build', targets, cross, 1)
+ build_dir, bb_args = self.prep('WIC exclude build', targets, cross, 1, 0)
layerdir_isar = self.getlayerdir('isar')
@@ -108,7 +110,7 @@ class CIBaseTest(CIBuilder):
def perform_container_test(self, targets, bitbake_cmd):
cross = int(self.params.get('cross', default=0))
- build_dir, bb_args = self.prep('Isar Container', targets, cross, 1)
+ build_dir, bb_args = self.prep('Isar Container', targets, cross, 1, 0)
self.containerprep(build_dir)
--
2.30.2
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2021-11-04 11:51 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-04 11:51 [PATCH v5 00/13] Add sstate-cache Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 01/13] oe imports in central location Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 02/13] images: create deploy dir Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 03/13] rootfs: recursively depend on packages Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 04/13] base: remove unneeded "before do_build" task dependencies Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 05/13] dpkg: add explicit dependency to isar-apt Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 06/13] meta: add sstate feature from oe Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 07/13] sstate: configure Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 08/13] sstate: add caching to isar-bootstrap Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 09/13] sstate: add caching to rootfs Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 10/13] sstate: add caching to debian packages Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 11/13] test: pass absolute path for build_dir Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 12/13] test: make bitbake_args a list Adriaan Schmidt
2021-11-04 11:51 ` [PATCH v5 13/13] sstate: add test case Adriaan Schmidt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox