[OE-core] [PATCH 1/1] Add wic support to generate rootfs image for uboot

Adrian Calianu adrian.calianu at enea.com
Tue Jun 10 13:41:31 UTC 2014


Add support in wic to build ramdisk uboot images from list of packages or
from an existing rootfs folder.

Some prerequisites are required for this new build setup:
../poky/scripts/lib/image/config/wic.conf
[create]
arch=target_arch (example: powerpc, arm, x86)
pkgmgr=opkg
repourl=http://example.distro/p2020rdb/ipk/all http://example.distro/p2020rdb/ipk/p2020rdb http://example.distro/p2020rdb/ipk/ppce500v2

    1) Build an rootfs image from an existing bitbake build:
       wic create ../poky/scripts/lib/image/canned-wks/uboot.wks -e core-image-minimal -o .../output

    2) Build an rootfs image from an existing rootfs and native_sysroot:
       wic create ../poky/scripts/lib/image/canned-wks/uboot.wks -r .../path_to_rootfs -n .../path_to_native_sysroot -o .../output

    3) Build an rootfs image only from a package list (on wks file):

       Add the package list to be installed as rootfs on ../poky/scripts/lib/image/canned-wks/uboot.wks:
       %packages
       packagegroup-core-boot
       pramfs-init
       run-postinsts
       packagegroup-core-ssh-dropbear
       %end

       Generate rootfs image:
       wic create ../poky/scripts/lib/image/canned-wks/uboot.wks  -n .../path_to_native_sysroot -o .../output

Signed-off-by: Adrian Calianu <adrian.calianu at enea.com>
---
 scripts/lib/image/canned-wks/uboot.wks             |   17 +
 scripts/lib/image/config/wic.conf                  |    4 +
 scripts/lib/image/engine.py                        |    7 +-
 scripts/lib/mic/imager/direct.py                   |    4 +
 .../lib/mic/kickstart/custom_commands/partition.py |  128 ++++
 scripts/lib/mic/plugin.py                          |   11 +-
 scripts/lib/mic/pluginbase.py                      |    9 +
 scripts/lib/mic/plugins/source/uboot.py            |  173 +++++
 scripts/lib/mic/utils/oe/package_manager.py        |  810 ++++++++++++++++++++
 scripts/wic                                        |   88 ++-
 10 files changed, 1219 insertions(+), 32 deletions(-)
 create mode 100644 scripts/lib/image/canned-wks/uboot.wks
 create mode 100644 scripts/lib/mic/plugins/source/uboot.py
 create mode 100644 scripts/lib/mic/utils/oe/package_manager.py

diff --git a/scripts/lib/image/canned-wks/uboot.wks b/scripts/lib/image/canned-wks/uboot.wks
new file mode 100644
index 0000000..7de0572
--- /dev/null
+++ b/scripts/lib/image/canned-wks/uboot.wks
@@ -0,0 +1,17 @@
+# short-description: .       Create a ramdisk image for U-Boot
+# long-description: Creates a ramdisk image for U-Boot that user
+# can directly load it into ram through tftp
+#
+# part - is a wic command that drive the process of generating a valid file system
+#      - --source=uboot : wic plugin that generates a ramdisk image for U-Boot
+#      - --fstype=ext2  : file system type( ext2 / ext3 / ext 4)
+#
+# %packages %end - option to provide a list of packages that will be installed
+#                  into rootfs. All packages dependencies will be installed by
+#                  package manager(default opkg).
+
+
+part / --source=uboot --fstype=ext2 --label imageName  --align 1024
+
+%packages
+%end
diff --git a/scripts/lib/image/config/wic.conf b/scripts/lib/image/config/wic.conf
index e96d6ae..2a2750b 100644
--- a/scripts/lib/image/config/wic.conf
+++ b/scripts/lib/image/config/wic.conf
@@ -4,4 +4,8 @@ distro_name = OpenEmbedded
 
 [create]
 ; settings for create subcommand
+; repourl=http://linux.com/ipk/all http://linux.com/ipk/target http://linux.com/ipk/arch
+arch=powerpc
+pkgmgr=opkg
 runtime=native
+install_pkgs=source
diff --git a/scripts/lib/image/engine.py b/scripts/lib/image/engine.py
index 3bda1bf..1256236 100644
--- a/scripts/lib/image/engine.py
+++ b/scripts/lib/image/engine.py
@@ -96,9 +96,10 @@ def build_canned_image_list(dl):
     layers_path = get_bitbake_var("BBLAYERS")
     canned_wks_layer_dirs = []
 
-    for layer_path in layers_path.split():
-        path = os.path.join(layer_path, SCRIPTS_CANNED_IMAGE_DIR)
-        canned_wks_layer_dirs.append(path)
+    if layers_path is not None:
+        for layer_path in layers_path.split():
+            path = os.path.join(layer_path, SCRIPTS_CANNED_IMAGE_DIR)
+            canned_wks_layer_dirs.append(path)
 
     path = os.path.join(dl, CANNED_IMAGE_DIR)
     canned_wks_layer_dirs.append(path)
diff --git a/scripts/lib/mic/imager/direct.py b/scripts/lib/mic/imager/direct.py
index 2cf4c8d..fef9d0e 100644
--- a/scripts/lib/mic/imager/direct.py
+++ b/scripts/lib/mic/imager/direct.py
@@ -262,6 +262,10 @@ class DirectImageCreator(BaseImageCreator):
             # when/if we need to actually do package selection we
             # should modify things to use those objects, but for now
             # we can avoid that.
+
+            p.install_pkgs(self, self.workdir, self.oe_builddir, self.rootfs_dir,
+                           self.bootimg_dir, self.kernel_dir, self.native_sysroot)
+
             p.prepare(self, self.workdir, self.oe_builddir, self.rootfs_dir,
                       self.bootimg_dir, self.kernel_dir, self.native_sysroot)
 
diff --git a/scripts/lib/mic/kickstart/custom_commands/partition.py b/scripts/lib/mic/kickstart/custom_commands/partition.py
index 6b575c0..450d2d4 100644
--- a/scripts/lib/mic/kickstart/custom_commands/partition.py
+++ b/scripts/lib/mic/kickstart/custom_commands/partition.py
@@ -31,7 +31,11 @@ from mic.utils.oe.misc import *
 from mic.kickstart.custom_commands import *
 from mic.plugin import pluginmgr
 
