3.1- Freeze des applications sous Linux

Notre version de Linux: Ubuntu 16.04 LTS 64 bits. Ce qui suit doit être valable pour des distributions Linux de type Debian ou dérivées de celle-ci.

3.1.1- Avertissement à propos du freeze sous conda

Sous Linux j’utilise généralement pour installation python un environnement dédié au projet dans conda. Néanmoins l’utilisation de cx_Freeze dans miniconda3 sur Ubuntu a posé un problème. Si le freeze se déroule correctement, l’exécution de la version standalone du jeu compilée a provoquée l’erreur fatale suivante :

$./Labpyrinthe
Traceback (most recent call last):
  File "/home/user/miniconda3/envs/labpyp/lib/python3.7/site-packages/cx_Freeze/initscripts/__startup__.py", line 40, in run
    module.run()
[…]
  File "/home/user/miniconda3/envs/labpyp/lib/python3.7/site-packages/PIL/Image.py", line 69, in <module>
    from . import _imaging as core
ImportError: libjpeg.so.9: cannot open shared object file: No such file or directory

Cette erreur serait due à l’incapacité pour Pillow de retrouver dans conda les librairies avec lesquelles il a été compilé:

Pillow needs the same version of the libraries as it was built with. So, when it was build against libjpeg.so.9, it’s going to require that in the future. Your best bet is to chose one set of jpeg libraries + dev headers and rebuild pillow against those. Conda makes it a bit more complicated, since they preload some of their libraries and Pillow has some trouble determining that at build time. —https://github.com/python-pillow/Pillow/issues/1952.

Pour contourner ce problème nous allons utiliser pyenv.

3.1.2- Installation de pyenv

Installation des pré requis: d’après https://github.com/pyenv/pyenv/wiki/Common-build-problems

$ sudo apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev \
libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev \
xz-utils tk-dev libffi-dev liblzma-dev python-openssl git

Installation de pyenv: d’après https://github.com/pyenv/pyenv-installer

$ curl https://pyenv.run | bash

Redémarrage du shell:

$ exec $SHELL

Mise à jour de .bashrc:

Ajout des trois lignes suivantes au fichier $HOME/.bashrc :

export PATH="$HOME/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

On redémarre le shell (équivalent à fermer/ouvrir le terminal) et on installe python 3.7.5:

$ pyenv install 3.7.5
Downloading Python-3.7.5.tar.xz...
-> https://www.python.org/ftp/python/3.7.5/Python-3.7.5.tar.xz
Installing Python-3.7.5...
Installed Python-3.7.5 to /home/user/.pyenv/versions/3.7.5

On affecte cette version de python au dossier contenant notre projet:

$ cd /home/user/Documents/Workdir/LabPyProject
pyenv local 3.7.5

Cette commande a créé un fichier .python-version dans le dossier.

Vérification:

$ pyenv versions
  system
* 3.7.5 (set by /home/user/Documents/Workdir/LabPyProject/.python-version)

A présent quand on appellera la commande python dans le dossier contenant les sources c’est cette version de python qui sera exécutée.

Configuration des packages python dans pyenv:

$ pip install --upgrade pip

Installation de notre wheel Numpy + OpenBLAS :

$ pip install /home/…/numpy-1.19.0.dev0+4787918-cp37-cp37m-linux_x86_64.whl

Installation des dépendances du jeu :

$ pip install --force pillow==7.0.0
$ pip install pygame

Remarque:

A partir de la version 7.1 Pillow modifie le dossier d’installation de ses librairies, ce qui cause une erreur d’importation lors de l’exécution du freeze de l’application Labpyrinthe:

ImportError: libjpeg-ba7bf5af.so.9.4.0: cannot open shared object file: No such file or directory

Référence: https://github.com/matrix-org/synapse/issues/7208

Et de cx_Freeze :

$ pip install cx_Freeze

3.1.3- Freeze de plusieurs exécutables avec cx_Freeze

La structure de fichiers est la suivante :

/home/user/Documents/Workdir
└── LabPyProject
    ├── …
    ├── labpyproject
    │   ├── apps
    │   ├── core
    │   └── …
    ├── dist
    │   ├── pc
    │   └── linux
    │       └── cxf
    ├── clientPygame.py
    ├── labpy_ico0001.ico
    ├── serverConsole.py
    ├── setup_cxf_linux.py
    ├── standalonePygame.py
    └── tcp_config.txt

La configuration du freeze se fait dans le fichier setup_cxf_linux.py (pour setup cx_Freeze sur linux), dont le contenu est le suivant:

#!/usr/bin/python
# -*- coding: utf-8 -*-
# Python 3
"""
Setup CX_freeze pour linux
"""
import sys, os, glob
from cx_Freeze import setup, Executable
# 1- Ressources à inclure
root = ""
root_gui = root + "labpyproject/apps/labpyrinthe/gui/"
datas_dir_list = list()
# dirs de png :
for x in glob.glob(root_gui + "**/*.png", recursive=True):
    relpath = os.path.relpath(x, start=root)
    reldir = os.path.dirname(relpath)
    if reldir not in datas_dir_list:
        datas_dir_list.append(reldir)
