Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python 3.5 create .rpm with pyinstaller generated executable

I've got a build generated with a pyinstaller. I need to create .rpm package which will put the executable into the /usr/bin/ and create a systemd service which will run that executable.

I found this https://docs.python.org/3/distutils/builtdist.html and https://docs.python.org/2.0/dist/creating-rpms.html

However it doesn't give me a full picture.

  1. Is it possible to make it?

  2. What toolset do i need to use? (Basically, how to make it).

  3. If possible - sample code

like image 337
user1935987 Avatar asked Aug 01 '18 19:08

user1935987


People also ask

How to create an executable using pyinstaller?

Creating an executable using PyInstaller 1 Reading the script file. 2 Analyzing the python code to identify all the dependent libraries required for the execution 3 Creates a Spec file that contains the script name, dependent libraries, any data files, including different options that you provide to PyInstaller command. More items...

How do I create an executable file in Python?

It turns out that there is an easy way to create executable files in Python using pyinstaller package. Official documentation can be found below: PyInstaller freezes (packages) Python applications into stand-alone executables, under Windows, GNU/Linux, Mac OS X…

How do I install pyinstaller without changing the registry?

If you don’t need to change the registry, you could just distribute the .exe file that PyInstaller generated, since the .exe file is basically a portable version of your program. Otherwise, you’ll need to create a separate installer executable to set things up correctly.

What is the difference between PEC and pyinstaller?

The .S pec file has the same name as the python script file. PyInstaller creates a distribution directory, DIST containing the main executable and the dynamic libraries bundled in an executable file.


1 Answers

First of all, forget about bdist_rpm. It's for a distutils/setuptools project, so you would need a setup.py script that invokes pyinstaller under the hood to bundle the executable, somehow redefines the install_scripts command to be able to package binary executables and also handles the packaging of the systemd unit files. Instead, write a spec file which is the instruction manual for rpm to build and install your package.

setup

This is the example project to play with.

so-51640995
├── bacon.service
├── bacon.spec
├── bacon.timer
└── spam.py

spam.py

No magic here - prints eggs once called. Will be bundled via pyinstaller to a binary named bacon. I didn't call the project spam to avoid ambiguity, because pyinstaller also creates a file with .spec extension, so that running it does not overwrite the rpm spec file.

#!/usr/bin/env python3

def eggs():
    print('eggs!')


if __name__ == '__main__':
    eggs()

bacon.service

Simple service calling the binary bacon.

[Unit]
Description=Bacon emitting eggs

[Service]
ExecStart=/usr/bin/bacon
Restart=always

bacon.timer

Will call bacon every ten seconds.

[Unit]
Description=Timer for bacon to emit eggs from time to time

[Timer]
OnUnitInactiveSec=10s
OnBootSec=10s
Unit=bacon.service

[Install]
WantedBy=timers.target

bacon.spec

The instruction for the package. In %build section, we bundle spam.py, then install the bundled executable dist/spam to /usr/bin/bacon along with the systemd unit files.

Name: bacon
Version: 1
Release: 1
Summary: bacon that shouts 'eggs!' from time to time
License: MIT
Requires: systemd

%description
bacon that shouts 'eggs!' from time to time

%build
pyinstaller --onefile %{_sourcedir}/spam.py

%install
mkdir -p %{buildroot}%{_bindir}
mkdir -p %{buildroot}%{_unitdir}
install -m 755 dist/spam %{buildroot}%{_bindir}/bacon
install -m 755 %{_sourcedir}/bacon.service %{buildroot}%{_unitdir}/bacon.service
install -m 755 %{_sourcedir}/bacon.timer %{buildroot}%{_unitdir}/bacon.timer

%files
%{_bindir}/bacon
%{_unitdir}/bacon.service
%{_unitdir}/bacon.timer

build the package

There are lots of tutorials out there that explain building rpm packages in-depth, for example Fedora Packaging Guidelines, so just listing the minimal sequence of commands here:

$ # install the bare minimum of required packages
$ sudo dnf install rpm-build rpm-devel rpmdevtools
$ # first-time setup of build dirs
$ rpmdev-setuptree
$ # copy the source files
$ cp * $HOME/rpmbuild/SOURCES/
$ # invoke the build
$ rpmbuild -ba bacon.spec

test the package

$ sudo rpm -ivp $HOME/rpmbuild/RPMS/x86_64/bacon-1-1.x86_64.rpm

Edit: as mentioned in the comments, use -U in favor of -i. Quote from the rpm mans:

The general form of an rpm upgrade command is

 rpm {-U|--upgrade} [install-options] PACKAGE_FILE ...

This upgrades or installs the package currently installed to a newer version. This is the same as install, except all other version(s) of the package are removed after the new package is installed.

So use

$ sudo rpm -Uvp $HOME/rpmbuild/RPMS/x86_64/bacon-1-1.x86_64.rpm

for test installation.

Now bacon should be available from command line:

$ bacon
eggs!

Start the timer:

$ sudo systemctl start bacon.timer
$ systemctl status bacon.timer
● bacon.timer - Timer for bacon to emit eggs from time to time
   Loaded: loaded (/usr/lib/systemd/system/bacon.timer; disabled; vendor preset: disabled)
   Active: active (waiting) since Tue 2018-08-07 15:36:28 CEST; 29s ago
  Trigger: Tue 2018-08-07 15:36:58 CEST; 979ms left

Check the logs:

$ sudo journalctl -u bacon
-- Logs begin at Mon 2017-07-03 12:49:51 CEST, end at Tue 2018-08-07 15:37:02 CEST. --
Aug 07 15:36:28 XXX systemd[1]: Started Bacon emitting eggs.
Aug 07 15:36:28 XXX bacon[128222]: eggs!
Aug 07 15:36:28 XXX systemd[1]: bacon.service: Service hold-off time over, scheduling restart.
Aug 07 15:36:28 XXX systemd[1]: Stopped Bacon emitting eggs.
Aug 07 15:36:28 XXX systemd[1]: Started Bacon emitting eggs.
Aug 07 15:36:28 XXX bacon[128224]: eggs!
Aug 07 15:36:28 XXX systemd[1]: bacon.service: Service hold-off time over, scheduling restart.
Aug 07 15:36:28 XXX systemd[1]: Stopped Bacon emitting eggs.
Aug 07 15:36:28 XXX systemd[1]: Started Bacon emitting eggs.
Aug 07 15:36:29 XXX bacon[128226]: eggs!
...

Once verified things work, stop the timer and uninstall bacon:

$ sudo systemctl stop bacon.timer
$ sudo rpm -e bacon
$ sudo systemctl daemon-reload
$ sudo systemctl reset-failed
like image 121
hoefling Avatar answered Oct 06 '22 16:10

hoefling