본문 바로가기
PyQt5_

Creating a Linux Package with

by 자동매매 2023. 3. 13.

40. Creating a Linux Package with

In an previous chapter we used PyInstaller to bundle the application into a Linux executable, along with the associated data files. The output of this bundling process is a folder which can be shared with other users. However, in order to make it easy for them to install it on their system, we need to create a Linux package.

Packages are distributable files which allow users to install software on their Linux system. They automatically handle putting files in the correct places, as well as setting up application entries in the dock/menu to make it easier to launch the app.

On Ubuntu (and Debian) packages are named .deb files, on Redhat .rpm and on Arch Linux .pacman. These files are all different formats, but thankfully the process for building them is the same using a tool named fpm. fpm is a packaging system by Jordan Issel, which takes a folder (or list of files) and assembles them into a Linux package.

Z

In this chapter we’ll work through the steps for creating a Linux

package, using an Ubuntu .deb file as an example. However,

thanks to the magic of fpm , you will be able to use the same

approach for other Linux systems.

Figure 266. Ubuntu Package, for our "Hello World" application

ë

If you’re impatient, you can download the Example Ubuntu

Package first.

Installing fpm

The fpm tool is written in ruby and requires ruby to be installed to use it. Install ruby using your systems package manager, for example.

$ sudo apt-get install ruby

Once ruby is installed, you can install fpm using the gem tool.

$ gem install fpm --user-install

ë

If you see a warning that you don’t have