# dirs de fonts :
ttf_root = root_gui + "skinPygame/rsc/fonts/"
for x in glob.glob(ttf_root + "**/*.ttf", recursive=True):
    relpath = os.path.relpath(x, start=root)
    reldir = os.path.dirname(relpath)
    if reldir not in datas_dir_list:
        datas_dir_list.append(reldir)
# création de la liste de tupples décrivant les dirs de ressources à inclure
datas_files = [(r, r) for r in datas_dir_list]
# ajout du .txt de config reseau
datas_files.append(("tcp_config.txt", "tcp_config.txt"))
# 2- options
options = dict(
        path=sys.path,
        zip_include_packages=["*"],
        zip_exclude_packages=[],
        include_files = datas_files,
        bin_path_includes = ["/usr/lib"],
        optimize = 1,
        excludes = ["atomicwrites",
                    "curses",
                    "email",
                    "html",
                    "http",
                    "json",
                    "multiprocessing",
                    "pluggy",
                    "pydoc_data",
                    "tkinter.tk.demos",
                    "tkinter.tk.images",
                    ],
                )
# 3- exe à générer
ex_std = Executable(
    script="standalonePygame.py",
    targetName="Labpyrinthe",
    base=None,
    icon=None
    )
ex_clt = Executable(
    script="clientPygame.py",
    targetName="Labpyrinthe_client",
    base=None,
    icon=None
    )
ex_svr = Executable(
    script="serverConsole.py",
    targetName="Labpyrinthe_serveur",
    base="Console",
    icon=None
    )
# 4- setup
setup(
    name="Labpyrinthe",
    version="1.00",
    description="Python arcade maze game",
    author="Gabriel Leroy",
    options={"build_exe": options},
    executables=[ex_std, ex_clt, ex_svr]
    )

La version linux se distingue de la version pc du setup par quelques légères différences :

  • Dans l’expression des paths de la partie1
  • Dans les options path et bin_path_include de la partie2
  • Dans les bases de la partie3

Il ne reste plus qu’à lancer le freeze:

$ cd /home/user/Documents/Workdir/LabPyProject
$ python setup_cxf_linux.py build -b dist/linux/cxf

Le résultat est comparable à celui obtenu sur pc:

/home/user/Documents/Workdir
└── LabPyProject
    ├── …
    └── dist
        ├── pc
        └── linux
            └── cxf
                └── exe.linux-x86_64-3.7
                    ├── labpyproject
                    ├── lib
                    ├── Labpyrinthe
                    ├── Labpyrinthe_client
                    ├── Labpyrinthe_serveur
                    └── tcp_config.txt

A la différence de la version pc:

  • le dossier exe.linux-x86_64-3.7 pèse 141.2 Mo (contre 96 Mo sur pc) et ne contient pas de librairie dynamique liée à python à sa racine
  • les exécutables sont plus lourds (3.1 Mo contre 30 ko environ sur pc), ils embarquent probablement l’interpréteur python.

Remarque:

Le sous dossier lib contient bien une librairie OpenBLAS (libopenblas.so.o de 32 Mo).

Pour rendre ces applications exécutables, il faut leur accorder les droits d’exécution :

$ cd dist/linux/cxf/exe.linux-x86_64-3.7
$ chmod +x Labpyrinthe
$ chmod +x Labpyrinthe_client
$ chmod +x Labpyrinthe_serveur

Pour que la console de Labpyrinthe_serveur s’affiche il faut également créer dans exe.linux-x86_64-3.7 un fichier texte que l’on nomme serverlauncher.desktop et qui contient les lignes suivantes:

[Desktop Entry]
Exec=/home/user/Documents/Workdir/LabPyProject/dist/linux/cxf/exe.linux-x86_64-3.7/serverConsole
Name=serverlauncher.desktop
Terminal=true
Type=Application

Référence: https://stackoverflow.com/questions/23103683/ubuntu-desktop-script-to-open-terminal-navigate-to-a-folder-and-run-compass-wat

A présent on peut lancer les applications par double clic ou via la commande (exemple pour la version standalone):

$ ./Labpyrinthe

3.2- Création d’une archive auto extractible du freeze

Pour obtenir un fichier unique à distribuer j’ai adopté (faute de temps) la solution la plus simple, créer une archive auto extractible du dossier de freeze exe.linux-x86_64-3.7.

Pour cela j’utilise l’outil p7zip, d’après https://linuxhint.com/install-7zip-compression-tool-on-ubuntu/.

Installation:

$ sudo apt-get update
$ sudo apt-get install p7zip-full

Vérification de l’installation:

$ 7z
7-Zip [64] 9.20  Copyright (c) 1999-2010 Igor Pavlov  2010-11-18
p7zip Version 9.20 (locale=fr_FR.UTF-8,Utf16=on,HugeFiles=on,2 CPUs)
[…]

Création de l’archive:

Par simple clic droit sur le dossier exe.linux-x86_64-3.7, option compresser, je crée l’archive exe.linux-x86_64-3.7.7z dans dist/linux/7zip. L’archive pèse 36.7 Mo (contre 141 Mo pour le dossier d’origine).

J’ai à présent un fichier unique à distribuer pour que l’utilisateur puisse installer facilement l’application (par décompression de l’archive dans un dossier).

Remarque:

L’utilisateur devra néanmoins accorder les droits d’exécution aux applications et créer un fichier .desktop pour lancer l’application serveur dans une console.

Archive auto extractible du jeu Labpyrinthe pour Linux 64: Télécharger