How to create a rpm for python application
This is a mini demo structure output by tree
command, color_print
is the package name and directory
.
├── color_print
│ ├── color_print.py
│ └── __init__.py
├── __init__.py
└── setup.py
Here is an example setup.py
for demo
from setuptools import setup
setup(name='color_print',
version='0.1',
description='Color String',
url='http://github/xxxx/color_print/',
author='Joe Bob',
author_email='joe.bob@gmail.com',
license='MIT',
packages=['color_print'],
zip_safe=False)
There is no need to change directory, run this one command to build rpms
python setup.py bdist_rpm
Here is the output, it is that easy:
-bash-4.1$ find . -name "*.spec"
./build/bdist.linux-x86_64/rpm/SPECS/color_print.spec
-bash-4.1$ find . -name "*.rpm"
./dist/color_print-0.1-1.noarch.rpm
./dist/color_print-0.1-1.src.rpm
In reality, you will definitely need to modify the spec files manually. and run
rpmbuild -ba ./build/bdist.linux-x86_64/rpm/SPECS/color_print.spec
Creating Python RPM
- This command has to be typed wherever your
setup.py
is located. - It packages everything that would show up in a
bdist
tarball. - Err... sort of. While it works, the package it creates is not of very high quality. It's better to use
sdist_rpm
, then unpack the resulting SRPM and then apply your distro's Python packaging guidelines to the generated spec file. - Get it to work via
bdist
first. That way any issues that crop up will be more manageable.
Python 3.5 create .rpm with pyinstaller generated executable
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
How to use setuptools to create rpm packages for linux
How to build RPM package using bdist directly from setup.py http://jeromebelleman.gitlab.io/posts/devops/setuppy/
Note that this method is easy and can produce just simply RPM packages. And for example, you cannot put requires (or build requires) in metadata, you have to remember to put them on the command line all the times.
I would say that bdist is suitable just for initial work. If you want to ship and support it then creating SPEC file is a must.
One more example - AFAIK you cannot specify %post
or %pre
scriptlets using bdist and setup.py.
Here is an example of python SPEC file: https://fedoraproject.org/wiki/Packaging:Python#Example_common_spec_file
How to create a RPM which install python dependencies?
bdist_rpm
lacks of a lot of functionality and IMO is not very well maintained. E.g. pyp2rpm
is much better for converting existing PyPI modules. But your module does not seem to be on PyPI, so you need to specify it to bdist_rpm
manually because it cannot retrieve this information from setup.py
.
Run:
python setup.py bdist_rpm --requires python-flask
This will produce an rpm file which requires the python-flask
package. For more recent RHEL/Fedora it would be python3-flask
.
Related Topics
Gae " No Attribute 'Httpshandler' " Dev_Appserver.Py
Setuid Bit on Python Script:Linux VS Solaris
Pip Error:'Module' Object Has No Attribute 'Cryptography_Has_Ssl_St'
How to Install Python Developer Package
/Usr/Bin/Ld: Cannot Find -Lpython2.7
Find "Home Directory" in Python
Find Broken Symlinks with Python
Importerror: No Module Named _Io in Ubuntu 14.04
How to Parse the Output of /Proc/Net/Dev into Key:Value Pairs Per Interface Using Python
Linux/Python: Encoding a Unicode String for Print
Detect Log File Rotation (While Watching Log File for Modification)
Running Python Script as a Systemd Service
How to Get the Day of Week Given a Date