Snapcraft building errors

I tried a few years ago to create a Snap and gave up. Instead I built and published a Flatpak. I’ve since discovered ChatGPT and thought I might succeed in creating a Snap if I used it to convert the Flatpak manifest to a snapcraft.yaml.

The build starts working then fails with a mysterious message.:

chris@UNKNOWN:~/snap$ snapcraft
'override-build' in part 'myapp' failed with code 1.
Review the scriptlet and make sure it's correct.
Failed to execute pack in instance.
Recommended resolution: Run the same command again with --debug to shell into the environment if you wish to introspect this failure.
Full execution log: '/home/chris/.local/state/snapcraft/log/snapcraft-20250130-163007.810377.log'

I don’t think that “- -debug to shell into the environment” would help as I wouldn’t know what to look for in the shell. “introspect this failure” to me this means “examine myself this failure” so means nothing to me :). Any ideas what might have gone wrong?

Comments on the manifest would be helpful in case ChatGPT was hallucinating. It is written with Python and GTK4 and has dependencies on python3, python3-pil, python3-numpy and python3-opencv.

I want to avoid setup.py etc and have used Meson to avoid them, however I would prefer to use Bash to install the files instead of Meson, if it would be possible here “override-build: |”.

snapcraft.yaml

name: myapp
base: core22
version: "0.0.0"
summary: An application
description: |
  An application to do something.

grade: stable
confinement: strict
adopt-info: myapp

apps:
  myapp:
    command: bin/myapp
    extensions: [gnome]
    environment:
      EGL_PLATFORM: wayland
    plugs:
      - home
      - wayland
      - opengl

parts:
  myapp:
    plugin: meson
    source: local
    source-type: local
    build-packages:
      - python3-pip
      - python3-setuptools
      - python3-wheel
      - meson
      - ninja-build
    stage-packages:
      - python3
      - python3-pil
      - python3-numpy
      - python3-opencv
    override-build: |
      pip install --prefix=$SNAPCRAFT_PART_INSTALL meson meson-python
      meson setup builddir --prefix=$SNAPCRAFT_PART_INSTALL
      meson install -C builddir

plugs:
  wayland:
    interface: wayland
  opengl:
    interface: opengl

meson.build

project(
  'myapp',
  version: '1.0.0',
  meson_version: '>= 0.46.0'
)

bindir = get_option('bindir')
datadir = get_option('datadir')
pkgdatadir = join_paths(get_option('prefix'), get_option('datadir'), 'myapp')

conf = configuration_data()
conf.set('pkgdatadir', pkgdatadir)

configure_file(
  input: 'myapp.py',
  output: 'myapp',
  configuration: conf,
  install_dir: bindir
)

data_files = [
  'com.github.myapps.myapp.png',
  'dummy.png'
]

foreach file : data_files
  install_data(file, install_dir: pkgdatadir)
endforeach

install_data(
  'com.github.myapps.myapp.appdata.xml',
  install_dir: join_paths(datadir, 'metainfo')  
)

install_data(
  'com.github.myapps.myapp.desktop',
  install_dir: join_paths(datadir, 'applications')
)

install_data(
  'com.github.myapps.myapp.png',
  install_dir: join_paths(datadir, 'icons', 'hicolor', '128x128', 'apps')  
)

install_data(
  'com.github.myapps.myapp64x64.png', rename : 'com.github.myapps.myapp.png',
  install_dir: join_paths(datadir, 'icons', 'hicolor', '64x64', 'apps')
)

install_data(
  'myapp_de_DE.mo', rename : 'myapp.mo',
  install_dir: join_paths(get_option('prefix'), get_option('localedir'), 'de_DE', 'LC_MESSAGES')
)

I’ve realised that this doesn’t even include GTK4, so I think it’s useless. A sample manifest for a text editor which does is quite complicated without any of my stuff. I think I’ll abandon this until someone writes a utility to convert a Flatpak to Snap.

There wouldn’t ever be a utility to convert a Flatpak to Snap, the models are just too different. If you’re still interested, I’d recommend starting from a good GTK4 example, which I have one to hand.

I’ve then removed some of the cruft for yourself:

name: my-snap
base: core24
version: 'R1'
summary: 'My Summary'
description: |
  My Description
grade: stable
confinement: strict
compression: lzo

parts:
  my-part:
    plugin: meson
    source: https://github.com/my-project/my-repo.git
    build-packages:
      - build-essential

apps:
  my-app:
    command: path/to/executable
    plugs:
      - home
      - network
    extensions: [gnome]

Without knowing the project you’re using (maybe I’ve glanced over and not seen it), at the high level, this would build a Meson project with GTK4 libs, graphic drivers, sound, etc; all by just providing it a source repository to build, and assuming it is meson, it should even do the building automatically. If its actually something else, there’s plugins for e.g., CMake and the like too.

