All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 01/10] utils: Add multiprocess_launch API and testcase
@ 2018-07-20 10:39 Richard Purdie
  2018-07-20 10:39 ` [PATCH 02/10] package: Rework PACKAGELOCK based upon sstate for do_packagedata Richard Purdie
                   ` (9 more replies)
  0 siblings, 10 replies; 11+ messages in thread
From: Richard Purdie @ 2018-07-20 10:39 UTC (permalink / raw)
  To: openembedded-core

The current methods of spawning processes for parallel execution have
issues around collection of results or exceptions.

Take the code from package_ipk/deb, make it generic, add a results
collection mechanism, fix the exception handling and for it into a
standard library function.

Also add a test case which tests both the success and failure modes
of operation to stop this functionality regressiing again.

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 meta/lib/oe/utils.py                        | 70 +++++++++++++++++++++
 meta/lib/oeqa/selftest/cases/oelib/utils.py | 46 +++++++++++++-
 2 files changed, 115 insertions(+), 1 deletion(-)

diff --git a/meta/lib/oe/utils.py b/meta/lib/oe/utils.py
index 6aed6dc993b..77c1939b8ab 100644
--- a/meta/lib/oe/utils.py
+++ b/meta/lib/oe/utils.py
@@ -1,4 +1,6 @@
 import subprocess
+import multiprocessing
+import traceback
 
 def read_file(filename):
     try:
@@ -280,6 +282,74 @@ def multiprocess_exec(commands, function):
 
     return results
 
+# For each item in items, call the function 'target' with item as the first 
+# argument, extraargs as the other arguments and handle any exceptions in the
+# parent thread
+def multiprocess_launch(target, items, d, extraargs=None):
+
+    class ProcessLauch(multiprocessing.Process):
+        def __init__(self, *args, **kwargs):
+            multiprocessing.Process.__init__(self, *args, **kwargs)
+            self._pconn, self._cconn = multiprocessing.Pipe()
+            self._exception = None
+            self._result = None
+
+        def run(self):
+            try:
+                ret = self._target(*self._args, **self._kwargs)
+                self._cconn.send((None, ret))
+            except Exception as e:
+                tb = traceback.format_exc()
+                self._cconn.send((e, tb))
+
+        def update(self):
+            if self._pconn.poll():
+                (e, tb) = self._pconn.recv()
+                if e is not None:
+                    self._exception = (e, tb)
+                else:
+                    self._result = tb
+
+        @property
+        def exception(self):
+            self.update()
+            return self._exception
+
+        @property
+        def result(self):
+            self.update()
+            return self._result
+
+    max_process = int(d.getVar("BB_NUMBER_THREADS") or os.cpu_count() or 1)
+    launched = []
+    errors = []
+    results = []
+    items = list(items)
+    while (items and not errors) or launched:
+        if not errors and items and len(launched) < max_process:
+            args = (items.pop(),)
+            if extraargs is not None:
+                args = args + extraargs
+            p = ProcessLauch(target=target, args=args)
+            p.start()
+            launched.append(p)
+        for q in launched:
+            # The finished processes are joined when calling is_alive()
+            if not q.is_alive():
+                if q.exception:
+                    errors.append(q.exception)
+                if q.result:
+                    results.append(q.result)
+                launched.remove(q)
+    # Paranoia doesn't hurt
+    for p in launched:
+        p.join()
+    if errors:
+        for (e, tb) in errors:
+            bb.error(str(tb))
+        bb.fatal("Fatal errors occurred in subprocesses, tracebacks printed above")
+    return results
+
 def squashspaces(string):
     import re
     return re.sub("\s+", " ", string).strip()
diff --git a/meta/lib/oeqa/selftest/cases/oelib/utils.py b/meta/lib/oeqa/selftest/cases/oelib/utils.py
index 9fb6c1576e0..275aeda74eb 100644
--- a/meta/lib/oeqa/selftest/cases/oelib/utils.py
+++ b/meta/lib/oeqa/selftest/cases/oelib/utils.py
@@ -1,5 +1,8 @@
+import sys
 from unittest.case import TestCase
-from oe.utils import packages_filter_out_system, trim_version
+from contextlib import contextmanager
+from io import StringIO
+from oe.utils import packages_filter_out_system, trim_version, multiprocess_launch
 
 class TestPackagesFilterOutSystem(TestCase):
     def test_filter(self):
@@ -49,3 +52,44 @@ class TestTrimVersion(TestCase):
         self.assertEqual(trim_version("1.2.3", 2), "1.2")
         self.assertEqual(trim_version("1.2.3", 3), "1.2.3")
         self.assertEqual(trim_version("1.2.3", 4), "1.2.3")
+
+
+class TestMultiprocessLaunch(TestCase):
+
+    def test_multiprocesslaunch(self):
+        import bb
+
+        def testfunction(item, d):
+            if item == "2" or item == "1":
+                raise KeyError("Invalid number %s" % item)
+            return "Found %s" % item
+
+        def dummyerror(msg):
+            print("ERROR: %s" % msg)
+
+        @contextmanager
+        def captured_output():
+            new_out, new_err = StringIO(), StringIO()
+            old_out, old_err = sys.stdout, sys.stderr
+            try:
+                sys.stdout, sys.stderr = new_out, new_err
+                yield sys.stdout, sys.stderr
+            finally:
+                sys.stdout, sys.stderr = old_out, old_err
+
+        d = bb.data_smart.DataSmart()
+        bb.error = dummyerror
+
+        # Assert the function returns the right results
+        result = multiprocess_launch(testfunction, ["3", "4", "5", "6"], d, extraargs=(d,))
+        self.assertIn("Found 3", result)
+        self.assertIn("Found 4", result)
+        self.assertIn("Found 5", result)
+        self.assertIn("Found 6", result)
+        self.assertEqual(len(result), 4)
+
+        # Assert the function prints exceptions
+        with captured_output() as (out, err):
+            self.assertRaises(bb.BBHandledException, multiprocess_launch, testfunction, ["1", "2", "3", "4", "5", "6"], d, extraargs=(d,))
+        self.assertIn("KeyError: 'Invalid number 1'", out.getvalue())
+        self.assertIn("KeyError: 'Invalid number 2'", out.getvalue())
-- 
2.17.1



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 02/10] package: Rework PACKAGELOCK based upon sstate for do_packagedata
  2018-07-20 10:39 [PATCH 01/10] utils: Add multiprocess_launch API and testcase Richard Purdie
@ 2018-07-20 10:39 ` Richard Purdie
  2018-07-20 10:39 ` [PATCH 03/10] package_ipk|deb: Use oe.utils.multiprocess_launch Richard Purdie
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Richard Purdie @ 2018-07-20 10:39 UTC (permalink / raw)
  To: openembedded-core

