In this post we are going to see how to create a snap for a utility called timg. If this is the very first time you heard about snap installation packages, see How to create your first snap.
Today we learn the following items about creating snaps with snapcraft,
the source of timg comes with a handmade Makefile, and requires to tinker a bit the make plugin parameters.
the program is written in C and requires a couple of external libraries. We make sure their relevant code has been added to the snap.
should we select strict confinement or classic confinement? We discuss how to choose between these two.
So, what does this timg do?
The Linux terminal emulators have become quite cool and they have colors!
Apart from the standard colors, most terminal emulators (like the GNOME Terminal depicted above) support true color (16 million colors!).
Yes! True color in the terminal emulators! Get the AWK code from the page True Colour (16 million colours) support in various terminal applications and terminals
and test it yourself. You may notice in the code that it uses some escape sequences to specify the RGB value (256*256*256 ~= 16 million colors).
What is timg?
Alright, back to the point of the post. What does timg do? Well, it takes an image as input and resizes it down to the character dimensions of your terminal window (for example, 80×25). And shows the image in color, just by using characters in color, in whatever resolution the terminal window is in!
This one is the Ubuntu logo, a PNG image file, shown as colored block characters.
And here is a flower, by @Doug8888.
timg would be especially helpful if you are connecting remotely to your server, minding your own business, and want to see what an image file looks like. Bam!
Apart from static images, timg can also display animated gifs! Let’s start snapping!
Getting familiar with the timg source
The source of timg can be found at https://github.com/hzeller/timg Let’s try to compile it manually in order to get an idea what requirements it may have.
The Makefile is in the src/ subdirectory and not in the root directory of the project. At the github page they say to install these two development packages (GraphicsMagic++, WebP) and then make would simply work and generate the executable. In the screenshot I show for brevity that I have them already installed (after I read the Readme.md file about this).
Therefore, four mental notes when authoring the snapcraft.yaml file:
The Makefile is in a subdirectory, src/, and not in the root directory of the project.
The program requires two development libraries in order to compile.
In order for timg to run as a snap, we need to somehow bundle these two libraries inside the snap (or link them statically).
timg is written in C++ and requires g++. Let’s instruct the snapcraft.yaml file to check that the build-essential metapackage is installed before compiling.
Starting off with snapcraft
Let’s create a directory timg-snap/, and run snapcraft init in there in order to create a skeleton snapcraft.yaml to work on.
Filling in the metadata
The upper part of a snapcraft.yaml configuration file is the metadata. We fill them in in one go, and they are the easy part. The metadata consist of the following fields
name, the name of the snap as it will be known publicly at the Ubuntu Store.
version, the version of the snap. Can be an appropriate branch or tag in the source code repository, or the current date if no branch or tag exist.
summary, a short description under 80 characters.
description, a bit longer description, under 100 words.
grade, either stable or devel. We want to release the snap in the stable channel of the Ubuntu Store. We make sure the snap works, and write stable here.
confinement, initially we put devmode so as not to restrict the snap in any way. Once it works as devmode, we later consider whether to select strict or classic confinement.
For the name, we are going to use timg,
Yeah, I registered the name the other day :-).
Next, which version of timg?
When we look for a branch or a tag in the repository, we find that there is a v0.9.5 tag, with the latest commit in 27th Jun 2016.
However, in the main branch (master) there are two commits that look significant. Therefore, instead of using tag v0.9.5, we are snapping the master branch. We are using today’s date as the version, 20170226.
We glean the summary and description from the repository. Therefore, the summary is A terminal image viewer, and the description is A viewer that uses 24-Bit color capabilities and unicode character blocks to display images in the terminal.
Finally, the grade will be stable and the confinement will be devmode (initially, until the snap actually works).
Here is the updated snapcraft.yaml with the completed metadata section:
Figuring out the “parts:”
This is the moment where we want to replace the stub parts: section shown above with a real parts:.
We know the URL to the git repository and we have seen that there is an existing Makefile. The existing Makefile commands for the make Snapcraft plugin. As the documentation says, This plugin always runs ‘make’ followed by ‘make install’. In order to identify the make plugin, we went through the list of available Snapcraft plugins.
Therefore, we initially change from the stub
Here is the current snapcraft.yaml,
Let’s run the snapcraft prime command and see what happens!
snapcraft could not find a Makefile in the source and as we hinted earlier, the Makefile is only located inside the src/ subdirectory. Can we instruct snapcraft to look into src/ of the source for the Makefile?
Each snapcraft plugin has its own options, and there are general shared options that relate to all plugins. In this case, we want to look into the snapcraft options relating to the source code. Here we go,
source-subdir: pathSnapcraft will checkout the repository or unpack the archive referred to by the source keyword into parts/<part-name>/src/ but it will only copy the specified subdirectory into parts/<part-name>/build/
We have the appropriate option, let’s update the parts:
And run snapcraft again!
Here it tells us that it cannot find the development library GraphicsMagick++. According to the Snapcraft common keywords, we need to specify this development library so that Snapcraft can install it:
build-packages: [deb, deb, deb…]A list of Ubuntu packages to install on the build host before building the part. The files from these packages typically will not go into the final snap unless they contain libraries that are direct dependencies of binaries within the snap (in which case they’ll be discovered via ldd), or they are explicitly described in stage-packages.
Therefore, let’s find the name of the development package,
The package name is a lib + graphicsmagick++ and ends with -dev. We got it! Here is the updated parts:
Let’s run snapcraft prime again!
Simply by specifying the development library libgraphicsmagick++1-dev, Ubuntu also installs the code libraries, including libgraphicsmagick++-q16-12, along with the (dynamic) code library libwebp.
We still got an error, and the error is related to the webp library. The development version of the webp library (a static library) is missing. Let’s find it!
Bingo! The libwebp5 package that was installed further up, provides only a dynamic (.so) library. By requiring the libwebp-dev package, we get the static (.a) library as well. Let’s update the parts: section!
Here is the updated snapcraft.yaml file,
Let’s run snapcraft!
We have a new problem. The Makefile was hand-crafted and does not obey the parameters of the Snapcraft make plugin to install into the prime/ directory. The Makefile tries to install in /usr/local/bin/ !
We need to instruct the Snapcraft make plugin not to run make install but instead pick the generated executable timg and place it into the prime/ directory. According to the documentation,
So, we need to put something in artifacts:. But what?
In the build/ subdirectory we can find the make output. Since we specified source-subdir: src, our base directory for artifacts: is build/src/. And in there we can find the executable timg, which will be the parameter for artifacts:. With artifacts: we specify the files from the make output that will be copied to the snap installation directory (in prime/).
Here is the updated parts: of snapcraft.yaml,
Let’s run snapcraft prime!
We are rolling!
Exposing the command
Up to now, snapcraft generated the executable but did not expose a command for the users to run. We need to expose a command and this is done in the apps: section.
First of all, where is the command located in the prime/ subdirectory?
It is in the root of the prime/ subdirectory. We are ready to add the apps: section in snapcraft.yaml,
Let’s run snapcraft prime again and then try the snap!
Image source: https://www.flickr.com/photos/mustangjoe/6091603784/
We used snap try –devmode prime/ as a way to enable the snap and try the command. It is an efficient way for testing and avoids the alternative of generating .snap files, installing them, then uninstalling them. With snap try prime/, it uses directly the directory (in this case, prime/) which has the snap content.
Restricting the snap
Up to now, the snap has been running in devmode (developer mode), which is unrestricted. Let’s see how it runs in a confinement,
Here we quickly switch from devmode to jailmode (confinement: strict) without having to touch the snapcraft.yaml file. As expected, timg could not read the image because we did not permit any access to the filesystem.
At this stage we need to make a decision. With jailmode, we can easily specify that a command has access to the files of the user’s $HOME directory, and only there. If an image file is located elsewhere, we can always copy of the $HOME directory and run timg on the copy in $HOME. If we are happy with this, we can set up snapcraft.yaml as follows:
On the other hand, if we want the timg snap to have read-access to all the filesystem, we can use confinement: classic and be done with it. Here is how snapcraft.yaml would look in that case,
In the following we are selecting the option of the strict confinement. Therefore, images should be in the $HOME only.
Packaging and testing
Let’s package the snap, that is, create the .snap file and try it out on a brand-new installation of Ubuntu!
How do we get a brand new installation of Ubuntu in seconds so that we can test the snap?
See Trying out LXD containers on our Ubuntu and set up LXD on your system. Then, come back here and try the following commands,
So, we launched an LXD container called snaptesting, then copied in there the .snap file. Then, we connected to the container as a normal user and tried to install the snap. Initially, the installation failed because we need sudo when we install snaps in unprivileged LXD containers. It again failed because the .snap was unsigned (we need the –dangerous parameter). Then, it further failed because we need to install the squashfuse package (not preinstalled in the Ubuntu 16.04 images). Eventually, the snap was installed and we managed to view the image.
It is important to test a snap in a brand new installation of Linux in order to make sure whether we need to stage any code library inside the snap. In this case, static libraries were used and all went well!
Publishing to the Ubuntu Store
Here are the instructions to publish a snap to the Ubuntu Store. We have already published a few snaps in the previous tutorials. For timg, we got confinement: strict, and grade: stable. We are therefore publishing in the stable channel.
We pushed the .snap file to the Ubuntu Store and we got a revision number 6. Then, we released the timg revision 6 to the stable channel of the Ubuntu Store.
There was no released snap in the candidate channel, therefore, it inherits the package from the stable channel. Hence, the ^ characters.
In previous tests I uploaded some older versions of the snap to the beta and edge channels. These older versions refer to the old tag 0.9.5 of the timg source code.
Let’s knock down the old 0.9.5 by releasing the stable version to the beta and edge channels as well.
Playing with timg
Let’s run timg without parameters,
Here it says that for the current zoom level of our terminal emulator, our resolution is a mere 80×48.
Let’s zoom out a bit and maximize the GNOME Terminal window.
It is a better resolution but I can hardly see the characters because they are too small. Let’s invoke the old command to show this car again.
What you are seeing is the resized image (from 1080p). Looks great, even if it is make of colored text characters!
What next? timg can play animated gifs as well!
Try to install the timg snap yourself in order to experience the animated gif! Failing that, watch the asciinema recording (if the video looks choppy, run it a second time), https://asciinema.org/a/dezbe2gpye84e0pjndp8t0pvh
Thanks for reading!