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.
Is it possible to make it?
What toolset do i need to use? (Basically, how
to make it).
If possible - sample code
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...
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…
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.
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.
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.
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
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
$ 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With