Update-alternatives not placed in snap

numpy is used within my snap and is no longer installing all of the components the same way with the update to snapcraft.

I tracked it to the libblas3 package, which uses update-alternatives to place the symlink /usr/lib/libblas.so.3 to /etc/alternatives/libblas.so.3. This file is no longer being packaged into the snap causing numpy, and any code depending on it, to not run when run from within the snap.

Below is the postinst from the libblas3_3.6.0-2ubuntu2_amd64.deb file

#! /bin/sh

set -e

update-alternatives --install /usr/lib/libblas.so.3 libblas.so.3 /usr/lib/libblas/libblas.so.3 10

# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.



exit 0

An example snapcraft.yaml

name: update-alts
version: '0.1' # just for humans, typically '1.2+git' or '1.3.2'
summary: snap for the update-alternatives issue demo
description: |
  Demo snapcraft.yaml to show update-alternatives issue.

grade: devel # must be 'stable' to release into candidate/stable channels
confinement: devmode

parts:
  bar-base:
    plugin: catkin-tools
    rosdistro: kinetic
    # source: catkin_ws
    stage-packages: [
      libblas3
    ]

apps:
  bash:
    command: bash

It looks like the issue is the way update-alternatives uses a hard path to /usr/lib/libblas.so.3 without a prefix allowing for installation in an alternate location.

It would be simple enough to add another part or organize the files to fix this in the short term. But I’m wondering what the correct long term fix and just making sure this is known and that other snaps could be affected since it used to work and still does in older versions of snapcraft.

Should snapcraft go back to handling this, does the libblass3 package need to be modified to allow for installation in other locations, or should I add a part that places these missing files in the correct location after the part handling their install is done?

Postinst scripts for stage-packages are in general not executed (if you would do cross builds it would require that you can actually execute whatever the postinst does (adding users, setting alternatives, you name it)) if you think the missing link is really the issue here you need to create it yourself from snapcraft.yaml

For a strict confined snap, you won’t be able to update symlinks in /etc, so update-alternatives won’t help. What you probably want to do is bake the correct symlink into the snap’s squashfs file system so that it isn’t necessary to fix anything up at runtime.

It might be easiest to do this by adding an install: stanza to your part that creates the appropriate symlink. I haven’t tested it, but the following might do the trick:

