public inbox for isar-users@googlegroups.com
 help / color / mirror / Atom feed
From: Henning Schild <henning.schild@siemens.com>
To: Alexander Smirnov <asmirnov@ilbers.de>
Cc: <isar-users@googlegroups.com>, Frank Lenormand <lenormf@gmail.com>
Subject: Re: [PATCH 1/6] meta-isar-bin: Enable caching of deb packages
Date: Wed, 6 Sep 2017 16:21:40 +0200	[thread overview]
Message-ID: <20170906162140.50ce9210@md1em3qc> (raw)
In-Reply-To: <20170827151339.12806-2-asmirnov@ilbers.de>

Am Sun, 27 Aug 2017 18:13:34 +0300
schrieb Alexander Smirnov <asmirnov@ilbers.de>:

> From: Frank Lenormand <lenormf@gmail.com>
> 
> Adds the possibility to create an apt repository for newly built
> packages and then use that repo for populating the target filesystem.
> 
> Signed-off-by: Frank Lenormand <lenormf@gmail.com>
> Signed-off-by: Alexander Smirnov <asmirnov@ilbers.de>
> ---
>  meta-isar-bin/conf/layer.conf                      |  18 +++
>  meta-isar-bin/files/distributions.in               |   3 +
>  meta-isar/conf/bblayers.conf.sample                |   1 +
>  .../images/files/debian-configscript.sh            |   8 ++
>  .../images/files/raspbian-configscript.sh          |   8 ++
>  meta-isar/recipes-core/images/isar-image-base.bb   |   8 +-
>  meta/classes/dpkg.bbclass                          | 126
> +++++++++++++++++++--
> meta/classes/image.bbclass                         |  30 +++-- 8
> files changed, 186 insertions(+), 16 deletions(-) create mode 100644
> meta-isar-bin/conf/layer.conf create mode 100644
> meta-isar-bin/files/distributions.in
> 
> diff --git a/meta-isar-bin/conf/layer.conf
> b/meta-isar-bin/conf/layer.conf new file mode 100644
> index 0000000..24534e1
> --- /dev/null
> +++ b/meta-isar-bin/conf/layer.conf
> @@ -0,0 +1,18 @@
> +# Enable package caching with '1'
> +DEBCACHE_ENABLED ?= "0"
> +
> +# Codename of the repository created by the caching class
> +DEBDISTRONAME = "isar"
> +
> +# Path to the caching repository
> +DEBCACHEDIR ?= "${LAYERDIR}/apt"
> +
> +# Path to the mount point of the repository within the target
> rootfs, during +# population
> +DEBCACHEMNT ?= "/opt/cache/apt"
> +
> +# Path to the databases used by `reprepro`
> +DEBDBDIR ?= "${LAYERDIR}/db"
> +
> +# Path to the configuration files templates used by `reprepro`
> +DEBFILESDIR ?= "${LAYERDIR}/files"
> diff --git a/meta-isar-bin/files/distributions.in
> b/meta-isar-bin/files/distributions.in new file mode 100644
> index 0000000..59f4429
> --- /dev/null
> +++ b/meta-isar-bin/files/distributions.in
> @@ -0,0 +1,3 @@
> +Codename: {DISTRO_NAME}
> +Architectures: i386 armhf source
> +Components: main
> diff --git a/meta-isar/conf/bblayers.conf.sample
> b/meta-isar/conf/bblayers.conf.sample index 80867e7..53a362b 100644
> --- a/meta-isar/conf/bblayers.conf.sample
> +++ b/meta-isar/conf/bblayers.conf.sample
> @@ -8,6 +8,7 @@ BBFILES ?= ""
>  BBLAYERS ?= " \
>    ##ISARROOT##/meta \
>    ##ISARROOT##/meta-isar \
> +  ##ISARROOT##/meta-isar-bin \
>    "
>  BBLAYERS_NON_REMOVABLE ?= " \
>    ##ISARROOT##/meta \
> diff --git
> a/meta-isar/recipes-core/images/files/debian-configscript.sh
> b/meta-isar/recipes-core/images/files/debian-configscript.sh index
> 4ac37d0..b05babb 100644 ---
> a/meta-isar/recipes-core/images/files/debian-configscript.sh +++
> b/meta-isar/recipes-core/images/files/debian-configscript.sh @@ -8,6
> +8,8 @@ set -e readonly MACHINE_SERIAL="$1" readonly BAUDRATE_TTY="$2"
>  readonly ROOTFS_DEV="$3"
> +readonly DEBCACHEMNT="$4"
> +readonly DEBDISTRONAME="$5"
>  
>  cat >> /etc/default/locale << EOF
>  LANG=en_US.UTF-8
> @@ -83,3 +85,9 @@ fi
>  if [ -x "$TARGET/sbin/init" -a -x "$TARGET/usr/sbin/policy-rc.d" ];
> then rm -f $TARGET/usr/sbin/policy-rc.d
>  fi
> +
> +mkdir -p /etc/apt/sources.list.d/
> +cat <<EOF >/etc/apt/sources.list.d/${DEBDISTRONAME}.list
> +deb file:${DEBCACHEMNT}/ ${DEBDISTRONAME} main
> +deb-src file:${DEBCACHEMNT}/ ${DEBDISTRONAME} main
> +EOF
> diff --git
> a/meta-isar/recipes-core/images/files/raspbian-configscript.sh
> b/meta-isar/recipes-core/images/files/raspbian-configscript.sh index
> 2454481..f995f4d 100644 ---
> a/meta-isar/recipes-core/images/files/raspbian-configscript.sh +++
> b/meta-isar/recipes-core/images/files/raspbian-configscript.sh @@
> -8,6 +8,8 @@ set -e readonly MACHINE_SERIAL="$1" readonly
> BAUDRATE_TTY="$2" readonly ROOTFS_DEV="$3"
> +readonly DEBCACHEMNT="$4"
> +readonly DEBDISTRONAME="$5"
>  
>  cat >> /etc/default/locale << EOF
>  LANG=en_US.UTF-8
> @@ -89,3 +91,9 @@ KERNEL_IMAGE=`ls /boot | grep vmlinuz`
>  cat > /boot/config.txt << EOF
>  kernel=$KERNEL_IMAGE
>  EOF
> +
> +mkdir -p /etc/apt/sources.list.d/
> +cat <<EOF >/etc/apt/sources.list.d/${DEBDISTRONAME}.list
> +deb file:${DEBCACHEMNT}/ ${DEBDISTRONAME} main
> +deb-src file:${DEBCACHEMNT}/ ${DEBDISTRONAME} main
> +EOF
> diff --git a/meta-isar/recipes-core/images/isar-image-base.bb
> b/meta-isar/recipes-core/images/isar-image-base.bb index
> b679d97..85e6b27 100644 ---
> a/meta-isar/recipes-core/images/isar-image-base.bb +++
> b/meta-isar/recipes-core/images/isar-image-base.bb @@ -49,8 +49,12 @@
> do_rootfs() { sudo multistrap -a ${DISTRO_ARCH} -d "${S}" -f
> "${WORKDIR}/multistrap.conf" || true 
>      # Configure root filesystem
> -    sudo chroot ${S} /configscript.sh ${MACHINE_SERIAL}
> ${BAUDRATE_TTY} \
> -        ${ROOTFS_DEV}
> +    sudo chroot ${S} /configscript.sh \
> +        ${MACHINE_SERIAL}             \
> +        ${BAUDRATE_TTY}               \
> +        ${ROOTFS_DEV}                 \
> +        ${DEBCACHEMNT}                \
> +        ${DEBDISTRONAME}
>      sudo rm ${S}/configscript.sh
>  }
>  
> diff --git a/meta/classes/dpkg.bbclass b/meta/classes/dpkg.bbclass
> index 360a95c..b1e201d 100644
> --- a/meta/classes/dpkg.bbclass
> +++ b/meta/classes/dpkg.bbclass
> @@ -1,5 +1,5 @@
>  # This software is a part of ISAR.
> -# Copyright (C) 2015-2016 ilbers GmbH
> +# Copyright (C) 2015-2017 ilbers GmbH
>  
>  # Add dependency from buildchroot creation
>  DEPENDS += "buildchroot"
> @@ -55,12 +55,124 @@ do_build() {
>      sudo chroot ${BUILDCHROOT_DIR} /build.sh ${PP}/${SRC_DIR}
>  }
>  
> -
>  # Install package to dedicated deploy directory
> -do_install() {
> -    install -m 644 ${BUILDROOT}/*.deb ${DEPLOY_DIR_DEB}/
> +do_binary_deb_install() {
> +    readonly DIR_CACHE="${DEBCACHEDIR}/${DISTRO}"
> +    readonly DIR_DB="${DEBDBDIR}/${DISTRO}"
> +
> +    if [ "${DEBCACHE_ENABLED}" != "0" ]; then
> +        # If `bitbake` is running for the first time, the cache
> doesn't exist
> +        # yet and needs to be configured using a `distributions`
> file.
> +        # A template stored in the layer directory is pre-processed
> to
> +        # generate the configuration file, which is then placed in
> the
> +        # appropriate directory.
> +        if [ ! -e "${DIR_CACHE}/conf/distributions" ]; then
> +            mkdir -p "${DIR_CACHE}/conf"
> +            sed -e "s#{DISTRO_NAME}#${DEBDISTRONAME}#g" \
> +                "${DEBFILESDIR}/distributions.in" \
> +                > "${DIR_CACHE}/conf/distributions"
> +        fi
> +
> +        # Add binary and source packages to the deb cache
> +        # If the cache doesn't exist yet, it will be created using
> the
> +        # `distributions` file generated above.
> +        ls -1 "${BUILDROOT}"/*.deb "${BUILDROOT}"/*.dsc | while read
> -r p; do
> +            reprepro --waitforlock 3 -b "${DIR_CACHE}" --dbdir
> "${DIR_DB}" \
> +                -C main "include${p##*.}" "${DEBDISTRONAME}" "${p}"
> +        done
> +    else
> +        # Deb caching is disabled, simply copy all binary packages
> to the
> +        # deploy directory
> +        mkdir -p "${DEPLOY_DIR_DEB}"
> +        install -m 644 "${BUILDROOT}"/*.deb "${DEPLOY_DIR_DEB}/"
> +    fi
>  }
>  
> -addtask install after do_build
> -do_install[dirs] = "${DEPLOY_DIR_DEB}"
> -do_install[stamp-extra-info] = "${MACHINE}"
> +addtask binary_deb_install after do_build
> +do_binary_deb_install[stamp-extra-info] = "${DISTRO}-${DISTRO_ARCH}"
> +
> +# Deb caching lambda run during the parsing phase that checks
> whether the +# current package has to be rebuilt, or taken from the
> cache +python __anonymous () {
> +    if d.getVar("DEBCACHE_ENABLED", True) == "0":
> +        # Deb caching is disabled, do nothing
> +        return True
> +
> +    PN = d.getVar("PN", True)
> +    PV = d.getVar("PV", True)
> +    DISTRO_ARCH = d.getVar("DISTRO_ARCH", True)
> +    DEBCACHEDIR = d.getVar("DEBCACHEDIR", True)
> +    DEBDISTRONAME = d.getVar("DEBDISTRONAME", True)
> +    DEBDBDIR = d.getVar("DEBDBDIR", True)
> +    DISTRO = d.getVar("DISTRO", True)
> +    path_cache = os.path.join(DEBCACHEDIR, DISTRO)
> +    path_databases = os.path.join(DEBDBDIR, DISTRO)
> +    path_distributions = os.path.join(path_cache, "conf",
> "distributions") +
> +    # The distributions file is needed by `reprepro` to know what
> types
> +    # of packages are supported, what the distribution name is, etc.
> +    # If it doesn't exist, we have nothing in the cache, do nothing.
> +    if not os.path.exists(path_distributions):
> +        return
> +
> +    # Anonymous functions are run several times under different
> contexts
> +    # during the parsing phase, which would let the code that
> follows be run
> +    # as many times for the same package.
> +    # In order to guarantee that our subroutine only runs once per
> package, we
> +    # use bitbake's "persist" API in order to have reliable
> persistent storage
> +    # accross calls of the lambda (using a simple variable in the
> class won't
> +    # work, as several contexts won't allow fetching its value).
> +    pd = bb.persist_data.persist("DEBCACHE_PACKAGES", d)
> +    if PN in pd and pd[PN] == PV:
> +        return
> +
> +    import subprocess
> +    try:
> +        # The databases used by `reprepro` are not stored within the
> cache in
> +        # order to make versioning of only the files needed to use
> the cache
> +        # as an official Debian repository simpler.
> +        # As such, if a developer uses a peer's cache to speed up
> their build
> +        # time but have never run bitbake, the database will not
> have been
> +        # created, so we regenerate them here.
> +        if not os.path.exists(path_databases) and
> os.path.exists(path_cache):
> +            bb.note("Regenerating the cache databases...")
> +            subprocess.check_call([
> +                "reprepro",
> +                "--waitforlock", "3",
> +                "-b", path_cache,
> +                "--dbdir", path_databases,
> +                "export", DEBDISTRONAME,
> +            ])
> +
> +        # Get a list of the versions of all the packages named after
> the
> +        # current bitbake package, and check whether the current
> package
> +        # version is returned.
> +        # As `reprepro` always returns zero with this particular
> operation, we
> +        # have to use this workaround to check for a package in the
> cache.
> +        package_version = subprocess.check_output([
> +            "reprepro",
> +            "--waitforlock", "3",
> +            "-b", path_cache,
> +            "--dbdir", path_databases,
> +            "-C", "main",
> +            "-A", DISTRO_ARCH,
> +            "--list-format", "${version}",
> +            "list", DEBDISTRONAME, PN,
> +        ])
> +        package_version = package_version.decode("utf-8")
> +        if package_version == PV:
> +            # The below list contains the names of all the tasks are
> in charge
> +            # of building the package when the cache isn't enabled
> or if the
> +            # package hasn't been placed in it already.
> +            # As all tasks are enabled by default, we prevent their
> execution
> +            # by setting the `noexec` flag, which will prevent a
> rebuild of
> +            # the package when it's cached.
> +            for task in ["fetch", "unpack", "build", "install"]:
> +                d.setVarFlag("do_{}".format(task), "noexec", "1")
> +
> +            # Cache the results of this command so that subsequent
> executions
> +            # of this anonymous functions don't run the same code
> again.
> +            pd[PN] = PV
> +    except subprocess.CalledProcessError as e:
> +        bb.fatal("Unable to check for a candidate for package {0}
> (errorcode: {1})".format(PN, e.returncode)) +}
> diff --git a/meta/classes/image.bbclass b/meta/classes/image.bbclass
> index a7f0d74..f42aa48 100644
> --- a/meta/classes/image.bbclass
> +++ b/meta/classes/image.bbclass
> @@ -11,18 +11,34 @@ inherit ${IMAGE_TYPE}
>  
>  do_populate[stamp-extra-info] = "${MACHINE}-${DISTRO}"
>  
> -# Install Debian packages, that were built from sources
> +# Install Debian packages from the cache
>  do_populate() {
> +    readonly DIR_CACHE="${DEBCACHEDIR}/${DISTRO}"
> +
>      if [ -n "${IMAGE_INSTALL}" ]; then
> -        sudo mkdir -p ${S}/deb
> +        if [ "${DEBCACHE_ENABLED}" != "0" ]; then
> +            sudo mkdir -p "${S}/${DEBCACHEMNT}"
> +            sudo mount -o bind "${DIR_CACHE}" "${S}/${DEBCACHEMNT}"
> +
> +            sudo chroot "${S}" apt-get update -y
> +            for package in ${IMAGE_INSTALL}; do
> +                sudo chroot "${S}" apt-get install -t
> "${DEBDISTRONAME}" -y \
> +                    --allow-unauthenticated "${package}"

With do_populate using apt-get we now how to deal with package-name
collisions. We could have the case where our repo contains a package
that already exists in upstream debian.

We will have to decide how to deal with them if they occur. My
suggestions/first idea would be:
 - a recipe that creates a .deb with a taken package name fails by
   default -EBUSY
 - a recipe can set a variable to force itself in "OVERRIDE_DEBIAN =
   True"
 - a recipe that overrides the upstream package has to "win", we need
   a proper apt Pin-Priority

Henning

> +            done
> +
> +            sudo umount "${S}/${DEBCACHEMNT}"
> +        else
> +            sudo mkdir -p ${S}/deb
>  
> -        for p in ${IMAGE_INSTALL}; do
> -            sudo cp ${DEPLOY_DIR_DEB}/${p}_*.deb ${S}/deb
> -        done
> +            for p in ${IMAGE_INSTALL}; do
> +                find "${DEPLOY_DIR_DEB}" -type f -name '*.deb' -exec
> \
> +                    sudo cp '{}' "${S}/deb/" \;
> +            done
>  
> -        sudo chroot ${S} /usr/bin/dpkg -i -R /deb
> +            sudo chroot ${S} /usr/bin/dpkg -i -R /deb
>  
> -        sudo rm -rf ${S}/deb
> +            sudo rm -rf ${S}/deb
> +        fi
>      fi
>  }
>  


  parent reply	other threads:[~2017-09-06 14:21 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-08-27 15:13 [PATCH 0/6] Isar apt cache implementation Alexander Smirnov
2017-08-27 15:13 ` [PATCH 1/6] meta-isar-bin: Enable caching of deb packages Alexander Smirnov
2017-08-28 15:18   ` Henning Schild
2017-08-29  6:40     ` Alexander Smirnov
2017-08-29  7:51       ` Henning Schild
2017-08-29  8:20         ` Alexander Smirnov
2017-08-31 10:55     ` Claudius Heine
2017-08-31 11:20       ` Henning Schild
2017-08-31 12:08         ` Claudius Heine
2017-09-06 14:21   ` Henning Schild [this message]
2017-09-07 11:13     ` Claudius Heine
2017-08-27 15:13 ` [PATCH 2/6] classes/image: Provide /dev/null for Stretch apt Alexander Smirnov
2017-08-28 15:20   ` Henning Schild
2017-08-28 15:26     ` Henning Schild
2017-08-27 15:13 ` [PATCH 3/6] classes/dpkg: Split install for cache Alexander Smirnov
2017-08-28  8:00   ` Claudius Heine
2017-08-29  7:18     ` Alexander Smirnov
2017-08-30  8:54       ` Claudius Heine
2017-08-28 15:30   ` Henning Schild
2017-08-27 15:13 ` [PATCH 4/6] meta-isar-bin: Enable apt repo generation for amd64 Alexander Smirnov
2017-08-27 15:13 ` [PATCH 5/6] classes/dpkg: Properly update packages in the cache Alexander Smirnov
2017-08-28 15:32   ` Henning Schild
2017-08-29  7:20     ` Alexander Smirnov
2017-08-29  7:57       ` Henning Schild
2017-08-29 11:26         ` Jan Kiszka
2017-08-27 15:13 ` [PATCH 6/6] doc/technical_overview: Describe binary cache Alexander Smirnov
2017-08-28 15:36   ` Henning Schild
2017-08-29  7:29     ` Alexander Smirnov
2017-08-29  8:06       ` Henning Schild
2017-08-29 11:29     ` Jan Kiszka

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20170906162140.50ce9210@md1em3qc \
    --to=henning.schild@siemens.com \
    --cc=asmirnov@ilbers.de \
    --cc=isar-users@googlegroups.com \
    --cc=lenormf@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox