26

In the last few years I've written a nice little program in python and now I'd like to distribute it, but my first attempt didn't encounter much enthusiasm, since many potential users didn't like the idea of downloading and installing python and all the dependencies necessary to run my application.

I looked around for weeks searching for a solution to compile somehow my code into something easy to run for everybody, but I'm not really satisfied of what I found.

I have no problem with the idea of freezing the application instead of really compiling it. It just means that the end user will wait a few seconds more to download the application and will occupy more space on his/her hard disk.

My problem is to find a way to package all the application into an executable for every OS (let's say the big ones: windows, mac, linux - obviously a different executable for every OS) without the need to install virtual machines or wine or similar options. I strongly suspect it is possible, since I saw a bunch of ren'py games packaged exactly the way I'd like to package my application, but I don't know how to obtain the same result for my program.

Schematically, my application requires a console to run and includes a few home-made packages, numpy and matplotlib. It also has a system to support multiple languages that dynamically includes a language file, overwriting the previous settings:

exec('from lang.%s import lang' % language)

I suspect this could create some problems. Finally, the program requires a few folders to save its settings, logs, etc in the first run, so these should be included too. My computer runs ubuntu mate 16.10 (x64) and the python version is 2.7, but in the future I plan to translate it to python 3. The executables created can be single heavy files that contain almost everything needed or lightweight files and everything else can be found in a near folder; I don't really have a preference for one solution or the other.

I am aware that this question pops up from time to time and was probably already answered, but the last occurrence of this kind of question I could find dates back to 2014, which sounds like eons ago if we consider how quickly things change in this field.

Edit: Since there seems to be no solution with the conditions I proposed, I can think of setting up some emulators/wrapper/compatibility layer/whatever to run a freezer for different OSs, but I'm not sure what to do. I read here and there that wine doesn't always work properly and can't find anything to compile for mac on linux. As far as I understand the program shouldn't require a different compilation for 32/64 bit systems, but if there is an easy way to compile for every possibility it'd be nice. Virtual machines are still a no go, right now I don't have the space on my disk to set up two or more virtual machines (that's the great downside of ssd disks...), let alone paying for licences for OSs that would be used a few times a year to compile a free software.

GRB
  • 425
  • 1
  • 6
  • 20
  • 1
    Packaging all the files/folders your program requires shouldn't be a problem, most tools have support for that. I think the biggest obstacle is the cross-compilation - to my knowledge it's only possible to generate an executable for the platform you're currently on, so you'll have to freeze on Windows to get a Windows .exe and freeze on linux to get a linux executable etc. – Aran-Fey Feb 26 '17 at 11:27
  • @Rawing If I understood correctly, ren'py seems to cross-compile python without any problem. I don't have the possibility to run a virtual machine for every possible environment, so if there is another solution it'll be great. – GRB Feb 26 '17 at 14:52
  • If you truly insist on not having virtual machines, you can use the continuous integration services to run you packager in various OSes. – Stephen Rauch Feb 26 '17 at 16:42
  • @StephenRauch It's the first time I hear of continuous integration, but it looks to me like a good way to distribute code, not a way to compile/freeze it. Can you please explain what do you mean? I'm probably missing an important part of the process. – GRB Feb 26 '17 at 18:04
  • You missed the keyword of `services`. This [google](https://encrypted.google.com/search?hl=en&q=continuous%20integration%20services) search is a good place to start. These services will run your code on their servers. So you can run a packager on amny different OSes without having to configure these OSes yourself. – Stephen Rauch Feb 26 '17 at 18:17
  • @StephenRauch Sorry but I can't figure it out. Can you write a complete answer, containing what I have to download/install, where do I have to subscribe, where do I put my project, how do I configure everything and other things to do? I use to work by myself on my projects, I'm a complete novice with these code-sharing services such as github and usually distrust everything that looks like a cloud storage, so I'm completely lost at the moment. – GRB Feb 26 '17 at 21:52
  • 1
    Sorry, but this is an enormous request. – Stephen Rauch Feb 26 '17 at 21:54
  • Two options come to mind: 1) Python's standard distribution mechanism is wheels. If your native dependencies are all available as wheels on PyPI, then distributing as a wheel seems viable. Your users *may* need to install some OS packages along side it, but you could probably script this without too much trouble. 2) The second option that comes to mind is creating an OS installer for each OS. You'll need to build each one separately this way, but the installation experience will be pretty smooth. The bottom line of both of these is that you want to look at **standard installation mechanisms**. – jpmc26 Feb 28 '17 at 23:48
  • you can try conda-forge – denfromufa Mar 01 '17 at 00:09
  • @jpmc26 Wheels seem a good way to distribute a package, but as far as I know it's not ideal to distribute a program. The second option is more or less what I'm looking for, but the question is again how to obtain such a result without having to set up two or more different virtual machines. I don't care if the program is an installer that installs all the dependencies or a standalone program that contains everything needed. The first option has the advantage to be lightweight, the second to be cleaner. – GRB Mar 01 '17 at 00:40
  • @denfromufa It seems complete and free, which is good, but I still don't understand completely how this service works. I'll look into it tomorrow, but a working explanation could be worth the bounty. – GRB Mar 01 '17 at 00:44
  • @GRB That makes a wheel a very attractive option, then. Python wheels/eggs can generate an OS specific executable at install time. (It's an actual exe on Windows; it's a shell script on Linux. Not sure about Mac.) You could potentially have an installer add a shortcut in Windows to the executable and then some kind of synlink on other platforms. That said, I don't think you can build a wheel without the appropriate OS. Even if you could, I wouldn't *dare* release an installer of any kind to production without testing on the target system first. – jpmc26 Mar 01 '17 at 00:45
  • 1
    Unfortunately, I also feel this question should be closed. In particular, I think it's Too Broad. Packaging and release strategies are going to depend *very* strongly on the specifics of your use cases. "General advice" on the subject could easily span an entire book. This also comes dangerously close to a tool recommendation request. – jpmc26 Mar 01 '17 at 00:47
  • Create an `Virtual OS` and put in your application folder. Never a system allowed access/compile to `System Libraries`(`required > included > compiled > success`). Can't set a stable performance on every OS. – dsgdfg Mar 01 '17 at 07:26
  • @dsgdfg I'm not sure I understand what you are saying. What do you mean by virtual OS? – GRB Mar 01 '17 at 20:56

8 Answers8

9

Couple things first.

  • Python is already cross-platform
  • Python code is interpreted, not a natively compiled language and does not need compiling
  • There exist standard methods in Python setuptools to distribute executable scripts with cross-platform compatibility
  • Third party python libraries can include native code (in C) that will either need compiling when installing or for binary packages to have been available by the package maintainers. NumPy and matplotlib are in this category and do have binary packages available for windows AFAIK.

Here is some example setup.py code to install an executable script:

from setuptools import setup, find_packages
setup(<...>,
      entry_points={'console_scripts': [
          '<console cmd name here> = my_package.module:main'
]})

Where my_package.module is, for example

my_package/module.py:

<..>
def main():
    <what to run when <console cmd name here> is executed>

The package can then be distributed as a standard python module, installable via pip. If the code is open source, it can be hosted on PyPi (see instructions on PyPi site for how to setup).

Otherwise, a source or binary wheel package (python setup.py sdist and python setup.py bdist_wheel respectively) can be built and distributed to your users.

Binary wheels will need to built on the platform they are to be installed on, source distributions will work on any platform but will require code to be executed at installation as they contain purely source code.

Note that building/installing binary wheel packages requires pip version >= 6.0 and setuptools >= 26.0.

When this package is installed, a < console cmd name here > executable will be available to run on all platforms the package can be successfully installed.

For a simple example package that does this, see hello-world-python-package, specifically its setup.py.

See pythonwheels site for more information on binary wheel packages.

danny
  • 5,140
  • 1
  • 19
  • 31
  • 1
    Thanks for the answer, but it's not what I'm looking for. I'm trying to distribute a standalone program, not a python module that could be included in other programs. My problem is that right now my program requires the end user to install python, numpy and matplotlib in order to run, requirements that discourage the end users, particularly windows users. That's why I'm trying to "compile", or freeze more properly, my code. – GRB Mar 03 '17 at 21:43
  • The most used Python implementation (from python.org, often referenced as CPython) *does* compile Python code. Into python *bytecode* which are basically machine instructions for a CPython virtual machine. This is not a requirement of Python implementations, however. See the [documentation of the `dis` module](https://docs.python.org/3.6/library/dis.html). – Roland Smith Mar 04 '17 at 16:41
  • @GRB This case I don't think the other person need to install python and libs. At the end of setup you would get an executable file as you wanted. I suggest you explore more about setuptools – Satish Reddy Mar 07 '17 at 13:07
  • @RolandSmith Which is why "not a natively compiled language" was made explicit. Python byte code is not native machine code, it gets interpreted by the Python virtual machine which generated it and that virtual machine implementation alone. That byte code is in fact what gets packaged into binary wheel packages.. – danny Mar 07 '17 at 17:27
  • @GRB - the instructions will produce an installable, binary, python package with a globally available command with a name of your choosing. That package can then be frozen to create a native binary _on the platform the freeze command is executed_. All the freeze package does is bundle the python interpreter with the python code so the user does not have to have python installed to use it. A Python interpreter is _always_ required to run Python code, whether by user or bundled, for which you need a valid, installable package per above instructions. – danny Mar 07 '17 at 17:36
7

Since I haven't seen it on here yet I thought I would bring up Docker. It appears to be a distributable VM that contains a package or application of some sort. There is a free community edition available on github https://github.com/docker/docker. I have never used it myself but it seems to fit all of your criteria - lightweight, free, and platform-agnostic. Just something to explore.

Dan Temkin
  • 1,565
  • 1
  • 14
  • 18
  • I'm looking into this and it seems really promising. There are a huge lot of options I have to check, but right now I have a feeling this is what I need. – GRB Mar 08 '17 at 21:47
  • Yeah it seems pretty extensive but I am glad to hear that you are finding it useful. I will have to get around to learning more about one of these days. My neighbor us an IT Consultant and he has said that most of his work recently has been on integrating it into various companies as the new platform for their software development. In any case best of luck with. – Dan Temkin Mar 08 '17 at 23:34
  • 4
    Docker is another option yes, but keep in mind it requires user to install the docker runtime to be able to use a container that you build. Since you had already said you don't want user to have to install anything, it might not be an option for you. – danny Mar 09 '17 at 10:46
4

Initial reaction

  • For windows use p2exe
  • For linux use python's freeze
  • For macos use same freeze - should work (just a guess, held up only by one google search)

(changes might be needed to your program e.g. __file__ can't be used)


Possible alternative approach to initial problem

<...> didn't encounter much enthusiasm, since many potential users didn't like the idea of downloading and installing python

Maybe user objections could be cured, by including python installer itself next to your program. (either actual python installer or http://portablepython.com/)
i.e. add not just link to installer, but all installers and possibly bat/sh scripts to install everything.

industryworker3595112
  • 3,419
  • 2
  • 14
  • 18
4

At work, I do something like what you want, and I ended up writing my own code to do it. Everything is built on Linux, but I ship both Linux and Windows packages. The packages include the Python interpreter and library, and they work identically whether or not the customer already has Python installed.

Essentially, you need a small program that embeds the Python interpreter and sets it up to load only the Python code you ship, ignoring any system-installed Python. Written properly, this program can be cross-compiled for each architecture you want to support. You then ship the Python DLLs you need for the architecture, along with a zip file that has the Python code you'll use (you only need the .pyc or .pyo files). This zip file must also contain as much of the standard library as you need.

Caveats:

  • You will still want virtual machines for testing.
  • Setting up the C cross-compilers correctly for each architecture can be challenging.
  • Neither Python's site.py nor the one that virtualenv uses completely isolates you from a system Python install. I had to make my own version of site.py.
  • It's easy to link against the system Python by mistake. Be very careful about how you use pkg-config and compiler arguments in your build scripts.
  • It's easy for Python to try to load the system Python library by mistake. Be very careful about setting the Python path and probably Py_IgnoreEnvironmentFlag.
  • For Windows, you can get quite far with py2exe, and it will be easier than what I'm suggesting. I don't use py2exe mainly because I'm embedding Python code in a non-Python application.

Because my application is in Go with some Python, I wrote my wrapper as a Go package that calls Python WSGI code via cgo. You can see most of the Python embedding code here. That repo is not the whole story, though: the scripts for the custom Python build and the build scripts and initialization code for the app itself aren't public right now.

MicahStetson
  • 602
  • 4
  • 6
3

The only platform that expects programs to be a self-contained package is ms-windows. This is mostly for historical reasons pertaining to the native toolchain. For example up to version 2016 of the ms-toolchain (IIRC), Python extension modules like numpy must be compiled with exactly the same version of the ms-toolchain as Python itself. This makes building extension modules for ms-windows a huge pain.

Most UNIX-like operating systems have functional package management (although in Apple's OS-X this is supplied by a third-party). This makes installing Python relatively easy.

What could be helpful is to pack your application into a zipfile like youtube-dl does. I've written a script called build.py that does this for some of my programs.

Roland Smith
  • 42,427
  • 3
  • 64
  • 94
2

I am don't know what do you really thing by writing "freezing" application, but if I understand it, you need to compile your code into a installer, that simply install all libraries and files needed to run your program, so it can handle any noob computer user.

Ok. Now you have two choices:

A) Make installer for each system separately - You can use tools such as Inno setup or PackageBuilder (on mac) to create installer, what check if is python already installed (if not install it) and copy your files + make shortcut on desktop (or something like that) + run external scripts (if you need) or if you are need binaries for windows you can use Py2exe or on mac Py2app. On Linux there is not anything like that, but if someone using Linux usually is it advanced user.

If I know there is not something like automatic builder for all platforms to build python installers, but if you have some money you can boost your work with Bitrock Install Builder.

B) Make your own script - If user already have python you can make script for downloading and installing files manually (by me best solution if is product made for advanced users, but if you make some GUI it not will be problem for noob)

Samuel Tulach
  • 1,319
  • 13
  • 38
2

Ren'Py

You mention Ren'Py a couple of times. Its documentation describes how to add Python code to a game. The Python Statements section even describes how to add third party packages (e.g. numpy and matplotlib). It can generate files for Windows, Mac, and Linux. If you can get it to run your application the way you want, and you are already familiar with it, then you should use it.

PyInstaller

Another option is PyInstaller. In the Supporting Multiple Operating Systems section, it says this:

If you need to distribute your application for more than one OS, for example both Windows and Mac OS X, you must install PyInstaller on each platform and bundle your app separately on each.

The second paragraph describes how to do this on a single machine:

You can do this from a single machine using virtualization. The free virtualBox or the paid VMWare and Parallels allow you to run another complete operating system as a “guest”. You set up a virtual machine for each “guest” OS. In it you install Python, the support packages your application needs, and PyInstaller.

PyInstaller advertises support for numpy and matplotlib.

Virtual Machines and Hosted Operating Systems

You said you don't have room on your SSD for virtual machines. You could buy an external USB hard drive and store the virtual machines on it. Or you could use an operating system hosting provider. You can use Google to find out more about those. If you have questions about how to use a specific hosting provider, you should create new questions.

Cross Platform Testing

Even if you use Ren'Py, you will almost certainly need to test the results on virtual machines or hosted operating systems. The only way to be certain that your program will run correctly on other platforms is to test it on those platforms. The hosting solution allows you to avoid "paying for licences for OSs that would be used a few times a year to compile a free software". However, you will still be spending money for the hosting.

The Price of Free

If your goal is to develop Python applications for Windows, Mac, and Linux without spending any money, then you are probably stuck with Ren'Py and relying on users to help you test your application on their operating systems.

2

What about JAVA? Wrap you python code into java distribute as jar or .exe?

http://www.jython.org/jythonbook/en/1.0/JythonAndJavaIntegration.html

You may find this QA useful in your case. Distributing my Python scripts as JAR files with Jython?

Community
  • 1
  • 1
oshaiken
  • 2,593
  • 1
  • 15
  • 25