install: |
  set -ex
  for dir in $SNAPCRAFT_PART_INSTALL/usr/lib/*/; do
    (cd $dir;
     if [ -f blas/libblas.so.3.* ]; then
       ln -s blas/libblas.so.3.* libblas.so.3
     fi)
  done

This could be simplified a bit if you don’t mind your snapcraft.yaml hard coding the architecture in the ln -s invocation.

I’m certain the missing link is at least one of the issues. The stack traces explicitly mention that file as missing which is only placed through the postinst script.

Adding the link in the install scriplet as you and Jamesh said is what I did after posting, I was just wanting to say this was an issue that makes snapcraft less user friendly and have something posted in the forum in case other people are experiencing this issue. I know supporting anything and everything a package could do in a post install script would be very difficult and maybe not worth the effort. And I don’t know the demand for cross platform builds and the weight that might have against ease of use.

I do want to suggest it could be a good idea to have the option on same platform builds to run postinst scripts, especially considering that they seem to have worked in the past. For my purpose I am building a snap to run on specific hardware that is the same platform. I don’t know how many people are in the same boat, but for me the frustration of determining why my code doesn’t work when packaged in a snap is much greater than the convenience of building it cross platform.

Yeah, I don’t need to update the symlinks. The issue is just that it doesn’t exist at all when building the snap. Thanks for the script, that solution helps.

They were never run by snapcraft … IIRC snapcraft only runs the equivalent of dpkg -x <deb> since day one … and you really dont want it to execute things like adduser for some package only here for your snap (for something like update-alternatives you would also have to pull this into your snap … along with all its posible dependencies and you would have to patch it on the fly to get the paths right)…

You’re right. I didn’t check what the exact mechanism is that was placing the link in that location, but I do know that the link was placed in 2.35 and stopped being placed when I updated the snapcraft package.

You’re also right about not wanting packages to do things like adding users. I don’t like the idea of having to find and fixup packages when they don’t work as expected (especially when it previously “worked”), but it seems like the best way to go. It seems rare enough to not be a huge issue, but it is an inconvenience.

Thanks for the explanation.

sounds actually more like a regression … @sergiusens ?

I agree, but you make it sound like a regression in the right direction.

Just to give more information. Snapcraft version 2.39.3 caused this to fail, version 2.39.3+really2.35 is working as 2.35 did.

I did some more debugging with @kytrofa on this and found that is was not related to update-alternatives at all. The recent ROS package updates allowed me to see this. The ROS package updates do not use symlinks or update-alternatives to places $SNAP/usr/lib/libblas.so.3. The file does still exist on version 2.39.3+really2.35, while it does not exist on version 2.39.2.

An example can be found using this snapcraft.yaml

name: libdif
version: '0.1'
summary: snap for the lib dif
description: |
  Snap to examine library difference

grade: devel # must be 'stable' to release into candidate/stable channels
confinement: devmode

parts:
  bar-base:
    plugin: nil
    stage-packages: [
      ros-kinetic-laser-scan-matcher,
    ]

I talked to @kyrofa about this a few weeks ago and this is what I’m seeing now.

On this snapcraft.yaml it is really easy to see the issue.

name: libblas-error
version: '0.1' # just for humans, typically '1.2+git' or '1.3.2'

summary: snap for the libblas error
description: |
  This snap displays libblas issue

grade: devel # must be 'stable' to release into candidate/stable channels
confinement: devmode

parts:
  atlas:
    plugin: nil
    stage-packages: [
    libatlas3-base,
    ]

In the prime directories there are differences with the same snapcraft.yaml depending on which version of snapcraft you use to build with.

With version 2.35/2.36 you get.

prime/
├── command-bash.wrapper
├── meta
│   ├── gui
│   ├── hooks
│   │   ├── configure
│   │   └── install
│   └── snap.yaml
├── snap
│   └── hooks
│       ├── configure
│       └── install
└── usr
    ├── lib
    │   ├── atlas-base
    │   │   ├── atlas
    │   │   │   ├── libblas.so.3 -> libblas.so.3.0
    │   │   │   ├── libblas.so.3.0
    │   │   │   ├── liblapack.so.3 -> liblapack.so.3.0
    │   │   │   └── liblapack.so.3.0
    │   │   ├── libatlas.so.3 -> libatlas.so.3.0
    │   │   ├── libatlas.so.3.0
    │   │   ├── libcblas.so.3 -> libcblas.so.3.0
    │   │   ├── libcblas.so.3.0
    │   │   ├── libf77blas.so.3 -> libf77blas.so.3.0
    │   │   ├── libf77blas.so.3.0
    │   │   ├── liblapack_atlas.so.3 -> liblapack_atlas.so.3.0
    │   │   └── liblapack_atlas.so.3.0
    │   ├── gcc
    │   │   └── x86_64-linux-gnu
    │   │       ├── 6
    │   │       └── 6.0.0 -> 6
    │   ├── libatlas.so.3 -> atlas-base/libatlas.so.3
    │   ├── libblas.so.3
    │   ├── libcblas.so.3 -> atlas-base/libcblas.so.3
    │   ├── libf77blas.so.3 -> atlas-base/libf77blas.so.3
    │   ├── liblapack_atlas.so.3 -> atlas-base/liblapack_atlas.so.3
    │   └── x86_64-linux-gnu
    │       ├── libgfortran.so.3 -> libgfortran.so.3.0.0
    │       ├── libgfortran.so.3.0.0
    │       ├── libquadmath.so.0 -> libquadmath.so.0.0.0
    │       └── libquadmath.so.0.0.0
    └── share
        └── doc
            ├── gcc-6-base
            │   ├── changelog.Debian.gz
            │   ├── copyright
            │   ├── README.Debian.amd64.gz
            │   └── TODO.Debian
            ├── libatlas3-base
            │   ├── changelog.Debian.gz
            │   ├── copyright
            │   ├── README
            │   ├── README.Debian
            │   └── TODO.Debian
            ├── libblas-common
            │   ├── changelog.Debian.gz
            │   └── copyright
            ├── libgfortran3 -> gcc-5-base
            └── libquadmath0 -> gcc-5-base

and more recent versions return this:

prime
├── command-bash.wrapper
├── meta
│   ├── gui
│   ├── hooks
│   │   ├── configure
│   │   └── install
│   └── snap.yaml
├── snap
│   └── hooks
│       ├── configure
│       └── install
└── usr
    ├── lib
    │   ├── atlas-base
    │   │   ├── atlas
    │   │   │   ├── libblas.so.3 -> libblas.so.3.0
    │   │   │   ├── libblas.so.3.0
    │   │   │   ├── liblapack.so.3 -> liblapack.so.3.0
    │   │   │   └── liblapack.so.3.0
    │   │   ├── libatlas.so.3 -> libatlas.so.3.0
    │   │   ├── libatlas.so.3.0
    │   │   ├── libcblas.so.3 -> libcblas.so.3.0
    │   │   ├── libcblas.so.3.0
    │   │   ├── libf77blas.so.3 -> libf77blas.so.3.0
    │   │   ├── libf77blas.so.3.0
    │   │   ├── liblapack_atlas.so.3 -> liblapack_atlas.so.3.0
    │   │   └── liblapack_atlas.so.3.0
    │   ├── gcc
    │   │   └── x86_64-linux-gnu
    │   │       ├── 6
    │   │       └── 6.0.0 -> 6
    │   ├── libatlas.so.3 -> atlas-base/libatlas.so.3
    │   ├── libcblas.so.3 -> atlas-base/libcblas.so.3
    │   ├── libf77blas.so.3 -> atlas-base/libf77blas.so.3
    │   ├── liblapack_atlas.so.3 -> atlas-base/liblapack_atlas.so.3
    │   └── x86_64-linux-gnu
    │       ├── libgfortran.so.3 -> libgfortran.so.3.0.0
    │       ├── libgfortran.so.3.0.0
    │       ├── libquadmath.so.0 -> libquadmath.so.0.0.0
    │       └── libquadmath.so.0.0.0
    └── share
        └── doc
            ├── gcc-6-base
            │   ├── changelog.Debian.gz
            │   ├── copyright
            │   ├── README.Debian.amd64.gz
            │   └── TODO.Debian
            ├── libatlas3-base
            │   ├── changelog.Debian.gz
            │   ├── copyright
            │   ├── README
            │   ├── README.Debian
            │   └── TODO.Debian
            ├── libblas-common
            │   ├── changelog.Debian.gz
            │   └── copyright
            ├── libgfortran3 -> gcc-5-base
            └── libquadmath0 -> gcc-5-base

The difference being /usr/lib/libblas.so.3 missing when build with newer versions of snapcraft.

Hey @sergiusens this is still an issue, do you have any insights to share?

I’ve been using the workaround for a while and it works. I would suggest considering the user success story for someone trying to use opencv/numpy with snaps though.

I think given ROS2 Colcon Plugin - Failed to load entry point, Plugin properties failed, install unexpected, and Bundling OpenCV and/or Numpy in ROS snap for Ubuntu Core This issue affects multiple users and currently the pattern is try to snap, run into issues, post on forum, find work around, see the install keyword is deprecated, update to use override build, and then leave kind of ugly work around in snapcraft.yaml indefinitely. This probably takes several hours and coming from a robotics perspective I would argue it gives the impression that if a pretty core package like numpy is not well supported it makes you wonder about the usability of snaps.

Especially Gdal-bin "Cannot open shared object file" I didn’t see this one until now, but presumably this user received no help and hasn’t been active since.

Is there a simple reproducer here @kyrofa. The only relevant change that pops up in the history between 2.35 and 2.36 are:
https://github.com/snapcore/snapcraft/commit/ec4cac26a7e1ba150165931e90cdc440249f1677

Less relevant than the obvious ones to ignore are (but the surface changed does not seem relevant):
https://github.com/snapcore/snapcraft/commit/23c22fef3b87cead238688a702257d30c2261b41
https://github.com/snapcore/snapcraft/commit/7e31fa0dd8f6058f45f840785780254a915b0021
https://github.com/snapcore/snapcraft/commit/ec7ba948a5b71629b1dff13a3f023cd4bbb68b34

Since the documentation says that parts.<part-name>.install is deprecated, I found the following workaround for my package via setting the env variables for the app:

LD_LIBRARY_PATH: "$LD_LIBRARY_PATH:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/blas:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/lapack"

See here.