RFC: MirAL framework snaps

Hey all,

For a while now we’ve been pondering how to best solve building snaps of Mir-based servers. This post aims at discussing where we are, and where we could go.

Status quo

Mir is built in three PPAs: release, rc, dev - in increasing order of bleeding-edgedness. To build a Mir-based server (unity8, mir-kiosk, egmde, …), you need to somehow make its libraries available to your application. For a deb-based distribution, you’d just get the PPA in and install the packages. With snaps, it’s more of a Bring Your Own Dependencies situation, and Mir falls into that category.

Some existing snaps use something of a workaround for snapcraft's current lack of support for “additional archives” - they install the PPA in a part that other parts can depend on. Another approach, taken by mate-wayland-snap is to build Mir as a part directly from source.

Both of those approaches have pros and cons and may be valid for some software regardless of the proposal below.

Enter framework snaps

The approach taken by other collections of libraries (GNOME, KDE to name two) are so called “framework snaps”. They’re collections of libraries encapsulated in snaps that can be shared with others via content interface connections. Those snaps expose one or more slots in the form <library>-<api_version>-<build_environment>, like gnome-3.28-1804. This allows the consuming snap to ensure that the features required are always available for it, and binary-compatible. It’s the framework snaps’ responsibility to ensure binary compatibility of the libraries they provide.

The miral-2-* snap

MirAL was created to ensure binary backwards-compatibility to servers consuming Mir through the Mir Abstraction Library. If you build a server against MirAL 2.* today, it will continue to work (without rebuilding) with any subsequent release of that major version. It doesn’t mean there can’t be features added to it, rather that we’ll not be removing anything or changing in an incompatible way.

The miral-2-core18 snap could expose the following slots:

miral-24-core18:
  content: miral-24-core18
  # …
miral-25-core18:
  content: miral-25-core18
  # …
# etc.

A new release of MirAL with added features would add a new slot with a new version, so consumers that want to use those new features can ensure that the snap version installed includes them. Other consumers might not even notice that there’s a new version and can remain on the previous version.

The consumer snaps would have a plug declaration similar to:

miral:
  content: miral-24-core18
  default-provider: miral-2-core18:miral-24-core18
  # …

This would ensure that the MirAL framework snap is installed when you install the server snap and the correct minimal version of the libraries are available.

Building

To be able to build a server using that framework snap, we’ll need to introduce a build-snap of MirAL as well. It would include all the headers and bits required to build your software. Depending on size, we may actually include it all in the framework snap itself.

Pros

To weigh whether this approach is valid, I’ll try and enumerate the advantages below:

  • decoupling (Mir can be updated separately from the server)
  • simplified build (servers don’t need the Mir PPAs installed, or build it from source)
  • ensured compatibility (server snap won’t stop working if Mir introduces an incompatible change)
  • security (new Mir builds will get to your devices unattended if you use a custom server)
  • easier development (you can refresh miral-2-core18 to the candidate channel to ensure it works)
  • lower memory and disk footprint (if more than one snap uses the MirAL framework, they share the libraries)

Cons

Devil’s advocate voices as follows:

  • less control (Mir can be updated separately from the server)
  • interface connections (the content interface may not be auto-connected, requiring manual intervention)

Alternatives

PPAs

The PPAs will remain where they are, if your snap requires more than the framework snap, you can still use them when building your snap.

Building from source

Yes, source remains where it is, too - you can build it in your snap if required.

stage-snaps

Snapcraft supports stage-snaps: now as well, so you could actually take the miral snap and stage its contents into your snap.

Feedback

Please reply below with your thoughts!

This is a seductive idea. As you note, providing an ABI stable interface is what I aimed for when inventing MirAL and this sets it firmly in place as a key element of the architecture.

As the maintainer of several Mir based snaps I can confirm that the developer experience isn’t ideal. Pulling build-packages and stage-packages from a PPA isn’t directly supported by snapcraft. (But this isn’t the worst problem and hardly intractable.)

mir graphics-drivers

I should add another that there is also another interface needed if we are to do this well: the mir graphics-drivers. Currently we have two flavors of this: mir-graphics-drivers-desktop and mir-graphics-drivers-nvidia. But this is an intentional customization point for Mir so that alternative driver stacks can be substituted. For example, the UBports folks have mir-graphics-drivers-android.