To be explicit, you’re not supposed to use override-build except to work around problems. The default Meson should without having this defined be fully capable of managing the build itself, ChatGPT recommending you to manually control this entirely is NOT the typical case, and that’s not to say it’s uncommon, but statistically, you want to avoid this on off chance you can go down the intended & happy path. override-build is the workaround for the happy path not working, not the intended workflow by itself, and even then most uses of it call back to the main plugin code to handle the core build logic.

(Explicitly, the way ChatGPT uses override-build effectively render it nothing more than the nil plugin, because meson the plugin isn’t doing anything!)

The entire GTK3 to GTK4 update took ONE line (I’m discounting the version, I choose to hardcode it rather than write 10 more lines which I’ve done elsewhere but this snap barely updates so!):

Which is why I don’t think you’ll find it easy to find something to translate from Flatpak to Snap, there’s a lot of nuances that just don’t translate at all, like how the previous version actually had GTK3 and GTK4 at the same time in the same runtime, which I don’t think Flatpak does and splits them out much more precisely.

Thanks, that’s very helpful. I’m giving it a try with your yaml. I’ll add modules for the Python dependencies later if this works.

name: myapp
base: core24
version: 'R1'
summary: 'My Summary'
description: |
  My Description
grade: stable
confinement: strict
license: MIT
compression: lzo

parts:
  my-part:
    plugin: meson
    #source: https://github.com/my-project/my-repo.git
    source: local                        #changed by me for testing
    source-type: local    
    build-packages:
      - build-essential
    #stage-packages:                      #added by me
    #  - python3
    #  - python3-pil
    #  - python3-numpy
    #  - python3-opencv      

apps:
  my-app:
    command: bin/myapp
    plugs:
      - home
      - network
    extensions: [gnome]

Instead of Meson I’d rather use Bash commands to copy files in the yaml file if possible, but here is meson.build

project(
  'myapp',
  version: '0.0.00',
  meson_version: '>= 0.46.0'
)

bindir = get_option('bindir')
datadir = get_option('datadir')
pkgdatadir = join_paths(get_option('prefix'), get_option('datadir'), 'myapp')

conf = configuration_data()
conf.set('pkgdatadir', pkgdatadir)

configure_file(
  input: 'myapp.py',
  output: 'myapp',
  configuration: conf,
  install_dir: bindir
)

data_files = [
  'myapp.png'
]

foreach file : data_files
  install_data(file, install_dir: pkgdatadir)
endforeach

# per requirements
install_data(
  'myapp.appdata.xml',
  install_dir: join_paths(datadir, 'metainfo')  
)

install_data(
  'myapp.desktop',
  install_dir: join_paths(datadir, 'applications')
)

install_data(
  'myapp.png',
  install_dir: join_paths(datadir, 'icons', 'hicolor', '128x128', 'apps')  
)

It does quite well but errors with:

chris@fedora:~/snap$ snapcraft Snapd failed to pack Detailed information: cannot pack “/root/prime”: snap is unusable due to missing files: path “bin/myapp” does not exist Failed to execute snapcraft in instance.

It looks to me as though it has created a Snap and either not installed it or installed it somewhere unexpected. However I can’t find myapp* anywhere on the system apart from my source files. The log file doesn’t enlighten me.

The error is saying that when the last bit of the process is executing, after all the building is done, Snapcraft begins producing a .snap file but the bin/myapp part doesn’t exist and so rejects it. Specifically, your build script should be writing to $CRAFT_PART_INSTALL for its bindir. I’m not super familiar with Meson to know if your script might be writing to the Snapcraft images’ top level root environment, but it needs to write to $CRAFT_PART_INSTALL as the equivilent to e.g., a (c)Make prefix. (ChatGPT used $SNAPCRAFT_ earlier, which is deprecated in Core22 and gone in Core24).

Try running snapcraft --verbose --debug, when this errors again, it’ll open bash for you you in the Snapcraft container itself. Browsing around the root users $HOME (/root), you’ll usually find all the different directories being used. The one your interested in is called prime, which is what snap pack runs on and becomes the final .snap file.

Search this prime directory and find your executable if you can. It should be in there somewhere. It might be you change your app: line to have the new name (with a path relative to the prime folder, or effectively, $SNAP at runtime), or it might be you change the Meson script to place it in the right folder ( wouldn’t be surprised if it was e.g., in /bin/myapp in the build environments filesystem, whereas it needs to be specifically in $CRAFT_PART_INSTALL, to ultimately end up in $CRAFT_PART_PRIME).