+import os
+from mic.utils.oe.package_manager import *
+
 partition_methods = {
+    "do_install_pkgs":None,
     "do_stage_partition":None,
     "do_prepare_partition":None,
     "do_configure_partition":None,
@@ -115,6 +119,102 @@ class Wic_PartData(Mic_PartData):
         else:
             return 0
 
+    def install_pkgs(self, creator, cr_workdir, oe_builddir, rootfs_dir,
+                     bootimg_dir, kernel_dir, native_sysroot):
+        """
+        Prepare content for individual partitions, installing packages.
+        """
+
+        if not self.source:
+            return
+
+        self._source_methods = pluginmgr.get_source_plugin_methods(self.source, partition_methods)
+        self._source_methods["do_install_pkgs"](self, creator,
+                                                      cr_workdir,
+                                                      oe_builddir,
+                                                      rootfs_dir,
+                                                      bootimg_dir,
+                                                      kernel_dir,
+                                                      native_sysroot)
+
+    def install_pkgs_ipk(self, cr_workdir, oe_builddir, rootfs_dir,
+                             native_sysroot, packages, repourl):
+        """
+        Install packages specified into wks file using opkg package manager.
+        This method is dependend on bb module.
+        """
+
+        gVar = {}
+        gVar["DEPLOY_DIR_IPK"] = os.path.join(oe_builddir, "tmp/deploy/ipk")
+
+        # Run postinstall scripts even in offline mode
+        # Use the arch priority package rather than higher version one if more than one candidate is found.
+        #d.setVar("OPKG_ARGS", "--force_postinstall --prefer-arch-to-version")
+        gVar["OPKG_ARGS"] = "--force_postinstall"
+
+        # OPKG path relative to /output_path
+        gVar["OPKGLIBDIR"] = "var/lib"
+
+        source_url = repourl.split()
+
+        # Generate feed uri's names, it doesn't seem to matter what name they have
+        feed_uris = ""
+        cnt = 0
+        archs = ""
+        for url in source_url:
+            feed_uris += "cl_def_feed%d##%s\n" % (cnt, url)
+            cnt += 1
+            head, tail = os.path.split(url)
+            archs += " " + tail
+
+        # IPK_FEED_URIS with special formating defines the URI's used as source for packages
+        gVar['IPK_FEED_URIS'] = feed_uris
+
+        gVar['BUILD_IMAGES_FROM_FEEDS'] = "1"
+
+        # We need to provide sysroot for utilities
+        gVar['STAGING_DIR_NATIVE'] = native_sysroot
+
+        # Set WORKDIR for output
+        gVar['WORKDIR'] = cr_workdir
+
+        # Set TMPDIR for output
+        gVar['TMPDIR'] = os.path.join(cr_workdir, "tmp")
+
+        if 'ROOTFS_DIR' in rootfs_dir:
+            target_dir = rootfs_dir['ROOTFS_DIR']
+        elif os.path.isdir(rootfs_dir):
+            target_dir = rootfs_dir
+        else:
+            msg = "Couldn't find --rootfs-dir=%s connection"
+            msg += " or it is not a valid path, exiting"
+            msger.error(msg % rootfs_dir)
+
+        # Need native sysroot /usr/bin/ for opkg-cl
+        # chnage PATH var to avoid issues with host tools
+        defpath = os.environ['PATH']
+        os.environ['PATH'] = native_sysroot + "/usr/bin/" + ":/bin:/usr/bin:"
+
+        pseudo = "export PSEUDO_PREFIX=%s/usr;" % native_sysroot
+        pseudo += "export PSEUDO_LOCALSTATEDIR=%s/../pseudo;" % target_dir
+        pseudo += "export PSEUDO_PASSWD=%s;" % target_dir
+        pseudo += "export PSEUDO_NOSYMLINKEXP=1;"
+        pseudo += "%s/usr/bin/pseudo " % native_sysroot
+
+        pm = WicOpkgPM(gVar,
+                    target_dir,
+                    'opkg.conf',
+                    archs,
+                    pseudo,
+                    native_sysroot)
+
+        pm.update()
+
+        pm.install(packages)
+
+        os.environ['PATH'] += defpath + ":" + native_sysroot + "/usr/bin/"
+
+
     def prepare(self, cr, cr_workdir, oe_builddir, rootfs_dir, bootimg_dir,
                 kernel_dir, native_sysroot):
         """
@@ -225,6 +325,34 @@ class Wic_PartData(Mic_PartData):
 
         return 0
 
+    def prepare_for_uboot(self, arch, cr_workdir, oe_builddir, rootfs_dir,
+                             native_sysroot):
+        """
+        Generates u-boot image from source_file( ext2/3/4 )
+
+        """
+        pseudo = "export PSEUDO_PREFIX=%s/usr;" % native_sysroot
+        pseudo += "export PSEUDO_LOCALSTATEDIR=%s/../pseudo;" % rootfs_dir
+        pseudo += "export PSEUDO_PASSWD=%s;" % rootfs_dir
+        pseudo += "export PSEUDO_NOSYMLINKEXP=1;"
+        pseudo += "%s/usr/bin/pseudo " % native_sysroot
+
+        # 1) compress image
+        rootfs = self.source_file
+        rootfs_gzip = "%s.gz" % rootfs
+        gzip_cmd = "gzip -f -9 -c %s > %s" % (rootfs, rootfs_gzip)
+        rc, out = exec_native_cmd(pseudo + gzip_cmd, native_sysroot)
+
+        # 2) image for U-Boot
+        rootfs_uboot = "%s.u-boot" % rootfs_gzip
+        mkimage_cmd = "mkimage -A %s -O linux -T ramdisk -C gzip -n %s -d %s %s" % \
+               (arch, self.label, rootfs_gzip, rootfs_uboot)
+        rc, out = exec_native_cmd(pseudo + mkimage_cmd, native_sysroot)
+
+        msger.info("\n\n\tThe new U-Boot ramdisk image can be found here:\n\t\t%s\n\n"  % rootfs_uboot)
+
+        return 0
+
     def prepare_rootfs_btrfs(self, cr_workdir, oe_builddir, rootfs_dir,
                              native_sysroot, pseudo):
         """
diff --git a/scripts/lib/mic/plugin.py b/scripts/lib/mic/plugin.py
index bec33d6..585fd6d 100644
--- a/scripts/lib/mic/plugin.py
+++ b/scripts/lib/mic/plugin.py
@@ -53,12 +53,13 @@ class PluginMgr(object):
             self.layers_path = get_bitbake_var("BBLAYERS")
         layer_dirs = []
 
-        for layer_path in self.layers_path.split():
-            path = os.path.join(layer_path, SCRIPTS_PLUGIN_DIR, ptype)
-            layer_dirs.append(path)
+        if self.layers_path is not None:
+            for layer_path in self.layers_path.split():
+                path = os.path.join(layer_path, SCRIPTS_PLUGIN_DIR, ptype)
+                layer_dirs.append(path)
 
-            path = os.path.join(dl, ptype)
-            layer_dirs.append(path)
+        path = os.path.join(dl, ptype)
+        layer_dirs.append(path)
 
         return layer_dirs
 
diff --git a/scripts/lib/mic/pluginbase.py b/scripts/lib/mic/pluginbase.py
index 9cf4c62..881d996 100644
--- a/scripts/lib/mic/pluginbase.py
+++ b/scripts/lib/mic/pluginbase.py
@@ -89,6 +89,15 @@ class SourcePlugin(_Plugin):
     """
 
     @classmethod
+    def do_install_pkgs(self, part, creator, cr_workdir, oe_builddir, rootfs_dir,
+                        bootimg_dir, kernel_dir, native_sysroot):
+        """
+        Called before partitions have been prepared and assembled into a
+        disk image. Install packages into rootfs
+        """
+        msger.debug("SourcePlugin: do_install_pkgs: part %s" % part)
+
+    @classmethod
     def do_install_disk(self, disk, disk_name, cr, workdir, oe_builddir,
                         bootimg_dir, kernel_dir, native_sysroot):
         """
diff --git a/scripts/lib/mic/plugins/source/uboot.py b/scripts/lib/mic/plugins/source/uboot.py
new file mode 100644
index 0000000..57cb3cf
--- /dev/null
+++ b/scripts/lib/mic/plugins/source/uboot.py
@@ -0,0 +1,173 @@
+# ex:ts=4:sw=4:sts=4:et
+# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
+#
+# Copyright (c) 2014, Enea AB.
+# All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# DESCRIPTION
+# This implements the 'uboot' source plugin class for 'wic'
+#
+# AUTHORS
+# Adrian Calianu <adrian.calianu (at] enea.com>
+#
+
+import os
+import shutil
+import re
+import tempfile
+
+from mic import kickstart, chroot, msger
+from mic.utils import misc, fs_related, errors, runner, cmdln
+from mic.conf import configmgr
+from mic.plugin import pluginmgr
+from mic.utils.partitionedfs import PartitionedMount
+import mic.imager.direct as direct
+from mic.pluginbase import SourcePlugin
+from mic.utils.oe.misc import *
+from mic.imager.direct import DirectImageCreator
+
+def create_local_rootfs(part, creator, cr_workdir, krootfs_dir, native_sysroot):
+    # In order to have a full control over rootfs we will make a local copy under workdir
+    # and change rootfs_dir to new location.
+    # In this way we can install more than one ROOTFS_DIRs and/or use
+    # an empty rootfs to install packages, so a rootfs could be generated only from pkgs
+    # TBD: create workdir/rootfs ; copy rootfs-> workdir/rootfs; set rootfs=workdir/rootfs
+
+    cr_workdir = os.path.abspath(cr_workdir)
+    new_rootfs_dir = "%s/rootfs_%s" % (cr_workdir, creator.name)
+
+    rootfs_exists = 1
+    if part.rootfs is None:
+        if not 'ROOTFS_DIR' in krootfs_dir:
+            msg = "Couldn't find --rootfs-dir, exiting, "
+            msger.info(msg)
+            rootfs_exists = 0
+        rootfs_dir = krootfs_dir['ROOTFS_DIR']
+        creator.rootfs_dir['ROOTFS_DIR'] = new_rootfs_dir
+    else:
+        if part.rootfs in krootfs_dir:
+            rootfs_dir = krootfs_dir[part.rootfs]
+            creator.rootfs_dir[part.rootfs] = new_rootfs_dir
+        elif os.path.isdir(part.rootfs):
+            rootfs_dir = part.rootfs
+            part.rootfs = new_rootfs_dir
+        else:
+            msg = "Couldn't find --rootfs-dir=%s connection"
+            msg += " or it is not a valid path, exiting"
+            msger.info(msg % part.rootfs)
+            rootfs_exists = 0
+            creator.rootfs_dir['ROOTFS_DIR'] = new_rootfs_dir
+
+    pseudox = "export PSEUDO_PREFIX=%s/usr;" % native_sysroot
+    pseudox += "export PSEUDO_LOCALSTATEDIR=%s/../pseudo;" % new_rootfs_dir
+    pseudox += "export PSEUDO_PASSWD=%s;" % new_rootfs_dir
+    pseudox += "export PSEUDO_NOSYMLINKEXP=1;"
+    pseudox += "%s/usr/bin/pseudo " % native_sysroot
+
+    mkdir_cmd = "mkdir %s" % (new_rootfs_dir)
+    # rc, out = exec_native_cmd(pseudox + mkdir_cmd, native_sysroot)
+    rc, out = exec_cmd(mkdir_cmd, True)
+
+    if rootfs_exists == 1 and os.path.isdir(rootfs_dir):
+        defpath = os.environ['PATH']
+        os.environ['PATH'] = native_sysroot + "/usr/bin/" + ":/bin:/usr/bin:"
+
+        rootfs_dir = os.path.abspath(rootfs_dir)
+
+        pseudoc = "export PSEUDO_PREFIX=%s/usr;" % native_sysroot
+        pseudoc += "export PSEUDO_LOCALSTATEDIR=%s/../pseudo;" % rootfs_dir
+        pseudoc += "export PSEUDO_PASSWD=%s;" % rootfs_dir
+        pseudoc += "export PSEUDO_NOSYMLINKEXP=1;"
+        pseudoc += "%s/usr/bin/pseudo " % native_sysroot
+
+        tarc_cmd = "tar cvpf %s/rootfs.tar -C %s ." % (cr_workdir, rootfs_dir)
+        rc, out = exec_native_cmd(pseudoc + tarc_cmd, native_sysroot)
+
+        tarx_cmd = "tar xpvf %s/rootfs.tar -C %s" % (cr_workdir, new_rootfs_dir)
+        rc, out = exec_native_cmd(pseudox + tarx_cmd, native_sysroot)
+
+        rm_cmd = "rm %s/rootfs.tar" % cr_workdir
+        rc, out = exec_cmd(rm_cmd, True)
+
+        os.environ['PATH'] += defpath + ":" + native_sysroot + "/usr/bin/"
+
+    return new_rootfs_dir
+
+class UBootPlugin(SourcePlugin):
+    name = 'uboot'
+
+    @classmethod
+    def do_install_pkgs(self, part, creator, cr_workdir, oe_builddir, krootfs_dir,
+                        bootimg_dir, kernel_dir, native_sysroot):
+        """
+        Called before all partitions have been prepared and assembled into a
+        disk image. Intall packages based on wic configuration.
+        """
+
+        # set new rootfs_dir
+        rootfs_dir = create_local_rootfs(part, creator, cr_workdir, krootfs_dir, native_sysroot)
+
+        # wks file parsing
+        packages = kickstart.get_packages(creator.ks)
+
+        # wic.conf file parsing = found under 'creator'
+        local_pkgs_path = creator._local_pkgs_path
+        repourl = creator.repourl
+        pkgmgr = creator.pkgmgr_name
+
+        # install packages
+        if packages and pkgmgr in ["opkg"]:
+            if len(repourl) > 0 :
+                part.install_pkgs_ipk(cr_workdir, oe_builddir, rootfs_dir, native_sysroot,
+                                  packages, repourl)
+            else:
+                msger.error("No packages repository provided in wic.conf")
+
+    @classmethod
+    def do_prepare_partition(self, part, cr, cr_workdir, oe_builddir, bootimg_dir,
+                             kernel_dir, krootfs_dir, native_sysroot):
+        """
+        Called to do the actual content population for a partition i.e. it
+        'prepares' the partition to be incorporated into the image.
+        In this case, prepare content for legacy bios boot partition.
+        """
+        if part.rootfs is None:
+            if not 'ROOTFS_DIR' in krootfs_dir:
+                msg = "Couldn't find --rootfs-dir, exiting"
+                msger.error(msg)
+            rootfs_dir = krootfs_dir['ROOTFS_DIR']
+        else:
+            if part.rootfs in krootfs_dir:
+                rootfs_dir = krootfs_dir[part.rootfs]
+            elif os.path.isdir(part.rootfs):
+                rootfs_dir = part.rootfs
+            else:
+                msg = "Couldn't find --rootfs-dir=%s connection"
+                msg += " or it is not a valid path, exiting"
+                msger.error(msg % part.rootfs)
+
+        part.set_rootfs(rootfs_dir)
+
+        # change partition label wich will reflect into the final rootfs image name
+        part.label = "%s_%s" % (part.label, cr.name)
+
+        defpath = os.environ['PATH']
+        os.environ['PATH'] = native_sysroot + "/usr/bin/" + ":/bin:/usr/bin:"
+
+        part.prepare_rootfs(cr_workdir, oe_builddir, rootfs_dir, native_sysroot)
+        part.prepare_for_uboot(cr.target_arch,cr_workdir, oe_builddir, rootfs_dir, native_sysroot)
+
+        os.environ['PATH'] += defpath + ":" + native_sysroot + "/usr/bin/"
diff --git a/scripts/lib/mic/utils/oe/package_manager.py b/scripts/lib/mic/utils/oe/package_manager.py
new file mode 100644
index 0000000..92ce98e
--- /dev/null
+++ b/scripts/lib/mic/utils/oe/package_manager.py
@@ -0,0 +1,810 @@
+# ex:ts=4:sw=4:sts=4:et
+# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
+#
+# Copyright (c) 2014, Enea AB.
+# All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# DESCRIPTION
+# This implements the opkg package manager wrapper as a combination of
+# meta/lib/oe/package_manager.py and bitbake/lib/bb/utils.py files and
+# adaptation of those files to 'wic'.
+#
+# AUTHORS
+# Adrian Calianu <adrian.calianu (at] enea.com>
+#
+# This file incorporates work covered by the following copyright and
+# permission notice:
+#
+#     meta/COPYING.GPLv2 (GPLv2)
+#     meta/COPYING.MIT (MIT)
+#
+#     Copyright (C) 2004 Michael Lauer
+#
+#     Permission to use, copy, modify, and/or distribute this software
+#     for any purpose with or without fee is hereby granted, provided
+#     that the above copyright notice and this permission notice appear
+#     in all copies.
+#
+#     THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+#     WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+#     WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+#     AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
+#     CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+#     OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+#     NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+#     CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+from abc import ABCMeta, abstractmethod
+import os
+import glob
+import subprocess
+import shutil
+import multiprocessing
+import re
+import errno
+import fcntl
+
+from mic.utils.oe.misc import *
+from mic import msger
+
+def mkdirhier(directory):
+    """Create a directory like 'mkdir -p', but does not complain if
+    directory already exists like os.makedirs
+    """
+
+    try:
+        os.makedirs(directory)
+    except OSError as e:
+        if e.errno != errno.EEXIST:
+            raise e
+
+def remove(path, recurse=False):
+    """Equivalent to rm -f or rm -rf"""
+    if not path:
+        return
+    if recurse:
+        # shutil.rmtree(name) would be ideal but its too slow
+        subprocess.call(['rm', '-rf'] + glob.glob(path))
+        return
+    for name in glob.glob(path):
+        try:
+            os.unlink(name)
+        except OSError as exc:
+            if exc.errno != errno.ENOENT:
+                raise
+
+def lockfile(name, shared=False, retry=True):
+    """
+    Use the file fn as a lock file, return when the lock has been acquired.
+    Returns a variable to pass to unlockfile().
+    """
+    dirname = os.path.dirname(name)
+    mkdirhier(dirname)
+
+    if not os.access(dirname, os.W_OK):
+        logger.error("Unable to acquire lock '%s', directory is not writable",
+                     name)
+        sys.exit(1)
+
+    op = fcntl.LOCK_EX
+    if shared:
+        op = fcntl.LOCK_SH
+    if not retry:
+        op = op | fcntl.LOCK_NB
+
+    while True:
+        # If we leave the lockfiles lying around there is no problem
+        # but we should clean up after ourselves. This gives potential
+        # for races though. To work around this, when we acquire the lock
+        # we check the file we locked was still the lock file on disk.
+        # by comparing inode numbers. If they don't match or the lockfile
+        # no longer exists, we start again.
+
+        # This implementation is unfair since the last person to request the
+        # lock is the most likely to win it.
+
+        try:
+            lf = open(name, 'a+')
+            fileno = lf.fileno()
+            fcntl.flock(fileno, op)
+            statinfo = os.fstat(fileno)
+            if os.path.exists(lf.name):
+                statinfo2 = os.stat(lf.name)
+                if statinfo.st_ino == statinfo2.st_ino:
+                    return lf
+            lf.close()
+        except Exception:
+            try:
+                lf.close()
+            except Exception:
+                pass
+            pass
+        if not retry:
+            return None
+
+def unlockfile(lf):
+    """
+    Unlock a file locked using lockfile()
+    """
+    try:
+        # If we had a shared lock, we need to promote to exclusive before
+        # removing the lockfile. Attempt this, ignore failures.
+        fcntl.flock(lf.fileno(), fcntl.LOCK_EX|fcntl.LOCK_NB)
+        os.unlink(lf.name)
+    except (IOError, OSError):
+        pass
+    fcntl.flock(lf.fileno(), fcntl.LOCK_UN)
+    lf.close()
+
+def which(path, item, direction = 0, history = False):
+    """
+    Locate a file in a PATH
+    """
+
+    hist = []
+    paths = (path or "").split(':')
+    if direction != 0:
+        paths.reverse()
+
+    for p in paths:
+        next = os.path.join(p, item)
+        hist.append(next)
+        if os.path.exists(next):
+            if not os.path.isabs(next):
+                next = os.path.abspath(next)
+            if history:
+                return next, hist
+            return next
+
+    if history:
+        return "", hist
+    return ""
+
+
+
+# this can be used by all PM backends to create the index files in parallel
+def wic_create_index(arg):
+    index_cmd = arg
+
+    try:
+        msger.info("Executing '%s' ..." % index_cmd)
+        subprocess.check_output(index_cmd, stderr=subprocess.STDOUT, shell=True)
+    except subprocess.CalledProcessError as e:
+        return("Index creation command '%s' failed with return code %d:\n%s" %
+               (e.cmd, e.returncode, e.output))
+
+    return None
+
+
+class WicIndexer(object):
+    __metaclass__ = ABCMeta
+
+    def __init__(self, d, deploy_dir):
+        self.d = d
+        self.deploy_dir = deploy_dir
+
+    @abstractmethod
+    def write_index(self):
+        pass
+
+class WicOpkgIndexer(WicIndexer):
+    def write_index(self):
+        arch_vars = ["ALL_MULTILIB_PACKAGE_ARCHS",
+                     "SDK_PACKAGE_ARCHS",
+                     "MULTILIB_ARCHS"]
+
+        opkg_index_cmd = which(os.getenv('PATH'), "opkg-make-index")
+
+        if not os.path.exists(os.path.join(self.deploy_dir, "Packages")):
+            open(os.path.join(self.deploy_dir, "Packages"), "w").close()
+
+        index_cmds = []
+        for arch_var in arch_vars:
+            if self.d.has_key(arch_var):
+                archs = self.d[arch_var]
+            else:
+                archs = None
+
+            if archs is None:
+                continue
+
+            for arch in archs.split():
+                pkgs_dir = os.path.join(self.deploy_dir, arch)
+                pkgs_file = os.path.join(pkgs_dir, "Packages")
+
+                if not os.path.isdir(pkgs_dir):
+                    continue
+
+                if not os.path.exists(pkgs_file):
+                    open(pkgs_file, "w").close()
+
+                index_cmds.append('%s -r %s -p %s -m %s' %
+                                  (opkg_index_cmd, pkgs_file, pkgs_file, pkgs_dir))
+
+        if len(index_cmds) == 0:
+            msger.info("There are no packages in %s!" % self.deploy_dir)
+            return
+
+        nproc = multiprocessing.cpu_count()
+        pool = multiprocessing.Pool(nproc)
+        results = list(pool.imap(wic_create_index, index_cmds))
+        pool.close()
+        pool.join()
+
+        for result in results:
+            if result is not None:
+                return(result)
+
+class WicPkgsList(object):
+    __metaclass__ = ABCMeta
+
+    def __init__(self, d, rootfs_dir):
+        self.d = d
+        self.rootfs_dir = rootfs_dir
+
+    @abstractmethod
+    def list(self, format=None):
+        pass
+
+
+class WicOpkgPkgsList(WicPkgsList):
+    def __init__(self, d, rootfs_dir, config_file):
+        super(WicOpkgPkgsList, self).__init__(d, rootfs_dir)
+
+        self.opkg_cmd = which(os.getenv('PATH'), "opkg-cl")
+        self.opkg_args = "-f %s -o %s " % (config_file, rootfs_dir)
+        if self.d.has_key("OPKG_ARGS"):
+            self.opkg_args += self.d["OPKG_ARGS"]
+
+    def list(self, format=None):
+        opkg_query_cmd = which(os.getenv('PATH'), "opkg-query-helper.py")
+
+        if format == "arch":
+            cmd = "%s %s status | %s -a" % \
+                (self.opkg_cmd, self.opkg_args, opkg_query_cmd)
+        elif format == "file":
+            cmd = "%s %s status | %s -f" % \
+                (self.opkg_cmd, self.opkg_args, opkg_query_cmd)
+        elif format == "ver":
+            cmd = "%s %s status | %s -v" % \
+                (self.opkg_cmd, self.opkg_args, opkg_query_cmd)
+        elif format == "deps":
+            cmd = "%s %s status | %s" % \
+                (self.opkg_cmd, self.opkg_args, opkg_query_cmd)
+        else:
+            cmd = "%s %s list_installed | cut -d' ' -f1" % \
+                (self.opkg_cmd, self.opkg_args)
+
+        try:
+            output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).strip()
+        except subprocess.CalledProcessError as e:
+            msger.error("Cannot get the installed packages list. Command '%s' "
+                     "returned %d:\n%s" % (cmd, e.returncode, e.output))
+
+        if output and format == "file":
+            tmp_output = ""
+            for line in output.split('\n'):
+                pkg, pkg_file, pkg_arch = line.split()
+                full_path = os.path.join(self.rootfs_dir, pkg_arch, pkg_file)
+                if os.path.exists(full_path):
+                    tmp_output += "%s %s %s\n" % (pkg, full_path, pkg_arch)
+                else:
+                    tmp_output += "%s %s %s\n" % (pkg, pkg_file, pkg_arch)
+
+            output = tmp_output
+
+        return output
+
+
+class WicPackageManager(object):
+    """
+    This is an abstract class. Do not instantiate this directly.
+    """
+    __metaclass__ = ABCMeta
+
+    def __init__(self, d, pseudo, native_sysroot):
+        self.d = d
+        self.deploy_dir = None
+        self.deploy_lock = None
+        if self.d.has_key('PACKAGE_FEED_URIS'):
+            self.feed_uris = self.d['PACKAGE_FEED_URIS']
+        else:
+            self.feed_uris = ""
+        self.pseudo = pseudo
+        self.native_sysroot = native_sysroot
+
+    """
+    Update the package manager package database.
+    """
+    @abstractmethod
+    def update(self):
+        pass
+
+    """
+    Install a list of packages. 'pkgs' is a list object. If 'attempt_only' is
+    True, installation failures are ignored.
+    """
+    @abstractmethod
+    def install(self, pkgs, attempt_only=False):
+        pass
+
+    """
+    Remove a list of packages. 'pkgs' is a list object. If 'with_dependencies'
+    is False, the any dependencies are left in place.
+    """
+    @abstractmethod
+    def remove(self, pkgs, with_dependencies=True):
+        pass
+
+    """
+    This function creates the index files
+    """
+    @abstractmethod
+    def write_index(self):
+        pass
+
+    @abstractmethod
+    def remove_packaging_data(self):
+        pass
+
+    @abstractmethod
+    def list_installed(self, format=None):
+        pass
+
+    @abstractmethod
+    def insert_feeds_uris(self):
+        pass
+
+    """
+    Install complementary packages based upon the list of currently installed
+    packages e.g. locales, *-dev, *-dbg, etc. This will only attempt to install
+    these packages, if they don't exist then no error will occur.  Note: every
+    backend needs to call this function explicitly after the normal package
+    installation
+    """
+    def install_complementary(self, globs=None):
+        # we need to write the list of installed packages to a file because the
+        # oe-pkgdata-util reads it from a file
+        if self.d.has_key('WORKDIR'):
+            installed_pkgs_file = os.path.join(self.d['WORKDIR'],
+                                           "installed_pkgs.txt")
+        else:
+            msger.error("No WORKDIR provided!")
+
+        with open(installed_pkgs_file, "w+") as installed_pkgs:
+            installed_pkgs.write(self.list_installed("arch"))
+
+        if globs is None:
+            if self.d.has_key('IMAGE_INSTALL_COMPLEMENTARY'):
+                globs = self.d['IMAGE_INSTALL_COMPLEMENTARY']
+            split_linguas = set()
+
+            if self.d.has_key('IMAGE_LINGUAS'):
+                for translation in self.d['IMAGE_LINGUAS'].split():
+                    split_linguas.add(translation)
+                    split_linguas.add(translation.split('-')[0])
+
+            split_linguas = sorted(split_linguas)
+
+            for lang in split_linguas:
+                globs += " *-locale-%s" % lang
+
+        if globs is None:
+            return
+
+        if not self.d.has_key('PKGDATA_DIR'):
+            msger.error("No PKGDATA_DIR provided!")
+
+        cmd = [which(os.getenv('PATH'), "oe-pkgdata-util"),
+               "glob", self.d['PKGDATA_DIR'], installed_pkgs_file,
+               globs]
+
+        rc, out = exec_native_cmd(self.pseudo + cmd, self.native_sysroot)
+        if rc != 0:
+            msger.error("Could not compute complementary packages list. Command "
+                     "'%s' returned %d" %
+                     (' '.join(cmd), rc))
+
+        self.install(out.split(), attempt_only=True)
+
+
+    def deploy_dir_lock(self):
+        if self.deploy_dir is None:
+            raise RuntimeError("deploy_dir is not set!")
+
+        lock_file_name = os.path.join(self.deploy_dir, "deploy.lock")
+
+        self.deploy_lock = lockfile(lock_file_name)
+
+    def deploy_dir_unlock(self):
+        if self.deploy_lock is None:
+            return
+
+        unlockfile(self.deploy_lock)
+
+        self.deploy_lock = None
+
+
+class WicOpkgPM(WicPackageManager):
+    def __init__(self, d, target_rootfs, config_file, archs, pseudo, native_sysroot, task_name='target'):
+        super(WicOpkgPM, self).__init__(d, pseudo, native_sysroot)
+
+        self.target_rootfs = target_rootfs
+        self.config_file = config_file
+        self.pkg_archs = archs
+        self.task_name = task_name
+
+        if self.d.has_key("DEPLOY_DIR_IPK"):
+            self.deploy_dir = self.d["DEPLOY_DIR_IPK"]
+
+        self.deploy_lock_file = os.path.join(self.deploy_dir, "deploy.lock")
+        self.opkg_cmd = which(os.getenv('PATH'), "opkg-cl")
+        self.opkg_args = "-f %s -o %s " % (self.config_file, target_rootfs)
+        if self.d.has_key("OPKG_ARGS"):
+            self.opkg_args += self.d["OPKG_ARGS"]
+
+        if self.d.has_key('OPKGLIBDIR'):
+            opkg_lib_dir = self.d['OPKGLIBDIR']
+        else:
+            opkg_lib_dir = ""
+
+        if opkg_lib_dir[0] == "/":
+            opkg_lib_dir = opkg_lib_dir[1:]
+
+        self.opkg_dir = os.path.join(target_rootfs, opkg_lib_dir, "opkg")
+
+        mkdirhier(self.opkg_dir)
+
+        if self.d.has_key("TMPDIR"):
+            tmp_dir = self.d["TMPDIR"]
+        else:
+            tmp_dir = ""
+
+        self.saved_opkg_dir = '%s/saved/%s' % (tmp_dir, self.task_name)
+        if not os.path.exists('%s/saved' % tmp_dir):
+            mkdirhier('%s/saved' % tmp_dir)
+
+        if self.d.has_key('BUILD_IMAGES_FROM_FEEDS') and self.d['BUILD_IMAGES_FROM_FEEDS'] != "1":
+            self._create_config()
+        else:
+            self._create_custom_config()
+
+        self.indexer = WicOpkgIndexer(self.d, self.deploy_dir)
+
+    """
+    This function will change a package's status in /var/lib/opkg/status file.
+    If 'packages' is None then the new_status will be applied to all
+    packages
+    """
+    def mark_packages(self, status_tag, packages=None):
+        status_file = os.path.join(self.opkg_dir, "status")
+
+        with open(status_file, "r") as sf:
+            with open(status_file + ".tmp", "w+") as tmp_sf:
+                if packages is None:
+                    tmp_sf.write(re.sub(r"Package: (.*?)\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)",
+                                        r"Package: \1\n\2Status: \3%s" % status_tag,
+                                        sf.read()))
+                else:
+                    if type(packages).__name__ != "list":
+                        raise TypeError("'packages' should be a list object")
+
+                    status = sf.read()
+                    for pkg in packages:
+                        status = re.sub(r"Package: %s\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)" % pkg,
+                                        r"Package: %s\n\1Status: \2%s" % (pkg, status_tag),
+                                        status)
+
+                    tmp_sf.write(status)
+
+        os.rename(status_file + ".tmp", status_file)
+
+    def _create_custom_config(self):
+        msger.info("Building from feeds activated!")
+
+        with open(self.config_file, "w+") as config_file:
+            priority = 1
+            for arch in self.pkg_archs.split():
+                config_file.write("arch %s %d\n" % (arch, priority))
+                priority += 5
+
+            if self.d.has_key('IPK_FEED_URIS'):
+                ipk_feed_uris = self.d['IPK_FEED_URIS']
+            else:
+                ipk_feed_uris = ""
+
+            for line in ipk_feed_uris.split():
+                feed_match = re.match("^[ \t]*(.*)##([^ \t]*)[ \t]*$", line)
+
+                if feed_match is not None:
+                    feed_name = feed_match.group(1)
+                    feed_uri = feed_match.group(2)
+
+                    msger.info("Add %s feed with URL %s" % (feed_name, feed_uri))
+
+                    config_file.write("src/gz %s %s\n" % (feed_name, feed_uri))
+
+            """
+            Allow to use package deploy directory contents as quick devel-testing
+            feed. This creates individual feed configs for each arch subdir of those
+            specified as compatible for the current machine.
+            NOTE: Development-helper feature, NOT a full-fledged feed.
+            """
+            if self.d.has_key('FEED_DEPLOYDIR_BASE_URI'):
+                feed_deploydir_base_dir = self.d['FEED_DEPLOYDIR_BASE_URI']
+            else:
+                feed_deploydir_base_dir = ""
+
+            if feed_deploydir_base_dir != "":
+                for arch in self.pkg_archs.split():
+                    if self.d.has_key("sysconfdir"):
+                        sysconfdir = self.d["sysconfdir"]
+                    else:
+                        sysconfdir = None
+
+                    cfg_file_name = os.path.join(self.target_rootfs,
+                                                 sysconfdir,
+                                                 "opkg",
+                                                 "local-%s-feed.conf" % arch)
+
+                    with open(cfg_file_name, "w+") as cfg_file:
+                        cfg_file.write("src/gz local-%s %s/%s" %
+                                       arch,
+                                       feed_deploydir_base_dir,
+                                       arch)
+
+    def _create_config(self):
+        with open(self.config_file, "w+") as config_file:
+            priority = 1
+            for arch in self.pkg_archs.split():
+                config_file.write("arch %s %d\n" % (arch, priority))
+                priority += 5
+
+            config_file.write("src oe file:%s\n" % self.deploy_dir)
+
+            for arch in self.pkg_archs.split():
+                pkgs_dir = os.path.join(self.deploy_dir, arch)
+                if os.path.isdir(pkgs_dir):
+                    config_file.write("src oe-%s file:%s\n" %
+                                      (arch, pkgs_dir))
+
+    def insert_feeds_uris(self):
+        if self.feed_uris == "":
+            return
+
+        rootfs_config = os.path.join('%s/etc/opkg/base-feeds.conf'
+                                  % self.target_rootfs)
+
+        with open(rootfs_config, "w+") as config_file:
+            uri_iterator = 0
+            for uri in self.feed_uris.split():
+                config_file.write("src/gz url-%d %s/ipk\n" %
+                                  (uri_iterator, uri))
+
+                for arch in self.pkg_archs.split():
+                    if not os.path.exists(os.path.join(self.deploy_dir, arch)):
+                        continue
+                    msger.info('Note: adding opkg channel url-%s-%d (%s)' %
+                        (arch, uri_iterator, uri))
+
+                    config_file.write("src/gz uri-%s-%d %s/ipk/%s\n" %
+                                      (arch, uri_iterator, uri, arch))
+                uri_iterator += 1
+
+    def update(self):
+        self.deploy_dir_lock()
+
+        cmd = "%s %s update" % (self.opkg_cmd, self.opkg_args)
+
+        rc, out = exec_native_cmd(self.pseudo + cmd, self.native_sysroot)
+        if rc != 0:
+            self.deploy_dir_unlock()
+            msger.error("Unable to update the package index files. Command '%s' "
+                     "returned %d" % (cmd, rc))
+
+        self.deploy_dir_unlock()
+
+    def install(self, pkgs, attempt_only=False):
+        if attempt_only and len(pkgs) == 0:
+            return
+
+        cmd = "%s %s install %s" % (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
+
+        os.environ['D'] = self.target_rootfs
+        os.environ['OFFLINE_ROOT'] = self.target_rootfs
+        os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
+        os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
+        if self.d.has_key('WORKDIR'):
+            os.environ['INTERCEPT_DIR'] = os.path.join(self.d['WORKDIR'],
+                                                   "intercept_scripts")
+        else:
+            os.environ['INTERCEPT_DIR'] = "."
+            msger.warning("No WORKDIR provided!")
+
+        if self.d.has_key('STAGING_DIR_NATIVE'):
+            os.environ['NATIVE_ROOT'] = self.d['STAGING_DIR_NATIVE']
+        else:
+            msger.error("No STAGING_DIR_NATIVE provided!")
+
+        rc, out = exec_native_cmd(self.pseudo + cmd, self.native_sysroot)
+        if rc != 0:
+            msger.error("Unable to install packages. "
+                        "Command '%s' returned %d" % (cmd, rc))
+
+
+    def remove(self, pkgs, with_dependencies=True):
+        if with_dependencies:
+            cmd = "%s %s --force-depends --force-remove --force-removal-of-dependent-packages remove %s" % \
+                (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
+        else:
+            cmd = "%s %s --force-depends remove %s" % \
+                (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
+
+        rc, out = exec_native_cmd(self.pseudo + cmd, self.native_sysroot)
+        if rc != 0:
+            msger.error("Unable to remove packages. Command '%s' "
+                     "returned %d" % (cmd, rc))
+
+
+    def write_index(self):
+        self.deploy_dir_lock()
+
+        result = self.indexer.write_index()
+
+        self.deploy_dir_unlock()
+
+        if result is not None:
+            msger.error(result)
+
+    def remove_packaging_data(self):
+        remove(self.opkg_dir, True)
+        # create the directory back, it's needed by PM lock
+        mkdirhier(self.opkg_dir)
+
+    def list_installed(self, format=None):
+        return WicOpkgPkgsList(self.d, self.target_rootfs, self.config_file).list(format)
+
+    def handle_bad_recommendations(self):
+        if self.d.has_key("BAD_RECOMMENDATIONS"):
+            bad_recommendations = self.d["BAD_RECOMMENDATIONS"]
+        else:
+            bad_recommendations = ""
+
+        if bad_recommendations.strip() == "":
+            return
+
+        status_file = os.path.join(self.opkg_dir, "status")
+
+        # If status file existed, it means the bad recommendations has already
+        # been handled
+        if os.path.exists(status_file):
+            return
+
+        cmd = "%s %s info " % (self.opkg_cmd, self.opkg_args)
+
+        with open(status_file, "w+") as status:
+            for pkg in bad_recommendations.split():
+                pkg_info = cmd + pkg
+
+                try:
+                    output = subprocess.check_output(pkg_info.split(), stderr=subprocess.STDOUT).strip()
+                except subprocess.CalledProcessError as e:
+                    msger.error("Cannot get package info. Command '%s' "
+                             "returned %d:\n%s" % (pkg_info, e.returncode, e.output))
+
+                if output == "":
+                    msger.info("Ignored bad recommendation: '%s' is "
+                            "not a package" % pkg)
+                    continue
+
+                for line in output.split('\n'):
+                    if line.startswith("Status:"):
+                        status.write("Status: deinstall hold not-installed\n")
+                    else:
+                        status.write(line + "\n")
+
+    '''
+    The following function dummy installs pkgs and returns the log of output.
+    '''
+    def dummy_install(self, pkgs):
+        if len(pkgs) == 0:
+            return
+
+        # Create an temp dir as opkg root for dummy installation
+        if self.d.has_key("TMPDIR"):
+            tmp_dir = self.d["TMPDIR"]
+        else:
+            tmp_dir = "."
+            msger.warning("No TMPDIR provided!")
+
+        temp_rootfs = '%s/opkg' % tmp_dir
+        temp_opkg_dir = os.path.join(temp_rootfs, 'var/lib/opkg')
+        mkdirhier(temp_opkg_dir)
+
+        opkg_args = "-f %s -o %s " % (self.config_file, temp_rootfs)
+        if self.d.has_key("OPKG_ARGS"):
+            opkg_args += self.d["OPKG_ARGS"]
+
+        cmd = "%s %s update" % (self.opkg_cmd, opkg_args)
+        try:
+            subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
+        except subprocess.CalledProcessError as e:
+            msger.error("Unable to update. Command '%s' "
+                     "returned %d:\n%s" % (cmd, e.returncode, e.output))
+
+        # Dummy installation
+        cmd = "%s %s --noaction install %s " % (self.opkg_cmd,
+                                                opkg_args,
+                                                ' '.join(pkgs))
+        try:
+            output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
+        except subprocess.CalledProcessError as e:
+            msger.error("Unable to dummy install packages. Command '%s' "
+                     "returned %d:\n%s" % (cmd, e.returncode, e.output))
+
+        remove(temp_rootfs, True)
+
+        return output
+
+    def backup_packaging_data(self):
+        # Save the opkglib for increment ipk image generation
+        if os.path.exists(self.saved_opkg_dir):
+            remove(self.saved_opkg_dir, True)
+        shutil.copytree(self.opkg_dir,
+                        self.saved_opkg_dir,
+                        symlinks=True)
+
+    def recover_packaging_data(self):
+        # Move the opkglib back
+        if os.path.exists(self.saved_opkg_dir):
+            if os.path.exists(self.opkg_dir):
+                remove(self.opkg_dir, True)
+
+            msger.info('Recover packaging data')
+            shutil.copytree(self.saved_opkg_dir,
+                            self.opkg_dir,
+                            symlinks=True)
+
+
+def wic_generate_index_files(d):
+    if d.has_key('PACKAGE_CLASSES'):
+        classes = d['PACKAGE_CLASSES'].replace("package_", "").split()
+    else:
+        classes = ""
+        msger.warning("No PACKAGE_CLASSES provided!")
+
+    if d.has_key('DEPLOY_DIR_IPK'):
+        deploy_dir_ipk = d['DEPLOY_DIR_IPK']
+    else:
+        deploy_dir_ipk = None
+        msger.warning("No DEPLOY_DIR_IPK provided!")
+
+    indexer_map = {
+        "ipk": (WicOpkgIndexer, deploy_dir_ipk)
+    }
+
+    result = None
+
+    for pkg_class in classes:
+        if not pkg_class in indexer_map:
+            continue
+
+        if os.path.exists(indexer_map[pkg_class][1]):
+            result = indexer_map[pkg_class][0](d, indexer_map[pkg_class][1]).write_index()
+
+            if result is not None:
+                msger.error(result)
diff --git a/scripts/wic b/scripts/wic
index 2d3fd09..b9c8756 100755
--- a/scripts/wic
+++ b/scripts/wic
@@ -99,6 +99,10 @@ def wic_create_subcommand(args, usage_str):
 
     (options, args) = parser.parse_args(args)
 
+    if options.debug:
+        loglevel = logging.DEBUG
+        start_logging(loglevel)
+
     if len(args) != 1:
         logging.error("Wrong number of arguments, exiting\n")
         parser.print_help()
@@ -107,9 +111,11 @@ def wic_create_subcommand(args, usage_str):
     if not options.image_name and not (options.rootfs_dir and
                                        options.bootimg_dir and
                                        options.kernel_dir and
+                                       options.native_sysroot or
                                        options.native_sysroot):
         print "Build artifacts not completely specified, exiting."
-        print "  (Use 'wic -e' or 'wic -r -b -k -n' to specify artifacts)"
+        print "  (Use 'wic -e' or 'wic -r -b -k -n' or 'wic -r -n' to specify artifacts)"
+        print options
         sys.exit(1)
 
     if not options.image_name:
@@ -125,13 +131,16 @@ def wic_create_subcommand(args, usage_str):
 
     print "Creating image(s)...\n"
 
-    bitbake_env_lines = find_bitbake_env_lines(options.image_name)
-    if not bitbake_env_lines:
-        print "Couldn't get bitbake environment, exiting."
-        sys.exit(1)
-    set_bitbake_env_lines(bitbake_env_lines)
+    # If '-e' option is used the values are extracted from bitbake env.
+    if options.image_name:
+        bitbake_env_lines = find_bitbake_env_lines(options.image_name)
+        if not bitbake_env_lines:
+            print "Couldn't get bitbake environment, exiting."
+            sys.exit(1)
+        set_bitbake_env_lines(bitbake_env_lines)
 
     bootimg_dir = staging_data_dir = hdddir = ""
+    rootfs_dir = native_sysroot = kernel_dir = image_output_dir = ""
 
     if options.image_name:
         (rootfs_dir, kernel_dir, hdddir, staging_data_dir, native_sysroot) = \
@@ -140,34 +149,65 @@ def wic_create_subcommand(args, usage_str):
     wks_file = args[0]
 
     if not wks_file.endswith(".wks"):
+        # Return full path of the .wks file
         wks_file = find_canned_image(scripts_path, wks_file)
         if not wks_file:
-            print "No image named %s found, exiting.  (Use 'wic list images' to list available images, or specify a fully-qualified OE kickstart (.wks) filename)\n" % wks_file
+            print "No image named %s found, exiting.\n" % wks_file
+            print "(Use 'wic list images' to list available images, or specify a fully-qualified OE kickstart (.wks) filename)\n"
             sys.exit(1)
 
-    image_output_dir = ""
     if options.outdir:
         image_output_dir = options.outdir
 
-    if not options.image_name:
-        rootfs_dir = ''
-        if 'ROOTFS_DIR' in options.rootfs_dir:
-            rootfs_dir = options.rootfs_dir['ROOTFS_DIR']
-        bootimg_dir = options.bootimg_dir
-        kernel_dir = options.kernel_dir
+    if options.native_sysroot:
         native_sysroot = options.native_sysroot
-        if rootfs_dir and not os.path.isdir(rootfs_dir):
-            print "--roofs-dir (-r) not found, exiting\n"
-            sys.exit(1)
-        if not os.path.isdir(bootimg_dir):
-            print "--bootimg-dir (-b) not found, exiting\n"
-            sys.exit(1)
-        if not os.path.isdir(kernel_dir):
-            print "--kernel-dir (-k) not found, exiting\n"
-            sys.exit(1)
+        print "Using native_sysroot from user command: %s" % native_sysroot
+
         if not os.path.isdir(native_sysroot):
-            print "--native-sysroot (-n) not found, exiting\n"
+            print "--native-sysroot (-n) not found, exiting"
             sys.exit(1)
+
+        native_sysroot = os.path.abspath(native_sysroot)
+
+    if not options.image_name:
+        if (options.bootimg_dir and options.kernel_dir and
+             options.rootfs_dir and options.native_sysroot):
+            rootfs_dir = ''
+            if 'ROOTFS_DIR' in options.rootfs_dir:
+                rootfs_dir = options.rootfs_dir['ROOTFS_DIR']
+            bootimg_dir = options.bootimg_dir
+            kernel_dir = options.kernel_dir
+            native_sysroot = options.native_sysroot
+
+            if rootfs_dir and not os.path.isdir(rootfs_dir):
+                print "--roofs-dir (-r) not found, exiting\n"
+                sys.exit(1)
+            if not os.path.isdir(bootimg_dir):
+                print "--bootimg-dir (-b) not found, exiting\n"
+                sys.exit(1)
+            if not os.path.isdir(kernel_dir):
+                print "--kernel-dir (-k) not found, exiting\n"
+                sys.exit(1)
+            if not os.path.isdir(native_sysroot):
+                print "--native-sysroot (-n) not found, exiting\n"
+                sys.exit(1)
+        else:
+            print 'Build image from rootfs and a package list using native rootfs\n'
+            if options.rootfs_dir and 'ROOTFS_DIR' in options.rootfs_dir:
+                rootfs_dir = options.rootfs_dir['ROOTFS_DIR']
+            elif options.rootfs_dir:
+                rootfs_dir = options.rootfs_dir
+            else:
+                rootfs_dir = ""
+
+            native_sysroot = options.native_sysroot
+
+            if rootfs_dir and not os.path.isdir(rootfs_dir):
+                print "--roofs-dir (-r) not found, exiting\n"
+                sys.exit(1)
+            if not os.path.isdir(native_sysroot):
+                print "--native-sysroot (-n) not found, exiting\n"
+                sys.exit(1)
     else:
         not_found = not_found_dir = ""
         if not os.path.isdir(rootfs_dir):
-- 
1.7.10.4




More information about the Openembedded-core mailing list