Skip to content

Commit

Permalink
Add QT extension
Browse files Browse the repository at this point in the history
  • Loading branch information
Jarred Wilson committed Jul 26, 2023
1 parent e57f979 commit 365703e
Show file tree
Hide file tree
Showing 12 changed files with 638 additions and 0 deletions.
34 changes: 34 additions & 0 deletions extensions/desktop/qt-framework/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/make -f

SRC_DIR ?= .

DATA_DIR := $(DESTDIR)/data-dir
BIN_DIR := $(DESTDIR)/snap/command-chain
LIB_DIR := $(DESTDIR)/lib
DEST_LAUNCHER := desktop-launch
LOCALE_GENERATOR := locale-gen
DEST_CONFIGURE_HOOK := hooks-configure-desktop

build: $(DEST_LAUNCHER) $(DEST_CONFIGURE_HOOK)

clean:
rm -f $(DEST_LAUNCHER)
rm -f $(DEST_CONFIGURE_HOOK)
rm -f $(BINDTEXTDOMAIN)

$(DEST_LAUNCHER):
@cat $(SRC_DIR)/init > $(DEST_LAUNCHER)
# tail -n +2 to remove the shebang
@tail -n +2 $(SRC_DIR)/desktop-exports | sed -e "s/%PLATFORM_PLUG%/$${PLATFORM_PLUG:?}/" >> $(DEST_LAUNCHER)
@tail -n +2 $(SRC_DIR)/launcher-specific >> $(DEST_LAUNCHER)
@tail -n +2 $(SRC_DIR)/mark-and-exec >> $(DEST_LAUNCHER)

$(DEST_CONFIGURE_HOOK):
@cat $(SRC_DIR)/fonts > $(DEST_CONFIGURE_HOOK)

install: $(DEST_LAUNCHER) $(DEST_CONFIGURE_HOOK)
install -d $(DATA_DIR)
install -d $(DATA_DIR)/kf5
install -D -m755 $(DEST_LAUNCHER) "$(BIN_DIR)"/$(DEST_LAUNCHER)
install -D -m755 $(LOCALE_GENERATOR) "$(BIN_DIR)"/$(LOCALE_GENERATOR)
install -D -m755 $(DEST_CONFIGURE_HOOK) "$(BIN_DIR)"/$(DEST_CONFIGURE_HOOK)
1 change: 1 addition & 0 deletions extensions/desktop/qt-framework/desktop-exports
1 change: 1 addition & 0 deletions extensions/desktop/qt-framework/fonts
1 change: 1 addition & 0 deletions extensions/desktop/qt-framework/init
63 changes: 63 additions & 0 deletions extensions/desktop/qt-framework/launcher-specific
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/bin/bash
###################################
# QT launcher specific part #
###################################

# Add paths for games
append_dir PATH "$SNAP/usr/games"
append_dir PATH "$SNAP_DESKTOP_RUNTIME/usr/games"

# Qt Libs
prepend_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/$ARCH/qt6-5/libs"