Ultimately it from a build POV you’re actually 99% of the way there and the last bit is tying the filepath together.

I’ve got into the container and gone into /root and I’ve found an interesting file called environment.sh and quite a choice of /bin files. They are the Python code except those which are marked as directories and contain myapp.png file.

> 
> parts/my-part/build/myapp
> parts/my-part/install/usr/local/bin/myapp
> parts/my-part/install/usr/local/share/myapp  # a directory
> prime/usr/local/bin/myapp
> prime/usr/local/share/myapp			    	# a directory
> stage/usr/local/bin/myapp
> stage/usr/local/share/myapp                  # a directory

I’ve changed to this command: usr/local/bin/myapp and get the result

Packed myapp_R1_amd64.snap

That looks promising, so included the

stage-packages:

for the Python dependencies. and got

Packed myapp_R1_amd64.snap

again. So it looks to me as though it has been packaged. I’ve successfully installed it with

sudo snap install --dangerous myapp_R1_amd64.snap

Running it is not so good

chris@UNKNOWN:~/snap$ snap run myapp
error: cannot find app "myapp" in "myapp"

Is that because the dummy Python script

#! /usr/bin/python3

from PIL import Image
import cv2
import numpy
import gi
sys.exit('OK')

app = Gtk.Application(application_id='myapp')
app.connect('activate', on_activate)
app.run(None)

only tries to import things and doesn’t mention “myapp”? In which case I could try using the real thing?

Or is there a reason relating to where things are? In which case I could try changing the Meson

bindir = get_option('bindir')

to

bindir = get_option($CRAFT_PART_INSTALL)

I had to do this to stop snapcraft trying to build in root:

export SNAPCRAFT_BUILDDIR=$HOME/snapcraft_build
export SNAPCRAFT_PART_INSTALL=$HOME/snapcraft_build/parts

I’ve removed the Meson to remove one level of complication and added a part to make sure that it built Python3. It now successfully builds the Snap and I can install it. When I try to run it I get

error: cannot find app “myapp” in “myapp”

Whatever I try here

command: usr/local/bin/myapp

I always get the same error. If I preface it with python3 it doesn’t like that either. Here is the latest manifest.


name: myapp
base: core24
version: 'R1'
summary: 'My Summary'
description: |
  My Description
grade: stable
confinement: strict
license: MIT
compression: lzo

parts:
  python3:
    plugin: python
    source: .
    build-packages:
      - python3

    stage-packages:
      - python3      

  my-part:
    plugin: nil
    source: local
    source-type: local
    build-packages:
      - build-essential
    override-build: |
      mkdir -p $SNAPCRAFT_PART_INSTALL/usr/local/bin

      mkdir -p $SNAPCRAFT_PART_INSTALL/usr/share/myapp      
      mkdir -p $SNAPCRAFT_PART_INSTALL/usr/share/metainfo
      mkdir -p $SNAPCRAFT_PART_INSTALL/usr/share/applications
      mkdir -p $SNAPCRAFT_PART_INSTALL/usr/share/icons/hicolor/128x128/apps

      
      # Install main script as an executable
      install -m 755 myapp.py $SNAPCRAFT_PART_INSTALL/usr/local/bin/myapp
      
      # Install data files
      install -m 644 myapp.png $SNAPCRAFT_PART_INSTALL/usr/share/myapp/
      install -m 644 myapp.appdata.xml $SNAPCRAFT_PART_INSTALL/usr/share/metainfo/
      install -m 644 myapp.desktop $SNAPCRAFT_PART_INSTALL/usr/share/applications/
      install -m 644 myapp.png $SNAPCRAFT_PART_INSTALL/usr/share/icons/hicolor/128x128/apps/myapp.png

apps:
  my-app:
    command: usr/local/bin/myapp
    plugs:
      - home
      - network
    extensions: [gnome]

That last 1% has taken 2 days so far without getting to 100% :frowning:

Sorry for not responding sooner, I think going back to where you were before adding the exports, you just had to change command: to point have a value of local/bin/myapp - assuming I’ve picked the right binary for your app.

The exports you’re using would break the build system in other ways, but you should be good with a snapcraft clean and another snapcraft --debug --verbose. Those variables aren’t intended to be edited and as Snapcraft proceeds through the build, it’ll lose whatever you exported for the next phase. (P.S, if you wanted to do this for other reasons, we’ve build-environment: for setting those declaritively :slight_smile: )

You might need to ensure this being Python has #!/bin/python3 at the start in order to get it to fit properly, alternatively I think the command for you would be python3 $SNAP/local/bin/myapp - there’s a bit of a perculiarity with Python itself existing in the base snap not the local snap that can lead to similar messages if you’re not e.g., using the Meson plugin (which automatically puts a python3 wrapper $SNAP/bin/python3 pointing to /usr/bin/python3 to avoid that edge case).

