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 Sep 26, 2023
1 parent 1898183 commit d52c124
Show file tree
Hide file tree
Showing 14 changed files with 491 additions and 9 deletions.
2 changes: 1 addition & 1 deletion extensions/desktop/command-chain/desktop-launch
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/bash

set -- "${SNAP}/gnome-platform/command-chain/desktop-launch" "$@"
set -- "${SNAP_DESKTOP_RUNTIME}/command-chain/desktop-launch" "$@"
# shellcheck source=/dev/null
source "${SNAP}/snap/command-chain/run"
2 changes: 1 addition & 1 deletion extensions/desktop/command-chain/hooks-configure-fonts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/bash

set -- "${SNAP}/gnome-platform/command-chain/hooks-configure-fonts" "$@"
set -- "${SNAP_DESKTOP_RUNTIME}/command-chain/hooks-configure-desktop" "$@"
# shellcheck source=/dev/null
source "${SNAP}/snap/command-chain/run"
2 changes: 2 additions & 0 deletions schema/snapcraft.json
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,8 @@
"gnome-3-34",
"gnome-3-38",
"kde-neon",
"qt5-15",
"qt6-5",
"ros1-noetic",
"ros1-noetic-desktop",
"ros1-noetic-perception",
Expand Down
2 changes: 1 addition & 1 deletion snapcraft/extensions/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def apply_extensions(
for extension_name in sorted(declared_extensions.keys()):
extension_class = get_extension_class(extension_name)
extension = extension_class(
yaml_data=copy.deepcopy(yaml_data), arch=arch, target_arch=target_arch
name=extension_name, yaml_data=copy.deepcopy(yaml_data), arch=arch, target_arch=target_arch
)
extension.validate(extension_name=extension_name)
_apply_extension(yaml_data, declared_extensions[extension_name], extension)
Expand Down
3 changes: 2 additions & 1 deletion snapcraft/extensions/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ class Extension(abc.ABC):
"""

def __init__(
self, *, yaml_data: Dict[str, Any], arch: str, target_arch: str
self, *, name: str, yaml_data: Dict[str, Any], arch: str, target_arch: str
) -> None:
"""Create a new Extension."""
self.name = name
self.yaml_data = yaml_data
self.arch = arch
self.target_arch = target_arch
Expand Down
226 changes: 226 additions & 0 deletions snapcraft/extensions/qt_framework.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
# Copyright 2022 Canonical Ltd.
#
# 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"}

_CONTENT_SNAP = {
"qt6-5": {"core22": "qt-framework-6-5-core22"},
"qt5-15": {"core22": "qt-framework-5-15-core22"},
}

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

cmake_args: list


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

sdk: dict
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"],
"environment": {
"QT_PLUGIN_PATH": "$SNAP/qt-framework/usr/plugins:$SNAP/usr/lib/plugins",
},
}

@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]
sdk_channel = f"{self.name[2:].replace('-','.')}/stable"

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]:
try:
sdk_channel = snap.split("/", 1)[1]
except IndexError:
pass
builtin = False
break

sdk={"snap": sdk_snap, "channel": sdk_channel}

# The same except the trailing -sd
content = _CONTENT_SNAP[self.name][base]

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

@functools.cached_property
def ext_info(self) -> ExtensionInfo:
"""Return the extension info cmake_args, provider, content, build_snaps."""
cmake_args = [
f"-DCMAKE_FIND_ROOT_PATH=/snap/{self.qt_snaps.sdk['snap']}/current",
f"-DCMAKE_PREFIX_PATH=/snap/{self.qt_snaps.sdk['snap']}/current/usr",
"-DZLIB_INCLUDE_DIR=/lib/x86_64-linux-gnu",
]

return ExtensionInfo(cmake_args=cmake_args)

@overrides
def get_root_snippet(self) -> Dict[str, Any]:
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",
},
"qt-framework": {
"interface": "content",
"default-provider": self.qt_snaps.content,
"target": "$SNAP/qt-framework",
},
},
"environment": {"SNAP_DESKTOP_RUNTIME": "$SNAP/qt-framework"},
"hooks": {
"configure": {
"plugs": ["desktop"],
"command-chain": ["snap/command-chain/hooks-configure-fonts"],
}
},
"layout": {
"/usr/share/X11": {"symlink": "$SNAP/qt-framework/usr/share/X11"}
},
}

@overrides
def get_part_snippet(self, *, plugin_name: str) -> Dict[str, Any]:
sdk_snap = self.qt_snaps.sdk["snap"]
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
),
},
],
"build-packages": [
"libgl1-mesa-dev",
],
}

@overrides
def get_parts_snippet(self) -> Dict[str, Any]:
source = get_extensions_data_dir() / "desktop" / "command-chain"
sdk_snap = self.qt_snaps.sdk['snap']
sdk_channel = self.qt_snaps.sdk['channel']

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

return {
f"{self.name}/sdk": {
"source": str(source),
"plugin": "make",
"make-parameters": [f"PLATFORM_PLUG=qt-framework"],
},
}
3 changes: 3 additions & 0 deletions snapcraft/extensions/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

from .gnome import GNOME
from .kde_neon import KDENeon
from .qt_framework import QTFramework
from .ros2_humble import ROS2HumbleExtension
from .ros2_humble_desktop import ROS2HumbleDesktopExtension
from .ros2_humble_ros_base import ROS2HumbleRosBaseExtension
Expand All @@ -39,6 +40,8 @@
"ros2-humble-ros-base": ROS2HumbleRosBaseExtension,
"ros2-humble-desktop": ROS2HumbleDesktopExtension,
"kde-neon": KDENeon,
"qt6-5": QTFramework,
"qt5-15": QTFramework,
}


Expand Down
4 changes: 4 additions & 0 deletions tests/unit/commands/test_list_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ def test_command(emitter, command):
gnome-3-34 core18
gnome-3-38 core20
kde-neon core18, core20, core22
qt5-15 core22
qt6-5 core22
ros1-noetic core20
ros1-noetic-desktop core20
ros1-noetic-perception core20
Expand Down Expand Up @@ -79,6 +81,8 @@ def test_command_extension_dups(emitter, command):
gnome-3-34 core18
gnome-3-38 core20
kde-neon core18, core20, core22
qt5-15 core22
qt6-5 core22
ros1-noetic core20
ros1-noetic-desktop core20
ros1-noetic-perception core20
Expand Down
6 changes: 5 additions & 1 deletion tests/unit/extensions/test_gnome.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
from snapcraft.extensions import gnome
from snapcraft.extensions.extension import get_extensions_data_dir

_EXTENSION_NAME = "gnome"

############
# Fixtures #
############
Expand All @@ -27,13 +29,14 @@
@pytest.fixture
def gnome_extension():
return gnome.GNOME(
yaml_data={"base": "core22", "parts": {}}, arch="amd64", target_arch="amd64"
name=_EXTENSION_NAME, yaml_data={"base": "core22", "parts": {}}, arch="amd64", target_arch="amd64"
)


@pytest.fixture
def gnome_extension_with_build_snap():
return gnome.GNOME(
name=_EXTENSION_NAME,
yaml_data={
"base": "core22",
"parts": {"part1": {"build-snaps": ["gnome-44-2204-sdk"]}},
Expand All @@ -46,6 +49,7 @@ def gnome_extension_with_build_snap():
@pytest.fixture
def gnome_extension_with_default_build_snap_from_latest_edge():
return gnome.GNOME(
name=_EXTENSION_NAME,
yaml_data={
"base": "core22",
"parts": {"part1": {"build-snaps": ["gnome-42-2204-sdk/latest/edge"]}},
Expand Down
6 changes: 5 additions & 1 deletion tests/unit/extensions/test_kde_neon.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
from snapcraft.extensions import kde_neon
from snapcraft.extensions.extension import get_extensions_data_dir

_EXTENSION_NAME = "kde-neon"

############
# Fixtures #
############
Expand All @@ -27,13 +29,14 @@
@pytest.fixture
def kde_neon_extension():
return kde_neon.KDENeon(
yaml_data={"base": "core22", "parts": {}}, arch="amd64", target_arch="amd64"
name=_EXTENSION_NAME, yaml_data={"base": "core22", "parts": {}}, arch="amd64", target_arch="amd64"
)


@pytest.fixture
def kde_neon_extension_with_build_snap():
return kde_neon.KDENeon(
name=_EXTENSION_NAME,
yaml_data={
"base": "core22",
"parts": {
Expand All @@ -50,6 +53,7 @@ def kde_neon_extension_with_build_snap():
@pytest.fixture
def kde_neon_extension_with_default_build_snap_from_latest_edge():
return kde_neon.KDENeon(
name=_EXTENSION_NAME,
yaml_data={
"base": "core22",
"parts": {
Expand Down
Loading

0 comments on commit d52c124

Please sign in to comment.