32

Aim

To create an installable .deb file (or package). Which when clicked would install the software on a Linux machine and an icon would be put on the GNOME panel. So as to launch this application from there.

What I have referred to

I referred to two debianizing guides.

Guide 1

Guide 2

The first one had a video which was impossible to understand, partly because of the accent and partly because it was hopelessly outdated.(it was uploaded in 2007)

And the second one was completely text. I got till the 4th Step, Builds the package. But when I did it I got output that did not match what was given in the guide.

What I require

I have a simple python program. It takes your age and then prints back out if the age is below, equal to, or above 18 years. There is just one file and no other dependency for this program. And I want to build this into a .deb.

Specs

-Python 2.7

-Linux Mint

Edit

I followed the exact directory structure as you instructed as you. And replaced all myscript with cowsandbulls. The build completed and I got the Debian. When I installed it and then ran the command cowsandbulls from the terminal I got the following error:

Traceback (most recent call last):
  File "/usr/bin/cowsandbulls", line 9, in <module>
    load_entry_point('cowsandbulls==1.0', 'gui_scripts', 'cowsandbulls')()
  File "/usr/lib/python2.7/dist-packages/pkg_resources.py", line 337, in load_entry_point
    return get_distribution(dist).load_entry_point(group, name)
  File "/usr/lib/python2.7/dist-packages/pkg_resources.py", line 2311, in load_entry_point
    return ep.load()
  File "/usr/lib/python2.7/dist-packages/pkg_resources.py", line 2017, in load
    entry = __import__(self.module_name, globals(),globals(), ['__name__'])
ImportError: No module named cowsandbulls
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
IcyFlame
  • 5,059
  • 21
  • 50
  • 74

2 Answers2

33