Edit:

If I preface it with python3 it doesn’t like that either

That’s the edgecase behaviour that the Meson (and Python) plugins solve by putting that wrapper in, the command: is effectively relative to $SNAP - not the filesystem root itself (where Python ends up, being in the Core snap). That’s why there’ll be a wrapper set up with these plugins. Something like java which isn’t in core doesn’t have this, it’s just an unfortunate quirk of Python being deduplicated in the Core snap.

No worries! I’ve made your changes and then left that for later. I’ve gone back to work through snapcraft.io/docs/create-a-new-snap in case ChatGPT has misled me. It probably missed out “init” or something like that. The first example runs, so I’ll compare that with mine tomorrow.

The current tutorial example is actually useful. I’ve got a sort of merger of the tutorial example and yours and am changing it in very small steps towards what my app needs. It’s being built successfully so far. I’ll keep updating until I succeed.

I’m back to Meson to avoid using the override-build statement that you deprecated, and in the hope that it was more likely the files would be put into the right places. Using snapcraft --verbose --debug I found the executable in usr/local/bin and putting that in the manifest makes the thing run.


name: myapp
base: core24
version: '0.1'
summary: Summary
description: |
  This is my-snap's description.

grade:        devel
confinement:  devmode

parts:
  my-part:              # I don't think the name does anything
    plugin: meson
    #source-type: git
    #source: {https://myapp.com}
    source-type: local
    source: local
    build-packages:
      - build-essential
    stage-packages:
       - python3-pil
       - python3-numpy
       - python3-opencv

apps:
  myapp:
    command: usr/local/bin/myapp
    plugs:
      - home
      - network
    extensions: [gnome]

There were some warnings when it was being built. When I run it I can see that these imports into the Python program work:

sys, os, shutil, shlex, glob, subprocess, multiprocessing, warnings, gettext, locale, webbrowser

gi (That’s GTK)

but these all fail:

PIL, cv2, numpy.

Unsurprisingly it’s the bits I added. Do I need to put something into Meson as well as the manifest? I’ll have to search for some documentation. It’s probably something like this:

  py-dependencies:
    plugin: python
    source-type: *archive*
    source: "https://github.com/python-pillow/Pillow/archive/refs/tags/8.3.2.tar.gz"
    *sha256*: "8252b6b514aed2743abb5b7259b3253d6c4bf86902b9c5acd33fe79d24ec7b2f"
   *build-commands:*   "pip3 install . --prefix=${SNAP_DEST}"
    python-packages:
       - python3-pil

Sill making progress, albeit slowly. This is the latest

name: myapp
base: core24
version: '0.0.0'
summary: A Snap
description: |
      All about the Snap
grade: devel            # change for release
confinement: devmode    # change for release
#license: MIT           # Needed? Should be GPL-3
#compression: lzo       # Needed?

parts:
  
  my-part:
    plugin: dump
    source: local
    source-type: local
    build-packages:
      - build-essential
    organize:
      myapp.py: bin/myapp
      myapp.appdata.xml: usr/share/metainfo/myapp.appdata.xml
      myapp.desktop: usr/share/applications/myapp.desktop
      myapp128x128.png: usr/share/icons/hicolor/128x128/apps/myapp.png
      myapp64x64.png: usr/share/icons/hicolor/64x64/apps/myapp.png  
    
  python-part:
    plugin: python
    source: .
    stage-packages:
       - python3-pillow
       - python3-numpy
       - python3-opencv

apps:
  myapp:
    command: bin/myapp
    plugs:
      - home
      - network
    extensions: [gnome]

I’ve builtthis successfully, and it loads and runs the GUI! There are some errors though. touch: cannot touch '/home/chris/snap/myapp/common/.cache/desktop-runtime-date': No such file or directory

I assume that this is because the directory structure I’m using comes from the Flatpak and I will have to find the documentation for Snap and copy that.

libpxbackend-1.0.so: cannot open shared object file: No such file or directory
Failed to load module: /home/chris/snap/myapp/common/.cache/gio-modules/libgiolibproxy.so

ChatGPT says this is related to Gnome (for GTK), is that correct? If so I’m surprised that extensions: [gnome] doesn’t provide it. There are several possibly related Lint errors.


Traceback (most recent call last):
  File "/snap/myapp/x1/usr/local/bin/myapp", line 10, in <module>
    from PIL import Image, ImageOps, __version__
ModuleNotFoundError: No module named 'PIL'

This is obviously coming from the Python program looking for python-pillow, but I don’t know why it’s not working, since the building process appeared to have worked.