Being able to substitute the “appropriate” mir graphics-drivers for a device would have definite benefits, especially once the issues around managing the kernel and userspace parts of the GL stack have been addressed.

cons & pros

However, making all snapped Mir servers dependent on a miral-framework snap adds another thing that can go wrong in deployment (e.g. the content interface not auto connecting, or a snap updating at a “critical moment”) and removes control from the server developer.

Some of the suggested benefits are dubious:

  • how may different Mir server snaps are likely to exist on a typical system?
    I know there are several on my systems, but that’s because I’m actively developing them. For the typical IoT device where space matters there’s likely to be only one.

  • Is using a miral-framework snap actually going to be any simpler for the developer?
    Most of the “incantations” needed in the snapcraft.yaml are unchanged or remain with minor changes. The only significant difference is pulling a content interface for the libraries and “something” for the .h and .rc files instead of pulling a -dev package from a PPA.

Conclusion

This idea needs further exploration before it is clear whether and how to proceed.

Hey @alan_g, thanks for reading.

Regarding graphics drivers, how compatible are they between different Mir releases? How stable is the platform ABI? While not ideal, I wonder if we wouldn’t need to go the route of having miral-24-android-core18 for the “external” platforms? Also, it’s not just graphics, isn’t it? Input is another area where we may need to think about?

Sure, for IoT, probably little chance. But it’s not that clear in a desktop scenario. Think multiple different DEs, maybe a system compositor.

I’d say “pulling a -dev package from a PPA” is sometimes quite a challenge, as you’ve learned from the snap experience. There’s also the cross-distribution story that a build snap would help with. Agreed, it binds you to the base the framework was built on, but at least you have the option of building and installing it on a different distro without necessarily building / bundling it all.

As you observer there are also input drivers, but there’s only one “production” option based on libinput, so bundling that with the miral-framework is less of a concern.

It is a problem that there are sometimes incompatible changes in the graphics driver ABIs:

  • Nov 2018: Mir 1.1 - mirplatformgraphics ABI bumped to 16
  • Jun 2018: Mir 0.32 - mirplatformgraphics ABI bumped to 15 (It looks like 14 got skipped)
  • Apr 2017: Mir 0.27 - mirplatformgraphics ABI bumped to 13
  • Feb 2017: Mir 0.26 - mirplatformgraphics ABI bumped to 12

That would argue for a mir-platform-graphics-16 interface with mesa-kms, nvidia and, potentially, other providers.

All of this does make for manual dependency management by the snap deployer.

Note: As Mir tolerates different versions of the drivers being installed side-by-side it is possible to imagine managing transitions by adding new platform modules to old content interfaces without removing the originals. But that is an ugly solution.

That’d actually be mir-platform-graphics-16-core16 and -core18, assuming we wanted to support the two platforms. I suppose 2 bumps a year isn’t that much of a problem, especially since customers of the framework wouldn’t be affected. Any updates here would happen behind the scenes.

Why that? With the default-provider: stanza and auto-connections in place this should be handled fine, with the potential exception of gadget snaps and custom images, where more control is retained for the image creator.

Can you elaborate what you’re describing here?

I see no reason to have a core16 variants, all of our (server) snaps are core18 and the Wayland support in the 16.04 archives is meh.

How do you envisage usingdefault-provider - default to the mesa-kms provider?

Something (the gadget snap?) needs to point to the right platform for the GL implementation. Maybe we can bundle the Mir platform with the GL userspace?

Sure. Suppose we have:

  1. A Mir 1.1.2 based framework snap (miral-framework?) providing the miral-framework-2.4 content interface.
  2. A Mir 1.1.2 based mesa-kms graphics platform snap (mir-platform-graphics-mesa-kms?) providing the mir-platform-graphics-16 content interface.
  3. A consumer snap (e.g. mate-wayland) consuming these interfaces.

Now along comes Mir 1.2 and, for the sake of illustration, this bumps the mirplatformgraphics ABI to 17

We update the mir-platform-graphics-mesa-kms snap to provide both the mir-platform-graphics-16 and mir-platform-graphics-17 interfaces. mir-platform-graphics-16 provides both mesa-kms16 and mesa-kms17, while mir-platform-graphics-17 only provides mesa-kms17.

That will allow mate-wayland to continue to consume the legacy interfaces and work with both the old and new miral-framework snaps.