I think this lock dates from before we had sstate for do_packagedata.

Since WORKDIR is recipe specific and we write into WORKDIR, we no longer need
any write locks in the do_packagedata code itself, its handled by the sstate
task lock for the final copy in at the end. The final write lock can be simply
removed.

The only time we need read locking is when actually reading data from the
shared directory. We can therefore reduce the window the lock is held
significantly as well, hence improving the speed of packagedata tasks running
in parallel.

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 meta/classes/package.bbclass | 23 +++++++++--------------
 1 file changed, 9 insertions(+), 14 deletions(-)

diff --git a/meta/classes/package.bbclass b/meta/classes/package.bbclass
index 07bc37879c6..6f7015d9129 100644
--- a/meta/classes/package.bbclass
+++ b/meta/classes/package.bbclass
@@ -1411,9 +1411,6 @@ fi
     pkgdest = d.getVar('PKGDEST')
     pkgdatadir = d.getVar('PKGDESTWORK')
 
-    # Take shared lock since we're only reading, not writing
-    lf = bb.utils.lockfile(d.expand("${PACKAGELOCK}"), True)
-
     data_file = pkgdatadir + d.expand("/${PN}" )
     f = open(data_file, 'w')
     f.write("PACKAGES: %s\n" % packages)
@@ -1518,7 +1515,6 @@ fi
     if bb.data.inherits_class('allarch', d) and not bb.data.inherits_class('packagegroup', d):
         write_extra_runtime_pkgs(global_variants, packages, pkgdatadir)
 
-    bb.utils.unlockfile(lf)
 }
 emit_pkgdata[dirs] = "${PKGDESTWORK}/runtime ${PKGDESTWORK}/runtime-reverse ${PKGDESTWORK}/runtime-rprovides"
 
