Guidelines and HOWTOs/Snap: Difference between revisions

From KDE Community Wiki
No edit summary
 
(31 intermediate revisions by 6 users not shown)
Line 1: Line 1:
{{Construction}}
= Put Your App in the Snap Store =
= Put Your App in the Snap Store =


[[File:Screenshot 20210318 150131.png|thumb|Snap Store KDE Page]]
[[File:Screenshot 20210318 150131.png|thumb|Snap Store KDE Page]]


It is a KDE goal to be [https://kde.org/goals/ All About the Apps] to deliver our apps directly to our users. Snaps is one of the ways to do this. Snaps are Linux app packages that can run on pretty much any Linux distro. There is a single centralised Snap store that delivers them to users.  Take a look at the [https://snapcraft.io/publisher/kde KDE page on the Snap Store] to see what's available.
It is a KDE goal to be [https://community.kde.org/Goals All About the Apps] to deliver our apps directly to our users. Snaps is one of the ways of doing this. Snaps are Linux app packages that can run on pretty much any Linux operating system. There is a single centralized Snap store ( https://snapcraft.io/store ) that delivers them to users.  Take a look at the [https://snapcraft.io/publisher/kde KDE page on the Snap Store] to see what's available.


== Snap intro ==
= Snap intro =


A Snap package typically contains all the files, including libraries and data files, to run the app. There are also Content Snaps which contain reuseble libraries. In KDE land we have the [https://snapcraft.io/kde-frameworks-5-qt-5-15-core20 KDE Frameworks Content Snap] which includes recent Qt and KDE Frameworks and this is shared between all KDE apps so we do not have to waste disk space and build resources.
A Snap package typically contains all the files, including libraries and data files, to run the app. There are also Content Snaps which contain reusable libraries. KDE has the KDE Frameworks Content Snaps [https://snapcraft.io/search?q=kf6 for KF6] and [https://snapcraft.io/search?q=kf5 for KF5] which include recent Qt and KDE Frameworks and are shared between all KDE apps so we do not have to waste disk space and build resources.


Give it a try by installing a package or two on your system
For KDE Frameworks 6, the content pack has been split in three:
{{Input|1=<nowiki>snap install kcalc</nowiki>}}
* [https://snapcraft.io/kde-qt6-core22-sdk Qt6 SDK (Core 22 based)]
And run kcalc from your apps menu.
* [https://snapcraft.io/kf6-core22-sdk KDE Frameworks 6 SDK (Core 22 based)]
* [https://snapcraft.io/kf6-core22 Qt6 + KDE Frameworks 6 runtime (Core 22 based)]


This will have downloaded the kcalc Snap package from the Snap store into e.g. <code>/var/lib/snapd/snaps/kcalc_73.snap</code> and mounted it into e.g. <code>/snap/kcalc/current/</code>.  You can also just download it to a local directory with  <code>snap download kcalc</code>, use <code>lesspipe kcalc*snap</code> to see what it inside it.
The first two are needed to build a snap depending on the KDE Frameworks stack, the third one is the only one needed at runtime.


<code>snap list</code> will show your currently installed snaps and it will now show that you have <code>kcalc</code> and the content snap <code>kde-frameworks-5-qt-5-15-core20</code> as well as the <code>core20</code> content snap installed.
Give it a try by installing a package or two on your system, e.g. the KDE desktop notepad app:
{{Input|1=<nowiki>snap install kwrite</nowiki>}}
And run kwrite from your apps menu.


Snaps are containers, similar to Docker.  From inside the Snap container access to the file system and system resources are limited. This is good for inter-app security but means the app sees your system quite differently from how it might expect. You can "log" into the container with <code>snap run --shell kcalc</code> to have a look at how the Snapped kcalc app sees your system.
This will have downloaded the kwrite Snap package from the Snap store into e.g. <code>/var/lib/snapd/snaps/kwrite_4.snap</code> and mounted it into e.g. <code>/snap/kwrite/current/</code>. You can also just download it to a local directory with <code>snap download kwrite</code>, use <code>lesspipe kwrite*snap</code> to see what is inside it.


To give the app controlled permissions to the system it plugs connections into resources such as the network or container snaps.  Run <code>snap connections kcalc</code> to see what it gets given access to.  The connections are controlled by the store and app maintainers need to ask the store to apply the auto-connections.  They can also be overridden locally.
<code>snap list</code> will show your currently installed snaps and it will now show that you have <code>kwrite</code> and the content snap <code>kf6-core22</code> as well as the <code>core22</code> content snap installed.


You can take a look at the snap package with <code>snap download kcalc</code> which will download files such as <code>kcalc_73.assert</code> and <code>kcalc_73.snap</code>.  The .assert has the checksums and signatures for the packageThe .snap has the (non-store) metadata and all the files of the package.  <code>lesspipe kcalc_73.snap</code> to take a look.
Snaps are containers, similar to Docker. From inside the Snap container access to the file system and system resources are limited. This is good for inter-app security but means the app sees your system quite differently from how you might expectYou can "log" into the container with <code>snap run --shell kwrite</code> to have a look at how the snapped kwrite app sees your system.


== Concepts ==
To give the app controlled permissions to the system it plugs connections into resources such as the network or container snaps. Run <code>snap connections kwrite</code> to see what it gets given access to. The auto connections are controlled by the snap store and app maintainers need to ask the [https://forum.snapcraft.io/ snap store] for the desired auto-connections. Connections can also be overridden locally.
 
You can take a look at the snap package with <code>snap download kwrite</code> which will download files such as <code>kwrite_4.assert</code> and <code>kwrite_4.snap</code>. The .assert has the checksums and signatures for the package.  The .snap has the (non-store) metadata and all the files of the package. <code>lesspipe kwrite_4.snap</code> to take a look.
 
= How to contribute to the Qt6 and KF6 content snaps =
 
== kde-qt6-core22-sdk ==
First we start with the Qt6 SDK snap by cloning the [https://invent.kde.org/neon/snap-packaging/kde-qt6-core-sdk kde-qt6-core-sdk repository].
 
This snap should not need much altering. Versions on the store are coming from the CI. In the <code>snapcraft.yaml</code> file the <code>version</code> value will indicate which Qt version is built.
 
When updating is needed, changing this value should be enough.
Commit and push which should trigger the CI build.
 
Once this is completed and uploaded to the Snap store, you can proceed tuning the other content snaps.


Snaps are usually one app per Snap package.  The Snap package contains all the libraries and resources it needs to run except those in the shared content kde-frameworks Snap.
==kf6-core22-sdk==
The KDE Frameworks SDK snap recipe is cloned from the [https://invent.kde.org/neon/snap-packaging/kf6-core-sdk kf6-core-sdk repository].


In practice this means all of Qt and KF5 including Breeze icons and themes are in the kde-frameworks content Snap and your app Snap only needs to compile its own sources.  If you apps needs other libraries it can either install these as Apt packages from the Ubuntu or KDE neon or other repository, or it can compile them from source as well.  You will need to manually list the build-packages (all the -dev packages) and the stage-packages used in the final package, it'll warn you if any final libraries it expects are missing.
This file is very long holding one part per framework. Here is an example:


'''Snapcraft''' is used to build snaps. It can be installed as a snap with <code>snap install snapcraft --classic</code>. Snap packages are defined with snapcraft.yaml files. Snapcraft will build them inside a virtual machine, we use LXD to build the KDE ones (the default is to use Multipass another virtual machine manager but that has problems on cloud machines).  Using a virtual machine makes it reliable to build the Snaps on any system with identical results.
{{Input|<syntaxhighlight lang="yaml" line>
kcrash:
    after:
        - extra-cmake-modules
        - kcoreaddons
        - kwindowsystem
    source: https://invent.kde.org/frameworks/kcrash.git
    source-tag: *kf6-version
    source-depth: 1
    build-packages:
        - doxygen
        - graphviz
        - libx11-dev
    build-snaps:
        - kde-qt6-core22-sdk
    plugin: cmake
    cmake-generator: Ninja
    cmake-parameters: *cmakeparameters
    build-environment: *buildenvironment
    stage-packages:
        - libx11-6
</syntaxhighlight>}}


snapcraft.yaml files are kept in either [https://invent.kde.org/neon KDE neon git repositories] or in the apps repository.  They can be built on the [https://build.neon.kde.org/view/Snaps/ KDE neon Jenkins CI].  All KDE Developers have access to all these git repos and use of the Jenkins CI. Stable versions are kept in the Neon/release branches or the stable KDE git branch of your app.  Unstable versions in Neon/unstable or the unstable branch of your app (usually master).
The name of the framework built above is <code>kcrash</code>. It is built after <code>extra-cmake-modules</code>, <code>kcoreaddons</code> and <code>kwindowsystem</code> (each of those must be listed as another part in the file). It also indicates the extra deb or snap package dependencies needed to build the framework, the build options and so on.


Our Snaps read metadata from AppStream metadata files so it is important the metadata is up to date including current release versions.
For further details on the <code>snapcraft.yaml</code> syntax and features please head to the [https://snapcraft.io/docs/snapcraft-reference Snapcraft reference].


The [https://snapcraft.io/ Snap Store] is the centralised app store by Canonical.  There is no practical way to use other stores or repositories with Snaps.  It is what Snapcraft uploads built snaps to and what your local snapd will download and install snaps from.  It also says what permissions those snaps should have.  As an app developer if you want your app to have extra permissions (for example [https://invent.kde.org/neon/kde/kdf/-/blob/Neon/unstable/snapcraft.yaml kdf uses mount-observe]) then you need to ask for it on the [https://forum.snapcraft.io/t/request-for-connection-kdf-mount-observe/10953 snapcraft forum].
Once you have all of your parts in place and are ready to build. You can test locally using <code>snapcraft</code>. When satisfied, commit and push which will trigger the CI.


A Classic containment Snap has no restrictions on what files it can see on your system or what external executable can be run.  This is useful for IDEs and similar apps such as [https://forum.snapcraft.io/t/kate-as-classic-snap/23514 Kate] which runs external programs.  Again this needs to be set in your [https://invent.kde.org/neon/kde/kate/-/blob/Neon/release/snapcraft.yaml#L3 snapcraft.yaml] then you need to ask on the Snap forum for the store to set it to classic.  The Store will then tell snapd for anyone installing the Snap to have it installed as a Classic confinement Snap.
This file shouldn't need much altering unless there is a new framework. You will however need to update the release version in 2 places when building a new release.


There is a KDE account on the Snap store which is run by the KDE neon developers Jonathan Riddell and Harald Sitter. One Snap on the store can be shared between more than one account so app maintainers can also create a separate account if they want to have more control over when their app is released and what the store says about it.
{{Input|<syntaxhighlight lang="yaml" line>
version: 6.6.0
</syntaxhighlight>}}


The store has four channels for different levels of stabilityOur stable branch builds get uploaded to the Candidate channel and can be moved to the Stable channel once tested.
{{Input|<syntaxhighlight lang="yaml" line>
    extra-cmake-modules:
        after:
        - qtconf
        source: https://invent.kde.org/frameworks/extra-cmake-modules.git
        source-tag: &kf6-version v6.6.0
  ...
</syntaxhighlight>}}


== Example ==
==kf6-core22==
Once we're happy with the two SDKs the runtime must be updated. Its recipe is coming from the [https://invent.kde.org/neon/snap-packaging/kf6-core kf6-core repository].


[https://apps.kde.org/blinken/ Blinken] is an exciting memory game from KDE.  It's [https://snapcraft.io/blinken available on the Snap store]. The Snap package is defined by a <code>snapcraft.yaml</code> file which is in the <code>Neon/release</code> branch of [https://invent.kde.org/neon/kde/blinken/-/blob/Neon/release/snapcraft.yaml KDE neon's Blinken packging].  Any update to that branch triggest a build of the [https://build.neon.kde.org/view/Snaps/job/focal_stable_kde_blinken_snap_amd64/ Blinken Snap job] in KDE neon's Jenkins builder.  If the build is successful it will be uploaded to the <code>Candidate channel</code> of the Snap store ready for review.
It is merely assembling and pruning the previous two snaps to have only what's required at runtime. The recipe often doesn't need to be changed but (even though the CI should do it automatically) the CI build might need to be triggered manually to pickup the SDKs latest changes.


The <code>snapcraft.yaml</code> file looks like this:
=Creating Applications Snaps=


Fork the application into your userspace. Aka fork https://invent.kde.org/utilities/ark into your user account on invent.
Make sure you on the master branch.
Add your snapcraft.yaml
You can use the ark snapcraft.yaml as a template.
{{Input|<syntaxhighlight lang="yaml" line>
{{Input|<syntaxhighlight lang="yaml" line>
# SPDX-FileCopyrightText: 2023 Scarlett Moore <[email protected]>
#
# SPDX-License-Identifier: CC0-1.0
---
---
name: blinken
name: ark
confinement: strict
confinement: strict
grade: stable
grade: stable
base: core20
base: core22
adopt-info: blinken
adopt-info: ark
apps:
apps:
     blinken:
     ark:
         extensions:
         extensions:
         - kde-neon
         - kde-neon-6
         common-id: org.kde.blinken.desktop
         common-id: org.kde.ark.desktop
         command: usr/bin/blinken
        desktop: usr/share/applications/org.kde.ark.desktop
         command: usr/bin/ark
         plugs:
         plugs:
         - home
         - home
         - network
         - system-backup
         - network-bind
         command-chain:
         - audio-playback
         - snap/command-chain/desktop-launch6
        - removable-media
slots:
slots:
     session-dbus-interface:
     session-dbus-interface:
         interface: dbus
         interface: dbus
         name: org.kde.blinken
         name: org.kde.ark
         bus: session
         bus: session
package-repositories:
package-repositories:
Line 78: Line 137:
     - main
     - main
     suites:
     suites:
     - focal
     - jammy
     key-id: 444DABCF3667D0283F894EDDE6D4736255751E5D
     key-id: 444DABCF3667D0283F894EDDE6D4736255751E5D
     url: http://origin.archive.neon.kde.org/user
     url: http://origin.archive.neon.kde.org/user
     key-server: keyserver.ubuntu.com
     key-server: keyserver.ubuntu.com
parts:
parts:
     blinken:
     ark:
        parse-info:
        - usr/share/metainfo/org.kde.ark.appdata.xml
         plugin: cmake
         plugin: cmake
         build-packages:
         build-packages:
         - libkf5doctools-dev
         - libarchive-dev
         - libphonon4qt5-dev
         - libbz2-dev
         - libphonon4qt5experimental-dev
         - liblzma-dev
         source: http://download.kde.org/stable/release-service/20.12.3/src/blinken-20.12.3.tar.xz
         - libzip5-dev
        - pkg-config
        - zlib1g-dev
        stage-packages:
        - bzip2
        - p7zip-full
        - unrar
        - unzip
        - zip
        - to amd64: [rar]
        - libarchive13
        - libzip4
        - zlib1g
        source: .
        source-type: local
         cmake-parameters:
         cmake-parameters:
         - "-DKDE_INSTALL_USE_QT_SYS_PATHS=ON"
         - -DCMAKE_INSTALL_PREFIX=/usr
        - "-DCMAKE_INSTALL_PREFIX=/usr"
         - -DCMAKE_BUILD_TYPE=Release
         - "-DCMAKE_BUILD_TYPE=Release"
         - -DQT_MAJOR_VERSION=6
         - "-DENABLE_TESTING=OFF"
         - -DBUILD_WITH_QT6=ON
         - "-DBUILD_TESTING=OFF"
         - -DBUILD_TESTING=OFF
         - "-DKDE_SKIP_TEST_SETTINGS=ON"
         - -DCMAKE_INSTALL_SYSCONFDIR=/etc
         - "-DCMAKE_FIND_ROOT_PATH=/usr\\;/root/stage\\;/snap/kde-frameworks-5-qt-5-15-core20-sdk/current"
         - -DCMAKE_INSTALL_LOCALSTATEDIR=/var
         parse-info:
         - -DCMAKE_EXPORT_NO_PACKAGE_REGISTRY=ON
        - usr/share/metainfo/org.kde.blinken.appdata.xml
        - -DCMAKE_FIND_USE_PACKAGE_REGISTRY=OFF
         filesets:
        - -DCMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY=ON
            exclusion:
        - -DCMAKE_INSTALL_RUNSTATEDIR=/run
            - "-usr/lib/*/cmake/*"
        - -DCMAKE_SKIP_INSTALL_ALL_DEPENDENCY=ON
            - "-usr/include/*"
        - -DCMAKE_VERBOSE_MAKEFILE=ON
            - "-usr/share/ECM/*"
        - -DCMAKE_INSTALL_LIBDIR=lib/$CRAFT_ARCH_TRIPLET
            - "-usr/share/doc/*"
        - --log-level=STATUS
            - "-usr/share/man/*"
        - -DCMAKE_LIBRARY_PATH=lib/$CRAFT_ARCH_TRIPLET
            - "-usr/share/icons/breeze-dark*"
            - "-usr/bin/X11"
            - "-usr/lib/gcc/x86_64-linux-gnu/6.0.0"
            - "-usr/lib/aspell/*"
         prime:
         prime:
         - "$exclusion"
         - -usr/lib/*/cmake/*
        - -usr/include/*
        - -usr/share/ECM/*
        - -usr/share/man/*
        - -usr/bin/X11
        - -usr/lib/gcc/$CRAFT_ARCH_TRIPLET_BUILD_FOR/6.0.0
        - -usr/lib/aspell/*
        - -usr/share/lintian
    cleanup:
        after:
        - ark
        plugin: nil
        build-snaps:
        - core22
        - kf6-core22
        override-prime: |
            set -eux
            for snap in "core22" "kf6-core22"; do
                cd "/snap/$snap/current" && find . -type f,l -exec rm -rf "${CRAFT_PRIME}/{}" \;
            done
</syntaxhighlight>}}
</syntaxhighlight>}}


Check [https://snapcraft.io/docs/snapcraft-yaml-reference Snapcraft YAML reference] if unsure.


=== Top Level ===
{{Input|<syntaxhighlight lang="shell" line>
git add snapcraft.yaml
git commit
</syntaxhighlight>}}


* name: blinken ← the snap name registered on the snap store
Add the changes you made to the commit message.
* confinement: strict ← Snaps are a containerised format and can't see the outside system from inside their container.  Strict is the normal container method.  Classic is also possible which allows it to see the outside system and is used by e.g. Kate because Kate needs to run external programs like Git.  It can only be Classic on request.  Can also be devmode for testing.
{{Input|<syntaxhighlight lang="shell" line>
* grade: stable ← It must be stable to be in a released channel, can also be devel.
git push
* base: core20 ← which base system to build on, core20 means Ubuntu 20.04 and is the current recommended.
* adopt-info: blinken ← Which Snap part to get the appstream info from.  This sets version, icon, description.
 
You might also need to add <code>version</code> if it is not in the appstream file.  This is just a version read by users it does not affect the revision number which is tracked by the store.
 
=== apps ===
 
{{Input|<syntaxhighlight lang="yaml" line>
apps:
    blinken:
        extensions:
        - kde-neon
        common-id: org.kde.blinken.desktop
        command: usr/bin/blinken
        plugs:
        - home
        - network
        - network-bind
        - audio-playback
        - removable-media
</syntaxhighlight>}}
</syntaxhighlight>}}
Now create a merge request.


<code>apps</code> are the programs which the snap includes for users to run. Usually there is only one in a Snap but sometimes e.g. [https://invent.kde.org/neon/extras/calligra/-/blob/Neon/release/snapcraft.yaml Calligra] there are more than one.
Once you are happy with the snap (master will publish to edge) you will want to commit your snapcraft.yaml to the current release branch, aka Release/24.08


The [https://snapcraft.io/docs/kde-neon-extension KDE neon extension] adds some commonly used features to the KDE snaps including using the [https://snapcraft.io/kde-frameworks-5-qt-5-15-core20 KDE Frameworks 5 content Snap].
== Concepts ==


The <code>common-id</code> comes from the Appstream file.  You ''must'' check what it is in the appstream file. <code>org.kde.blinken.appdata.xml</code> contains <code><id>org.kde.blinken.desktop</id></code> so we use that.  Sometimes apps use the .desktop and sometimes they don't, this is at random.
Snaps are usually one app per Snap package.  The Snap package contains all the libraries and resources it needs to run except those in the shared content <code>kf6-core22</code> Snap.


The command to run is listedThe KDE neon extension will run a script first which sets many necessary environment variables.
In practice this means all of Qt and KF(5/6) including Breeze icons and themes are in the kde-frameworks content Snap and your app Snap only needs to compile its own sourcesIf you apps needs more libraries you can either install these as DEB packages e.g. from the Linux operating system Ubuntu LTS or KDE neon, or you can compile them from source as well. You will need to manually list the build-packages (all the -dev packages) and the stage-packages used in the final package, it'll warn you if any final libraries it expects are missing.


The plugs give access to the outside system, see [https://snapcraft.io/docs/supported-interfaces Supported interfaces] for descriptionsWhen a Snap is installed from the Store it is up to the Store to say which plugs get used. Thost listed as auto connect in the docs are permitted. Otherwise you must ask on the Snap forum for permission to have the Snap connected. (Locally installed snaps with --devmode have access to everything, you can also manually connect snaps to interfaces on your local system.)
'''snapcraft''' is used to build snaps. It can be installed as a snap with <code>snap install snapcraft --classic</code>A snap package (app) is defined in the snapcraft.yaml file. Snapcraft will build the package inside a virtual machine; it uses LXD to build the KDE snap packages. Using a virtual machine makes it reliable to build the Snaps on different Linux operating systems with identical results.  


<code>slots</code> are the complement to plugs, they allow the outside system to access our Snap app. In this case we are allowing a dbus interface into the Snap. All KDE apps have a dbus interface and you can check what it is called by running the app and using <code>qdbus</code>.
snapcraft.yaml files are kept in their respective upstream repo. Eg. kcalc snapcraft file resides [https://invent.kde.org/utilities/kcalc/-/blob/release/23.08/snapcraft.yaml?ref%20type=heads https://invent.kde.org/utilities/kcalc/-/blob/release/23.08/snapcraft.yaml?ref_type=heads]


<code>package-repositories</code> add the KDE neon apt repo to build against, this will give you the latest libraries to compile with.
Our Snaps read metadata from AppStream metadata files so it is important the metadata is up to date including current release versions.


The source of a Snap is the <code>parts</code> and some snaps have several parts made of different sources e.g. [https://invent.kde.org/neon/extras/ktorrent/-/blob/Neon/release/snapcraft.yaml KTorrent] has both libktorrent and ktorrent parts.  Blinken is not complex so it has only one part made of the compiles Blinken source.
The [https://snapcraft.io/ Snap Store] is the centralized app store by Canonical. There is no practical way to use other stores or repositories with Snaps. It is what Snapcraft uploads built snaps to and what your local snapd will download and install snaps from. It also says what permissions those snaps should have. As an app developer if you want your app to have extra permissions (for example [https://invent.kde.org/utilities/kdf/-/blob/release/23.08/snapcraft.yaml?ref_type=heads kdf] uses mount-observe) then you need to ask for it on the [https://forum.snapcraft.io/t/request-for-connection-kdf-mount-observe/10953 snapcraft forum].


=== Parts ===
A Classic containment Snap has no restrictions on what files it can see on your system or what external executable can be run.  This is useful for Integrated Development Environments (IDEs) and similar apps such as [https://forum.snapcraft.io/t/kate-as-classic-snap/23514 Kate] which runs external programs. Again this needs to be set in your [https://invent.kde.org/utilities/kate/-/blob/release/23.08/snapcraft.yaml?ref_type=heads] then you need to ask on the Snap forum for the store to set it to classic. The Store will then tell snapd for anyone installing the Snap to have it installed as a Classic confinement Snap.


* plugin ← which [https://snapcraft.io/docs/supported-plugins Snap build plugin] to use
The KDE account on the Snap store is run by the Snap team developers Jonathan Esk-Riddell, Harald Sitter, Scarlett Moore, Carlos DeMaine, Kevin Ottens, Benjamin Port. One Snap on the store can be shared between more than one account so app maintainers can also create a separate account if they want to have more control over when their app is released and what the store says about it.
* build-packages ← most build packages are in the KDE Frameworks content snap but some need added explicitly and some are not in there.  They will be downloaded from the neon and ubuntu apt repos.  [https://invent.kde.org/neon/extras/ktorrent/-/blob/Neon/release/snapcraft.yaml#L40 KTorrent] uses non-KDE libraries and it needs to list the -dev packages in the <code>build-packages</code> then the library itself in the <code>stage-packages</code>.
* source ← link to the tar
* cmake-parameters ← copy and paste this, it sets the right paths.
* parse-info ← where the appstream file is to be installed
* filesets and prime ← snap parts get build then copied into a <code>stage</code> area, when all the parts are built they are copied into the <code>prime</code> area which is converted into the Snap package.  This lists a common set of excluded files we do not want copied.  You can add more here if you end up with unnecessary files in your snap.
 
== Building ==
Install snapcraft with <code>snap install snapcraft --classic</code>.


In the directory with the <code>snapcraft.yaml</code> run: <code>snapcraft --enable-experimental-package-repositories --enable-experimental-extensions --use-lxd</code>
The store has four channels for different levels of stability. Our stable branch builds get uploaded to the Candidate channel and can be moved to the Stable channel once tested.


This will start a virtual machine and build the package.  If all is well you will have <code>blinken_20.12.3_amd64.snap</code> or similar created.
== Example ==


Install with <code>snap install --devmode blinken_20.12.3_amd64.snap</code>
[https://apps.kde.org/blinken/ Blinken] is an exciting memory game from KDE. It's [https://snapcraft.io/blinken available on the Snap store]. The Snap package is defined by a <code>snapcraft.yaml</code> file which is in the https://invent.kde.org/education/blinken/-/tree/release/24.08?ref_type=heads repo. Any update to that branch triggers a build of the [https://launchpad.net/~kde-community/kde-snap-blinken/+snap/blinken] in launchpad. If the build is successful it will be uploaded to the <code>Candidate channel</code> of the Snap store ready for review.
 
Run with <code>snap run blinken</code> or check it is in the app menu and run from there (remove any versions of blink you have installed from your normal distro packages just to be sure).
 
== Qt only ==
 
= Old Page =
 
Want to run your application binaries on any Linux distribution? Snap makes that possible.
 
For general purpose information about snap, snapcraft and how to use them please have a look at their documentation  as it excellently teaches you most generic information about snaps https://docs.snapcraft.io
Even so this page will teach you a lot of the basics of snaps.
 
For a primer on YAML, the format used to describe snap building,  have a look here https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html
 
For additional information on Snaps in KDE neon and how the KF5 content snap works, please check out the [[Neon/Snap | neon snap page]].
 
== How it Works ==
 
Snaps work very much like DMG files on OS X. A snap is a compressed squashfs file containing the entire file tree of the program. A system-level helper called snapd manages the snap file and mounts it into the file system. Snapd also manages host-level integration (e.g. creation of .desktop files). Running an application from the snap needs to go through snapd (e.g. snap run foo) which needs to be installed on the target system. Snap, the format, could be used by various daemon implementations, but as a matter of fact the only viable implementation is snapd and maintained by Canonical. Snapd frowns upon side-loaded snaps. You may still install a snap downloaded manually, but the preferred way to use snaps is through snapd directly which in turn will go to the associated [https://snapcraft.io/store store] as a remote source. The store too is run by Canonical. Most aspects of snaps may be inspected/changed through the CLI for snapd. It's simply called "snap" <code>snap help</code> for help.
 
== Binary Sources ==
 
Building a snap we'll lovingly call "snapping". At the time of writing you can snapcraft on two core systems: one is Ubuntu 16.04, the other is Ubuntu 18.04. Both of them are the LTS version of Ubuntu and therefore supported for what seems like forever. Snapcraft has native support to pull binary packages (i.e. debs) into the snap so that you might build against them without having to build the binaries yourselves. For example you could use Qt from Ubuntu directly without the need to build it in your snap. However, since the base systems are LTS the software is usually very dated. Seeing as you may need newer dependencies there's a bunch of ways you can get them besides pre-built Ubuntu debs:
 
=== As Snapcraft Parts ===
 
You can add any number of additional parts that build dependencies. For example you might build your own qtbase as part of snap. How much work this is can be vastly different between software. Building all of Qt can get quickly slow, at the same time relying on an ancient Qt may not be practical either. To gain access to newer foundations software you may instead want to get them ...
 
=== From KDE neon ===
 
We are in luck and KDE neon already builds packages for Ubuntu LTS, so you may choose to simply use neon debs on top of Ubuntu debs. This gives access to the latest Qt, KDE frameworks and other related libraries. This can be a huge time saver. Unfortunately Snapcraft's support for adding additional repositories is non-existent and so if you choose to go this route you may need to somewhat manually manage the build environment yourself e.g. using an especially prepared LXD container.
 
=== From Content Snaps ===
 
Content Snaps are other Snaps you can "include" in your Snap to get access to pre-existing binary (or data) assets so you don't need to ship them. Notable advantage is that one content Snap may be shared across multiple consumers and thus reduce the disk and network footprint. For example there is a ready to use [https://snapcraft.io/kde-frameworks-5-core18 KF5 content snap] which you can use to get access to all of KF5 and Qt5. Content Snaps may be combined with the other means of sourcing binaries.
 
=== From PPAs ===
 
Much like KDE neon, this too will require you to manage the build environment manually. Also, when using PPAs beware that they may not be compatible with neon (so, ideally you should use either-or) and that many of them are not particularly trustworthy or well maintained (important vis-a-vis security).
 
== Snapcraft ==
 
The tool to build snaps is called snapcraft. The definition for how to craft a snap is written down in snapcraft.yaml. Generally speaking a snapcraft.yaml will contain global metadata of the snap, a list of applications provided by the snap, and lastly a list of parts that when put together result in the snap. Access to the host system is controlled through a plug-and-slot system which is also used in the snapcraft.yaml. Each application may have one or multiple plugs it uses to get access to resources of the host system or other snaps. For example the 'desktop' plug gives access to host fonts and icons. Also see https://docs.snapcraft.io/interface-management/6154
 
== Types of Snap==
 
With a broad overview of abilities and shortcomings let's dive right in and look at types of snaps we might build.
 
=== Standalone  ===
 
A standalone snap is a snap which solely relies on a "core" but no other snaps. This is generally speaking the most reliable type of snap as everything the snap needs is inside the snap (except libc and friends which are in the core).
 
It is also the best supported way of building a snap since it's been around since the very beginning.
 
Advantages:
 
* Very reliable
* Easy to build and test
* You are always on your own and unrelated changes rarely if ever can impair your snap
 
Disadvantages:
 
* Huge in size (each standalone snap needs to ship their own Qt/l10n and necessary kf5 and other dependencies)
* You need to take care of setting up your execution environment yourself.
* You are always on your own and unrelated changes rarely if ever can improve your snap
 
==== Example ====


{{Input|<syntaxhighlight lang="yaml" line>
{{Input|<syntaxhighlight lang="yaml" line>
name: qtnetsample
version: '0'  # the version of the snap. has no semantic meaning
summary: This is my-snap's summary  # 79 char long summary
description: This is my-snap's description  # a longer description for the snap
confinement: strict  # use "strict" to enforce system access only via declared interfaces
grade: devel # use "stable" to assert the snap quality
base: core18 # the core this snap depends on
apps:
    qtnetsample:
        command: launcher qtnetsample # the launcher will setup the environment for qtnetsample to find libraries/plugins/data etc
        plugs: [x11, network, network-bind] # this snap will be able to act as xclient and talk over the network
parts:
    qtnetsample:
        build-packages: [qt5-default]
        plugin: cmake
        stage-packages: [libqt5network5, libqt5core5a]
        source: .
</syntaxhighlight>}}


=== Shared Snap ===
# SPDX-FileCopyrightText: 2024 Scarlett Moore <sgmoore@kde.org>
 
#
A snap may also choose to use one or more Content Snaps (see glossary) to share part of the binaries or UI assets with other snaps. As shared content will generally be in the content snap, the ultimate size of the snap can be fairly small. Think of this as an approach more akin to how traditional binary package dependencies work. Albeit with many of the same complexities surrounding it.
# SPDX-License-Identifier: CC0-1.0
 
For example KDE neon builds the kde-frameworks-5 content snap. It contains all of Qt and all (not-deprecated) KDE frameworks along with Plasma integration rigging.
 
Advantages:
 
* Application snap is super small
* You don't need to care of setting up the execution environment
* Integration and international improvements are all in one place (shared environment setup etc)
* Generally speaking when using the KF5 content snap SDK you can get access to KDE neon's Qt and Frameworks without having to actually add the deb sources.
 
Disadvantages:
 
* Up-front "cost" of a single application may be higher. e.g. if the application only uses QtCore, the content snap will still bring in all of Qt and all of KF5 through the content snap. It's like a shared library, the more it is used the smaller the cost per-user.
* Somewhat harder to build and test because of added complexity. Also managing deb build dependencies in addition to content snap SDKs is problematic '''TBD link to forum post'''
* Unrelated changes in the content snaps may impair your snap
* Since this type was introduced a while after snap initially came into being you still can feel rough edges when working with content snaps.
 
==== Example ====
 
{{Input|<syntaxhighlight lang="yaml" line>
---
---
name: kbruch
name: blinken
version: 18.12.1
confinement: strict
confinement: strict
grade: stable
grade: stable
base: core18
base: core22
adopt-info: kbruch # part to adopt appstream data from (first in parse list)
adopt-info: blinken
apps:
apps:
     kbruch:
     blinken:
         command: kf5-launch kbruch
        extensions:
        - kde-neon-6
        common-id: org.kde.blinken.desktop
        desktop: usr/share/applications/org.kde.blinken.desktop
         command: usr/bin/blinken
         plugs:
         plugs:
        - kde-frameworks-5-plug
         - home
         - home
         - x11
         - system-backup
        - opengl
         command-chain:
         - network
         - snap/command-chain/desktop-launch6
         - network-bind
        - unity7
        - pulseaudio
        - desktop
        - desktop-legacy
        common-id: org.kde.kbruch.desktop
        desktop: "usr/share/applications/org.kde.kbruch.desktop"
slots:
slots:
     session-dbus-interface:
     session-dbus-interface:
         interface: dbus
         interface: dbus
         name: org.kde.kbruch
         name: org.kde.blinken
         bus: session
         bus: session
plugs:
package-repositories:
    kde-frameworks-5-plug:
-   type: apt
        content: kde-frameworks-5-core18-all
    components:
        interface: content
    - main
        default-provider: kde-frameworks-5-core18
    suites:
        target: kf5 # target directory where the content is mounted i.e. $SNAP/kf5/
    - jammy
    key-id: 444DABCF3667D0283F894EDDE6D4736255751E5D
    url: http://origin.archive.neon.kde.org/user
    key-server: keyserver.ubuntu.com
parts:
parts:
     kbruch:
     blinken:
        parse-info:
        - usr/share/metainfo/org.kde.blinken.appdata.xml
        plugin: cmake
        source: .
        source-type: local
        cmake-parameters:
        - -DCMAKE_INSTALL_PREFIX=/usr
        - -DCMAKE_BUILD_TYPE=RelWithDebInfo
        - -DQT_MAJOR_VERSION=6
        - -DBUILD_WITH_QT6=ON
        - -DBUILD_TESTING=OFF
        - -DCMAKE_INSTALL_SYSCONFDIR=/etc
        - -DCMAKE_INSTALL_LOCALSTATEDIR=/var
        - -DCMAKE_EXPORT_NO_PACKAGE_REGISTRY=ON
        - -DCMAKE_FIND_USE_PACKAGE_REGISTRY=OFF
        - -DCMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY=ON
        - -DCMAKE_INSTALL_RUNSTATEDIR=/run
        - -DCMAKE_SKIP_INSTALL_ALL_DEPENDENCY=ON
        - -DCMAKE_VERBOSE_MAKEFILE=ON
        - -DCMAKE_INSTALL_LIBDIR=lib/$CRAFT_ARCH_TRIPLET
        - --log-level=STATUS
        - -DCMAKE_LIBRARY_PATH=lib/$CRAFT_ARCH_TRIPLET
        prime:
        - -usr/lib/*/cmake/*
        - -usr/include/*
        - -usr/share/ECM/*
        - -usr/share/man/*
        - -usr/share/icons/breeze-dark*
        - -usr/bin/X11
        - -usr/lib/gcc/$SNAPCRAFT_ARCH_TRIPLET/6.0.0
        - -usr/lib/aspell/*
        - "-usr/share/lintian"
    cleanup:
        after:
        - blinken
        plugin: nil
         build-snaps:
         build-snaps:
         - kde-frameworks-5-core18-sdk
         - core22
        after:
        - kf6-core22
         - kde-frameworks-5-env
         - kde-qt6-core22
         plugin: cmake
         override-prime: |
        source: src
            set -eux
        configflags:
            for snap in "core22" "kf6-core22" "kde-qt6-core22"; do
        - "-DKDE_INSTALL_USE_QT_SYS_PATHS=ON"
                cd "/snap/$snap/current" && find . -type f,l -exec rm -rf "${CRAFT_PRIME}/{}" \;
        - "-DCMAKE_INSTALL_PREFIX=/usr"
            done
        - "-DCMAKE_BUILD_TYPE=Release"
        - "-DENABLE_TESTING=OFF"
        - "-DBUILD_TESTING=OFF"
        - "-DKDE_SKIP_TEST_SETTINGS=ON"
        parse-info: [usr/share/metainfo/org.kde.kbruch.appdata.xml]
    kde-frameworks-5-env:
        plugin: dump
        source: https://github.com/apachelogger/kf5-snap-env.git
</syntaxhighlight>}}
 
== Execution Environment and Launchers ==
 
When binaries inside snaps get executed they only get a super minimal environment set up by snapd. The snap itself needs to take care of most of the higher level spin up of the environment.
 
Inside a confined snap the <code>/</code> will be the core snap, while the actual snap will be in <code>SNAP=/snap/name/rev/...</code>. As a result for example icons, which usually would be expected in <code>$XDG_DATA_DIRS/icons</code> meaning <code>/usr/share/icons</code>, will need to actually be looked for in <code>$SNAP/usr/share/icons</code>. The same applies to pretty much all XDG_* variables, LD_LIBRARY_PATH, various QT_* variables and so on and so forth.
 
Simply put: a snap's tree is not "merged" with the core's tree, rather it is "mounted" inside the core tree under $SNAP and so each snap needs to set up an environment which redirects or adds $SNAP to all lookup locations you can possibly imagine. As general assumptions about where things are on a Linux system no longer hold true. One the one hand that technically allows you to create a snap which entirely does away with the [https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard FHS], on the other it means someone needs to actually mangle the environment so files may be located properly.
 
That's why most, if not all, desktop application snaps will need a launch helper. The launcher will set up all the general purpose path variables so they point to $SNAP. A standard desktop launcher implementation is available here https://github.com/ubuntu/snapcraft-desktop-helpers. Obviously you can also write your own, but since there are lots of things to consider, even for simple applications, it's probably not a good idea to do so.


You can have a look at the standard environment by running


{{Input|1=<nowiki>
snap install hello-world
snap run --shell hello-world
env
</nowiki>}}
This will drop you on a minimal shell inside the confined snap, where you can have a look around to see what the snap sees.
== A Snap from Scratch ==
== Standalone ==
We'll create a snap bundle from scratch using LXD, the KDE neon repositories, and will also look at how to make use of the KDE Frameworks 5 content snap. Using LXD and managing the environment manually  allows us to use the KDE neon repositories, it does however also mean that we need to take care of more things ourselves. It also means that snapcraft will need to be run with <code>--destructive-mode</code> to instruct it that it may install dependencies and the like into the actual system.
To follow along you'll need a working LXD setup. A KDE neon VM would do as well. Docker however is pretty unsuitable as we need a working systemd, which is hard to get with docker. To get started with LXD, you need to run <code>lxd init</code> on most distributions.
{{Input|<nowiki>
sudo lxc launch --ephemeral ubuntu:18.04 mycontainer # start an ephemeral container. it will be deleted once stopped
sudo lxc exec mycontainer -- bash
</nowiki>}}
You are now inside an Ubuntu 18.04 container. We'll continue to setup the neon repositories and the latest  stable snapcraft.
{{Input|<nowiki>
apt-key adv --keyserver keyserver.ubuntu.com --recv E6D4736255751E5D
echo 'deb http://archive.neon.kde.org/unstable bionic main' > /etc/apt/sources.list.d/neon.list
apt update
snap install --stable --classic snapcraft
mkdir /workspace
cd /workspace
</nowiki>}}
Now we can start writing our snapcraft.yaml. You can either install a command line editor and write it inside the container or write it on your system and "upload" it to the container with the command <code>sudo lxc file push --recursive snapcraft.yaml mycontainer/workspace/</code>. Let's start with the absolutely bare minimum.
{{Input|<syntaxhighlight lang="yaml" line>
---
name: kmplot
version: '0'
summary: Function Plotter
description: KmPlot is a program to plot graphs of functions.
confinement: strict
grade: stable
base: core18
parts:
    kmplot:
        plugin: cmake
        source: https://anongit.kde.org/kmplot.git
        configflags: ['-DCMAKE_INSTALL_PREFIX=/usr']
</syntaxhighlight>}}
</syntaxhighlight>}}


We've defined (not very good) metadata for the snap and a single part to build. Attempting to build this using <code>snapcraft --destructive-mode</code> will however result in an error similar to


{{Output|<nowiki>
CMake Error at CMakeLists.txt:1 (project):
  No CMAKE_CXX_COMPILER could be found.
</nowiki>}}


We haven't installed any of our build dependencies. An easy fix. We'll simply add build-packages to our part. In this case the compiler is missing and it's usually best pulled in via the package "build-essential". You'd continue building your build-packages list until the software starts building. Fortunately I already know all the stuff that is needed so we can move ahead.
Check [https://snapcraft.io/docs/snapcraft-yaml-reference Snapcraft YAML reference] if unsure.


{{Tip|1=For software which is packaged through KDE neon (which is just about everything KDE) you can get a good list to start with by looking at the debian/control file of the Neon/release branch of the packaging repository at https://packaging.neon.kde.org}}
=== Top Level ===


The part needs editing with build-packges specified before we can do another snapcraft run:
* name: blinken ← the snap name registered on the snap store
* confinement: strict ← Snaps are a containerised format and can't see the outside system from inside their container.  Strict is the normal container method.  Classic is also possible which allows it to see the outside system and is used by e.g. Kate because Kate needs to run external programs like Git.  It can only be Classic on request.  Can also be devmode for testing.
* grade: stable ← It must be stable to be in a released channel, can also be devel.
* base: core22 ← which base system to build on, core22 means Ubuntu 22.04 and is the current recommended.
* adopt-info: blinken ← Which Snap part to get the appstream info from.  This sets version, icon, description.


{{Input|<syntaxhighlight lang="yaml" line>
You might also need to add <code>version</code> if it is not in the appstream file. This is just a version read by users it does not affect the revision number which is tracked by the store.
parts:
    kmplot:
        plugin: cmake
        source: https://anongit.kde.org/kmplot.git
        source-branch: Applications/19.04 # not needed for master
        configflags: ['-DCMAKE_INSTALL_PREFIX=/usr']
        build-packages: [build-essential, extra-cmake-modules, libkf5widgetsaddons-dev, libqt5svg5-dev, libkf5parts-dev, libkf5doctools-dev, libkf5crash-dev, gettext, docbook-xml, docbook-xsl]
</syntaxhighlight>}}


Snapcrafting our refined snapcraft.yaml should finally succeed in creating a snap. Unfortunately it will also print a long list of missing dependencies:
=== apps ===
 
{{Output|<nowiki>
[...]
Staging kmplot
Priming kmplot
The 'kmplot' part needs the following libraries that are not included in the snap or base:
usr/lib/x86_64-linux-gnu/libGL.so.1
usr/lib/x86_64-linux-gnu/libGLX.so.0
usr/lib/x86_64-linux-gnu/libGLdispatch.so.0
usr/lib/x86_64-linux-gnu/libKF5Archive.so.5
usr/lib/x86_64-linux-gnu/libKF5Attica.so.5
usr/lib/x86_64-linux-gnu/libKF5AuthCore.so.5
usr/lib/x86_64-linux-gnu/libKF5Codecs.so.5
usr/lib/x86_64-linux-gnu/libKF5Completion.so.5
usr/lib/x86_64-linux-gnu/libKF5ConfigCore.so.5
usr/lib/x86_64-linux-gnu/libKF5ConfigGui.so.5
usr/lib/x86_64-linux-gnu/libKF5ConfigWidgets.so.5
usr/lib/x86_64-linux-gnu/libKF5CoreAddons.so.5
usr/lib/x86_64-linux-gnu/libKF5Crash.so.5
usr/lib/x86_64-linux-gnu/libKF5DBusAddons.so.5
usr/lib/x86_64-linux-gnu/libKF5GlobalAccel.so.5
usr/lib/x86_64-linux-gnu/libKF5GuiAddons.so.5
usr/lib/x86_64-linux-gnu/libKF5I18n.so.5
usr/lib/x86_64-linux-gnu/libKF5IconThemes.so.5
usr/lib/x86_64-linux-gnu/libKF5ItemViews.so.5
usr/lib/x86_64-linux-gnu/libKF5JobWidgets.so.5
usr/lib/x86_64-linux-gnu/libKF5KIOCore.so.5
usr/lib/x86_64-linux-gnu/libKF5KIOWidgets.so.5
usr/lib/x86_64-linux-gnu/libKF5Parts.so.5
usr/lib/x86_64-linux-gnu/libKF5Service.so.5
usr/lib/x86_64-linux-gnu/libKF5SonnetCore.so.5
usr/lib/x86_64-linux-gnu/libKF5SonnetUi.so.5
usr/lib/x86_64-linux-gnu/libKF5TextWidgets.so.5
usr/lib/x86_64-linux-gnu/libKF5WidgetsAddons.so.5
usr/lib/x86_64-linux-gnu/libKF5WindowSystem.so.5
usr/lib/x86_64-linux-gnu/libKF5XmlGui.so.5
usr/lib/x86_64-linux-gnu/libQt5Core.so.5
usr/lib/x86_64-linux-gnu/libQt5DBus.so.5
usr/lib/x86_64-linux-gnu/libQt5Gui.so.5
usr/lib/x86_64-linux-gnu/libQt5Network.so.5
usr/lib/x86_64-linux-gnu/libQt5PrintSupport.so.5
usr/lib/x86_64-linux-gnu/libQt5Svg.so.5
usr/lib/x86_64-linux-gnu/libQt5TextToSpeech.so.5
usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
usr/lib/x86_64-linux-gnu/libQt5X11Extras.so.5
usr/lib/x86_64-linux-gnu/libQt5Xml.so.5
usr/lib/x86_64-linux-gnu/libX11.so.6
usr/lib/x86_64-linux-gnu/libXau.so.6
usr/lib/x86_64-linux-gnu/libXdmcp.so.6
usr/lib/x86_64-linux-gnu/libdouble-conversion.so.1
usr/lib/x86_64-linux-gnu/libfam.so.0
usr/lib/x86_64-linux-gnu/libfreetype.so.6
usr/lib/x86_64-linux-gnu/libgraphite2.so.3
usr/lib/x86_64-linux-gnu/libharfbuzz.so.0
usr/lib/x86_64-linux-gnu/libicudata.so.60
usr/lib/x86_64-linux-gnu/libicui18n.so.60
usr/lib/x86_64-linux-gnu/libicuuc.so.60
usr/lib/x86_64-linux-gnu/libpcre2-16.so.0
usr/lib/x86_64-linux-gnu/libpng16.so.16
usr/lib/x86_64-linux-gnu/libxcb-keysyms.so.1
usr/lib/x86_64-linux-gnu/libxcb.so.1
These dependencies can be satisfied via more stage-packages, more parts, or content sharing.
Snapping 'kmplot' |                                                                                                                                 
Snapped kmplot_18.12.1_amd64.snap
</nowiki>}}
 
We can build the software fine, but snapcraft is concerned that we haven't "snapped" all the necessary dependencies. The list it prints is by no means exhaustive, it's only things snapcraft can easily detect, such as missing shared libraries. Getting the necessary dependencies on board is a bit tricky. The easiest way would be to simply take the list of build-packages and use the exact same list as stage-packages. Stage packages (and their dependencies) get put into the final snap. So, by using the build-packages also as stage-packages we'd put a whole bunch of buildtime-only stuff into our final snap unless we explicitly exclude files from getting primed. Since that is somewhat unreliable and probably not particularly advisable unless you have a firm grasp of all concepts involved, we'll opt to do it the other way around and only stage packages we know are need. So from the list of build-packages packages we'll simply look at their dependencies and try to infer which of their dependencies we need (if any).
 
* build-essential we'll leave out entirely, as the name suggests it only contains buildtime stuff such as make and gcc
* extra-cmake-modules similarly is only useful at build time as it contains cmake extensions
* libkf5widgetsaddons-dev is a dev package of a library and thus actually needed at runtime.
 
We'll inspect its package relationships with the command <code>apt show libkf5widgetsaddons-dev | grep -P "(Depends|Recommends)"</code>.
 
{{Output|<nowiki>
Depends: libkf5widgetsaddons5 (= 5.56.0+p18.04+git20190322.0124-0), qtbase5-dev (>= 5.8.0~)
Recommends: libkf5widgetsaddons-doc (= 5.56.0+p18.04+git20190322.0124-0)
</nowiki>}}
 
We can ignore qtbase5, its another dev package.  libkf5widgetsaddons-doc is library documentation which we also do not need. libkf5widgetsaddons5 is the actual library and we'll definitely want it staged.
We'll continue this review for all build-packages:
 
* libqt5svg5-dev: libray is libqt5svg5
* libkf5parts-dev: library is libkf5parts5
* libkf5doctools-dev: buildtime only, builds documentation
* gettext: buildtime only, builds localization
* docbook-xml, docbook-xsl: buildtime only, build documentation
* libkf5crash-dev: library is libkf5crash5
 
We now have a list of packages that need staging and can update our part accordingly:
 
{{Input|<syntaxhighlight lang="yaml" line>
parts:
    kmplot:
        plugin: cmake
        source: https://anongit.kde.org/kmplot.git
        source-branch: Applications/19.04 # not needed for master
        configflags: ['-DCMAKE_INSTALL_PREFIX=/usr']
        build-packages: [build-essential, extra-cmake-modules, libkf5widgetsaddons-dev, libqt5svg5-dev, libkf5parts-dev, libkf5doctools-dev, libkf5crash-dev, gettext, docbook-xml, docbook-xsl]
        stage-packages: [libkf5widgetsaddons5, libqt5svg5, libkf5parts5, libkf5crash5]
</syntaxhighlight>}}
 
Snapcrafting our refined data should now create a snap without any additional warnings. This should be good enough to now. You can pull the snap out of the container with <code>sudo lxc file pull mycontainer/workspace/kmplot_0_amd64.snap .</code> and install it into with <code>snap install --force-dangerous kmplot_0_amd64.snap</code> and try to run it <code>snap run kmplot</code>. Unsuccessfully...
 
{{Output|<nowiki>
error: cannot find app "kmplot" in "kmplot"
</nowiki>}}
 
Before snapd can run an application the snap first needs to declare one.  Let's add one:


{{Input|<syntaxhighlight lang="yaml" line>
{{Input|<syntaxhighlight lang="yaml" line>
apps:
apps:
     kmplot:
     blinken:
         command: kmplot
         extensions:
         plugs: [x11, opengl, desktop, desktop-legacy]
         - kde-neon-6
</syntaxhighlight>}}
        common-id: org.kde.blinken.desktop
 
        desktop: usr/share/applications/org.kde.blinken.desktop
Plugs are a bit out of scope, for more information on them refer to the upstream snapcraft documentation. Snapcrafting, installing and running the new snap will unfortunately still result in problems:
        command: usr/bin/blinken
 
        plugs:
{{Output|<nowiki>
        - home
kmplot(4481)/(qt.qpa.plugin) unknown: Could not find the Qt platform plugin "xcb" in ""
        - system-backup
kmplot(4481)/(default) unknown: This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this proble
        command-chain:
</nowiki>}}
        - snap/command-chain/desktop-launch6
 
This is where the environment helpers come in. Qt attempts to find the xcb plugin in /usr/... but actually needs to look in $SNAP/usr/... Easiest solution is using the kf5-launch helper. We need to define a new part for it. It doesn't have any build system so we simply use the 'dump' plugin which copies the source tree verbatim into the snap. One caveat is that we need to skip the kf5 directory, otherwise the launcher thinks we want to use it with the KF5 content snap. To skip directories or files in one of the stage or prime lists simply prefix it with a minus character:


{{Input|<syntaxhighlight lang="yaml" line>
parts:
    [...]
    kde-frameworks-5-env:
        plugin: dump
        source: https://github.com/apachelogger/kf5-snap-env.git
        stage: ['-kf5/'] # keeps it out of
</syntaxhighlight>}}
</syntaxhighlight>}}


snapcraft and install again and you should see some progress:
<code>apps</code> are the programs which the snap includes for users to run.  Usually there is only one in a Snap but sometimes e.g. [https://invent.kde.org/office/calligra/-/blob/calligra/3.3/snapcraft.yaml?ref_type=heads Calligra] there are more than one.


{{Output|<nowiki>
The [https://snapcraft.io/docs/kde-neon-extension KDE neon extension] adds some commonly used features to the KDE snaps including using the [https://snapcraft.io/kf6-core22 content Snap].
kmplot(8069)/(default) unknown: "Couldn't register name 'org.kde.kmplot-8069' with DBUS - another process owns it already!"
</nowiki>}}


Many of our applications will expose themselves on the dbus session bus. Doing so does require extra permissions though. The snap needs a dbus slot. A dbus slot allows us to claim a well-known name on the session bus, meaning snapd will allow us to register a service on it. In our case the name is org.kde.kmplot and snapd will be smart enough to figure out that this name also entails our per-process variants org.kde.kmplot-$PID. To define a slot we'll need a completely new section in our snapcraft.yaml:
The <code>common-id</code> comes from the Appstream file.  You ''must'' check what it is in the appstream file. <code>org.kde.blinken.appdata.xml</code> contains <code><id>org.kde.blinken.desktop</id></code> so we use that.  Sometimes apps use the .desktop and sometimes they don't, this is at random.


{{Input|<syntaxhighlight lang="yaml" line>
The command to run is listed.  The KDE neon extension will run a script first which sets many necessary environment variables.
slots:
    session-dbus-interface: # semi-arbitrary name
        interface: dbus # type of the interface; this allows snapd to restrict/allow what is necessary
        name: org.kde.kmplot
        bus: session
</syntaxhighlight>}}


With this the application should now be actually starting. Alas, it looks terrible! We haven't yet added any styles or icons! Let's solve this. We can either use a new part of extend our kmplot. For easier reading we'll use a new part, in practice that is not really necessary though.
The plugs give access to the outside system, see [https://snapcraft.io/docs/supported-interfaces Supported interfaces] for descriptions.  When a Snap is installed from the Store it is up to the Store to say which plugs get used. Those listed as auto connect in the docs are permitted.  Otherwise you must ask on the Snap forum for permission to have the Snap connected. (Locally installed snaps with --devmode have access to everything, you can also manually connect snaps to interfaces on your local system.)


{{Input|<syntaxhighlight lang="yaml" line>
<code>slots</code> are the complement to plugs, they allow the outside system to access our Snap app. In this case we are allowing a dbus interface into the Snap. All KDE apps have a dbus interface and you can check what it is called by running the app and using <code>qdbus</code>.
parts:
    [...]
    plasma-integration:
        plugin: nil
        stage-packages: [plasma-integration] # this also pulls in all of breeze, so it forms a complete theme set
</syntaxhighlight>}}


Snapcraft and install and the application should look like a piece of art! In fact, it's almost ready for production now. We are still missing a bunch of metadata.
<code>package-repositories</code> add the KDE neon apt repo to build against, this will give you the latest libraries to compile with.


# Desktop file (so snapd can shove us into the host's XDG menu)
The source of a Snap is the <code>parts</code> and some snaps have several parts made of different sources e.g. [TBD] has both libktorrent and ktorrent parts. Blinken is not complex so it has only one part made of the compiles Blinken source.
# Icon file for snap store
# Appstream association, so discover can find the snap


Since we can derive all of this from appstream data we'll start by extending our kmplot part to parse the appstream file by setting the 'parse-info' attribute:
=== Parts ===


{{Input|<syntaxhighlight lang="yaml" line>
* plugin ← which [https://snapcraft.io/docs/supported-plugins Snap build plugin] to use
parts:
* build-packages ← most build packages are in the KDE Frameworks content snap but some need added explicitly and some are not in there.  They will be downloaded from the neon and ubuntu apt repos.  [TBD] uses non-KDE libraries and it needs to list the -dev packages in the <code>build-packages</code> then the library itself in the <code>stage-packages</code>.
    [...]
* source ← link to the tar
    kmplot:
* cmake-parameters ← copy and paste this, it sets the right paths.
        plugin: cmake
* parse-info ← where the appstream file is to be installed
        source: https://anongit.kde.org/kmplot.git
* filesets and prime ← snap parts get build then copied into a <code>stage</code> area, when all the parts are built they are copied into the <code>prime</code> area which is converted into the Snap package. This lists a common set of excluded files we do not want copied. You can add more here if you end up with unnecessary files in your snap.
        source-branch: Applications/19.04 # not needed for master
        configflags: ['-DCMAKE_INSTALL_PREFIX=/usr']
        build-packages: [build-essential, extra-cmake-modules, libkf5widgetsaddons-dev, libqt5svg5-dev, libkf5parts-dev, libkf5doctools-dev, libkf5crash-dev, gettext, docbook-xml, docbook-xsl]
        stage-packages: [libkf5widgetsaddons5, libqt5svg5, libkf5parts5, libkf5crash5]
        parse-info: [usr/share/metainfo/org.kde.kmplot.appdata.xml]
</syntaxhighlight>}}


From this we can let our app automatically determine the desktop file and also hint the appstream id to discover (appstream id is called common-id in snap):
== Building ==
 
Install snapcraft with <code>snap install snapcraft --classic</code>.
{{Input|<syntaxhighlight lang="yaml" line>
apps:
    kmplot:
        command: kmplot
        plugs: [x11, opengl, desktop, desktop-legacy]
        common-id: org.kde.kmplot.desktop
</syntaxhighlight>}}
 
And lastly we can drop our summary and description fields in favor of adopting the information from the parsed appstream data of our kmplot part. The entire refined snapcraft.yaml should now look something like this:
 
{{Input|<syntaxhighlight lang="yaml" line>
---
name: kmplot
version: '0'
confinement: strict
grade: stable
base: core18
adopt-info: kmplot # part to adopt from (first in parse list)
 
apps:
    kmplot:
        command: kf5-launch kmplot
        plugs: [x11, opengl, desktop, desktop-legacy]
        common-id: org.kde.kmplot.desktop


slots:
In the directory with the <code>snapcraft.yaml</code> run: <code>snapcraft</code>
    session-dbus-interface: # semi-arbitrary name
        interface: dbus # type of the interface; this allows snapd to restrict/allow what is necessary
        name: org.kde.kmplot
        bus: session


parts:
This will start a virtual machine and build the package. If all is well you will have <code>blinken_24.05_amd64.snap</code> or similar created. If you had not installed lxd before, you may need to add your user to the lxd group to allow access to lxd.
    kmplot:
        plugin: cmake
        source: https://anongit.kde.org/kmplot.git
        source-branch: Applications/19.04 # not needed for master
        configflags: ['-DCMAKE_INSTALL_PREFIX=/usr']
        build-packages: [build-essential, extra-cmake-modules, libkf5widgetsaddons-dev, libqt5svg5-dev, libkf5parts-dev, libkf5doctools-dev, libkf5crash-dev, gettext, docbook-xml, docbook-xsl]
        stage-packages: [libkf5widgetsaddons5, libqt5svg5, libkf5parts5, libkf5crash5]
        parse-info: [usr/share/metainfo/org.kde.kmplot.appdata.xml]
    kde-frameworks-5-env:
        plugin: dump
        source: https://github.com/apachelogger/kf5-snap-env.git
        stage: ['-kf5/'] # keeps it out of
    plasma-integration:
        plugin: nil
        stage-packages: [plasma-integration] # this also pulls in all of breeze
</syntaxhighlight>}}
 
Before you run snapcraft this time, make sure to clean up everything <code>rm -r prime stage parts</code>. Appstream extraction strings across multiple stages, so you'll need a fairly clean build to actually pick up the data. After installation you should find kmplot listed in your menu and having an icon.
 
This snap is now in a very good shape and could be uploaded to the snap [https://snapcraft.io/store store] for either public testing or actual release.


=== Using a Content Snap ===
Install with <code>snap install blinken_24.05_amd64.snap --devmode</code>.
 
You should read through the standalone example first. Content snaps build on how standalone snaps are built and much of the same concepts are involved, so much so that understanding standalone snap building is almost a prerequisite.
 
Now that we have a lovely kmplot snap we can try to optimize it. While the standalone snap is fully functional and perfect as it is, it's also very large. Size may vary over time but at the time of writing it's about 115 MiB. That is fairly excessive, considering how tiny the application really is.
 
As mentioned earlier a good way to bring down the footprint of an application is to use the KF5 content snap. It allows sharing the entire KF5/Qt/Mesa stack across multiple snaps. Ideally this comes at next to no extra cost.
 
Let's start converting the snapcraft.yaml. We start with what we had for our standalone snap. The first change is to move away from our manual build-packages and stage-packages list. Content snaps may provide their own SDKs called build-snaps. They will generally include just about everything needed to build a snap on top of the content snap. What exactly is included is entirely up to the build-snap authors. In KF5's case Make and GCC are not included, so we'll want to still pull them in via
build-packages. But since we only have KF5 and Qt5 dependencies in our stage-packages list we can probably entirely get rid of it. Same goes for the plasma-integration part since the content snap comes with its own runtime theming. Lastly, as previously mentioned, the KF5 launcher we use needs to have the kf5/ place holder directory available, so we'll also update the
env part accordingly. To make the changes easier to spot they have a change comment after them:
 
{{Input|<syntaxhighlight lang="yaml" line>
parts:
    [...]
    kmplot:
        plugin: cmake
        source: https://anongit.kde.org/kmplot.git
        source-branch: Applications/19.04
        configflags: ['-DCMAKE_INSTALL_PREFIX=/usr']
        build-packages: [build-essential, docbook-xml, docbook-xsl] # dropped everything kf5+qt5
        build-snaps: [kde-frameworks-5-core18-sdk] # new, installs SDK via snapd
        # stage-packages gone entirely
        parse-info: [usr/share/metainfo/org.kde.kmplot.appdata.xml]
    kde-frameworks-5-env:
        plugin: dump
        source: https://github.com/apachelogger/kf5-snap-env.git
        # stage rule is gone, we now want everything from the tree
  # plasma-integration is gone entirely
</syntaxhighlight>}}


This will pass snapcraft, but once again not be functional. The building itself largely works out of the box because of additional magic in snapcraft itself. When setting a build-snap it will automatically inject the build-snap's paths into CMAKE_FIND_ROOT_PATH which allows all cmake find_* commands to locate assets in the snap's in addition to the system paths. This ideally means you can mix additional dependencies in via the build-packages. In practice this may be more complicated because you then may have two sources provide the same library or cmake package. This can become a problem, so watch your cmake output carefully when using build-snaps.
Run with <code>blinken</code> or check if blinken is available in the app menu and run it from there (remove any versions of blink you have installed from your normal distro packages just to be sure). <code>which -a blinken</code> should say that blinken is available at least as <code>/snap/bin/blinken</code>.


{{Output|<nowiki>
== Quirks ==
$ snap run kmplot
You need to connect this snap to the kde-frameworks-5-core18 snap
</nowiki>}}


The snap is again broken because we haven't actually added anything to indicate what kind of content snap we want to use. We only use the build-snap thus far. Unfortunately using the content snap is a bit more lines of yaml. Similarly to how we earlier defined a slot for dbus we'll now need to define a plug for the KF5 content snap. Conceptually the way this works is that the content snap (e.g. the KF5 one) defines a 'content' slot, while we define a 'content' plug that uses the specific slot defined by the content snap, and snapd will then auto connect a matching combination of the slot and plug. The plug definition is fairly boilerplate and doesn't really need changing, ever. On top of this we'll also need to mark our kmplot app as wanting to use the plug, we'll simply add it to the already existing plugs list there:
=== Patches ===


If you need to update some code in the release you can patch it in the Snap package.  But please get the patch upstream into the Git archive first.
To add a patch to a snap, one must add the snippet to the part that needs patched:
{{Input|<syntaxhighlight lang="yaml" line>
{{Input|<syntaxhighlight lang="yaml" line>
apps:
         override-pull: |
    kmplot:
            craftctl default
         command: kf5-launch kmplot
              for file in ${CRAFT_PROJECT_DIR}/snap/local/patches/*
        plugs: [x11, opengl, desktop, desktop-legacy, kde-frameworks-5-plug]
              do
        common-id: org.kde.kmplot.desktop
                patch -i $file -p 1
 
              done
plugs:
    kde-frameworks-5-plug: # semi-arbitrary name
        interface: content # type
        content: kde-frameworks-5-core18-all # the slot
        default-provider: kde-frameworks-5-core18 # the snap which provides the slot (ideally)
        target: kf5 # the target path where we want the content snap mounted into our scope
</syntaxhighlight>}}
</syntaxhighlight>}}


snapcraft & install and we should now have a working ... Or maybe we don't :)
Add add the patch in snap/local/patches/


You'll encounter the same error when running, except this time the commands it tells you to run will actually work. The reason for this is that automatic connection of some interfaces (among them the content type) is not by default enabled. Content snaps auto connection works when the application snap and the content snap are from the same publisher though.
== Help ==


Simply put: when your snap is offered by the KDE account on the snap store it will auto connect, when sideloading/installing manual builds it won't auto-connect.
[https://snapcraft.io/docs Snapcraft docs] including tutorials on using and building.


So, run <code>snap connect kmplot:kde-frameworks-5-plug kde-frameworks-5-core18:kde-frameworks-5-core18-slot</code> and the application should start working.
[https://snapcraft.io/docs/snap-format snapcraft.yaml format].


At this point you may also note that while the old snap was more than 100 MiB in size, the new one isn't even 1!
[https://forum.snapcraft.io/ Snap forum] for asking for help or asking to get the store to allow your Snaps to auto connect.


The final snapcraft.yaml looks like this:
[https://webchat.kde.org/#/room/#kde-neon:kde.org KDE neon devs] talk to sgmoore for help getting your app into the Store.
 
{{Input|<syntaxhighlight lang="yaml" line>
---
name: kmplot
version: '0'
confinement: strict
grade: stable
base: core18
adopt-info: kmplot # part to adopt from (first in parse list)
 
apps:
    kmplot:
        command: kf5-launch kmplot
        plugs: [x11, opengl, desktop, desktop-legacy, kde-frameworks-5-plug]
        common-id: org.kde.kmplot.desktop
 
plugs:
    kde-frameworks-5-plug: # semi-arbitrary name
        interface: content # type
        content: kde-frameworks-5-core18-all # the slot
        default-provider: kde-frameworks-5-core18 # the snap which provides the slot (ideally)
        target: kf5 # the target path where we want the content snap mounted into our scope
 
slots:
    session-dbus-interface: # semi-arbitrary name
        interface: dbus # type of the interface; this allows snapd to restrict/allow what is necessary
        name: org.kde.kmplot
        bus: session
 
parts:
    kmplot:
        plugin: cmake
        source: https://anongit.kde.org/kmplot.git
        source-branch: Applications/19.04
        configflags: ['-DCMAKE_INSTALL_PREFIX=/usr']
        build-packages: [build-essential, docbook-xml, docbook-xsl] # dropped everything kf5+qt5
        build-snaps: [kde-frameworks-5-core18-sdk/latest/candidate] # new, installs SDK via snapd
        # stage-packages gone entirely
        parse-info: [usr/share/metainfo/org.kde.kmplot.appdata.xml]
    kde-frameworks-5-env:
        plugin: dump
        source: https://github.com/apachelogger/kf5-snap-env.git
        # stage rule is gone, we now want everything from the tree
</syntaxhighlight>}}


== Glossary ==
== Glossary ==
Line 784: Line 430:
Words you'll hear and not know what they mean:
Words you'll hear and not know what they mean:


* '''snap''': The actual bundle format.
* '''snap''': The actual package format.
* '''snapd''': The daemon that manages snap on a system.
* '''snapd''': The daemon that manages snap on a system.
* '''snapcraft''': The build tool for building a snap.
* '''snapcraft''': The build tool for building a snap.

Latest revision as of 10:39, 26 September 2024

 
Under Construction
This is a new page, currently under construction!


Put Your App in the Snap Store

Snap Store KDE Page

It is a KDE goal to be All About the Apps to deliver our apps directly to our users. Snaps is one of the ways of doing this. Snaps are Linux app packages that can run on pretty much any Linux operating system. There is a single centralized Snap store ( https://snapcraft.io/store ) that delivers them to users. Take a look at the KDE page on the Snap Store to see what's available.

Snap intro

A Snap package typically contains all the files, including libraries and data files, to run the app. There are also Content Snaps which contain reusable libraries. KDE has the KDE Frameworks Content Snaps for KF6 and for KF5 which include recent Qt and KDE Frameworks and are shared between all KDE apps so we do not have to waste disk space and build resources.

For KDE Frameworks 6, the content pack has been split in three:

The first two are needed to build a snap depending on the KDE Frameworks stack, the third one is the only one needed at runtime.

Give it a try by installing a package or two on your system, e.g. the KDE desktop notepad app:

snap install kwrite

And run kwrite from your apps menu.

This will have downloaded the kwrite Snap package from the Snap store into e.g. /var/lib/snapd/snaps/kwrite_4.snap and mounted it into e.g. /snap/kwrite/current/. You can also just download it to a local directory with snap download kwrite, use lesspipe kwrite*snap to see what is inside it.

snap list will show your currently installed snaps and it will now show that you have kwrite and the content snap kf6-core22 as well as the core22 content snap installed.

Snaps are containers, similar to Docker. From inside the Snap container access to the file system and system resources are limited. This is good for inter-app security but means the app sees your system quite differently from how you might expect. You can "log" into the container with snap run --shell kwrite to have a look at how the snapped kwrite app sees your system.

To give the app controlled permissions to the system it plugs connections into resources such as the network or container snaps. Run snap connections kwrite to see what it gets given access to. The auto connections are controlled by the snap store and app maintainers need to ask the snap store for the desired auto-connections. Connections can also be overridden locally.

You can take a look at the snap package with snap download kwrite which will download files such as kwrite_4.assert and kwrite_4.snap. The .assert has the checksums and signatures for the package. The .snap has the (non-store) metadata and all the files of the package. lesspipe kwrite_4.snap to take a look.

How to contribute to the Qt6 and KF6 content snaps

kde-qt6-core22-sdk

First we start with the Qt6 SDK snap by cloning the kde-qt6-core-sdk repository.

This snap should not need much altering. Versions on the store are coming from the CI. In the snapcraft.yaml file the version value will indicate which Qt version is built.

When updating is needed, changing this value should be enough. Commit and push which should trigger the CI build.

Once this is completed and uploaded to the Snap store, you can proceed tuning the other content snaps.

kf6-core22-sdk

The KDE Frameworks SDK snap recipe is cloned from the kf6-core-sdk repository.

This file is very long holding one part per framework. Here is an example:

kcrash:
    after:
        - extra-cmake-modules
        - kcoreaddons
        - kwindowsystem
    source: https://invent.kde.org/frameworks/kcrash.git
    source-tag: *kf6-version
    source-depth: 1
    build-packages:
        - doxygen
        - graphviz
        - libx11-dev
    build-snaps:
        - kde-qt6-core22-sdk
    plugin: cmake
    cmake-generator: Ninja
    cmake-parameters: *cmakeparameters
    build-environment: *buildenvironment
    stage-packages:
        - libx11-6

The name of the framework built above is kcrash. It is built after extra-cmake-modules, kcoreaddons and kwindowsystem (each of those must be listed as another part in the file). It also indicates the extra deb or snap package dependencies needed to build the framework, the build options and so on.

For further details on the snapcraft.yaml syntax and features please head to the Snapcraft reference.

Once you have all of your parts in place and are ready to build. You can test locally using snapcraft. When satisfied, commit and push which will trigger the CI.

This file shouldn't need much altering unless there is a new framework. You will however need to update the release version in 2 places when building a new release.

version: 6.6.0
    extra-cmake-modules:
        after:
        - qtconf
        source: https://invent.kde.org/frameworks/extra-cmake-modules.git
        source-tag: &kf6-version v6.6.0
 ...

kf6-core22

Once we're happy with the two SDKs the runtime must be updated. Its recipe is coming from the kf6-core repository.

It is merely assembling and pruning the previous two snaps to have only what's required at runtime. The recipe often doesn't need to be changed but (even though the CI should do it automatically) the CI build might need to be triggered manually to pickup the SDKs latest changes.

Creating Applications Snaps

Fork the application into your userspace. Aka fork https://invent.kde.org/utilities/ark into your user account on invent. Make sure you on the master branch. Add your snapcraft.yaml You can use the ark snapcraft.yaml as a template.

# SPDX-FileCopyrightText: 2023 Scarlett Moore <[email protected]>
#
# SPDX-License-Identifier: CC0-1.0
---
name: ark
confinement: strict
grade: stable
base: core22
adopt-info: ark
apps:
    ark:
        extensions:
        - kde-neon-6
        common-id: org.kde.ark.desktop
        desktop: usr/share/applications/org.kde.ark.desktop
        command: usr/bin/ark
        plugs:
        - home
        - system-backup
        command-chain:
        - snap/command-chain/desktop-launch6
slots:
    session-dbus-interface:
        interface: dbus
        name: org.kde.ark
        bus: session
package-repositories:
-   type: apt
    components:
    - main
    suites:
    - jammy
    key-id: 444DABCF3667D0283F894EDDE6D4736255751E5D
    url: http://origin.archive.neon.kde.org/user
    key-server: keyserver.ubuntu.com
parts:
    ark:
        parse-info:
        - usr/share/metainfo/org.kde.ark.appdata.xml
        plugin: cmake
        build-packages:
        - libarchive-dev
        - libbz2-dev
        - liblzma-dev
        - libzip5-dev
        - pkg-config
        - zlib1g-dev
        stage-packages:
        - bzip2
        - p7zip-full
        - unrar
        - unzip
        - zip
        - to amd64: [rar]
        - libarchive13
        - libzip4
        - zlib1g
        source: .
        source-type: local
        cmake-parameters:
        - -DCMAKE_INSTALL_PREFIX=/usr
        - -DCMAKE_BUILD_TYPE=Release
        - -DQT_MAJOR_VERSION=6
        - -DBUILD_WITH_QT6=ON
        - -DBUILD_TESTING=OFF
        - -DCMAKE_INSTALL_SYSCONFDIR=/etc
        - -DCMAKE_INSTALL_LOCALSTATEDIR=/var
        - -DCMAKE_EXPORT_NO_PACKAGE_REGISTRY=ON
        - -DCMAKE_FIND_USE_PACKAGE_REGISTRY=OFF
        - -DCMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY=ON
        - -DCMAKE_INSTALL_RUNSTATEDIR=/run
        - -DCMAKE_SKIP_INSTALL_ALL_DEPENDENCY=ON
        - -DCMAKE_VERBOSE_MAKEFILE=ON
        - -DCMAKE_INSTALL_LIBDIR=lib/$CRAFT_ARCH_TRIPLET
        - --log-level=STATUS
        - -DCMAKE_LIBRARY_PATH=lib/$CRAFT_ARCH_TRIPLET
        prime:
        - -usr/lib/*/cmake/*
        - -usr/include/*
        - -usr/share/ECM/*
        - -usr/share/man/*
        - -usr/bin/X11
        - -usr/lib/gcc/$CRAFT_ARCH_TRIPLET_BUILD_FOR/6.0.0
        - -usr/lib/aspell/*
        - -usr/share/lintian
    cleanup:
        after:
        - ark
        plugin: nil
        build-snaps:
        - core22
        - kf6-core22
        override-prime: |
            set -eux
            for snap in "core22" "kf6-core22"; do
                cd "/snap/$snap/current" && find . -type f,l -exec rm -rf "${CRAFT_PRIME}/{}" \;
            done


git add snapcraft.yaml
git commit

Add the changes you made to the commit message.

git push

Now create a merge request.

Once you are happy with the snap (master will publish to edge) you will want to commit your snapcraft.yaml to the current release branch, aka Release/24.08

Concepts

Snaps are usually one app per Snap package. The Snap package contains all the libraries and resources it needs to run except those in the shared content kf6-core22 Snap.

In practice this means all of Qt and KF(5/6) including Breeze icons and themes are in the kde-frameworks content Snap and your app Snap only needs to compile its own sources. If you apps needs more libraries you can either install these as DEB packages e.g. from the Linux operating system Ubuntu LTS or KDE neon, or you can compile them from source as well. You will need to manually list the build-packages (all the -dev packages) and the stage-packages used in the final package, it'll warn you if any final libraries it expects are missing.

snapcraft is used to build snaps. It can be installed as a snap with snap install snapcraft --classic. A snap package (app) is defined in the snapcraft.yaml file. Snapcraft will build the package inside a virtual machine; it uses LXD to build the KDE snap packages. Using a virtual machine makes it reliable to build the Snaps on different Linux operating systems with identical results.

snapcraft.yaml files are kept in their respective upstream repo. Eg. kcalc snapcraft file resides https://invent.kde.org/utilities/kcalc/-/blob/release/23.08/snapcraft.yaml?ref_type=heads

Our Snaps read metadata from AppStream metadata files so it is important the metadata is up to date including current release versions.

The Snap Store is the centralized app store by Canonical. There is no practical way to use other stores or repositories with Snaps. It is what Snapcraft uploads built snaps to and what your local snapd will download and install snaps from. It also says what permissions those snaps should have. As an app developer if you want your app to have extra permissions (for example kdf uses mount-observe) then you need to ask for it on the snapcraft forum.

A Classic containment Snap has no restrictions on what files it can see on your system or what external executable can be run. This is useful for Integrated Development Environments (IDEs) and similar apps such as Kate which runs external programs. Again this needs to be set in your [1] then you need to ask on the Snap forum for the store to set it to classic. The Store will then tell snapd for anyone installing the Snap to have it installed as a Classic confinement Snap.

The KDE account on the Snap store is run by the Snap team developers Jonathan Esk-Riddell, Harald Sitter, Scarlett Moore, Carlos DeMaine, Kevin Ottens, Benjamin Port. One Snap on the store can be shared between more than one account so app maintainers can also create a separate account if they want to have more control over when their app is released and what the store says about it.

The store has four channels for different levels of stability. Our stable branch builds get uploaded to the Candidate channel and can be moved to the Stable channel once tested.

Example

Blinken is an exciting memory game from KDE. It's available on the Snap store. The Snap package is defined by a snapcraft.yaml file which is in the https://invent.kde.org/education/blinken/-/tree/release/24.08?ref_type=heads repo. Any update to that branch triggers a build of the [2] in launchpad. If the build is successful it will be uploaded to the Candidate channel of the Snap store ready for review.

# SPDX-FileCopyrightText: 2024 Scarlett Moore <[email protected]>
#
# SPDX-License-Identifier: CC0-1.0
---
name: blinken
confinement: strict
grade: stable
base: core22
adopt-info: blinken
apps:
    blinken:
        extensions:
        - kde-neon-6
        common-id: org.kde.blinken.desktop
        desktop: usr/share/applications/org.kde.blinken.desktop
        command: usr/bin/blinken
        plugs:
        - home
        - system-backup
        command-chain:
        - snap/command-chain/desktop-launch6
slots:
    session-dbus-interface:
        interface: dbus
        name: org.kde.blinken
        bus: session
package-repositories:
-   type: apt
    components:
    - main
    suites:
    - jammy
    key-id: 444DABCF3667D0283F894EDDE6D4736255751E5D
    url: http://origin.archive.neon.kde.org/user
    key-server: keyserver.ubuntu.com
parts:
    blinken:
        parse-info:
        - usr/share/metainfo/org.kde.blinken.appdata.xml
        plugin: cmake
        source: .
        source-type: local
        cmake-parameters:
        - -DCMAKE_INSTALL_PREFIX=/usr
        - -DCMAKE_BUILD_TYPE=RelWithDebInfo
        - -DQT_MAJOR_VERSION=6
        - -DBUILD_WITH_QT6=ON
        - -DBUILD_TESTING=OFF
        - -DCMAKE_INSTALL_SYSCONFDIR=/etc
        - -DCMAKE_INSTALL_LOCALSTATEDIR=/var
        - -DCMAKE_EXPORT_NO_PACKAGE_REGISTRY=ON
        - -DCMAKE_FIND_USE_PACKAGE_REGISTRY=OFF
        - -DCMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY=ON
        - -DCMAKE_INSTALL_RUNSTATEDIR=/run
        - -DCMAKE_SKIP_INSTALL_ALL_DEPENDENCY=ON
        - -DCMAKE_VERBOSE_MAKEFILE=ON
        - -DCMAKE_INSTALL_LIBDIR=lib/$CRAFT_ARCH_TRIPLET
        - --log-level=STATUS
        - -DCMAKE_LIBRARY_PATH=lib/$CRAFT_ARCH_TRIPLET
        prime:
        - -usr/lib/*/cmake/*
        - -usr/include/*
        - -usr/share/ECM/*
        - -usr/share/man/*
        - -usr/share/icons/breeze-dark*
        - -usr/bin/X11
        - -usr/lib/gcc/$SNAPCRAFT_ARCH_TRIPLET/6.0.0
        - -usr/lib/aspell/*
        - "-usr/share/lintian"
    cleanup:
        after:
        - blinken
        plugin: nil
        build-snaps:
        - core22
        - kf6-core22
        - kde-qt6-core22
        override-prime: |
            set -eux
            for snap in "core22" "kf6-core22" "kde-qt6-core22"; do
                cd "/snap/$snap/current" && find . -type f,l -exec rm -rf "${CRAFT_PRIME}/{}" \;
            done


Check Snapcraft YAML reference if unsure.

Top Level

  • name: blinken ← the snap name registered on the snap store
  • confinement: strict ← Snaps are a containerised format and can't see the outside system from inside their container. Strict is the normal container method. Classic is also possible which allows it to see the outside system and is used by e.g. Kate because Kate needs to run external programs like Git. It can only be Classic on request. Can also be devmode for testing.
  • grade: stable ← It must be stable to be in a released channel, can also be devel.
  • base: core22 ← which base system to build on, core22 means Ubuntu 22.04 and is the current recommended.
  • adopt-info: blinken ← Which Snap part to get the appstream info from. This sets version, icon, description.

You might also need to add version if it is not in the appstream file. This is just a version read by users it does not affect the revision number which is tracked by the store.

apps

apps:
    blinken:
        extensions:
        - kde-neon-6
        common-id: org.kde.blinken.desktop
        desktop: usr/share/applications/org.kde.blinken.desktop
        command: usr/bin/blinken
        plugs:
        - home
        - system-backup
        command-chain:
        - snap/command-chain/desktop-launch6

apps are the programs which the snap includes for users to run. Usually there is only one in a Snap but sometimes e.g. Calligra there are more than one.

The KDE neon extension adds some commonly used features to the KDE snaps including using the content Snap.

The common-id comes from the Appstream file. You must check what it is in the appstream file. org.kde.blinken.appdata.xml contains <id>org.kde.blinken.desktop</id> so we use that. Sometimes apps use the .desktop and sometimes they don't, this is at random.

The command to run is listed. The KDE neon extension will run a script first which sets many necessary environment variables.

The plugs give access to the outside system, see Supported interfaces for descriptions. When a Snap is installed from the Store it is up to the Store to say which plugs get used. Those listed as auto connect in the docs are permitted. Otherwise you must ask on the Snap forum for permission to have the Snap connected. (Locally installed snaps with --devmode have access to everything, you can also manually connect snaps to interfaces on your local system.)

slots are the complement to plugs, they allow the outside system to access our Snap app. In this case we are allowing a dbus interface into the Snap. All KDE apps have a dbus interface and you can check what it is called by running the app and using qdbus.

package-repositories add the KDE neon apt repo to build against, this will give you the latest libraries to compile with.

The source of a Snap is the parts and some snaps have several parts made of different sources e.g. [TBD] has both libktorrent and ktorrent parts. Blinken is not complex so it has only one part made of the compiles Blinken source.

Parts

  • plugin ← which Snap build plugin to use
  • build-packages ← most build packages are in the KDE Frameworks content snap but some need added explicitly and some are not in there. They will be downloaded from the neon and ubuntu apt repos. [TBD] uses non-KDE libraries and it needs to list the -dev packages in the build-packages then the library itself in the stage-packages.
  • source ← link to the tar
  • cmake-parameters ← copy and paste this, it sets the right paths.
  • parse-info ← where the appstream file is to be installed
  • filesets and prime ← snap parts get build then copied into a stage area, when all the parts are built they are copied into the prime area which is converted into the Snap package. This lists a common set of excluded files we do not want copied. You can add more here if you end up with unnecessary files in your snap.

Building

Install snapcraft with snap install snapcraft --classic.

In the directory with the snapcraft.yaml run: snapcraft

This will start a virtual machine and build the package. If all is well you will have blinken_24.05_amd64.snap or similar created. If you had not installed lxd before, you may need to add your user to the lxd group to allow access to lxd.

Install with snap install blinken_24.05_amd64.snap --devmode.

Run with blinken or check if blinken is available in the app menu and run it from there (remove any versions of blink you have installed from your normal distro packages just to be sure). which -a blinken should say that blinken is available at least as /snap/bin/blinken.

Quirks

Patches

If you need to update some code in the release you can patch it in the Snap package. But please get the patch upstream into the Git archive first. To add a patch to a snap, one must add the snippet to the part that needs patched:

        override-pull: |
            craftctl default
              for file in ${CRAFT_PROJECT_DIR}/snap/local/patches/*
              do
                patch -i $file -p 1
              done

Add add the patch in snap/local/patches/

Help

Snapcraft docs including tutorials on using and building.

snapcraft.yaml format.

Snap forum for asking for help or asking to get the store to allow your Snaps to auto connect.

KDE neon devs talk to sgmoore for help getting your app into the Store.

Glossary

Words you'll hear and not know what they mean:

  • snap: The actual package format.
  • snapd: The daemon that manages snap on a system.
  • snapcraft: The build tool for building a snap.
  • 'app: In the context of snapcraft/snapd this is the (portable) description of an 'executable' exposed to the outside (i.e. something snapd knows how to run).
  • parts: In the context of snapcraft a part refers to one build entity. They describe where to get the source of the entity, how to build it, how to stage it into the final snap and which other parts are a dependency and need to be built first. A part is much like a "makefile" target.
  • interfaces: A way for a snap to talk to the outside world (or another snaps). Split into slots and plugs. Each of which has their own security permissions as a client may need to be able to do different things from a server. https://docs.snapcraft.io/interface-management
  • slot: The provider part of an interface. e.g. a kwin snap might have a wayland-client slot which exposes a way for clients to talk to kwin.
  • plug: The client part of an interface. e.g. an application may plug into the wayland-client slot of kwin to talk to it.
  • Core: A special snap containing the core aspects of any Linux OS (libc/libpthread/...). All snaps depend on exactly one core which provides the snap's understanding of what will be in "/" from the snap's POV. The core does not include a kernel! Kernels may be snaps.
  • Content Snap: Special kind of snap that implements the "content" interface. It's kind of like a shared dependency between snaps allowing one snap to be bound into the scope of another snap. For example the KF5 content snap may be used to share all of KF5 across multiple snaps.
  • Build Snap: Also a special kind of snap, it's the build-time variant of the Content Snap and contains header files etc. necessary to build against a Content Snap.
  • stage, staging: As part of snapcrafting parts get "staged". This kind of means the same as make install, but it's actually a separate step after make install. For the process of staging, snapcraft will copy all files created by make install into a stage directory. You may also exclude certain files or reorganize the files (e.g. rename, or move to different directory). The stage is available for parts ordered after the current one, meaning that they for example can link against a newly built library.
  • prime, priming: Is similar to staging but happens once all parts are built and staged. Priming is the process by which the snap tree is actually constructed. Priming, like staging, allows for excluding files (e.g. dev headers may be staged so other parts can build using them but later excluded from priming and thus left out of the final bundle).