Next we update the miral-framework snaps to provide both the miral-framework-2.4 and miral-framework-2.5 interfaces. They can have the same content as miral is ABI stable.

At some point after this the mate-wayland snap gets updated to consume the new interfaces.

After a suitable interval, we can drop the mir-platform-graphics-16 and miral-framework-2.4 interfaces and the mesa-kms16 platform module required by the former.

It is ugly, but supportable.

Sure, but we’re likely to want -core20, so we’d still need to encode the base in the snap name.

That’s what mir-graphics-drivers-desktop defaults to, I’d say that makes sense as the most common platform?

Yes, if someone builds an appliance, they’d choose the right platform snaps to include in their image through the gadget snap. We’ll have to see how the GL story pans out to decide what to do exactly there.

I thought it would be miral-framework consuming the platform interface? That said, I’m not sure snapd supports “chaining” default providers… TBD

Do servers get built against a certain version of the graphics platform? MirAL doesn’t abstract this?

Yeah the problem is… what is “a suitable interval”? This would also mean that devices in the wild would stop working when we decide to drop support from the snap. If we had “dedicated” snaps, they’d stop updating, but continue to work.

I don’t think it is about chaining default-providers, I don’t see why that wouldn’t work.

It is about chaining targets. The process is running in the mate-wayland snap context, unless that inherits namespaced mounts (why would it?) it has to consume the mir-platform-graphics-16[-core18] interface.

I agree, the solution to that will inform this discussion.

Given your earlier point about switching to a new coreXX every two years that would be a natural “cleanup” point for this.

Well, that’s where I think we may try and do better. Why would mate-wayland care about updates to the graphics platforms? Think there’s a new platform that’s only compatible with the next version of the platform ABI. If the server snap consumes the graphics platform directly, it has to update to become compatible with the new platform. If possible, I’d avoid that requirement.

I wouldn’t say “switching” here, but rather “adding support for”. We’d still maintain the previous core versions at least some time after a new one is available. And in any case us stopping support would not mean breakage to existing deployments. Removing slots from a snap would.

I meant that we would have a mesa-kms-core18 snap with, say:

  • mir-platform-graphics-16-core18
  • mir-platform-graphics-XX-core18

And that we’d switch development to mesa-kms-core20 with just:

  • mir-platform-graphics-XX-core20

Oh ok, so we’d never go “back” in that sense… yeah that may work.

EDIT:
Except if external platforms did not follow that same approach, you’d end up with mate-wayland that would be incompatible with that platform…

That’s true. Even if they did follow the approach, they’d have to update their mir-platform-graphics-16-core18 to contain graphics-17 libs before we could release a new miral-framework-core18.

So. Not “ugly, but supportable” after all.

So: miral-framework24-mirgraphics16-core18 etc.?

Why doesn’t mir-platform-graphics-16-core18miral-2-core18mate-wayland work?

Could you expand on how you think it can work?

I’m assuming here that it’s possible to “proxy” content from a content interface connection here. So miral-framework would expose the connected platform’s mir-platform-graphics-16-core18's content through its own miral-24-core18 content slot. So customer would only need the miral-… connection. This assumption may very well be wrong, though…

@saviq no point in speculating. One of us should find time to install a few test snaps locally and try it.

I experimented a bit and it doesn’t seem possible to “forward” content from one snap to another.

I created three dummy snaps (pastebin) and connected the interfaces:

mir-graphics-16-core18:mir-graphics-16-core18  miral-2-core18
miral-2-core18:miral-framework-2-4-core18      mate-wayland:miral-2-4-core18

miral-2-core18 could see the graphics contents:

$ snap run --shell miral-2-core18.nil -c "find \$SNAP -name lib*"   
/snap/miral-2-core18/x1/miral-framework/mir-graphics/lib1
/snap/miral-2-core18/x1/miral-framework/lib2

Unfortunately mate-wayland only saw the contents from the miral… snap:

$ snap run --shell mate-wayland.nil -c "find \$SNAP -name lib*"
/snap/mate-wayland/x1/miral/miral-framework/lib2

We’d need another way to share the platforms with the client… I would really like to limit the Mir interfaces to just one, as far as the server is concerned. That said, servers are not as many and they are bound to Mir in the first place. So maybe them declaring compatibility with a certain platform ABI is not that bad. If only MirAL could abstract this away as well…

I’ll post on the snapcraft forum to see what they can come up with.

1 Like