diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 003c77b7..2af322c1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,8 +3,7 @@ on: pull_request: push: branches: - - gnome-42-2204-sdk - - gnome-46-2404-sdk + - gnome-core26-sdk jobs: snap: @@ -30,7 +29,7 @@ jobs: - name: Prepare runtime run: | set -eu - git clone --depth 1 --branch gnome-46-2404 https://github.com/ubuntu/gnome-sdk gnome-runtime + git clone --depth 1 --branch gnome-core26 https://github.com/ubuntu/gnome-sdk gnome-runtime mkdir gnome-runtime/base-gnome-sdk mv ${{ steps.snapcraft.outputs.snap}} gnome-runtime/base-gnome-sdk/ cd gnome-runtime @@ -53,6 +52,7 @@ jobs: path: ${{ steps.snapcraft-runtime.outputs.snap}} testing: + if: false # temporarily disable testing until we have snaps with the new runtime strategy: fail-fast: false matrix: diff --git a/patches/fontconfig-delay.patch b/patches/fontconfig-delay.patch index 9740bbb3..24f828df 100644 --- a/patches/fontconfig-delay.patch +++ b/patches/fontconfig-delay.patch @@ -1,12 +1,12 @@ diff --git a/fc-cache/fc-cache.c b/fc-cache/fc-cache.c -index a99adba9..e9956fe7 100644 +index e225bdc4..29972741 100644 --- a/fc-cache/fc-cache.c +++ b/fc-cache/fc-cache.c -@@ -442,16 +442,6 @@ main (int argc, char **argv) +@@ -414,16 +414,6 @@ main (int argc, char **argv) FcConfigDestroy (config); - FcFini (); -- /* + FcFini(); +- /* - * Now we need to sleep a second (or two, to be extra sure), to make - * sure that timestamps for changes after this run of fc-cache are later - * then any timestamps we wrote. We don't use gettimeofday() because diff --git a/patches/glycin-snap-no-sandbox.patch b/patches/glycin-snap-no-sandbox.patch new file mode 100644 index 00000000..96f80eb8 --- /dev/null +++ b/patches/glycin-snap-no-sandbox.patch @@ -0,0 +1,234 @@ +diff --git a/glycin/src/api_common.rs b/glycin/src/api_common.rs +index 77285117..4a35f6d6 100644 +--- a/glycin/src/api_common.rs ++++ b/glycin/src/api_common.rs +@@ -22,6 +22,7 @@ pub enum SandboxMechanism { + impl SandboxMechanism { + pub async fn detect() -> Self { + match RunEnvironment::cached().await { ++ RunEnvironment::Snap => Self::NotSandboxed, + RunEnvironment::FlatpakDevel => Self::NotSandboxed, + RunEnvironment::Flatpak => Self::FlatpakSpawn, + RunEnvironment::Host => Self::Bwrap, +diff --git a/glycin/src/sandbox.rs b/glycin/src/sandbox.rs +index 2e6d5a15..b2105395 100644 +--- a/glycin/src/sandbox.rs ++++ b/glycin/src/sandbox.rs +@@ -18,7 +18,7 @@ use nix::libc::siginfo_t; + use nix::sys::resource; + + use crate::config::{ConfigEntry, ImageLoaderConfig}; +-use crate::util::{self, AsyncMutex, new_async_mutex, spawn_blocking}; ++use crate::util::{self, AsyncMutex, RunEnvironment, new_async_mutex, spawn_blocking}; + use crate::{Error, SandboxMechanism}; + + type SystemSetupStore = Arc>>; +@@ -171,6 +171,39 @@ const ALLOWED_SYSCALLS_FONTCONFIG: &[&str] = &[ + ]; + + const INHERITED_ENVIRONMENT_VARIABLES: &[&str] = &["RUST_BACKTRACE", "RUST_LOG", "XDG_RUNTIME_DIR"]; ++const SNAP_INHERITED_ENVIRONMENT_VARIABLES: &[&str] = &[ ++ "RUST_BACKTRACE", ++ "RUST_LOG", ++ "XDG_RUNTIME_DIR", ++ "LD_LIBRARY_PATH", ++ "PATH", ++ "SNAP", ++ "SNAP_ARCH", ++ "SNAP_COMMON", ++ "SNAP_DATA", ++ "SNAP_INSTANCE_KEY", ++ "SNAP_INSTANCE_NAME", ++ "SNAP_LIBRARY_PATH", ++ "SNAP_NAME", ++ "SNAP_REAL_HOME", ++ "SNAP_REVISION", ++ "SNAP_USER_COMMON", ++ "SNAP_USER_DATA", ++ "GIO_EXTRA_MODULES", ++ "GI_TYPELIB_PATH", ++ "GDK_PIXBUF_MODULEDIR", ++ "GDK_PIXBUF_MODULE_FILE", ++ "GST_PLUGIN_PATH", ++ "GST_PLUGIN_SCANNER", ++ "GST_PLUGIN_SYSTEM_PATH", ++ "LIBGL_DRIVERS_PATH", ++ "FONTCONFIG_FILE", ++ "FONTCONFIG_PATH", ++ "FONTCONFIG_SYSROOT", ++ "XDG_CACHE_HOME", ++ "XDG_CONFIG_DIRS", ++ "XDG_DATA_DIRS", ++]; + + pub struct Sandbox { + sandbox_mechanism: SandboxMechanism, +@@ -216,10 +249,11 @@ impl Sandbox { + let dbus_fd = self.dbus_socket.as_raw_fd(); + + let mut shared_fds = Vec::new(); ++ let run_environment = RunEnvironment::cached().await; + + let (mut command, seccomp_fd) = match self.sandbox_mechanism { + SandboxMechanism::Bwrap => { +- let seccomp_memfd = Self::seccomp_export_bpf(&self.seccomp_filter()?)?; ++ let seccomp_memfd = Self::seccomp_export_bpf(&Self::seccomp_filter(&self.config_entry)?)?; + let command = self.bwrap_command(&seccomp_memfd).await?; + + shared_fds.push(seccomp_memfd.as_raw_fd()); +@@ -232,8 +266,16 @@ impl Sandbox { + (command, None) + } + SandboxMechanism::NotSandboxed => { +- eprintln!("WARNING: Glycin running without sandbox."); +- let command = self.no_sandbox_command(); ++ let snap_confined = matches!(run_environment, RunEnvironment::Snap); ++ let command = if snap_confined { ++ eprintln!( ++ "WARNING: Glycin running without additional sandbox inside snap confinement.", ++ ); ++ self.snap_command()? ++ } else { ++ eprintln!("WARNING: Glycin running without sandbox."); ++ self.no_sandbox_command() ++ }; + + (command, None) + } +@@ -560,6 +602,39 @@ impl Sandbox { + command + } + ++ fn snap_command(&self) -> Result { ++ let mut command = Command::new(self.exec()); ++ ++ command.env_clear(); ++ for key in SNAP_INHERITED_ENVIRONMENT_VARIABLES { ++ if let Some(val) = std::env::var_os(key) { ++ command.env(key, val); ++ } ++ } ++ ++ let config_entry = self.config_entry.clone(); ++ ++ unsafe { ++ command.pre_exec(move || { ++ nix::sys::prctl::set_pdeathsig(nix::sys::signal::SIGKILL) ++ .map_err(std::io::Error::from)?; ++ ++ let filter = Self::seccomp_filter(&config_entry).map_err(std::io::Error::other)?; ++ if let Err(err) = Self::load_seccomp_filter(&filter) { ++ libc_eprint( ++ "glycin sandbox: Failed to load direct seccomp filter in snap confinement, continuing without syscall filtering: ", ++ ); ++ libc_eprint(&err.to_string()); ++ libc_eprint("\n"); ++ } ++ ++ Ok(()) ++ }); ++ } ++ ++ Ok(command) ++ } ++ + /// Memory limit in bytes that should be applied to sandboxes + fn memory_limit() -> resource::rlim_t { + // Lookup free memory +@@ -635,7 +710,7 @@ impl Sandbox { + } + } + +- fn seccomp_filter(&self) -> Result { ++ fn seccomp_filter(config_entry: &ConfigEntry) -> Result { + // Using `KillProcess` allows rejected syscalls to be logged by auditd. But it + // doesn't work with tools like valgrind. That's why it's not used by default. + let mut filter = if std::env::var("GLYCIN_SECCOMP_DEFAULT_ACTION") +@@ -649,7 +724,7 @@ impl Sandbox { + }; + + let mut syscalls = vec![ALLOWED_SYSCALLS]; +- if self.config_entry.fontconfig() { ++ if config_entry.fontconfig() { + // Enable some write operations for fontconfig to update its cache + syscalls.push(ALLOWED_SYSCALLS_FONTCONFIG); + } +@@ -666,6 +741,14 @@ impl Sandbox { + Ok(filter) + } + ++ fn load_seccomp_filter(filter: &ScmpFilterContext) -> io::Result<()> { ++ if unsafe { libc::prctl(libc::PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) } != 0 { ++ return Err(io::Error::last_os_error()); ++ } ++ ++ filter.load().map_err(io::Error::other) ++ } ++ + /// Make seccomp filters available under FD + /// + /// Bubblewrap supports taking an fd to seccomp filters in the BPF format. +@@ -710,7 +793,7 @@ impl Sandbox { + let (dbus_socket, _) = UnixStream::pair()?; + let sandbox = Self::new(SandboxMechanism::Bwrap, config_entry, dbus_socket); + +- let seccomp_memfd = Self::seccomp_export_bpf(&sandbox.seccomp_filter()?)?; ++ let seccomp_memfd = Self::seccomp_export_bpf(&Self::seccomp_filter(&sandbox.config_entry)?)?; + let mut command = sandbox.bwrap_command(&seccomp_memfd).await?; + + unsafe { +diff --git a/glycin/src/util.rs b/glycin/src/util.rs +index 765a5953..df48aede 100644 +--- a/glycin/src/util.rs ++++ b/glycin/src/util.rs +@@ -71,6 +71,8 @@ pub enum RunEnvironment { + Host, + + HostBwrapSyscallsBlocked, ++ /// Inside Snap confinement ++ Snap, + /// Inside Flatpak + Flatpak, + /// Inside Flatpak and development environment +@@ -86,7 +88,9 @@ impl RunEnvironment { + if let Some(result) = *run_environment { + result + } else { +- let run_env = if let Some(devel) = flatpak_devel().await { ++ let run_env = if snap_confined().await { ++ Self::Snap ++ } else if let Some(devel) = flatpak_devel().await { + if devel { + Self::FlatpakDevel + } else { +@@ -104,6 +108,30 @@ impl RunEnvironment { + } + } + ++async fn snap_confined() -> bool { ++ if let Ok(apparmor_label) = read("/proc/self/attr/current").await ++ && snap_apparmor_label(&apparmor_label) ++ { ++ return true; ++ } ++ ++ std::env::var_os("SNAP").is_some() || std::env::var_os("SNAP_NAME").is_some() ++} ++ ++fn snap_apparmor_label(label: &[u8]) -> bool { ++ let label = String::from_utf8_lossy(label); ++ let label = label.trim_matches(char::from(0)).trim(); ++ ++ if label == "unconfined" { ++ return false; ++ } ++ ++ label ++ .split_ascii_whitespace() ++ .next() ++ .is_some_and(|profile| profile.starts_with("snap.")) ++} ++ + /// Returns None if not in Flatpak environment, otherwise true if development + async fn flatpak_devel() -> Option { + let data = read("/.flatpak-info").await.ok()?; diff --git a/patches/libgweather.diff b/patches/libgweather.diff deleted file mode 100644 index 571ecaee..00000000 --- a/patches/libgweather.diff +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/data/meson.build b/data/meson.build -index c4d6445a..9cec37cf 100644 ---- a/data/meson.build -+++ b/data/meson.build -@@ -31,6 +31,7 @@ locations_bin = custom_target('locations-db', - output: '@BASENAME@.bin', - install: true, - install_dir: pkglibdir, -+ env: ['LD_LIBRARY_PATH=CRAFT_ENV_REPLACE'], - ) - - install_data('Locations.xml', diff --git a/snapcraft.yaml b/snapcraft.yaml index 09574970..4fd7b87e 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -1,8 +1,8 @@ -name: gnome-46-2404-sdk +name: gnome-core26-sdk version: git -summary: Shared GNOME 46 Ubuntu stack SDK +summary: Shared GNOME Ubuntu stack SDK description: | - This snap contains the GNOME 46 development files. These are used during the build process of snaps that depend on the GNOME 46 stack. This helps developers to make sure their application is built against the correct verson of GNOME and GTK libraries. + This snap contains the GNOME development files. These are used during the build process of snaps that depend on the GNOME stack. This helps developers to make sure their application is built against the correct verson of GNOME and GTK libraries. **For users** @@ -15,13 +15,14 @@ description: | * The `gnome` extension is the recommended way to use this in your own snap: https://snapcraft.io/docs/gnome-extension * You can report issues with this SDK snap on GitHub: https://github.com/ubuntu/gnome-sdk/issues - * The source code of this snap is available on GitHub in the `gnome-46-2404-sdk` branch: https://github.com/ubuntu/gnome-sdk/tree/gnome-46-2404-sdk + * The source code of this snap is available on GitHub in the `gnome-core26-sdk` branch: https://github.com/ubuntu/gnome-sdk/tree/gnome-core26-sdk * This snap is used for building snaps of applications using GNOME or GTK. If you are looking for instructions on how to build GNOME applications instead, take a look at https://developer.gnome.org/ contact: https://github.com/ubuntu/gnome-sdk/issues confinement: strict -grade: stable -base: core24 # if the base is changed, the BUILDENV part must be updated +grade: devel +base: core26 +build-base: devel parts: buildenv: @@ -38,13 +39,13 @@ parts: $CRAFT_STAGE/usr/lib/pkgconfig:\ $CRAFT_STAGE/usr/share/pkgconfig\ ${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" - - CRAFT_EXT_CORE_LEVEL: core24 - PATH: $CRAFT_STAGE/usr/bin:$PATH + - PYTHONPATH: $CRAFT_STAGE/usr/lib/python3:$CRAFT_STAGE/usr/lib/python3/dist-packages ninja: plugin: nil source: https://github.com/ninja-build/ninja.git - source-tag: 'v1.12.1' + source-tag: 'v1.13.2' source-depth: 1 override-build: | rm -rf build @@ -65,18 +66,17 @@ parts: after: [ ninja ] plugin: nil source: https://github.com/mesonbuild/meson.git - source-tag: '1.8.4' + source-tag: '1.11.1' source-depth: 1 override-build: | - python3 -m pip install --break-system-packages . mkdir -p $CRAFT_PART_INSTALL/usr/lib/python3/dist-packages rm -rf $CRAFT_PART_INSTALL/usr/lib/python3/dist-packages/meson* python3 -m pip install --target=$CRAFT_PART_INSTALL/usr --break-system-packages . mv $CRAFT_PART_INSTALL/usr/meson* $CRAFT_PART_INSTALL/usr/lib/python3/dist-packages/ - sed -i "s%^#!/usr/bin/python3$%#!/usr/bin/env python3%g" /usr/local/bin/meson sed -i "s%^#!/usr/bin/python3$%#!/usr/bin/env python3%g" $CRAFT_PART_INSTALL/usr/bin/meson build-packages: - python3-pip + # make some meson version available for the pull stage of other parts (e.g. meson subprojects download...) - meson libtool: @@ -94,22 +94,23 @@ parts: sed -i 's#pkgauxdir="#pkgauxdir="$CRAFT_STAGE#' $LIBTOOLIZE sed -i 's#pkgltdldir="#pkgltdldir="$CRAFT_STAGE#' $LIBTOOLIZE sed -i 's#aclocaldir="#aclocaldir="$CRAFT_STAGE#' $LIBTOOLIZE + for bin in aclocal automake; do + ln -sf --relative $(find $CRAFT_STAGE/usr/bin -type f -name ${bin}*) $CRAFT_STAGE/usr/bin/$bin + done fontconfig: - after: [ libtool ] + after: [ libtool, meson-deps ] source: https://gitlab.freedesktop.org/fontconfig/fontconfig.git - source-tag: 2.15.0 + source-tag: 2.18.0 source-depth: 1 - plugin: autotools - autotools-configure-parameters: + plugin: meson + meson-parameters: - --prefix=/usr - --libdir=/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR build-environment: *buildenv override-pull: | craftctl default patch -p1 < $CRAFT_PROJECT_DIR/patches/fontconfig-delay.patch - stage: - - usr/bin/fc-cache libffi: after: [ libtool ] @@ -120,6 +121,7 @@ parts: autotools-configure-parameters: - --prefix=/usr - --libdir=/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR + - --disable-multi-os-directory build-environment: *buildenv build-packages: - texinfo @@ -128,7 +130,7 @@ parts: glib: after: [ libffi, meson-deps ] source: https://gitlab.gnome.org/GNOME/glib.git - source-tag: '2.84.4' + source-tag: '2.88.1' # ext:updatesnap # version-format: # ignore-odd-minor: true @@ -174,7 +176,7 @@ parts: build-environment: *buildenv cairo: - after: [ pixman, meson-deps ] + after: [ fontconfig, pixman, meson-deps ] source: https://gitlab.freedesktop.org/cairo/cairo.git source-tag: '1.18.4' # ext:updatesnap @@ -194,7 +196,6 @@ parts: - -Dtests=disabled build-environment: *buildenv build-packages: - - libfontconfig1-dev - libfreetype-dev - libx11-dev - libxext-dev @@ -208,7 +209,7 @@ parts: gobject-introspection: after: [ cairo, meson-deps ] source: https://gitlab.gnome.org/GNOME/gobject-introspection.git - source-tag: '1.84.0' + source-tag: '1.86.0' # ext:updatesnap # version-format: # ignore-odd-minor: true @@ -233,7 +234,7 @@ parts: vala: after: [ gobject-introspection ] source: https://gitlab.gnome.org/GNOME/vala.git - source-tag: '0.56.18' + source-tag: '0.56.19' source-depth: 1 # ext:updatesnap # version-format: @@ -249,13 +250,12 @@ parts: at-spi2-core: after: [ glib, gobject-introspection, meson-deps ] source: https://gitlab.gnome.org/GNOME/at-spi2-core.git - source-tag: 'AT_SPI2_CORE_2_52_0' -# ext:updatesnap -# version-format: -# format: 'AT_SPI2_CORE_%M_%m_%R' -# lower-than: 2.53 + source-tag: '2.60.4' source-depth: 1 plugin: meson +# ext:updatesnap +# version-format: +# ignore-odd-minor: true meson-parameters: - --prefix=/usr - -Doptimization=3 @@ -284,7 +284,7 @@ parts: harfbuzz: after: [ fribidi, meson-deps, gobject-introspection ] source: https://github.com/harfbuzz/harfbuzz.git - source-tag: '11.4.5' # developers declared that they won't break ABI + source-tag: '14.2.0' # developers declared that they won't break ABI source-depth: 1 plugin: meson meson-parameters: @@ -314,7 +314,7 @@ parts: pango: after: [ libffi, harfbuzz, meson-deps, gobject-introspection ] source: https://gitlab.gnome.org/GNOME/pango.git - source-tag: '1.56.4' + source-tag: '1.57.1' source-depth: 1 plugin: meson meson-parameters: @@ -333,43 +333,10 @@ parts: - libxt-dev - cmake - gdk-pixbuf: - after: [ pango, meson-deps, gobject-introspection ] - source: https://gitlab.gnome.org/GNOME/gdk-pixbuf.git - source-tag: '2.42.12' - source-depth: 1 - plugin: meson - meson-parameters: - - --prefix=/usr - - -Doptimization=3 - - -Ddebug=true - - -Dinstalled_tests=false - - -Dgtk_doc=false - - -Ddocs=false - - -Dintrospection=enabled - - -Dman=false - - -Dtests=false - - -Dinstalled_tests=false - build-environment: *buildenv - override-build: | - set -eux - craftctl default - cp $CRAFT_PART_INSTALL/usr/bin/gdk-pixbuf-query-loaders $CRAFT_STAGE/usr/bin/ - LOADERS_PATH=$CRAFT_PART_INSTALL/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/gdk-pixbuf-2.0/2.10.0/loaders - $CRAFT_PART_INSTALL/usr/bin/gdk-pixbuf-query-loaders $LOADERS_PATH/*.so > $LOADERS_PATH.cache - # workaround for thumbnailer being in a different directory in the snap env - sed -i 's#/usr/bin/##' $CRAFT_PART_INSTALL/usr/share/thumbnailers/gdk-pixbuf-thumbnailer.thumbnailer - organize: - usr/bin/gdk-pixbuf-query-loaders: usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders - build-packages: - - libpng-dev - - libjpeg-dev - - libtiff-dev - librsvg: - after: [ gdk-pixbuf, vala, meson-deps, gobject-introspection ] + after: [ pango, vala, meson-deps, gobject-introspection ] source: https://gitlab.gnome.org/GNOME/librsvg.git - source-tag: '2.61.1' # they left the odd->unstable even->stable scheme, and now all tags are stable + source-tag: '2.62.2' # they left the odd->unstable even->stable scheme, and now all tags are stable # ext:updatesnap # version-format: # no-9x-revisions: true @@ -381,33 +348,21 @@ parts: - -Ddebug=true - -Dintrospection=enabled - -Dvala=enabled - - -Dpixbuf=enabled - - -Dpixbuf-loader=enabled + - -Dpixbuf=disabled + - -Dpixbuf-loader=disabled - -Ddocs=disabled - -Dtests=false build-environment: *buildenv build-packages: - libssl-dev - - meson - curl - - cargo-1.82 + - cargo + - cargo-c override-pull: | craftctl default meson subprojects download - # This avoids crate downloads at the build lifecycle, which comes too late - # in RISC-V Launchpad builders, so that it errors with HTTP 407. - # No blooming idea why '~/.cargo/bin/cargo update' does not suffice; - # launchpad.net/~desktop-snappers/+snap/gnome-46-2404-sdk/+build/2931431 - # shows it does not. - if test "$CRAFT_ARCH_BUILD_FOR" = riscv64; then - cargo-1.82 update - fi - # cargo version in .deb is too old - curl https://sh.rustup.rs -sSf > cargo.sh - sh ./cargo.sh -y - export PATH=$PATH:$HOME/.cargo/bin - ~/.cargo/bin/cargo install cargo-c --locked - cp ~/.cargo/bin/* /usr/local/bin + mkdir -p $CRAFT_PART_BUILD/.cargo + cargo vendor $CRAFT_PART_SRC/vendor > $CRAFT_PART_BUILD/.cargo/config.toml override-stage: | craftctl default sed -i 's#prefix=$CRAFT_STAGE${pcfiledir}#prefix=${pcfiledir}#' $CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/pkgconfig/librsvg-2.0.pc @@ -470,7 +425,7 @@ parts: libsoup3: after: [ libpsl, meson-deps, vala, gobject-introspection ] source: https://gitlab.gnome.org/GNOME/libsoup.git - source-tag: '3.6.5' + source-tag: '3.6.6' source-depth: 1 plugin: meson meson-parameters: @@ -511,7 +466,7 @@ parts: wayland-protocols: after: [ meson-deps ] source: https://gitlab.freedesktop.org/wayland/wayland-protocols.git - source-tag: '1.45' + source-tag: '1.48' source-depth: 1 build-packages: - libwayland-dev @@ -526,7 +481,7 @@ parts: gtk3: after: [ wayland-protocols, harfbuzz, pango, meson-deps, gobject-introspection ] source: https://gitlab.gnome.org/GNOME/gtk.git - source-tag: '3.24.50' + source-tag: '3.24.52' # ext:updatesnap # version-format: # ignore-odd-minor: true @@ -573,7 +528,7 @@ parts: gtk4: after: [ wayland-protocols, harfbuzz, pango, meson-deps, gobject-introspection ] source: https://gitlab.gnome.org/GNOME/gtk.git - source-tag: '4.18.6' + source-tag: '4.22.4' # ext:updatesnap # version-format: # ignore-odd-minor: true @@ -623,6 +578,66 @@ parts: - -usr/lib/*/libwayland-client.so.0.20.0 - -usr/lib/*/libwayland-server.so.0.20.0 + glycin: + after: [ pango, meson-deps, gobject-introspection, gtk4, fontconfig, cairo, librsvg ] + source: https://gitlab.gnome.org/GNOME/glycin.git + source-tag: '2.1.1' + source-depth: 1 + plugin: meson + meson-parameters: + - --prefix=/usr + - -Doptimization=3 + - -Ddebug=true + - -Dtests=false + - -Dglycin-thumbnailer=true + build-environment: *buildenv + build-packages: + - cargo + - liblcms2-dev + - libseccomp-dev + - libheif-dev + - libjxl-dev + override-pull: | + set -eux + craftctl default + patch -p1 < $CRAFT_PROJECT_DIR/patches/glycin-snap-no-sandbox.patch + mkdir -p $CRAFT_PART_BUILD/.cargo + cargo vendor $CRAFT_PART_SRC/vendor > $CRAFT_PART_BUILD/.cargo/config.toml + override-build: | + set -eux + craftctl default + # workaround for thumbnailer being in a different directory in the snap env + sed -i 's#/usr/bin/##' $CRAFT_PART_INSTALL/usr/share/thumbnailers/glycin-*.thumbnailer + + gdk-pixbuf: + after: [ pango, meson-deps, gobject-introspection, glycin ] + source: https://gitlab.gnome.org/GNOME/gdk-pixbuf.git + source-tag: '2.44.6' + source-depth: 1 + plugin: meson + meson-parameters: + - --prefix=/usr + - -Doptimization=3 + - -Ddebug=true + - -Dinstalled_tests=false + - -Dgtk_doc=false + - -Ddocumentation=false + - -Dintrospection=enabled + - -Dman=false + - -Dtests=false + - -Dinstalled_tests=false + - -Dthumbnailer=disabled + build-environment: *buildenv + override-build: | + set -eux + craftctl default + cp $CRAFT_PART_INSTALL/usr/bin/gdk-pixbuf-query-loaders $CRAFT_STAGE/usr/bin/ + LOADERS_PATH=$CRAFT_PART_INSTALL/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/gdk-pixbuf-2.0/2.10.0/loaders + mkdir -p $LOADERS_PATH + $CRAFT_PART_INSTALL/usr/bin/gdk-pixbuf-query-loaders $LOADERS_PATH/*.so > $LOADERS_PATH.cache + organize: + usr/bin/gdk-pixbuf-query-loaders: usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders + gtk-locales: after: [ gtk3, gtk4 ] plugin: nil @@ -711,7 +726,7 @@ parts: glibmm: after: [ mm-common ] source: https://gitlab.gnome.org/GNOME/glibmm.git - source-tag: '2.84.0' + source-tag: '2.88.0' source-depth: 1 plugin: autotools override-build: | @@ -741,7 +756,7 @@ parts: cairomm: after: [ glibmm, meson-deps ] source: https://gitlab.freedesktop.org/cairo/cairomm.git - source-tag: '1.18.0' + source-tag: '1.19.0' source-depth: 1 plugin: meson meson-parameters: @@ -773,7 +788,6 @@ parts: - LD_LIBRARY_PATH: $CRAFT_STAGE/usr/lib/vala-0.56:$CRAFT_STAGE/usr/lib:$CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} - GDK_PIXBUF_MODULE_FILE: $CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/gdk-pixbuf-2.0/2.10.0/loaders.cache - PKG_CONFIG_PATH: $CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/pkgconfig:$CRAFT_STAGE/usr/lib/pkgconfig:$CRAFT_STAGE/usr/share/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH} - - CRAFT_EXT_CORE_LEVEL: core24 - M4PATH: $CRAFT_STAGE/usr/lib/glibmm-2.68/proc/m4 override-build: | set -eux @@ -784,7 +798,7 @@ parts: atkmm: after: [ pangomm ] source: https://gitlab.gnome.org/GNOME/atkmm.git - source-tag: '2.36.3' + source-tag: '2.36.4' source-depth: 1 # ext:updatesnap # version-format: @@ -802,13 +816,12 @@ parts: - LD_LIBRARY_PATH: $CRAFT_STAGE/usr/lib/vala-0.56:$CRAFT_STAGE/usr/lib:$CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} - GDK_PIXBUF_MODULE_FILE: $CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/gdk-pixbuf-2.0/2.10.0/loaders.cache - PKG_CONFIG_PATH: $CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/pkgconfig:$CRAFT_STAGE/usr/lib/pkgconfig:$CRAFT_STAGE/usr/share/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH} - - CRAFT_EXT_CORE_LEVEL: core24 - M4PATH: $CRAFT_STAGE/usr/lib/glibmm-2.68/proc/m4 gtkmm: after: [ atkmm, meson-deps ] source: https://gitlab.gnome.org/GNOME/gtkmm.git - source-tag: '4.18.0' + source-tag: '4.22.0' source-depth: 1 # ext:updatesnap # version-format: @@ -831,7 +844,6 @@ parts: - LD_LIBRARY_PATH: $CRAFT_STAGE/usr/lib/vala-0.56:$CRAFT_STAGE/usr/lib:$CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} - GDK_PIXBUF_MODULE_FILE: $CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/gdk-pixbuf-2.0/2.10.0/loaders.cache - PKG_CONFIG_PATH: $CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/pkgconfig:$CRAFT_STAGE/usr/lib/pkgconfig:$CRAFT_STAGE/usr/share/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH} - - CRAFT_EXT_CORE_LEVEL: core24 - M4PATH: $CRAFT_STAGE/usr/lib/glibmm-2.68/proc/m4 override-build: | set -eux @@ -842,7 +854,7 @@ parts: gtksourceview: after: [ gtkmm, meson-deps, gobject-introspection ] source: https://gitlab.gnome.org/GNOME/gtksourceview.git - source-tag: '5.16.0' + source-tag: '5.20.0' # ext:updatesnap # version-format: # ignore-odd-minor: true @@ -862,24 +874,6 @@ parts: craftctl default sed -i 's#Werror=missing-include-dirs#Wmissing-include-dirs#g' meson.build - libdazzle: - after: [ gtksourceview, meson-deps, vala, gobject-introspection ] - source: https://gitlab.gnome.org/Archive/libdazzle.git - source-tag: '3.44.0' - source-depth: 1 - plugin: meson - meson-parameters: - - --prefix=/usr - - -Doptimization=3 - - -Ddebug=true - - -Dwith_vapi=true - - -Dwith_introspection=true - build-environment: *buildenv - override-pull: | - set -eux - craftctl default - sed -i 's#Werror=missing-include-dirs#Wmissing-include-dirs#g' meson.build - gsound: after: [ meson-deps, vala, gobject-introspection ] source: https://gitlab.gnome.org/GNOME/gsound.git @@ -922,7 +916,7 @@ parts: gnome-desktop: after: [ gsettings-desktop-schemas, meson-deps, gobject-introspection ] source: https://gitlab.gnome.org/GNOME/gnome-desktop.git - source-tag: '44.4' + source-tag: '44.5' # ext:updatesnap # version-format: # same-major: true @@ -942,36 +936,11 @@ parts: - xkb-data - yelp-tools - cogl: - after: [ gnome-desktop, gobject-introspection ] - source: https://gitlab.gnome.org/Archive/cogl.git - source-tag: '1.22.8' - source-depth: 1 - plugin: autotools - autotools-configure-parameters: - - --prefix=/usr - - --enable-wayland-egl-platform=yes - - --enable-kms-egl-platform=yes - - --enable-introspection=yes - - --enable-gdk-pixbuf - - --enable-cogl-pango - - --enable-gl - - --enable-xlib-egl-platform - - --enable-gles2 - - --with-gles2-libname=libGLESv2.so.2 - build-environment: *buildenv - build-packages: - - libgbm-dev - - libgles2-mesa-dev - - xauth - - xvfb - - libgstreamer-plugins-base1.0-dev - libwacom: - after: [ cogl, meson-deps ] + after: [ gnome-desktop, meson-deps ] source: https://github.com/linuxwacom/libwacom source-type: git - source-tag: 'libwacom-2.16.1' + source-tag: 'libwacom-2.18.0' # ext:updatesnap # version-format: # format: 'libwacom-%M.%m.%R' @@ -988,7 +957,7 @@ parts: libinput: after: [ libwacom, meson-deps ] source: https://gitlab.freedesktop.org/libinput/libinput.git - source-tag: '1.29.1' + source-tag: '1.31.2' source-depth: 1 plugin: meson meson-parameters: @@ -1004,128 +973,10 @@ parts: - libudev-dev - libevdev-dev - clutter: - after: [ cogl, json-glib, libinput, meson-deps, gobject-introspection ] - source: https://gitlab.gnome.org/Archive/clutter.git - source-tag: '1.26.4' -# ext:updatesnap -# version-format: -# ignore: true - plugin: meson - meson-parameters: - - --prefix=/usr - - -Doptimization=3 - - -Ddebug=true - - -Dbuild_examples=false - - -Ddocumentation=false - - -Dbuild_tests=false - - -Dpixbuf_tests=false - - -Dintrospection=true - build-environment: *buildenv - build-packages: - - libdrm-dev - - xsltproc - - libgudev-1.0-dev - stage: - - -usr/bin/gapplication - - -usr/bin/gdbus - - -usr/bin/gio* - - -usr/bin/glib* - - -usr/bin/gobject-query - - -usr/bin/gresource - - -usr/bin/gsettings - - -usr/bin/gtester* - - -usr/include/gio-unix-2.0/gio/gdesktopappinfo.h - - -usr/include/gio-unix-2.0/gio/gunixmounts.h - - -usr/include/glib-2.0/* - - -usr/include/pixman-1/* - - -usr/lib/*/glib-2.0/* - - -usr/lib/*/libffi* - - -usr/lib/*/libgio* - - -usr/lib/*/libglib* - - -usr/lib/*/libgmodule* - - -usr/lib/*/libgobject* - - -usr/lib/*/libgthread* - - -usr/lib/*/libpixman* - - -usr/lib/*/pkgconfig/gio* - - -usr/lib/*/pkgconfig/glib-2.0.pc - - -usr/lib/*/pkgconfig/gmodule* - - -usr/lib/*/pkgconfig/gobject-2.0.pc - - -usr/lib/*/pkgconfig/gthread-2.0.pc - - -usr/lib/*/pkgconfig/libffi.pc - - -usr/lib/*/pkgconfig/pixman-1.pc - - -usr/share/aclocal/glib-2.0.m4 - - -usr/share/aclocal/gsettings.m4 - - -usr/share/glib-2.0/* - - -usr/lib/*/girepository-1.0/GIRepository-2.0.typelib - - -usr/lib/*/girepository-1.0/GLib-2.0.typelib - - -usr/lib/*/girepository-1.0/GObject-2.0.typelib - - -usr/lib/*/girepository-1.0/Gio-2.0.typelib - - -usr/lib/*/libgirepository* - - -usr/include/fribidi/* - - -usr/lib/*/libfribidi* - - -usr/lib/*/pkgconfig/fribidi.pc - - -usr/include/harfbuzz/* - - -usr/share/gir-1.0/HarfBuzz-0.0.gir - - -usr/bin/pango* - - -usr/include/pango-1.0/pango/* - - -usr/lib/*/girepository-1.0/Pango* - - -usr/lib/*/libpango* - - -usr/lib/*/pkgconfig/pango* - - -usr/share/gir-1.0/Pango* - - -usr/bin/gdk-pixbuf* - - -usr/include/gdk-pixbuf-2.0/gdk-pixbuf/gdk-pixbuf-features.h - - -usr/lib/*/gdk-pixbuf-2.0/* - - -usr/lib/*/girepository-1.0/GdkPixbuf-2.0.typelib - - -usr/lib/*/libgdk_pixbuf* - - -usr/lib/*/pkgconfig/gdk-pixbuf* - - -usr/share/gir-1.0/GdkPix* - - -usr/bin/wayland-scanner - - -usr/include/wayland* - - -usr/share/aclocal/wayland-scanner.m4 - - -usr/share/wayland/wayland.xml - - clutter-gtk: - after: [ clutter, meson-deps ] - source: https://gitlab.gnome.org/Archive/clutter-gtk.git - source-tag: '1.8.4' # ancient tag. should just build from master since it gets updates (like clutter) - source-depth: 1 - plugin: meson - meson-parameters: - - --prefix=/usr - - -Doptimization=3 - - -Ddebug=true - build-environment: *buildenv - - libpeas: - after: [ clutter-gtk, meson-deps, vala, gobject-introspection ] - source: https://gitlab.gnome.org/GNOME/libpeas.git - source-tag: 'libpeas-1.36.0' # developers say that they don't want to break ABI, so it should be safe to always update -# ext:updatesnap -# version-format: -# format: 'libpeas-%M.%m.%R' - source-depth: 1 - plugin: meson - meson-parameters: - - --prefix=/usr - - -Doptimization=3 - - -Ddebug=true - - -Dpython2=true - - -Dpython3=true - - -Dintrospection=true - - -Dvapi=true - - -Ddemos=true - - -Dglade_catalog=false - - -Dgtk_doc=false - build-environment: *buildenv - build-packages: - - python3-dev - - python-gi-dev - pycairo: - after: [ libpeas, meson-deps ] + after: [ libinput, meson-deps ] source: https://github.com/pygobject/pycairo.git - source-tag: 'v1.28.0' + source-tag: 'v1.29.0' source-depth: 1 plugin: meson meson-parameters: @@ -1138,7 +989,7 @@ parts: pygobject: after: [ pycairo, meson-deps ] source: https://gitlab.gnome.org/GNOME/pygobject.git - source-tag: '3.54.2' + source-tag: '3.56.3' source-depth: 1 plugin: meson meson-parameters: @@ -1170,9 +1021,9 @@ parts: build-environment: *buildenv gjs: - after: [ libhandy, meson-deps ] + after: [ libhandy, meson-deps, glib ] source: https://gitlab.gnome.org/GNOME/gjs.git - source-tag: '1.80.2' + source-tag: '1.88.0' # ext:updatesnap # version-format: # same-minor: true @@ -1188,12 +1039,12 @@ parts: build-environment: *buildenv build-packages: - dbus - - libmozjs-115-dev + - libmozjs-140-dev p11-kit: after: [ gjs, meson-deps ] source: https://github.com/p11-glue/p11-kit.git - source-tag: '0.25.9' + source-tag: '0.26.2' source-depth: 1 plugin: meson meson-parameters: @@ -1218,6 +1069,7 @@ parts: - -Doptimization=3 - -Ddebug=true - -Dgtk_doc=false + - -Dmanpage=false - -Dintrospection=true - -Dvapi=true build-packages: @@ -1261,10 +1113,22 @@ parts: build-packages: - libnghttp2-dev - libgweather: + gweather-locations: after: [ libgeocode, meson-deps, gobject-introspection, vala ] + source: https://github.com/GNOME/gweather-locations.git + source-tag: '2026.2' + source-depth: 1 + plugin: meson + build-environment: *buildenv + meson-parameters: + - --prefix=/usr + - -Doptimization=3 + - -Ddebug=true + + libgweather: + after: [ gweather-locations, libgeocode, meson-deps, gobject-introspection, vala ] source: https://gitlab.gnome.org/GNOME/libgweather.git - source-tag: '4.4.4' + source-tag: '4.6.0' # ext:updatesnap # version-format: # ignore-odd-minor: true @@ -1278,18 +1142,15 @@ parts: - -Dintrospection=true - -Dgtk_doc=false - -Dtests=false - - -Dsoup2=false - -Denable_vala=true override-pull: | craftctl default meson subprojects download - patch -p1 < $CRAFT_PROJECT_DIR/patches/libgweather.diff - sed -i "s#CRAFT_ENV_REPLACE#/usr/lib:/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:$CRAFT_STAGE/usr/lib:$CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR#" $CRAFT_PART_SRC/data/meson.build build-packages: - gir1.2-glib-2.0 libnotify: - after: [ glib, gobject-introspection, gdk-pixbuf ] + after: [ glib, gobject-introspection, gdk-pixbuf, meson-deps ] source: https://gitlab.gnome.org/GNOME/libnotify.git source-tag: '0.8.8' source-depth: 1 @@ -1306,20 +1167,6 @@ parts: stage: - -usr/bin - webp-pixbuf-loader: - after: [meson-deps, gdk-pixbuf] - source: https://github.com/aruiz/webp-pixbuf-loader.git - source-tag: '0.2.7' - source-depth: 1 - plugin: meson - build-environment: *buildenv - meson-parameters: - - --prefix=/usr - - -Doptimization=3 - - -Ddebug=true - build-packages: - - libwebp-dev - libayatana-ido: after: [glib, gtk3] source: https://github.com/AyatanaIndicators/ayatana-ido.git @@ -1365,17 +1212,11 @@ parts: - -DENABLE_TESTS=OFF - -DENABLE_GTKDOC=OFF - -DENABLE_BINDINGS_MONO=OFF - - -DCMAKE_SHARED_LINKER_FLAGS="-lpcre" build-packages: - libayatana-indicator3-dev - libdbusmenu-glib-dev - libdbusmenu-gtk3-dev - - libpcre3-dev - cmake - stage-packages: - - libayatana-indicator3-dev - - libdbusmenu-glib-dev - - libdbusmenu-gtk3-dev stage: - usr/share/gir-1.0/AyatanaAppIndicator* - usr/share/gir-1.0/Dbusmenu* @@ -1405,12 +1246,12 @@ parts: - at-spi2-core - cairomm - glibmm + - glycin - gtk-locales - gtkmm - gtksourceview - libadwaita - libayatana-appindicator - - libdazzle - libgnome-games-support - libgweather - libnotify @@ -1419,8 +1260,6 @@ parts: - libsoup3 - mm-common - pangomm - - webp-pixbuf-loader - - fontconfig plugin: nil stage-packages: - appstream @@ -1430,9 +1269,9 @@ parts: - g++ - gettext - gir1.2-poppler-0.18 - - heif-gdk-pixbuf - itstool - libappstream-dev + - libayatana-indicator3-dev # it seems that snapcraft not always include dependencies, so we # must add the "normal" library .deb files too in some cases. # That's why several "devel" packages come along with the corresponding @@ -1454,6 +1293,8 @@ parts: - libcurl4-openssl-dev - libdbus-1-3 - libdbus-1-dev + - libdbusmenu-glib-dev + - libdbusmenu-gtk3-dev - libdrm-common - libdrm2 - libdrm-dev @@ -1463,8 +1304,6 @@ parts: - libevdev-dev - libexpat1 - libexpat1-dev - - libffi8 - - libfontconfig1-dev - libfreetype6 - libfreetype-dev - libgbm-dev @@ -1488,18 +1327,25 @@ parts: - libgssapi-krb5-2 - libgstreamer-plugins-bad1.0-dev - libgstreamer-plugins-base1.0-dev - - libgstreamer-plugins-good1.0-dev + - libgstreamer-plugins-extra1.0-dev - libgudev-1.0-dev - - libgvc6 + - libgvc7 + - libheif1 - libheif-dev + - libheif-plugin-aomenc + - libheif-plugin-aomdec + - libheif-plugin-libde265 + - libheif-plugin-x265 - libhogweed6t64 - libidn2-0 - libidn2-dev - libjpeg-dev + - libjxl0.11 - libk5crypto3 - libkrb5support0 - libkrb5-3 - libkrb5-dev + - liblcms2-2 - liblcms2-dev - libltdl-dev - liblzma5 @@ -1508,28 +1354,27 @@ parts: - liblzo2-dev - libmount1 - libmount-dev - - libmozjs-115-dev + - libmozjs-140-dev - libmtdev1t64 - libnettle8t64 - libnghttp2-dev - libnsl-dev - libnss3-dev + - libopenjpip7 - libopenjp2-7-dev - libpciaccess0 - libpciaccess-dev - libpcre2-8-0 - libpcre2-dev - - libpcre3 - - libpcre3-dev - libpng-dev - libpng16-16t64 - libpoppler-dev - libpoppler-glib-dev - libpulse-dev - libpython3-dev - - libpython3.12-dev - - libpython3.12-minimal - - libpython3.12-stdlib + - libpython3.14-dev + - libpython3.14-minimal + - libpython3.14-stdlib - libseccomp2 - libseccomp-dev - libselinux1 @@ -1591,8 +1436,8 @@ parts: - python3-setuptools - python3-venv - python3-wheel - - python3.12-minimal - - python3.12-venv + - python3.14-minimal + - python3.14-venv - shared-mime-info - zlib1g - zlib1g-dev @@ -1615,7 +1460,8 @@ parts: find . -type f,l -exec rm -fv $CRAFT_PART_INSTALL/usr/lib/{} \; find . -type f,l -name "*.so.*" -exec sh -c "rm -fv $CRAFT_PART_INSTALL/usr/lib/{}*" \; stage: - - -usr/bin/fc-cache +# needed to avoid clashing with fontconfig + - -etc/fonts/fonts.conf conditioning: after: @@ -1624,8 +1470,6 @@ parts: - libtool plugin: nil build-environment: *buildenv - build-packages: - - execstack override-prime: | set -eux craftctl default @@ -1641,48 +1485,50 @@ parts: for PC in $(find . -path "*/pkgconfig/*.pc") do sed -i 's#prefix=$CRAFT_STAGE${pcfiledir}#prefix=${pcfiledir}#' $PC - sed -i 's#prefix=$CRAFT_STAGE#prefix=/snap/gnome-46-2404-sdk/current#' $PC - sed -i 's#prefix = /usr#prefix=/snap/gnome-46-2404-sdk/current/usr#' $PC - sed -i 's#prefix=/usr#prefix=/snap/gnome-46-2404-sdk/current/usr#' $PC - sed -i 's#original_prefix=/snap/gnome-46-2404-sdk/current/usr#original_prefix=/usr#' $PC + sed -i 's#prefix=$CRAFT_STAGE#prefix=/snap/$CRAFT_PROJECT_NAME/current#' $PC + sed -i 's#prefix = /usr#prefix=/snap/$CRAFT_PROJECT_NAME/current/usr#' $PC + sed -i 's#prefix=/usr#prefix=/snap/$CRAFT_PROJECT_NAME/current/usr#' $PC + sed -i 's#original_prefix=/snap/$CRAFT_PROJECT_NAME/current/usr#original_prefix=/usr#' $PC sed -i 's#libdir=/usr#libdir=${prefix}#' $PC - sed -i 's#libdir=/lib#libdir=/snap/gnome-46-2404-sdk/current/lib#' $PC + sed -i 's#libdir=/lib#libdir=/snap/$CRAFT_PROJECT_NAME/current/lib#' $PC sed -i 's#exec_prefix=/usr#exec_prefix=${prefix}#' $PC sed -i 's#includedir=/usr#includedir=${prefix}#' $PC - sed -i 's#sysconfdir=/etc#sysconfdir=/snap/gnome-46-2404-sdk/current/etc#' $PC + sed -i 's#sysconfdir=/etc#sysconfdir=/snap/$CRAFT_PROJECT_NAME/current/etc#' $PC - sed -i 's#/usr/#/snap/gnome-46-2404-sdk/current/usr/#g' $PC - sed -i 's#/etc/#/snap/gnome-46-2404-sdk/current/etc/#g' $PC + sed -i 's#/usr/#/snap/$CRAFT_PROJECT_NAME/current/usr/#g' $PC + sed -i 's#/etc/#/snap/$CRAFT_PROJECT_NAME/current/etc/#g' $PC done if [ "$CRAFT_ARCH_TRIPLET_BUILD_FOR" = "x86_64-linux-gnu" ]; then - sed -i 's#includedir=${prefix}/snap/gnome-46-2404-sdk/current#includedir=${prefix}#' $CRAFT_PRIME/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/pkgconfig/igdgmm.pc + sed -i 's#includedir=${prefix}/snap/$CRAFT_PROJECT_NAME/current#includedir=${prefix}#' $CRAFT_PRIME/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/pkgconfig/igdgmm.pc fi LIBTOOLIZE=usr/bin/libtoolize - sed -i 's#pkgauxdir="$CRAFT_STAGE#pkgauxdir="/snap/gnome-46-2404-sdk/current#' $LIBTOOLIZE - sed -i 's#pkgltdldir="$CRAFT_STAGE#pkgltdldir="/snap/gnome-46-2404-sdk/current#' $LIBTOOLIZE - sed -i 's#aclocaldir="$CRAFT_STAGE#aclocaldir="/snap/gnome-46-2404-sdk/current#' $LIBTOOLIZE + sed -i 's#pkgauxdir="$CRAFT_STAGE#pkgauxdir="/snap/$CRAFT_PROJECT_NAME/current#' $LIBTOOLIZE + sed -i 's#pkgltdldir="$CRAFT_STAGE#pkgltdldir="/snap/$CRAFT_PROJECT_NAME/current#' $LIBTOOLIZE + sed -i 's#aclocaldir="$CRAFT_STAGE#aclocaldir="/snap/$CRAFT_PROJECT_NAME/current#' $LIBTOOLIZE SCANNER=usr/bin/g-ir-scanner - sed -i 's#$CRAFT_STAGE#/snap/gnome-46-2404-sdk/current#g' $SCANNER + sed -i 's#$CRAFT_STAGE#/snap/$CRAFT_PROJECT_NAME/current#g' $SCANNER ITSTOOL=usr/bin/itstool - sed -i 's#/usr/local/share#/snap/gnome-46-2404-sdk/current/usr/local/share#g' $ITSTOOL - sed -i 's#/usr/share#/snap/gnome-46-2404-sdk/current/usr/share#g' $ITSTOOL + sed -i 's#/usr/local/share#/snap/$CRAFT_PROJECT_NAME/current/usr/local/share#g' $ITSTOOL + sed -i 's#/usr/share#/snap/$CRAFT_PROJECT_NAME/current/usr/share#g' $ITSTOOL GDK_PIXBUF_PATH=usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/gdk-pixbuf-2.0 LOADERS=$GDK_PIXBUF_PATH/2.10.0/loaders.cache rm -f $CRAFT_PRIME/$GDK_PIXBUF_PATH/2.10.0/loaders/*.so - cp -a $CRAFT_STAGE/$GDK_PIXBUF_PATH/2.10.0/loaders/*.so $CRAFT_PRIME/$GDK_PIXBUF_PATH/2.10.0/loaders/ + if [ "$(ls -A $CRAFT_STAGE/$GDK_PIXBUF_PATH/2.10.0/loaders)" ]; then + cp -a $CRAFT_STAGE/$GDK_PIXBUF_PATH/2.10.0/loaders/*.so $CRAFT_PRIME/$GDK_PIXBUF_PATH/2.10.0/loaders/ + fi $GDK_PIXBUF_PATH/gdk-pixbuf-query-loaders $CRAFT_PRIME/$GDK_PIXBUF_PATH/2.10.0/loaders/*.so > $CRAFT_PRIME/$LOADERS - sed -i 's#/root/parts/gdk-pixbuf/install#/snap/gnome-46-2404-sdk/current#g' $CRAFT_PRIME/$LOADERS - sed -i 's#/build/gnome-46-2404-sdk/parts/gdk-pixbuf/install#/snap/gnome-46-2404-sdk/current#g' $CRAFT_PRIME/$LOADERS + sed -i 's#/root/parts/gdk-pixbuf/install#/snap/$CRAFT_PROJECT_NAME/current#g' $CRAFT_PRIME/$LOADERS + sed -i 's#/build/$CRAFT_PROJECT_NAME/parts/gdk-pixbuf/install#/snap/$CRAFT_PROJECT_NAME/current#g' $CRAFT_PRIME/$LOADERS XML2_CONFIG=usr/bin/xml2-config - sed -i 's#/root/parts/debs/install#/snap/gnome-46-2404-sdk/current#g' $XML2_CONFIG + sed -i 's#/root/parts/debs/install#/snap/$CRAFT_PROJECT_NAME/current#g' $XML2_CONFIG rm -f usr/lib/vala-current rm -f usr/share/gettext-current @@ -1693,7 +1539,6 @@ parts: # Check dangling libraries symlinks invalid_links=() - core_root_prefix=/snap/$CRAFT_EXT_CORE_LEVEL/current for i in $(find "$CRAFT_PRIME"/{usr/,}lib -xtype l -name '*.so'); do echo "$i: dangling symlink!" invalid_links+=($i) @@ -1705,19 +1550,14 @@ parts: exit 1; fi - # Necessary for armhf builds, triggers review warning - if [ "$CRAFT_ARCH_TRIPLET_BUILD_FOR" = "arm-linux-gnueabihf" ]; then - execstack --clear-execstack usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libde265.so.0* - fi - # Used by the gnome snapcraft extensions to load translations from within the content snap bindtextdomain: after: [ conditioning ] plugin: nil override-build: | set -eux - mkdir -p $CRAFT_PART_INSTALL/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR - gcc -Wall -O2 -o $CRAFT_PART_INSTALL/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/bindtextdomain.so -fPIC -shared $CRAFT_PROJECT_DIR/tools/bindtextdomain.c -ldl + mkdir -p $CRAFT_PART_INSTALL/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR + gcc -Wall -O2 -o $CRAFT_PART_INSTALL/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/bindtextdomain.so -fPIC -shared $CRAFT_PROJECT_DIR/tools/bindtextdomain.c -ldl cleanup: after: [ conditioning ] diff --git a/tools/blame_package.py b/tools/blame_package.py new file mode 100755 index 00000000..2bb2671a --- /dev/null +++ b/tools/blame_package.py @@ -0,0 +1,487 @@ +#!/usr/bin/env python3 + +import argparse +import collections +import os +import sys +import tempfile +from dataclasses import dataclass + +try: + import yaml +except ImportError as error: + print( + "This script requires PyYAML. Install python3-yaml in the runtime environment.", + file=sys.stderr, + ) + raise SystemExit(1) from error + +try: + import apt_pkg +except ImportError as error: + print( + "This script requires python-apt (apt_pkg). Install python3-apt in the runtime environment.", + file=sys.stderr, + ) + raise SystemExit(1) from error + + +BASE_TO_SUITE = { + "core20": "focal", + "core22": "jammy", + "core24": "noble", + "core26": "resolute", +} + +PRIMARY_ARCHITECTURES = {"amd64", "i386"} +MAIN_ARCHIVE_URL = "http://archive.ubuntu.com/ubuntu" +SECURITY_ARCHIVE_URL = "http://security.ubuntu.com/ubuntu" +PORTS_ARCHIVE_URL = "http://ports.ubuntu.com/ubuntu-ports" +COMPONENTS = ("main", "restricted", "universe", "multiverse") +POCKETS = ("", "-updates", "-security", "-backports") +DEPENDENCY_FIELDS = ("Depends", "PreDepends", "Pre-Depends") + + +@dataclass(frozen=True) +class DependencyEdge: + parent_name: str + relation: str + alternatives: tuple[str, ...] + + +def parse_arguments() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description=( + "Explain why a Debian package is staged by tracing dependency chains " + "from snapcraft.yaml stage-packages." + ) + ) + parser.add_argument("package", help="Requested Debian binary package name") + parser.add_argument( + "--arch", + default="amd64", + help="Target architecture to resolve. Defaults to amd64.", + ) + parser.add_argument( + "--snapcraft-yaml", + default=None, + help="Path to snapcraft.yaml. Defaults to ./snapcraft.yaml or ./snap/snapcraft.yaml.", + ) + parser.add_argument( + "--scratch-dir", + default=None, + help="Existing or new directory for the isolated APT workspace. Defaults to a new temporary directory.", + ) + parser.add_argument( + "--max-chains", + type=int, + default=20, + help="Maximum number of dependency chains to print. Use 0 for no limit.", + ) + return parser.parse_args() + + +def resolve_snapcraft_path(cli_path: str | None) -> str: + if cli_path: + return cli_path + + for candidate in ("snapcraft.yaml", os.path.join("snap", "snapcraft.yaml")): + if os.path.exists(candidate): + return candidate + + raise FileNotFoundError("Could not find snapcraft.yaml in the current directory or in ./snap/") + + +def load_snapcraft(path: str) -> dict: + with open(path, "r", encoding="utf-8") as stream: + return yaml.safe_load(stream) + + +def normalize_package_name(package_name: str) -> str: + return package_name.split(":", 1)[0].strip() + + +def normalize_architecture_condition(condition: str) -> set[str]: + condition = condition.strip().lower() + if not condition.startswith("on "): + return set() + + normalized = condition[3:].replace(",", " ") + return {item for item in normalized.split() if item} + + +def collect_stage_packages(data: dict, architecture: str) -> dict[str, set[str]]: + roots: dict[str, set[str]] = collections.defaultdict(set) + for part_name, part in data.get("parts", {}).items(): + stage_packages = part.get("stage-packages", []) + for package_name, origin in iter_stage_package_entries(stage_packages, part_name, architecture): + roots[normalize_package_name(package_name)].add(origin) + return roots + + +def iter_stage_package_entries(entries, part_name: str, architecture: str, condition: str | None = None): + if isinstance(entries, str): + origin = part_name if condition is None else f"{part_name} [{condition}]" + yield entries, origin + return + + if isinstance(entries, dict): + for nested_condition, nested_entries in entries.items(): + allowed_architectures = normalize_architecture_condition(str(nested_condition)) + if allowed_architectures and architecture not in allowed_architectures: + continue + yield from iter_stage_package_entries( + nested_entries, + part_name, + architecture, + condition=str(nested_condition), + ) + return + + if not isinstance(entries, list): + return + + for entry in entries: + if isinstance(entry, str): + origin = part_name if condition is None else f"{part_name} [{condition}]" + yield entry, origin + continue + + if not isinstance(entry, dict): + continue + + for nested_condition, nested_entries in entry.items(): + allowed_architectures = normalize_architecture_condition(str(nested_condition)) + if allowed_architectures and architecture not in allowed_architectures: + continue + yield from iter_stage_package_entries( + nested_entries, + part_name, + architecture, + condition=str(nested_condition), + ) + + +def resolve_suite(base_name: str) -> str: + try: + return BASE_TO_SUITE[base_name] + except KeyError as error: + supported = ", ".join(sorted(BASE_TO_SUITE)) + raise ValueError( + f"Unsupported snap base {base_name!r}. Extend BASE_TO_SUITE for this script. Supported bases: {supported}" + ) from error + + +def build_repository_lines(suite: str, architecture: str) -> list[str]: + if architecture in PRIMARY_ARCHITECTURES: + archive_url = MAIN_ARCHIVE_URL + security_url = SECURITY_ARCHIVE_URL + else: + archive_url = PORTS_ARCHIVE_URL + security_url = PORTS_ARCHIVE_URL + + components = " ".join(COMPONENTS) + lines = [] + for pocket_suffix in POCKETS: + pocket = f"{suite}{pocket_suffix}" + base_url = security_url if pocket_suffix == "-security" else archive_url + lines.append(f"deb [arch={architecture} trusted=yes] {base_url} {pocket} {components}") + return lines + + +def ensure_directory(path: str) -> None: + os.makedirs(path, exist_ok=True) + + +def prepare_scratch_workspace(base_directory: str, suite: str, architecture: str) -> dict[str, str]: + etc_apt = os.path.join(base_directory, "etc", "apt") + preferences_dir = os.path.join(etc_apt, "preferences.d") + state_dir = os.path.join(base_directory, "var", "lib", "apt") + lists_dir = os.path.join(state_dir, "lists") + cache_dir = os.path.join(base_directory, "var", "cache", "apt") + archives_dir = os.path.join(cache_dir, "archives") + dpkg_dir = os.path.join(base_directory, "var", "lib", "dpkg") + + for path in ( + etc_apt, + preferences_dir, + os.path.join(etc_apt, "sources.list.d"), + lists_dir, + os.path.join(lists_dir, "partial"), + archives_dir, + os.path.join(archives_dir, "partial"), + dpkg_dir, + ): + ensure_directory(path) + + status_path = os.path.join(dpkg_dir, "status") + if not os.path.exists(status_path): + with open(status_path, "w", encoding="utf-8"): + pass + + sources_list_path = os.path.join(etc_apt, "sources.list") + with open(sources_list_path, "w", encoding="utf-8") as stream: + stream.write("\n".join(build_repository_lines(suite, architecture))) + stream.write("\n") + + return { + "etc_apt": etc_apt, + "state_dir": state_dir, + "lists_dir": lists_dir, + "cache_dir": cache_dir, + "archives_dir": archives_dir, + "status_path": status_path, + } + + +def configure_apt(workspace: dict[str, str], architecture: str) -> None: + apt_pkg.init_config() + apt_pkg.config.set("Dir", "/") + apt_pkg.config.set("Dir::Etc", workspace["etc_apt"]) + apt_pkg.config.set("Dir::Etc::sourcelist", "sources.list") + apt_pkg.config.set("Dir::Etc::sourceparts", "sources.list.d") + apt_pkg.config.set("Dir::Etc::preferencesparts", "preferences.d") + apt_pkg.config.set("Dir::State", workspace["state_dir"]) + apt_pkg.config.set("Dir::State::lists", workspace["lists_dir"]) + apt_pkg.config.set("Dir::State::status", workspace["status_path"]) + apt_pkg.config.set("Dir::Cache", workspace["cache_dir"]) + apt_pkg.config.set("Dir::Cache::archives", workspace["archives_dir"]) + apt_pkg.config.set("Debug::NoLocking", "1") + apt_pkg.config.set("Acquire::AllowInsecureRepositories", "1") + apt_pkg.config.set("Acquire::AllowDowngradeToInsecureRepositories", "1") + apt_pkg.config.set("Acquire::Languages", "none") + apt_pkg.config.set("APT::Install-Recommends", "0") + apt_pkg.config.set("APT::Install-Suggests", "0") + apt_pkg.config.set("APT::Architecture", architecture) + apt_pkg.config.clear("APT::Architectures") + apt_pkg.config.set("APT::Architectures::", architecture) + if os.path.isdir("/etc/apt/trusted.gpg.d"): + apt_pkg.config.set("Dir::Etc::trustedparts", "/etc/apt/trusted.gpg.d") + apt_pkg.init_system() + + +def refresh_package_lists() -> None: + source_list = apt_pkg.SourceList() + source_list.read_main_list() + + acquire = apt_pkg.Acquire() + source_list.get_indexes(acquire) + result = acquire.run() + if result != apt_pkg.Acquire.RESULT_CONTINUE: + raise RuntimeError(f"Repository metadata download failed with acquire result {result}") + + failed_items = [] + for item in acquire.items: + if getattr(item, "complete", False): + continue + error_text = (getattr(item, "error_text", "") or "").strip() + if not error_text: + continue + failed_items.append(f"{getattr(item, 'desc_uri', '')}: {error_text}") + if failed_items: + failed = "\n".join(failed_items) + raise RuntimeError(f"Repository metadata download did not complete successfully:\n{failed}") + + +def build_candidate_cache() -> tuple[dict[str, object], object]: + cache = apt_pkg.Cache() + dependency_cache = apt_pkg.DepCache(cache) + packages = {package.name: package for package in cache.packages} + return packages, dependency_cache + + +def get_candidate_version(dependency_cache, package): + getter = getattr(dependency_cache, "get_candidate_ver", None) + if getter is not None: + return getter(package) + + version_list = getattr(package, "version_list", None) or [] + if version_list: + return version_list[0] + return None + + +def iter_dependency_groups(version) -> list[tuple[str, tuple[str, ...]]]: + groups = [] + depends_list = getattr(version, "depends_list", {}) or {} + for relation in DEPENDENCY_FIELDS: + raw_groups = depends_list.get(relation) or [] + if not raw_groups and relation == "Pre-Depends": + raw_groups = depends_list.get("PreDepends") or [] + for raw_group in raw_groups: + alternatives = [] + for dependency in raw_group: + target_package = getattr(dependency, "target_pkg", None) + if target_package is None: + continue + alternatives.append(target_package.name) + if alternatives: + groups.append((relation, tuple(dict.fromkeys(alternatives)))) + return groups + + +def build_reverse_dependency_edges(packages: dict[str, object], dependency_cache) -> dict[str, list[DependencyEdge]]: + reverse_edges: dict[str, set[DependencyEdge]] = collections.defaultdict(set) + for package_name, package in packages.items(): + candidate = get_candidate_version(dependency_cache, package) + if candidate is None: + continue + for relation, alternatives in iter_dependency_groups(candidate): + edge = DependencyEdge(package_name, relation, alternatives) + for child_name in alternatives: + reverse_edges[child_name].add(edge) + + return { + package_name: sorted(edges, key=lambda edge: (edge.parent_name, edge.relation, edge.alternatives)) + for package_name, edges in reverse_edges.items() + } + + +def find_dependency_chains( + target_package: str, + roots: dict[str, set[str]], + reverse_edges: dict[str, list[DependencyEdge]], + max_chains: int, +) -> tuple[list[tuple[list[str], list[DependencyEdge]]], bool]: + queue = collections.deque([(target_package, [target_package], [])]) + chains: list[tuple[list[str], list[DependencyEdge]]] = [] + truncated = False + + while queue: + current_name, backward_nodes, backward_edges = queue.popleft() + + if current_name in roots: + chains.append((list(reversed(backward_nodes)), list(reversed(backward_edges)))) + if max_chains > 0 and len(chains) >= max_chains: + truncated = True + break + + for edge in reverse_edges.get(current_name, []): + parent_name = edge.parent_name + if parent_name in backward_nodes: + continue + queue.append((parent_name, backward_nodes + [parent_name], backward_edges + [edge])) + + return sorted(chains, key=lambda item: (len(item[0]), item[0])), truncated + + +def format_chain(nodes: list[str], edges: list[DependencyEdge]) -> list[str]: + lines = [] + if len(nodes) == 1: + lines.append(f" {nodes[0]} (direct stage-package root)") + return lines + + for index, edge in enumerate(edges): + parent = nodes[index] + child = nodes[index + 1] + if len(edge.alternatives) > 1: + alternative_text = " | ".join(edge.alternatives) + lines.append( + f" {parent} --{edge.relation}--> {child} (alternatives: {alternative_text})" + ) + else: + lines.append(f" {parent} --{edge.relation}--> {child}") + return lines + + +def print_report( + *, + base_name: str, + suite: str, + architecture: str, + scratch_directory: str, + target_package: str, + roots: dict[str, set[str]], + packages: dict[str, object], + chains: list[tuple[list[str], list[DependencyEdge]]], + truncated: bool, +) -> int: + print(f"Snap base: {base_name}") + print(f"Ubuntu suite: {suite}") + print(f"Architecture: {architecture}") + print(f"Scratch APT workspace: {scratch_directory}") + print(f"Requested package: {target_package}") + print() + + if target_package not in packages: + print("The requested package was not found in the downloaded Ubuntu repository metadata.") + return 1 + + if target_package in roots: + origins = ", ".join(sorted(roots[target_package])) + print("The requested package is listed directly in stage-packages.") + print(f"Origin: {origins}") + print() + + if not chains: + print("No dependency chain from any stage-package root reaches the requested package.") + return 1 + + print(f"Found {len(chains)} dependency chain(s):") + print() + + for index, (nodes, edges) in enumerate(chains, start=1): + root_name = nodes[0] + origins = ", ".join(sorted(roots[root_name])) + print(f"[{index}] Root package: {root_name}") + print(f" Origin: {origins}") + for line in format_chain(nodes, edges): + print(line) + print() + + if truncated: + print("Output truncated because the maximum number of chains was reached.") + + return 0 + + +def main() -> int: + arguments = parse_arguments() + snapcraft_path = resolve_snapcraft_path(arguments.snapcraft_yaml) + data = load_snapcraft(snapcraft_path) + + base_name = data.get("base") + if not base_name: + raise ValueError(f"No base was found in {snapcraft_path}") + + suite = resolve_suite(str(base_name)) + roots = collect_stage_packages(data, arguments.arch) + if not roots: + raise RuntimeError(f"No stage-packages were found for architecture {arguments.arch}") + + scratch_directory = arguments.scratch_dir or tempfile.mkdtemp(prefix="stage-package-chains-") + scratch_directory = os.path.abspath(scratch_directory) + + workspace = prepare_scratch_workspace(scratch_directory, suite, arguments.arch) + configure_apt(workspace, arguments.arch) + refresh_package_lists() + + packages, dependency_cache = build_candidate_cache() + reverse_edges = build_reverse_dependency_edges(packages, dependency_cache) + normalized_target = normalize_package_name(arguments.package) + chains, truncated = find_dependency_chains( + normalized_target, + roots, + reverse_edges, + arguments.max_chains, + ) + + return print_report( + base_name=str(base_name), + suite=suite, + architecture=arguments.arch, + scratch_directory=scratch_directory, + target_package=normalized_target, + roots=roots, + packages=packages, + chains=chains, + truncated=truncated, + ) + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except (FileNotFoundError, RuntimeError, ValueError, apt_pkg.Error) as error: + print(f"ERROR: {error}", file=sys.stderr) + raise SystemExit(1)