# Add QT_PLUGIN_PATH (Qt Modules).
append_dir QT_PLUGIN_PATH "$SNAP/usr/lib/$ARCH/qt6-5`/plugins"
append_dir QT_PLUGIN_PATH "$SNAP/usr/lib/$ARCH"
append_dir QT_PLUGIN_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/$ARCH/qt6-5/plugins"
append_dir QT_PLUGIN_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/$ARCH/"
# And QML2_IMPORT_PATH (Qt Modules).
append_dir QML2_IMPORT_PATH "$SNAP/usr/lib/$ARCH/qt6-5/qml"
append_dir QML2_IMPORT_PATH "$SNAP/lib/$ARCH"
append_dir QML2_IMPORT_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/$ARCH/qt6-5/qml"
append_dir QML2_IMPORT_PATH "$SNAP_DESKTOP_RUNTIME/lib/$ARCH"
# Fix locating the QtWebEngineProcess executable
export QTWEBENGINEPROCESS_PATH="$SNAP_DESKTOP_RUNTIME/usr/lib/$ARCH/qt6-5/libexec/QtWebEngineProcess"
# Removes Qt warning: Could not find a location
# of the system Compose files
export QTCOMPOSE="$SNAP_DESKTOP_RUNTIME/usr/share/X11/locale"
export QT_XKB_CONFIG_ROOT="/usr/share/X11/xkb"
# Add path to VLC plugins
export VLC_PLUGIN_PATH="$SNAP_DESKTOP_RUNTIME/usr/lib/x86_64-linux-gnu/vlc/plugins"
# Ensure QtChooser behaves.
export QTCHOOSER_NO_GLOBAL_DIR=1
export QT_SELECT=5
# qtchooser hardcodes reference paths, we'll need to rewrite them properly
ensure_dir_exists "$XDG_CONFIG_HOME/qtchooser"
echo "$SNAP/usr/lib/qt6-5/bin" > "$XDG_CONFIG_HOME/qtchooser/6.conf"
echo "$SNAP/usr/lib/$ARCH" >> "$XDG_CONFIG_HOME/qtchooser/6.conf"
echo "$SNAP/usr/lib/qt6-5/bin" > "$XDG_CONFIG_HOME/qtchooser/default.conf"
echo "$SNAP/usr/lib/$ARCH" >> "$XDG_CONFIG_HOME/qtchooser/default.conf"
# This relies on qtbase patch
# 0001-let-qlibraryinfo-fall-back-to-locate-qt.conf-via-XDG.patch
# to make QLibraryInfo look in XDG_* locations for qt.conf. The paths configured
# here are applied to everything that uses QLibraryInfo as final fallback and
# has no XDG_* fallback before that. Currently the most interesting offender
# is QtWebEngine which will not work unless the Data path is correctly set.
cat << EOF > "$XDG_CONFIG_HOME/qt.conf"
[Paths]
Data = $SNAP_DESKTOP_RUNTIME/usr/share/qt6-5/
Translations = $SNAP_DESKTOP_RUNTIME/usr/share/qt6-5/translations
EOF
if [ -e "$SNAP_DESKTOP_RUNTIME/usr/share/i18n" ]; then
export I18NPATH="$SNAP_DESKTOP_RUNTIME/usr/share/i18n"
locpath="$XDG_DATA_HOME/locale"
ensure_dir_exists "$locpath"
export LOCPATH="$locpath:/usr/lib/locale"
LC_ALL=C.UTF-8 async_exec "$SNAP/snap/command-chain/locale-gen"
fi
62 changes: 62 additions & 0 deletions extensions/desktop/qt-framework/locale-gen
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/usr/bin/perl

use strict;

sub uniq
{
my %seen;
grep !$seen{$_}++, @_;
}

# TODO support for KDE's in-app language switch feature.
sub get_languages
{
# Initialize with sane defaults.
my @found_languages = ($ENV{'LANG'} or 'C.UTF-8');

# Go through LC_.
foreach (sort keys %ENV)
{
if (substr($_, 0, length("LC_")) eq "LC_")
{
push(@found_languages, $ENV{$_});
}
}
# And finally LANGUAGE, but normalize it.
if (my $language = $ENV{'LANGUAGE'})
{
foreach (split(':', $language))
{
push(@found_languages, "$_.UTF-8");
}
}

# Remove duplicates before returning.
@found_languages = uniq(@found_languages);

return @found_languages
}

my $env_locpath = $ENV{'LOCPATH'} or die "LOCPATH must be set";
my @locpaths = split(/:/, $env_locpath);

foreach my $lang (get_languages())
{
my $found = 0;
foreach my $locpath (@locpaths)
{
my $loc_target = "$locpath/$lang";
if (-e $loc_target)
{
$found = 1;
last;
}
}
next if $found;
my $target = "@locpaths[0]/$lang";

# localedef will exit !0 for unknown reasons, even when everything was
# generated fine.
my ($locale, $encoding) = split(/\./, $lang);
system('localedef', '-i', $locale, '-f', $encoding, $target);
}
1 change: 1 addition & 0 deletions extensions/desktop/qt-framework/mark-and-exec
1 change: 1 addition & 0 deletions schema/snapcraft.json
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,7 @@
"gnome-3-34",
"gnome-3-38",
"kde-neon",
"qt-framework",
"ros1-noetic",
"ros2-foxy"
]
Expand Down
212 changes: 212 additions & 0 deletions snapcraft/extensions/qt_framework.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
# Copyright 2022 Canonical Ltd.
# 2023 Jarred Wilson <jarred.wilson@canonical.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""Generic QT Framework extension to support core22 and onwards."""
import dataclasses
import functools
from typing import Any, Dict, List, Optional, Tuple

from overrides import overrides

from .extension import Extension, get_extensions_data_dir, prepend_to_env

_SDK_SNAP = {"core22": "qt-framework-sdk"}


@dataclasses.dataclass
class ExtensionInfo:
"""Content/SDK build information."""

cmake_args: str


@dataclasses.dataclass
class QTSnaps:
"""A structure of QT related snaps."""

sdk: str
content: str
builtin: bool = True