@@ -1629,9 +1625,6 @@ python package_do_shlibs() {
 
     shlibswork_dir = d.getVar('SHLIBSWORKDIR')
 
-    # Take shared lock since we're only reading, not writing
-    lf = bb.utils.lockfile(d.expand("${PACKAGELOCK}"), True)
-
     def linux_so(file, needed, sonames, renames, pkgver):
         needs_ldconfig = False
         ldir = os.path.dirname(file).replace(pkgdest + "/" + pkg, '')
@@ -1744,7 +1737,11 @@ python package_do_shlibs() {
     use_ldconfig = bb.utils.contains('DISTRO_FEATURES', 'ldconfig', True, False, d)
 
     needed = {}
+
+    # Take shared lock since we're only reading, not writing
+    lf = bb.utils.lockfile(d.expand("${PACKAGELOCK}"), True)
     shlib_provider = oe.package.read_shlib_providers(d)
+    bb.utils.unlockfile(lf)
 
     for pkg in shlib_pkgs:
         private_libs = d.getVar('PRIVATE_LIBS_' + pkg) or d.getVar('PRIVATE_LIBS') or ""
@@ -1800,8 +1797,6 @@ python package_do_shlibs() {
             d.setVar('pkg_postinst_%s' % pkg, postinst)
         bb.debug(1, 'LIBNAMES: pkg %s sonames %s' % (pkg, sonames))
 
-    bb.utils.unlockfile(lf)
-
     assumed_libs = d.getVar('ASSUME_SHLIBS')
     if assumed_libs:
         libdir = d.getVar("libdir")
@@ -1914,9 +1909,6 @@ python package_do_pkgconfig () {
                             if hdr == 'Requires':
                                 pkgconfig_needed[pkg] += exp.replace(',', ' ').split()
 
-    # Take shared lock since we're only reading, not writing
-    lf = bb.utils.lockfile(d.expand("${PACKAGELOCK}"), True)
-
     for pkg in packages.split():
         pkgs_file = os.path.join(shlibswork_dir, pkg + ".pclist")
         if pkgconfig_provided[pkg] != []:
@@ -1925,6 +1917,9 @@ python package_do_pkgconfig () {
                 f.write('%s\n' % p)
             f.close()
 
+    # Take shared lock since we're only reading, not writing
+    lf = bb.utils.lockfile(d.expand("${PACKAGELOCK}"), True)
+
     # Go from least to most specific since the last one found wins
     for dir in reversed(shlibs_dirs):
         if not os.path.exists(dir):
@@ -1940,6 +1935,8 @@ python package_do_pkgconfig () {
                 for l in lines:
                     pkgconfig_provided[pkg].append(l.rstrip())
 
+    bb.utils.unlockfile(lf)
+
     for pkg in packages.split():
         deps = []
         for n in pkgconfig_needed[pkg]:
@@ -1957,8 +1954,6 @@ python package_do_pkgconfig () {
             for dep in deps:
                 fd.write(dep + '\n')
             fd.close()
-
-    bb.utils.unlockfile(lf)
 }
 
 def read_libdep_files(d):
-- 
2.17.1



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 03/10] package_ipk|deb: Use oe.utils.multiprocess_launch
  2018-07-20 10:39 [PATCH 01/10] utils: Add multiprocess_launch API and testcase Richard Purdie
  2018-07-20 10:39 ` [PATCH 02/10] package: Rework PACKAGELOCK based upon sstate for do_packagedata Richard Purdie
@ 2018-07-20 10:39 ` Richard Purdie
  2018-07-20 10:39 ` [PATCH 04/10] package: Drop subshell usage for dwarfsrcfile generation Richard Purdie
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Richard Purdie @ 2018-07-20 10:39 UTC (permalink / raw)
  To: openembedded-core

The current code had broken exception handling due to the use of a
"traceback" variable as well as an import. Use the new library code
for this instead which reduces code duplication and has fixed/improved
exception handling.

The chdir code can be dropped since any directory changes are in other
processes now so there is no need for it here and the code no longer
changes directory.

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 meta/classes/package_deb.bbclass | 51 +-------------------------------
 meta/classes/package_ipk.bbclass | 51 +-------------------------------
 2 files changed, 2 insertions(+), 100 deletions(-)

diff --git a/meta/classes/package_deb.bbclass b/meta/classes/package_deb.bbclass
index 2e8d17d3c71..6f815916531 100644
--- a/meta/classes/package_deb.bbclass
+++ b/meta/classes/package_deb.bbclass
@@ -41,32 +41,6 @@ def debian_arch_map(arch, tune):
     return arch
 
 python do_package_deb () {
-
-    import multiprocessing
-    import traceback
-
-    class DebianWritePkgProcess(multiprocessing.Process):
-        def __init__(self, *args, **kwargs):
-            multiprocessing.Process.__init__(self, *args, **kwargs)
-            self._pconn, self._cconn = multiprocessing.Pipe()
-            self._exception = None
-
-        def run(self):
-            try:
-                multiprocessing.Process.run(self)
-                self._cconn.send(None)
-            except Exception as e:
-                tb = traceback.format_exc()
-                self._cconn.send((e, tb))
-
-        @property
-        def exception(self):
-            if self._pconn.poll():
-                self._exception = self._pconn.recv()
-            return self._exception
-
-    oldcwd = os.getcwd()
-
     packages = d.getVar('PACKAGES')
     if not packages:
         bb.debug(1, "PACKAGES not defined, nothing to package")
@@ -76,30 +50,7 @@ python do_package_deb () {
     if os.access(os.path.join(tmpdir, "stamps", "DEB_PACKAGE_INDEX_CLEAN"),os.R_OK):
         os.unlink(os.path.join(tmpdir, "stamps", "DEB_PACKAGE_INDEX_CLEAN"))
 
-    max_process = int(d.getVar("BB_NUMBER_THREADS") or os.cpu_count() or 1)
-    launched = []
-    error = None
-    pkgs = packages.split()
-    while not error and pkgs:
-        if len(launched) < max_process:
-            p = DebianWritePkgProcess(target=deb_write_pkg, args=(pkgs.pop(), d))
-            p.start()
-            launched.append(p)
-        for q in launched:
-            # The finished processes are joined when calling is_alive()
-            if not q.is_alive():
-                launched.remove(q)
-            if q.exception:
-                error, traceback = q.exception
-                break
-
-    for p in launched:
-        p.join()
-
-    os.chdir(oldcwd)
-
-    if error:
-        raise error
+    oe.utils.multiprocess_launch(deb_write_pkg, packages.split(), d, extraargs=(d,))
 }
 do_package_deb[vardeps] += "deb_write_pkg"
 do_package_deb[vardepsexclude] = "BB_NUMBER_THREADS"
diff --git a/meta/classes/package_ipk.bbclass b/meta/classes/package_ipk.bbclass
index a0b34fa7a5d..5eb910ca318 100644
--- a/meta/classes/package_ipk.bbclass
+++ b/meta/classes/package_ipk.bbclass
@@ -17,32 +17,6 @@ OPKG_ARGS += "${@['', '--add-exclude ' + ' --add-exclude '.join((d.getVar('PACKA
 OPKGLIBDIR = "${localstatedir}/lib"
 
 python do_package_ipk () {
-    import multiprocessing
-    import traceback
-
-    class IPKWritePkgProcess(multiprocessing.Process):
-        def __init__(self, *args, **kwargs):
-            multiprocessing.Process.__init__(self, *args, **kwargs)
-            self._pconn, self._cconn = multiprocessing.Pipe()
-            self._exception = None
-
-        def run(self):
-            try:
-                multiprocessing.Process.run(self)
-                self._cconn.send(None)
-            except Exception as e:
-                tb = traceback.format_exc()
-                self._cconn.send((e, tb))
-
-        @property
-        def exception(self):
-            if self._pconn.poll():
-                self._exception = self._pconn.recv()
-            return self._exception
-
-
-    oldcwd = os.getcwd()
-
     workdir = d.getVar('WORKDIR')
     outdir = d.getVar('PKGWRITEDIRIPK')
     tmpdir = d.getVar('TMPDIR')
@@ -61,30 +35,7 @@ python do_package_ipk () {
     if os.access(os.path.join(tmpdir, "stamps", "IPK_PACKAGE_INDEX_CLEAN"), os.R_OK):
         os.unlink(os.path.join(tmpdir, "stamps", "IPK_PACKAGE_INDEX_CLEAN"))
 
-    max_process = int(d.getVar("BB_NUMBER_THREADS") or os.cpu_count() or 1)
-    launched = []
-    error = None
-    pkgs = packages.split()
-    while not error and pkgs:
-        if len(launched) < max_process:
-            p = IPKWritePkgProcess(target=ipk_write_pkg, args=(pkgs.pop(), d))
-            p.start()
-            launched.append(p)
-        for q in launched:
-            # The finished processes are joined when calling is_alive()
-            if not q.is_alive():
-                launched.remove(q)
-            if q.exception:
-                error, traceback = q.exception
-                break
-
-    for p in launched:
-        p.join()
-
-    os.chdir(oldcwd)
-
-    if error:
-        raise error
+    oe.utils.multiprocess_launch(ipk_write_pkg, packages.split(), d, extraargs=(d,))
 }
 do_package_ipk[vardeps] += "ipk_write_pkg"
 do_package_ipk[vardepsexclude] = "BB_NUMBER_THREADS"
-- 
2.17.1



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 04/10] package: Drop subshell usage for dwarfsrcfile generation.
  2018-07-20 10:39 [PATCH 01/10] utils: Add multiprocess_launch API and testcase Richard Purdie
  2018-07-20 10:39 ` [PATCH 02/10] package: Rework PACKAGELOCK based upon sstate for do_packagedata Richard Purdie
  2018-07-20 10:39 ` [PATCH 03/10] package_ipk|deb: Use oe.utils.multiprocess_launch Richard Purdie
@ 2018-07-20 10:39 ` Richard Purdie
  2018-07-20 10:39 ` [PATCH 05/10] package: Don't use subshell to execute file Richard Purdie
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Richard Purdie @ 2018-07-20 10:39 UTC (permalink / raw)
  To: openembedded-core

The command for running dwarfsrcfiles is simple and does not need a subshell
for each execution. By expanding out this function to use check_output()
from subprocess and a list of arguments, the shell overhead can be dropped.

For recipes with lots of files this gives a significant saving.

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 meta/classes/package.bbclass | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/meta/classes/package.bbclass b/meta/classes/package.bbclass
index 6f7015d9129..7a49e4f3516 100644
--- a/meta/classes/package.bbclass
+++ b/meta/classes/package.bbclass
@@ -345,8 +345,16 @@ def parse_debugsources_from_dwarfsrcfiles_output(dwarfsrcfiles_output):
     return debugfiles.keys()
 
 def append_source_info(file, sourcefile, d, fatal=True):
-    cmd = "'dwarfsrcfiles' '%s'" % (file)
-    (retval, output) = oe.utils.getstatusoutput(cmd)
+    import subprocess
+
+    cmd = ["dwarfsrcfiles", file]
+    try:
+        output = subprocess.check_output(cmd, universal_newlines=True, stderr=subprocess.STDOUT)
+        retval = 0
+    except subprocess.CalledProcessError as exc:
+        output = exc.output
+        retval = exc.returncode
+
     # 255 means a specific file wasn't fully parsed to get the debug file list, which is not a fatal failure
     if retval != 0 and retval != 255:
         msg = "dwarfsrcfiles failed with exit code %s (cmd was %s)%s" % (retval, cmd, ":\n%s" % output if output else "")
-- 
2.17.1



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 05/10] package: Don't use subshell to execute file
  2018-07-20 10:39 [PATCH 01/10] utils: Add multiprocess_launch API and testcase Richard Purdie
                   ` (2 preceding siblings ...)
  2018-07-20 10:39 ` [PATCH 04/10] package: Drop subshell usage for dwarfsrcfile generation Richard Purdie
@ 2018-07-20 10:39 ` Richard Purdie
  2018-07-20 10:39 ` [PATCH 06/10] package: Allow parallel processing of debug splitting Richard Purdie
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Richard Purdie @ 2018-07-20 10:39 UTC (permalink / raw)
  To: openembedded-core

We don't need any functionality from the shell here, its just extra fork
overhead. Therefore remove it and use subprocess directly.

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 meta/classes/package.bbclass | 8 ++------
 meta/lib/oe/package.py       | 8 ++------
 2 files changed, 4 insertions(+), 12 deletions(-)

diff --git a/meta/classes/package.bbclass b/meta/classes/package.bbclass
index 7a49e4f3516..02914b52bb1 100644
--- a/meta/classes/package.bbclass
+++ b/meta/classes/package.bbclass
@@ -888,6 +888,7 @@ python fixup_perms () {
 
 python split_and_strip_files () {
     import stat, errno
+    import subprocess
 
     dvar = d.getVar('PKGD')
     pn = d.getVar('PN')
@@ -933,12 +934,7 @@ python split_and_strip_files () {
     # 16 - kernel module
     def isELF(path):
         type = 0
-        ret, result = oe.utils.getstatusoutput("file -b '%s'" % path)
-
-        if ret:
-            msg = "split_and_strip_files: 'file %s' failed" % path
-            package_qa_handle_error("split-strip", msg, d)
-            return type
+        result = subprocess.check_output(["file", "-b", path], stderr=subprocess.STDOUT).decode("utf-8")
 
         # Not stripped
         if "ELF" in result:
diff --git a/meta/lib/oe/package.py b/meta/lib/oe/package.py
index 4f3e21ad40f..8a303106a9f 100644
--- a/meta/lib/oe/package.py
+++ b/meta/lib/oe/package.py
@@ -56,7 +56,7 @@ def strip_execs(pn, dstdir, strip_cmd, libdir, base_libdir, qa_already_stripped=
     :param qa_already_stripped: Set to True if already-stripped' in ${INSANE_SKIP}
     This is for proper logging and messages only.
     """
-    import stat, errno, oe.path, oe.utils, mmap
+    import stat, errno, oe.path, oe.utils, mmap, subprocess
 
     # Detect .ko module by searching for "vermagic=" string
     def is_kernel_module(path):
@@ -72,11 +72,7 @@ def strip_execs(pn, dstdir, strip_cmd, libdir, base_libdir, qa_already_stripped=
     # 16 - kernel module
     def is_elf(path):
         exec_type = 0
-        ret, result = oe.utils.getstatusoutput("file -b '%s'" % path)
-
-        if ret:
-            bb.error("split_and_strip_files: 'file %s' failed" % path)
-            return exec_type
+        result = subprocess.check_output(["file", "-b", path], stderr=subprocess.STDOUT).decode("utf-8")
 
         if "ELF" in result:
             exec_type |= 1
-- 
2.17.1



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 06/10] package: Allow parallel processing of debug splitting
  2018-07-20 10:39 [PATCH 01/10] utils: Add multiprocess_launch API and testcase Richard Purdie
                   ` (3 preceding siblings ...)
  2018-07-20 10:39 ` [PATCH 05/10] package: Don't use subshell to execute file Richard Purdie
@ 2018-07-20 10:39 ` Richard Purdie
  2018-07-20 10:39 ` [PATCH 07/10] package: Allow parallel processing of shlib analysis Richard Purdie
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Richard Purdie @ 2018-07-20 10:39 UTC (permalink / raw)
  To: openembedded-core

Adjust the code so that the splitting of debug symbols from files happens
in parallel. To to this we need to move some path handling code into the
main function and pass more parameters in.

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 meta/classes/package.bbclass | 24 +++++++++++++-----------
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/meta/classes/package.bbclass b/meta/classes/package.bbclass
index 02914b52bb1..03fe18de580 100644
--- a/meta/classes/package.bbclass
+++ b/meta/classes/package.bbclass
@@ -367,10 +367,12 @@ def append_source_info(file, sourcefile, d, fatal=True):
     # of rpm's debugedit, which was writing them out that way, and the code elsewhere
     # is still assuming that.
     debuglistoutput = '\0'.join(debugsources) + '\0'
+    lf = bb.utils.lockfile(sourcefile + ".lock")
     open(sourcefile, 'a').write(debuglistoutput)
+    bb.utils.unlockfile(lf)
 
 
-def splitdebuginfo(file, debugfile, debugsrcdir, sourcefile, d):
+def splitdebuginfo(file, dvar, debugdir, debuglibdir, debugappend, debugsrcdir, sourcefile, d):
     # Function to split a single file into two components, one is the stripped
     # target system binary, the other contains any debugging information. The
     # two files are linked to reference each other.
@@ -379,6 +381,15 @@ def splitdebuginfo(file, debugfile, debugsrcdir, sourcefile, d):
 
     import stat
 
+    src = file[len(dvar):]
+    dest = debuglibdir + os.path.dirname(src) + debugdir + "/" + os.path.basename(src) + debugappend
+    debugfile = dvar + dest
+
+    # Split the file...
+    bb.utils.mkdirhier(os.path.dirname(debugfile))
+    #bb.note("Split %s -> %s" % (file, debugfile))
+    # Only store off the hard link reference if we successfully split!
+
     dvar = d.getVar('PKGD')
     objcopy = d.getVar("OBJCOPY")
 
@@ -1046,16 +1057,7 @@ python split_and_strip_files () {
     # First lets process debug splitting
     #
     if (d.getVar('INHIBIT_PACKAGE_DEBUG_SPLIT') != '1'):
-        for file in elffiles:
-            src = file[len(dvar):]
-            dest = debuglibdir + os.path.dirname(src) + debugdir + "/" + os.path.basename(src) + debugappend
-            fpath = dvar + dest
-
-            # Split the file...
-            bb.utils.mkdirhier(os.path.dirname(fpath))
-            #bb.note("Split %s -> %s" % (file, fpath))
-            # Only store off the hard link reference if we successfully split!
-            splitdebuginfo(file, fpath, debugsrcdir, sourcefile, d)
+        oe.utils.multiprocess_launch(splitdebuginfo, list(elffiles), d, extraargs=(dvar, debugdir, debuglibdir, debugappend, debugsrcdir, sourcefile, d))
 
         if debugsrcdir and not targetos.startswith("mingw"):
             for file in staticlibs:
-- 
2.17.1



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 07/10] package: Allow parallel processing of shlib analysis
  2018-07-20 10:39 [PATCH 01/10] utils: Add multiprocess_launch API and testcase Richard Purdie
                   ` (4 preceding siblings ...)
  2018-07-20 10:39 ` [PATCH 06/10] package: Allow parallel processing of debug splitting Richard Purdie
@ 2018-07-20 10:39 ` Richard Purdie
  2018-07-20 10:39 ` [PATCH 08/10] package: Refactor to remove isElf/is_elf function duplication Richard Purdie
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Richard Purdie @ 2018-07-20 10:39 UTC (permalink / raw)
  To: openembedded-core

This function is a bit more invasive to add parallelism to but
allows the shlibs analysis to happen in multiple threads. In order
to return values correctly/safely the data types needed tweaking
to avoid lists and use immutable objects.

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 meta/classes/package.bbclass | 39 ++++++++++++++++++++++++------------
 1 file changed, 26 insertions(+), 13 deletions(-)

diff --git a/meta/classes/package.bbclass b/meta/classes/package.bbclass
index 03fe18de580..74c96b9b725 100644
--- a/meta/classes/package.bbclass
+++ b/meta/classes/package.bbclass
@@ -1631,25 +1631,28 @@ python package_do_shlibs() {
 
     shlibswork_dir = d.getVar('SHLIBSWORKDIR')
 
-    def linux_so(file, needed, sonames, renames, pkgver):
+    def linux_so(file, pkg, pkgver, d):
         needs_ldconfig = False
+        needed = set()
+        sonames = set()
+        renames = []
         ldir = os.path.dirname(file).replace(pkgdest + "/" + pkg, '')
         cmd = d.getVar('OBJDUMP') + " -p " + pipes.quote(file) + " 2>/dev/null"
         fd = os.popen(cmd)
         lines = fd.readlines()
         fd.close()
-        rpath = []
+        rpath = tuple()
         for l in lines:
             m = re.match("\s+RPATH\s+([^\s]*)", l)
             if m:
                 rpaths = m.group(1).replace("$ORIGIN", ldir).split(":")
-                rpath = list(map(os.path.normpath, rpaths))
+                rpath = tuple(map(os.path.normpath, rpaths))
         for l in lines:
             m = re.match("\s+NEEDED\s+([^\s]*)", l)
             if m:
                 dep = m.group(1)
-                if dep not in needed[pkg]:
-                    needed[pkg].append((dep, file, rpath))
+                if dep not in needed:
+                    needed.add((dep, file, rpath))
             m = re.match("\s+SONAME\s+([^\s]*)", l)
             if m:
                 this_soname = m.group(1)
@@ -1657,12 +1660,12 @@ python package_do_shlibs() {
                 if not prov in sonames:
                     # if library is private (only used by package) then do not build shlib for it
                     if not private_libs or this_soname not in private_libs:
-                        sonames.append(prov)
+                        sonames.add(prov)
                 if libdir_re.match(os.path.dirname(file)):
                     needs_ldconfig = True
                 if snap_symlinks and (os.path.basename(file) != this_soname):
                     renames.append((file, os.path.join(os.path.dirname(file), this_soname)))
-        return needs_ldconfig
+        return (needs_ldconfig, needed, sonames, renames)
 
     def darwin_so(file, needed, sonames, renames, pkgver):
         if not os.path.exists(file):
@@ -1761,9 +1764,10 @@ python package_do_shlibs() {
         if not pkgver:
             pkgver = ver
 
-        needed[pkg] = []
-        sonames = list()
-        renames = list()
+        needed[pkg] = set()
+        sonames = set()
+        renames = []
+        linuxlist = []
         for file in pkgfiles[pkg]:
                 soname = None
                 if cpath.islink(file):
@@ -1773,8 +1777,17 @@ python package_do_shlibs() {
                 elif targetos.startswith("mingw"):
                     mingw_dll(file, needed, sonames, renames, pkgver)
                 elif os.access(file, os.X_OK) or lib_re.match(file):
-                    ldconfig = linux_so(file, needed, sonames, renames, pkgver)
-                    needs_ldconfig = needs_ldconfig or ldconfig
+                    linuxlist.append(file)
+
+        if linuxlist:
+            results = oe.utils.multiprocess_launch(linux_so, linuxlist, d, extraargs=(pkg, pkgver, d))
+            for r in results:
+                ldconfig = r[0]
+                needed[pkg] |= r[1]
+                sonames |= r[2]
+                renames.extend(r[3])
+                needs_ldconfig = needs_ldconfig or ldconfig
+
         for (old, new) in renames:
             bb.note("Renaming %s to %s" % (old, new))
             os.rename(old, new)
@@ -1840,7 +1853,7 @@ python package_do_shlibs() {
                 for k in shlib_provider[n[0]].keys():
                     shlib_provider_path.append(k)
                 match = None
-                for p in n[2] + shlib_provider_path + libsearchpath:
+                for p in list(n[2]) + shlib_provider_path + libsearchpath:
                     if p in shlib_provider[n[0]]:
                         match = p
                         break
-- 
2.17.1



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 08/10] package: Refactor to remove isElf/is_elf function duplication
  2018-07-20 10:39 [PATCH 01/10] utils: Add multiprocess_launch API and testcase Richard Purdie
                   ` (5 preceding siblings ...)
  2018-07-20 10:39 ` [PATCH 07/10] package: Allow parallel processing of shlib analysis Richard Purdie
@ 2018-07-20 10:39 ` Richard Purdie
  2018-07-20 10:39 ` [PATCH 09/10] package: Call file to determine elf status in parallel Richard Purdie
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Richard Purdie @ 2018-07-20 10:39 UTC (permalink / raw)
  To: openembedded-core

There are probably further cleanups needed here but this at least removes
the major code duplication between these two similar funcitons, keeping the
kernel module ".ko" extension check for efficiency to avoid opening and
reading file contents in the general case.

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 meta/classes/package.bbclass | 40 ++------------------
 meta/lib/oe/package.py       | 73 +++++++++++++++++++++---------------
 2 files changed, 47 insertions(+), 66 deletions(-)

diff --git a/meta/classes/package.bbclass b/meta/classes/package.bbclass
index 74c96b9b725..222b78fe165 100644
--- a/meta/classes/package.bbclass
+++ b/meta/classes/package.bbclass
@@ -936,38 +936,6 @@ python split_and_strip_files () {
     sourcefile = d.expand("${WORKDIR}/debugsources.list")
     bb.utils.remove(sourcefile)
 
-    # Return type (bits):
-    # 0 - not elf
-    # 1 - ELF
-    # 2 - stripped
-    # 4 - executable
-    # 8 - shared library
-    # 16 - kernel module
-    def isELF(path):
-        type = 0
-        result = subprocess.check_output(["file", "-b", path], stderr=subprocess.STDOUT).decode("utf-8")
-
-        # Not stripped
-        if "ELF" in result:
-            type |= 1
-            if "not stripped" not in result:
-                type |= 2
-            if "executable" in result:
-                type |= 4
-            if "shared" in result:
-                type |= 8
-        return type
-
-    def isStaticLib(path):
-        if path.endswith('.a') and not os.path.islink(path):
-            with open(path, 'rb') as fh:
-                # The magic must include the first slash to avoid
-                # matching golang static libraries
-                magic = b'!<arch>\x0a/'
-                start = fh.read(len(magic))
-                return start == magic
-        return False
-
     #
     # First lets figure out all of the files we may have to process ... do this only once!
     #
@@ -987,7 +955,7 @@ python split_and_strip_files () {
                 if file.endswith(".ko") and file.find("/lib/modules/") != -1:
                     kernmods.append(file)
                     continue
-                if isStaticLib(file):
+                if oe.package.is_static_lib(file):
                     staticlibs.append(file)
                     continue
 
@@ -1017,14 +985,14 @@ python split_and_strip_files () {
                     # If it's a symlink, and points to an ELF file, we capture the readlink target
                     if cpath.islink(file):
                         target = os.readlink(file)
-                        if isELF(ltarget):
-                            #bb.note("Sym: %s (%d)" % (ltarget, isELF(ltarget)))
+                        if oe.package.is_elf(ltarget):
+                            #bb.note("Sym: %s (%d)" % (ltarget, oe.package.is_elf(ltarget)))
                             symlinks[file] = target
                         continue
 
                     # It's a file (or hardlink), not a link
                     # ...but is it ELF, and is it already stripped?
-                    elf_file = isELF(file)
+                    elf_file = oe.package.is_elf(file)
                     if elf_file & 1:
                         if elf_file & 2:
                             if 'already-stripped' in (d.getVar('INSANE_SKIP_' + pn) or "").split():
diff --git a/meta/lib/oe/package.py b/meta/lib/oe/package.py
index 8a303106a9f..a435d661a62 100644
--- a/meta/lib/oe/package.py
+++ b/meta/lib/oe/package.py
@@ -1,8 +1,11 @@
+import mmap
+import subprocess
+
 def runstrip(arg):
     # Function to strip a single file, called from split_and_strip_files below
     # A working 'file' (one which works on the target architecture)
     #
-    # The elftype is a bit pattern (explained in split_and_strip_files) to tell
+    # The elftype is a bit pattern (explained in is_elf below) to tell
     # us what type of file we're processing...
     # 4 - executable
     # 8 - shared library
@@ -44,6 +47,44 @@ def runstrip(arg):
 
     return
 
+# Detect .ko module by searching for "vermagic=" string
+def is_kernel_module(path):
+    with open(path) as f:
+        return mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ).find(b"vermagic=") >= 0
+
+# Return type (bits):
+# 0 - not elf
+# 1 - ELF
+# 2 - stripped
+# 4 - executable
+# 8 - shared library
+# 16 - kernel module
+def is_elf(path):
+    exec_type = 0
+    result = subprocess.check_output(["file", "-b", path], stderr=subprocess.STDOUT).decode("utf-8")
+
+    if "ELF" in result:
+        exec_type |= 1
+        if "not stripped" not in result:
+            exec_type |= 2
+        if "executable" in result:
+            exec_type |= 4
+        if "shared" in result:
+            exec_type |= 8
+        if "relocatable" in result:
+            if path.endswith(".ko") and path.find("/lib/modules/") != -1 and is_kernel_module(path):
+                exec_type |= 16
+    return exec_type
+
+def is_static_lib(path):
+    if path.endswith('.a') and not os.path.islink(path):
+        with open(path, 'rb') as fh:
+            # The magic must include the first slash to avoid
+            # matching golang static libraries
+            magic = b'!<arch>\x0a/'
+            start = fh.read(len(magic))
+            return start == magic
+    return False
 
 def strip_execs(pn, dstdir, strip_cmd, libdir, base_libdir, qa_already_stripped=False):
     """
@@ -56,35 +97,7 @@ def strip_execs(pn, dstdir, strip_cmd, libdir, base_libdir, qa_already_stripped=
     :param qa_already_stripped: Set to True if already-stripped' in ${INSANE_SKIP}
     This is for proper logging and messages only.
     """
-    import stat, errno, oe.path, oe.utils, mmap, subprocess
-
-    # Detect .ko module by searching for "vermagic=" string
-    def is_kernel_module(path):
-        with open(path) as f:
-            return mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ).find(b"vermagic=") >= 0
-
-    # Return type (bits):
-    # 0 - not elf
-    # 1 - ELF
-    # 2 - stripped
-    # 4 - executable
-    # 8 - shared library
-    # 16 - kernel module
-    def is_elf(path):
-        exec_type = 0
-        result = subprocess.check_output(["file", "-b", path], stderr=subprocess.STDOUT).decode("utf-8")
-
-        if "ELF" in result:
-            exec_type |= 1
-            if "not stripped" not in result:
-                exec_type |= 2
-            if "executable" in result:
-                exec_type |= 4
-            if "shared" in result:
-                exec_type |= 8
-            if "relocatable" in result and is_kernel_module(path):
-                exec_type |= 16
-        return exec_type
+    import stat, errno, oe.path, oe.utils
 
     elffiles = {}
     inodes = {}
-- 
2.17.1



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 09/10] package: Call file to determine elf status in parallel
  2018-07-20 10:39 [PATCH 01/10] utils: Add multiprocess_launch API and testcase Richard Purdie
                   ` (6 preceding siblings ...)
  2018-07-20 10:39 ` [PATCH 08/10] package: Refactor to remove isElf/is_elf function duplication Richard Purdie
@ 2018-07-20 10:39 ` Richard Purdie
  2018-07-20 10:39 ` [PATCH 10/10] sstate/bitbake.conf: Use pigz if available Richard Purdie
  2018-07-20 11:02 ` ✗ patchtest: failure for "utils: Add multiprocess_launch..." and 9 more Patchwork
  9 siblings, 0 replies; 11+ messages in thread
From: Richard Purdie @ 2018-07-20 10:39 UTC (permalink / raw)
  To: openembedded-core

This allows the calls to is_elf (which calls file) to happen in parallel
allowing a speedup of do_package and do_populate_sysroot for native
recipes.

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 meta/classes/package.bbclass | 85 +++++++++++++++++++++---------------
 meta/lib/oe/package.py       |  8 +++-
 2 files changed, 56 insertions(+), 37 deletions(-)

diff --git a/meta/classes/package.bbclass b/meta/classes/package.bbclass
index 222b78fe165..978a8fbeb57 100644
--- a/meta/classes/package.bbclass
+++ b/meta/classes/package.bbclass
@@ -949,6 +949,8 @@ python split_and_strip_files () {
     skipfiles = (d.getVar("INHIBIT_PACKAGE_STRIP_FILES") or "").split()
     if (d.getVar('INHIBIT_PACKAGE_STRIP') != '1' or \
             d.getVar('INHIBIT_PACKAGE_DEBUG_SPLIT') != '1'):
+        checkelf = {}
+        checkelflinks = {}
         for root, dirs, files in cpath.walk(dvar):
             for f in files:
                 file = os.path.join(root, f)
@@ -982,44 +984,57 @@ python split_and_strip_files () {
                 # Check its an executable
                 if (s[stat.ST_MODE] & stat.S_IXUSR) or (s[stat.ST_MODE] & stat.S_IXGRP) or (s[stat.ST_MODE] & stat.S_IXOTH) \
                         or ((file.startswith(libdir) or file.startswith(baselibdir)) and (".so" in f or ".node" in f)):
-                    # If it's a symlink, and points to an ELF file, we capture the readlink target
+
                     if cpath.islink(file):
-                        target = os.readlink(file)
-                        if oe.package.is_elf(ltarget):
-                            #bb.note("Sym: %s (%d)" % (ltarget, oe.package.is_elf(ltarget)))
-                            symlinks[file] = target
+                        checkelflinks[file] = ltarget
                         continue
+                    # Use a reference of device ID and inode number to identify files
+                    file_reference = "%d_%d" % (s.st_dev, s.st_ino)
+                    checkelf[file] = (file, file_reference)
+
+        results = oe.utils.multiprocess_launch(oe.package.is_elf, checkelflinks.values(), d)
+        results_map = {}
+        for (ltarget, elf_file) in results:
+            results_map[ltarget] = elf_file
+        for file in checkelflinks:
+            ltarget = checkelflinks[file]
+            # If it's a symlink, and points to an ELF file, we capture the readlink target
+            if results_map[ltarget]:
+                target = os.readlink(file)
+                #bb.note("Sym: %s (%d)" % (ltarget, results_map[ltarget]))
+                symlinks[file] = target
+
+        results = oe.utils.multiprocess_launch(oe.package.is_elf, checkelf.keys(), d)
+        for (file, elf_file) in results:
+            # It's a file (or hardlink), not a link
+            # ...but is it ELF, and is it already stripped?
+            if elf_file & 1:
+                if elf_file & 2:
+                    if 'already-stripped' in (d.getVar('INSANE_SKIP_' + pn) or "").split():
+                        bb.note("Skipping file %s from %s for already-stripped QA test" % (file[len(dvar):], pn))
+                    else:
+                        msg = "File '%s' from %s was already stripped, this will prevent future debugging!" % (file[len(dvar):], pn)
+                        package_qa_handle_error("already-stripped", msg, d)
+                    continue
 
-                    # It's a file (or hardlink), not a link
-                    # ...but is it ELF, and is it already stripped?
-                    elf_file = oe.package.is_elf(file)
-                    if elf_file & 1:
-                        if elf_file & 2:
-                            if 'already-stripped' in (d.getVar('INSANE_SKIP_' + pn) or "").split():
-                                bb.note("Skipping file %s from %s for already-stripped QA test" % (file[len(dvar):], pn))
-                            else:
-                                msg = "File '%s' from %s was already stripped, this will prevent future debugging!" % (file[len(dvar):], pn)
-                                package_qa_handle_error("already-stripped", msg, d)
-                            continue
-
-                        # At this point we have an unstripped elf file. We need to:
-                        #  a) Make sure any file we strip is not hardlinked to anything else outside this tree
-                        #  b) Only strip any hardlinked file once (no races)
-                        #  c) Track any hardlinks between files so that we can reconstruct matching debug file hardlinks
-
-                        # Use a reference of device ID and inode number to identify files
-                        file_reference = "%d_%d" % (s.st_dev, s.st_ino)
-                        if file_reference in inodes:
-                            os.unlink(file)
-                            os.link(inodes[file_reference][0], file)
-                            inodes[file_reference].append(file)
-                        else:
-                            inodes[file_reference] = [file]
-                            # break hardlink
-                            bb.utils.copyfile(file, file)
-                            elffiles[file] = elf_file
-                        # Modified the file so clear the cache
-                        cpath.updatecache(file)
+                # At this point we have an unstripped elf file. We need to:
+                #  a) Make sure any file we strip is not hardlinked to anything else outside this tree
+                #  b) Only strip any hardlinked file once (no races)
+                #  c) Track any hardlinks between files so that we can reconstruct matching debug file hardlinks
+
+                # Use a reference of device ID and inode number to identify files
+                file_reference = checkelf[file]
+                if file_reference in inodes:
+                    os.unlink(file)
+                    os.link(inodes[file_reference][0], file)
+                    inodes[file_reference].append(file)
+                else:
+                    inodes[file_reference] = [file]
+                    # break hardlink
+                    bb.utils.copyfile(file, file)
+                    elffiles[file] = elf_file
+                # Modified the file so clear the cache
+                cpath.updatecache(file)
 
     #
     # First lets process debug splitting
diff --git a/meta/lib/oe/package.py b/meta/lib/oe/package.py
index a435d661a62..ac0f44efda1 100644
--- a/meta/lib/oe/package.py
+++ b/meta/lib/oe/package.py
@@ -74,7 +74,7 @@ def is_elf(path):
         if "relocatable" in result:
             if path.endswith(".ko") and path.find("/lib/modules/") != -1 and is_kernel_module(path):
                 exec_type |= 16
-    return exec_type
+    return (path, exec_type)
 
 def is_static_lib(path):
     if path.endswith('.a') and not os.path.islink(path):
@@ -107,6 +107,7 @@ def strip_execs(pn, dstdir, strip_cmd, libdir, base_libdir, qa_already_stripped=
     #
     # First lets figure out all of the files we may have to process
     #
+    checkelf = []
     for root, dirs, files in os.walk(dstdir):
         for f in files:
             file = os.path.join(root, f)
@@ -132,7 +133,10 @@ def strip_execs(pn, dstdir, strip_cmd, libdir, base_libdir, qa_already_stripped=
 
                 # It's a file (or hardlink), not a link
                 # ...but is it ELF, and is it already stripped?
-                elf_file = is_elf(file)
+                checkelf.append(file)
+    results = oe.utils.multiprocess_launch(is_elf, checkelf, d)
+    for (file, elf_file) in results:
+                #elf_file = is_elf(file)
                 if elf_file & 1:
                     if elf_file & 2:
                         if qa_already_stripped:
-- 
2.17.1



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 10/10] sstate/bitbake.conf: Use pigz if available
  2018-07-20 10:39 [PATCH 01/10] utils: Add multiprocess_launch API and testcase Richard Purdie
                   ` (7 preceding siblings ...)
  2018-07-20 10:39 ` [PATCH 09/10] package: Call file to determine elf status in parallel Richard Purdie
@ 2018-07-20 10:39 ` Richard Purdie
  2018-07-20 11:02 ` ✗ patchtest: failure for "utils: Add multiprocess_launch..." and 9 more Patchwork
  9 siblings, 0 replies; 11+ messages in thread
From: Richard Purdie @ 2018-07-20 10:39 UTC (permalink / raw)
  To: openembedded-core

Currently the compression of sstate objects is single threaded. In the case
of ltp, this takes around 33s. If we add pigz into the list of non-fatal
HOSTTOOLS and then use if it available when building the sstate object,
this time drops to around 6s.

Since pigz is now widely available this is an optimisation we should
utilise.

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 meta/classes/sstate.bbclass | 11 +++++++++--
 meta/conf/bitbake.conf      |  2 +-
 2 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/meta/classes/sstate.bbclass b/meta/classes/sstate.bbclass
index 5a0722567a5..350d3107f59 100644
--- a/meta/classes/sstate.bbclass
+++ b/meta/classes/sstate.bbclass
@@ -715,17 +715,24 @@ sstate_task_postfunc[dirs] = "${WORKDIR}"
 #
 sstate_create_package () {
 	TFILE=`mktemp ${SSTATE_PKG}.XXXXXXXX`
+
+        # Use pigz if available
+        OPT="-cz"
+        if [ -x "$(command -v pigz)" ]; then
+            OPT="-I pigz -c"
+        fi
+
 	# Need to handle empty directories
 	if [ "$(ls -A)" ]; then
 		set +e
-		tar -czf $TFILE *
+		tar $OPT -f $TFILE *
 		ret=$?
 		if [ $ret -ne 0 ] && [ $ret -ne 1 ]; then
 			exit 1
 		fi
 		set -e
 	else
-		tar -cz --file=$TFILE --files-from=/dev/null
+		tar $OPT --file=$TFILE --files-from=/dev/null
 	fi
 	chmod 0664 $TFILE
 	mv -f $TFILE ${SSTATE_PKG}
diff --git a/meta/conf/bitbake.conf b/meta/conf/bitbake.conf
index 8f738545995..e28f3c7311c 100644
--- a/meta/conf/bitbake.conf
+++ b/meta/conf/bitbake.conf
@@ -490,7 +490,7 @@ HOSTTOOLS += " \
 HOSTTOOLS += "${@'ip ping ps scp ssh stty' if (bb.utils.contains_any('IMAGE_CLASSES', 'testimage testsdk', True, False, d) or any(x in (d.getVar("BBINCLUDED") or "") for x in ["testimage.bbclass", "testsdk.bbclass"])) else ''}"
 
 # Link to these if present
-HOSTTOOLS_NONFATAL += "aws ccache gcc-ar gpg ld.bfd ld.gold nc sftp socat ssh sudo"
+HOSTTOOLS_NONFATAL += "aws ccache gcc-ar gpg ld.bfd ld.gold nc pigz sftp socat ssh sudo"
 
 # Temporary add few more detected in bitbake world
 HOSTTOOLS_NONFATAL += "join nl size yes zcat"
-- 
2.17.1



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* ✗ patchtest: failure for "utils: Add multiprocess_launch..." and 9 more
  2018-07-20 10:39 [PATCH 01/10] utils: Add multiprocess_launch API and testcase Richard Purdie
                   ` (8 preceding siblings ...)
  2018-07-20 10:39 ` [PATCH 10/10] sstate/bitbake.conf: Use pigz if available Richard Purdie
@ 2018-07-20 11:02 ` Patchwork
  9 siblings, 0 replies; 11+ messages in thread
From: Patchwork @ 2018-07-20 11:02 UTC (permalink / raw)
  To: Richard Purdie; +Cc: openembedded-core

== Series Details ==

Series: "utils: Add multiprocess_launch..." and 9 more
Revision: 1
URL   : https://patchwork.openembedded.org/series/13126/
State : failure

== Summary ==


Thank you for submitting this patch series to OpenEmbedded Core. This is
an automated response. Several tests have been executed on the proposed
series by patchtest resulting in the following failures:



* Issue             Series does not apply on top of target branch [test_series_merge_on_head] 
  Suggested fix    Rebase your series on top of targeted branch
  Targeted branch  master (currently at ff0b682b80)



If you believe any of these test results are incorrect, please reply to the
mailing list (openembedded-core@lists.openembedded.org) raising your concerns.
Otherwise we would appreciate you correcting the issues and submitting a new
version of the patchset if applicable. Please ensure you add/increment the
version number when sending the new version (i.e. [PATCH] -> [PATCH v2] ->
[PATCH v3] -> ...).

---
Guidelines:     https://www.openembedded.org/wiki/Commit_Patch_Message_Guidelines
Test framework: http://git.yoctoproject.org/cgit/cgit.cgi/patchtest
Test suite:     http://git.yoctoproject.org/cgit/cgit.cgi/patchtest-oe



^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2018-07-20 11:02 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-07-20 10:39 [PATCH 01/10] utils: Add multiprocess_launch API and testcase Richard Purdie
2018-07-20 10:39 ` [PATCH 02/10] package: Rework PACKAGELOCK based upon sstate for do_packagedata Richard Purdie
2018-07-20 10:39 ` [PATCH 03/10] package_ipk|deb: Use oe.utils.multiprocess_launch Richard Purdie
2018-07-20 10:39 ` [PATCH 04/10] package: Drop subshell usage for dwarfsrcfile generation Richard Purdie
2018-07-20 10:39 ` [PATCH 05/10] package: Don't use subshell to execute file Richard Purdie
2018-07-20 10:39 ` [PATCH 06/10] package: Allow parallel processing of debug splitting Richard Purdie
2018-07-20 10:39 ` [PATCH 07/10] package: Allow parallel processing of shlib analysis Richard Purdie
2018-07-20 10:39 ` [PATCH 08/10] package: Refactor to remove isElf/is_elf function duplication Richard Purdie
2018-07-20 10:39 ` [PATCH 09/10] package: Call file to determine elf status in parallel Richard Purdie
2018-07-20 10:39 ` [PATCH 10/10] sstate/bitbake.conf: Use pigz if available Richard Purdie
2018-07-20 11:02 ` ✗ patchtest: failure for "utils: Add multiprocess_launch..." and 9 more Patchwork

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.