I just tested stdeb (see https://pypi.python.org/pypi/stdeb), a Python package for turning any other Python package into a Debian package.

First I installed stdeb:

apt-get install python-stdeb

Then I made a simple script called myscript.py with the following contents:

def main():
    print "Hello world, says myscript!"
    # wait for input from the user
    raw_input()

if __name__ == '__main__':
    main()

Importantly, your directory structure should be:

somewhere/myscript/
    setup.py
    myscript/
        __init__.py
        myscript.py

In the setup.py file, you do something like:

import os
from setuptools import setup
from nvpy import nvpy

setup(
    name = "myscript",
    version = "1.0",
    author = "Charl P. Botha",
    author_email = "cpbotha@vxlabs.com",
    description = "Demo of packaging a Python script as DEB",
    license = "BSD",
    url = "https://github.com/cpbotha/nvpy",
    packages=['myscript'],
    entry_points = {
        'console_scripts' : ['myscript = myscript.myscript:main']
    },
    data_files = [
        ('share/applications/', ['vxlabs-myscript.desktop'])
    ],
    classifiers=[
        "License :: OSI Approved :: BSD License",
    ],
)

The console_scripts directive is important, and it'll create an executable script called my_script, which will be available system-wide after you install the resultant DEB. If your script uses something like tkinter or wxpython and has a graphical user interface, you should use gui_scripts instead of console_scripts.

The data_files directive will install a suitable desktop file into /usr/share/applications, so that you can also start myscript from your desktop environment. vxlabs-myscript.desktop looks like this:

[Desktop Entry]
Version=1.0
Type=Application
Name=myscript
Comment=Minimal stdeb example
# myscript should wait for user input at the end, else the terminal
# window will disappear immediately.
Exec=myscript
Icon=/usr/share/icons/gnome/48x48/apps/file-manager.png
Categories=Utility;
# desktop should run this in a terminal application
Terminal=true
StartupNotify=true
StartupWMClass=myscript

To build the DEB, you do the following in the top-level myscript:

python setup.py --command-packages=stdeb.command bdist_deb

Which will create a .deb in the deb_dist directory.

After having installed the DEB I created like this, I could run myscript from the command-line, and I could also invoke it from my desktop environment.

Here's a GitHub repository with the example code above: https://github.com/cpbotha/stdeb-minimal-example

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Charl Botha
  • 4,373
  • 34
  • 53
  • Sir: what should be the content of `__init__.py`? – IcyFlame Jul 01 '13 at 10:59
  • 1
    `__init__.py` is an empty file, but you need to have it, else the innermost `myscript` directory is not seen as a package, and will not be importable. The github link I posted at the bottom of my answer has all the code. – Charl Botha Jul 01 '13 at 11:01
  • can I change the entry point to the name of some other function? – IcyFlame Jul 01 '13 at 11:12
  • of course you can, as long as the module in question is importable, and the function is callable. :) – Charl Botha Jul 01 '13 at 11:17
  • That is the error you see if you forget the `__init__.py` in the same directory as your own .py file containing the entry point. Just to make 100% sure, it's two underscores init two underscores dot py. – Charl Botha Jul 01 '13 at 11:39
  • Got it! Thanks a lot! The module `myscript` should have the same name as the `.py` file itself.. I was messing that up! – IcyFlame Jul 01 '13 at 12:06
  • I don't understand how to use the `setup.py` file that you directed me to. Can you please add some clarification for the same? I got the data_files directive. But I don't know what to do with it. – IcyFlame Jul 01 '13 at 12:23
  • Added .desktop file example to answer. I believe my answer is now 100% complete and self-contained. :) SO also doesn't like this long discussion, so let's keep it at this. – Charl Botha Jul 01 '13 at 13:09
  • Yes sir...! It is a complete answer with all the specificities dealt with. And the process is so much more simpler than appeared in the two guides I referred to. Thanks a lot for the answer..! (P.S.: yeah I notice that it is kinda long. But it was necessary!) – IcyFlame Jul 01 '13 at 13:43
  • @CharlBotha I was practicing with your example code I downloaded from GitHub. I'm getting the following error: `error: can't copy 'vxlabs-myscript.desktop': doesn't exist or not a regular file`. I haven't changed anything to your file structure. The file is very much present in the folder. Do you know what might be going on? (I had the same problem with my own project, that's why I tried yours) – Stefan van den Akker Oct 09 '14 at 13:22
  • I got it. Apparently, in my case it was neccessary to create a `MANIFEST.in` file in the same directory as the `setup.py` file with the content `include vxlabs-myscript.desktop`. – Stefan van den Akker Oct 09 '14 at 14:00
  • Tip: Name shouldn't have any spaces. If it has spaces you'll get "Can't call method "as_string" on an undefined value at /usr/share/perl5/Dpkg/Changelog.pm line 249." – Sameera K May 26 '20 at 22:42
  • what does the packages parameter in `setup.py` do? –  Apr 15 '21 at 18:40
3

The right way of building a deb package is using dpkg-buildpackage, but sometimes it is a little bit complicated. Instead you can use dpkg -b <folder> and it will create your Debian package.

These are the basics for creating a Debian package with dpkg -b <folder> with any binary or with any kind of script that runs automatically without needing manual compilation (Python, Bash, Pearl, and Ruby):

  1. Create the files and folders in order to recreate the following structure:
    ProgramName-Version/
    ProgramName-Version/DEBIAN
    ProgramName-Version/DEBIAN/control
    ProgramName-Version/usr/
    ProgramName-Version/usr/bin/
    ProgramName-Version/usr/bin/your_script

The scripts placed at /usr/bin/ are directly called from the terminal. Note that I didn't add an extension to the script.

Also, you can notice that the structure of the deb package will be the structure of the program once it's installed. So if you follow this logic if your program has a single file, you can directly place it under ProgramName-Version/usr/bin/your_script, but if you have multiple files, you should place them under ProgramName-Version/usr/share/ProgramName/all your files and place only one file under /usr/bin/ that will call your scripts from /usr/share/ProgramName/.

  1. Change all the folder permission to root:

    chown root:root -R /path/to/ProgramName-Version
    
  2. Change the script's permissions:

    chmod 0755 /path/to/the/script
    
  3. Finally, you can run: dpkg -b /path/to/the/ProgramName-Version and your deb package will be created! (You can also add the post/pre inst scripts and everything you want; it works like a normal Debian package.)


Here is an example of the control file. You only need to copy-paste it in to an empty file called "control" and put it in the DEBIAN folder.

Package: ProgramName
Version: VERSION
Architecture: all
Maintainer: YOUR NAME <EMAIL>
Depends: python2.7, etc , etc,
Installed-Size: in_kb
Homepage: http://foo.com
Description: Here you can put a one line description. This is the short Description.
 Here you put the long description, indented by 1 space.

If you want to build using dpkg -b <folder> you can use this program that will do everything with one command. If you regularly build packages, it is a pain to do all the stuff that I mentioned!

*The guide was taken from Basics of Debian Packages.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • The problem with this answer is that it doesn't address the question - which boils down to how do you create the necessary structure via a `setup.py` file? I.e. how do you go from a standard python package to a .deb file. Is there a way for `pip` to install into a chroot that dpkg-buildpackage can see? – davidA Oct 05 '20 at 21:01
  • The guide reference doesn't exist now, can it be updated? – Sibidharan Mar 11 '23 at 09:41