Jérôme Belleman
Home  •  Tools  •  Posts  •  Talks  •  Travels  •  Graphics  •  About Me

Easy RPMs with setup.py

7 Jun 2015

Building RPMs is a business made easy and flexible with Python's Distutils, and it allows to package any files at all, not just Python scripts and modules.

If writing SPEC files and building RPMs has always been an ordeal, to the point that packaging your software takes longer than writing it (and I kid you not, it's often been the case for me) this approach might be of interest to you. And just because it involves using Python's Distutils doesn't mean it's only meant to package Python code. It works for just about anything.

1 It's a Doddle

Write a setup.py file looking like:

#!/usr/bin/env python

import distutils.core

name = 'yourtool'

distutils.core.setup(name=name,
    version='1.0',
    author="Firstname Surname",
    author_email="Firstname.Surname@cern.ch",
    url="http://cern.ch",
    description="Short description of your tool",
    long_description="Long description of your tool",
    license="ASL",

    scripts=[name],
    data_files=[
        ('/usr/share/man/man1', [name + '.1.gz']),
    ],
)

Run:

python setup.py bdist_rpm

And Bob's your uncle.

2 Specifying Dependencies

Not all the meta-data you need to write an RPM can be specified as setup() function parameters. This is true of several SPEC file options for which you'll need to use a specific command line option, e.g. for package dependencies. And you'll soon want to use a Makefile for this purpose:

rpm:
    python setup.py bdist_rpm --requires foo

3 Build Packages Programmatically

Since setup.py is nothing but a Python script, it's of course possible to make packaging a rather intelligent process. For instance, nothing keeps you from e.g. changing your package name depending on the distribution:

#!/usr/bin/env python

import platform

dist = platform.linux_distribution(full_distribution_name=0)

distutils.core.setup(name='foo' if dist == 'redhat' else 'bar',
[...]

4 Generating Files to Package?

This may unfortunately not work as expected. For instance, if you've got a Markdown manual page that you'd like to turn into a real groff manual page, calling pandoc with subprocess in the setup.py script will not work, because it seems that setup() is run not only when you execute setup.py but also at later stages in other directories where the original Markdown source may not be available. There's nothing for it, you're better off using a Makefile:

rpm: manpage
    python setup.py bdist_rpm

manpage: 
    pandoc -s -tman yourtool.md | gzip > yourtool.1.gz

clean:
    python setup.py clean
    rm yourtool.1

5 It Doesn't Work on AFS

That's because Distutils wants to create hard links if possible, rather than copying. But hard links aren't supported on AFS. An easy workaround is to trick Distutils into believing that hard links can't be used on your system, simply by removing the corresponding Python function:

#!/usr/bin/env python

import distutils.core
import os

delattr(os, 'link')

name = 'yourtool'

[...]

6 References