~/.local/share/gem/ruby/2.7.0/bin` in your PATH you will need

to add that to your path in your .bashrc file.

Once the installation is complete, you’re ready to use fpm. You can check it is installed and working by running:

$ fpm --version

1.14.2

Checking your build

In a terminal, change to the folder containing your application source files & run a PyInstaller build to generate the dist folder. Test that the generated build runs as expected (it works, and icons appear) by opening the dist folder in the file manager, and double-clicking on the application executable.

If everything works, you’re ready to package the application — if not, go back and

double check everything.

ë

It’s always a good idea to test your built application before

packaging it. That way, if anything goes wrong, you know where

the problem is!

Now let’s package our folder using fpm.

Structuring your package

Linux packages are used to install all sorts of applications, including system tools. Because of this they are set up to allow you to place files anywhere in the Linux filesystem — and there are specific correct places to put different files. For a GUI application like ours, we can put our executable and associated data files all under the same folder (in /opt). However, to have our application show up in the menus/search we’ll also need to install a .desktop file under /usr/share/applications.

The simplest way to ensure things end up in the correct location is to recreate the target file structure in a folder & then tell fpm to package using that folder as the root. This process is also easily automatable using a script (see later).

In your projects root folder, create a new folder called package and subfolders which map to the target filesystem — /opt will hold our application folder hello- world, and /usr/share/applications will hold our .desktop file, while /usr/share/icons... will hold our application icon.

$ mkdir -p package/opt

$ mkdir -p package/usr/share/applications

$ mkdir -p package/usr/share/icons/hicolor/scalable/apps

Next copy (recursively, with -r to include subfolders) the contents of dist/app to package/opt/hello-world — the /opt/hello-world path is the destination of our application folder after installation.

$ cp -r dist/hello-world package/opt/hello-world

Z

We’re copying the dist/hello-world folder. The name of this

folder will depend on the name configured in PyInstaller.

The icons

We’ve already set an icon for our application while it’s running, using the penguin.svg file. However, we want our application to show it’s icon in the dock/menus. To do this correctly, we need to copy our application icons into a specific location, under /usr/share/icons.

This folder contains all the icon themes installed on the system, but default icons for applications are always placed in the fallback hicolor theme, at /usr/share/icons/hicolor. Inside this folder, there are various folders for different sizes of icons.

$ ls /usr/share/icons/hicolor/

128x128/ 256x256/ 64x64/ scalable/

16x16/ 32x32/ 72x72/ symbolic/

192x192/ 36x36/ 96x96/

22x22/ 48x48/ icon-theme.cache

24x24/ 512x512/ index.theme

We’re using a Scalable Vector Graphics (SVG) file so our icon belongs under the scalable folder. If you’re using a specifically sized PNG file, place it in the correct location — and feel free to add multiple different sizes, to ensure your application icon looks good when scaled. Application icons go in the subfolder apps.

$ cp icons/penguin.svg

package/usr/share/icons/hicolor/scalable/apps/hello-world.svg

j

Name the destination filename of the icon after your application

to avoid it clashing with any others! Here we’re calling it hello-

world.svg.

The .desktop file

The .desktop file is a text configuration file which tells the Linux desktop about a desktop application — for example, where to fine the executable, the name and which icon to display. You should include a .desktop file for your apps to make them easy to use. An example .desktop file is shown below — add this to the root folder of your project — with the name hello-world.desktop, and make any changes you like.

Listing 261. packaging/installer/linux/hello-world.desktop

[Desktop Entry]

# The type of the thing this desktop file refers to (e.g. can be Link)

Type =Application

# The application name.

Name =Hello World

# Tooltip comment to show in menus.

Comment =A simple Hello World application.

# The path (folder) in which the executable is run

Path =/opt/hello-world

# The executable (can include arguments)

Exec =/opt/hello-world/hello-world

# The icon for the entry, use the target filesystem path.

Icon =hello-world

Now the hello-world.desktop file is ready, we can copy it into our install package with.

$ cp hello-world.desktop package/usr/share/applications

Permissions

Packages retain the permissions of installed files from when they were packaged, but will be installed by root. In order for ordinary users to be able to run the application, you need to change the permissions of the files created.

We can recursively apply the correct permissions 755 - owner can read/write/execute, group/others can read/execute. to our executable and folders, and 644, owner can read/write, group/others can read to all our other library and icons/desktop files.

$ find package/opt/hello-world -type f -exec chmod 644 -- {} +

$ find package/opt/hello-world -type d -exec chmod 755 -- {} +

$ find package/usr/share -type f -exec chmod 644 -- {} +

$ chmod +x package/opt/hello-world/hello-world

Building your package

Now everything is where it should be in our package "filesystem", we’re ready to start building the package itself.

Enter the following into your shell.

fpm -C package -s dir -t deb -n "hello-world" -v 0.1.0 -p hello-

world.deb

The arguments in order are:

  • -C the folder to change to before searching for files: our package folder
  • -s the type of source(s) to package: in our case dir, a folder
  • -t the type of package to build: a deb Debian/Ubuntu package
  • -n the name of the application: "hello-world"
  • -v the version of the application: 0.1.0
  • -p the package name to output: hello-world-deb

Z

You can create other package types (for other Linux

distributions) by changing the -t argument. For more command

line arguments, see the fpm documentation.

After a few seconds, you should see a message to indicate that the package has been created.

$ fpm -C package -s dir -t deb -n "hello-world" -v 0.1.0 -p hello-

world.deb

Created package {:path=>"hello-world.deb"}

Installation

The package is ready! Let’s install it.

$ sudo dpkg -i hello-world.deb

You’ll see some output as the install completes.

Selecting previously unselected package hello-world.

(Reading database ... 172208 files and directories currently

installed.)

Preparing to unpack hello-world.deb ...

Unpacking hello-world (0.1.0) ...

Setting up hello-world (0.1.0) ...

Once installation has completed, you can check the files are where you expect, under /opt/hello-world

$ ls /opt/hello-world

app libpcre2-8.so.0

base_library.zip libpcre.so.3

icons libpixman-1.so.0

libatk-1.0.so.0 libpng16.so.16

libatk-bridge-2.0.so.0 libpython3.9.so.1.0

etc.

Next try and run the application from the menu/dock — you can search for "Hello World" and the application will be found (thanks to the .desktop file).

Figure 267. Application shows up in the Ubuntu search panel, and will also appear in menus on other environments.

If you run the application, the icons will show up as expected.

Figure 268. Application runs and all icons show up as expected.

Scripting the build

We’ve walked through the steps required to build an installable Ubuntu .deb package from a PyQt6 application. While it’s relatively straightforward once you know what you’re doing, if you need to do it regularly it can get quite tedious and prone to mistakes.

To avoid problems, I recommend scripting this with a simple bash script & fpm’s own automation tool.

package.sh

Save in your project root and chmod +x to make it executable.

Listing 262. packaging/installer/linux/package.sh

#!/bin/sh

# Create folders.

[ -e package ] && rm -r package

mkdir -p package/opt

mkdir -p package/usr/share/applications

mkdir -p package/usr/share/icons/hicolor/scalable/apps

# Copy files (change icon names, add lines for non-scaled icons)

cp -r dist/hello-world package/opt/hello-world

cp icons/penguin.svg

package/usr/share/icons/hicolor/scalable/apps/hello-world.svg

cp hello-world.desktop package/usr/share/applications

# Change permissions

find package/opt/hello-world -type f -exec chmod 644 -- {} +

find package/opt/hello-world -type d -exec chmod 755 -- {} +

find package/usr/share -type f -exec chmod 644 -- {} +

chmod +x package/opt/hello-world/hello-world

.fpm file

fpm allows you to store the configuration for the packaging in a configuration file. The file name must be .fpm and it must be in the folder you run the fpm tool. Our configuration is as follows.

Listing 263. packaging/installer/linux/.fpm

-C package

-s dir

-t deb

-n "hello-world"

-v 0.1.0

-p hello-world.deb

ë

You can override any of the options you like when executing

fpm by passing command line arguments as normal.

Executing the build

With these scripts in place our application can be packaged reproducibly with the commands:

pyinstaller hello-world.spec

./package.sh

fpm

Feel free to customize these build scripts further yourself to suit your own project!

In this chapter we’ve stepped through the process of taking a working build from PyInstaller and using fpm to bundle this up into a distributable Linux package for Ubuntu. Following these steps you should be able to package up your own applications and make them available to other people.

Example applications

By now you should have a firm grasp of how to go about building simple applications with PyQt6. To show how you can put what you’ve learnt into practice, I’ve included a couple of example applications in this chapter. These applications are functional, simple and in some ways incomplete. Use them for inspiration, to pull apart and as an opportunity to improve. Read on for a walkthrough of each app’s most interesting parts.

The full source for both applications is available for download, along with 13 other applications in my 15 Minute Apps repository on Github. Have fun!

There are also other examples of miniature apps throughout this book — for example the Paint and Todo apps — I encourage you to extend these too, it’s the best way to learn.

댓글