class QTFramework(Extension):
r"""The QT Framework extension.
This extension makes it easy to assemble QT based applications.
It configures each application with the following plugs:
\b
- Common Icon Themes.
- Common Sound Themes.
- The QT Frameworks runtime libraries and utilities.
For easier desktop integration, it also configures each application
entry with these additional plugs:
\b
- desktop (https://snapcraft.io/docs/desktop-interface)
- desktop-legacy (https://snapcraft.io/docs/desktop-legacy-interface)
- opengl (https://snapcraft.io/docs/opengl-interface)
- wayland (https://snapcraft.io/docs/wayland-interface)
- x11 (https://snapcraft.io/docs/x11-interface)
"""

@staticmethod
@overrides
def get_supported_bases() -> Tuple[str, ...]:
return ("core22",)

@staticmethod
@overrides
def get_supported_confinement() -> Tuple[str, ...]:
return "strict", "devmode"

@staticmethod
@overrides
def is_experimental(base: Optional[str]) -> bool:
return False

@overrides
def get_app_snippet(self) -> Dict[str, Any]:
return {
"command-chain": ["snap/command-chain/desktop-launch"],
"plugs": ["desktop", "desktop-legacy", "opengl", "wayland", "x11"],
}

@functools.cached_property
def qt_snaps(self) -> QTSnaps:
"""Return the QT related snaps to use to construct the environment."""
base = self.yaml_data["base"]
sdk_snap = _SDK_SNAP[base]

build_snaps: List[str] = []
for part in self.yaml_data["parts"].values():
build_snaps.extend(part.get("build-snaps", []))

builtin = True

for snap in build_snaps:
if sdk_snap == snap.split('/')[0]:
builtin = False
break

# The same except the trailing -sd
content = sdk_snap[:-4]

return QTSnaps(sdk=sdk_snap, content=content, builtin=builtin)

@functools.cached_property
def ext_info(self) -> ExtensionInfo:
"""Return the extension info cmake_args, provider, content, build_snaps."""
cmake_args = "-DCMAKE_FIND_ROOT_PATH=/snap/" + self.qt_snaps.sdk + "/current"

return ExtensionInfo(cmake_args=cmake_args)

@overrides
def get_root_snippet(self) -> Dict[str, Any]:
platform_snap = self.qt_snaps.content
content_snap = self.qt_snaps.content + "-all"

return {
"assumes": ["snapd2.43"], # for 'snapctl is-connected'
"compression": "lzo",
"plugs": {
"desktop": {"mount-host-font-cache": False},
"icon-themes": {
"interface": "content",
"target": "$SNAP/data-dir/icons",
"default-provider": "gtk-common-themes",
},
"sound-themes": {
"interface": "content",
"target": "$SNAP/data-dir/sounds",
"default-provider": "gtk-common-themes",
},
platform_snap: {
"content": content_snap,
"interface": "content",
"default-provider": platform_snap,
"target": "$SNAP/qt-framework",
},
},
"environment": {"SNAP_DESKTOP_RUNTIME": "$SNAP/qt-framework"},
"hooks": {
"configure": {
"plugs": ["desktop"],
"command-chain": ["snap/command-chain/hooks-configure-desktop"],
}
},
"layout": {"/usr/share/X11": {"symlink": "$SNAP/qt-framework/usr/share/X11"}},
}

@overrides
def get_part_snippet(self) -> Dict[str, Any]:
sdk_snap = self.qt_snaps.sdk
cmake_args = self.ext_info.cmake_args

return {
"build-environment": [
{
"PATH": prepend_to_env(
"PATH", [f"/snap/{sdk_snap}/current/usr/bin"]
),
},
{
"XDG_DATA_DIRS": prepend_to_env(
"XDG_DATA_DIRS",
[
f"$CRAFT_STAGE/usr/share:/snap/{sdk_snap}/current/usr/share",
"/usr/share",
],
),
},
{
"SNAPCRAFT_CMAKE_ARGS": prepend_to_env(
"SNAPCRAFT_CMAKE_ARGS",
[
cmake_args,
],
),
},
],
}

@overrides
def get_parts_snippet(self) -> Dict[str, Any]:
# We can change this to the lightweight command-chain when
# the content snap includes the desktop-launch from
# https://github.com/snapcore/snapcraft-desktop-integration
source = get_extensions_data_dir() / "desktop" / "qt-framework"

if self.qt_snaps.builtin:
return {
"qt-framework/sdk": {
"source": str(source),
"plugin": "make",
"make-parameters": [f"PLATFORM_PLUG={self.qt_snaps.content}"],
"build-snaps": [self.qt_snaps.sdk],
},
}

return {
"qt-framework/sdk": {
"source": str(source),
"plugin": "make",
"make-parameters": [f"PLATFORM_PLUG={self.qt_snaps.content}"],
},
}
Loading

0 comments on commit 365703e

Please sign in to comment.