Ich habe ein Python-Modul geschrieben, das von einigen C-Erweiterungen abhängt. Diese C-Erweiterungen hängen wiederum von mehreren kompilierten C-Bibliotheken ab. Ich möchte dieses Modul gebündelt mit allen Abhängigkeiten verteilen können.
Ich habe ein minimales Beispiel zusammengestellt ( es kann auf GitHub in seiner Gesamtheit gefunden werden ).
Die Verzeichnisstruktur ist:
$ tree .
.
├── README.md
├── poc
│ ├── __init__.py
│ ├── cython_extensions
│ │ ├── __init__.py
│ │ ├── cvRoberts_dns.c
│ │ ├── cvRoberts_dns.h
│ │ ├── helloworld.c
│ │ ├── helloworld.pxd
│ │ ├── helloworld.pyx
│ │ ├── test.c
│ │ └── test.h
│ ├── do_stuff.c
│ └── do_stuff.pyx
└── setup.py
setup.py erstellt die Erweiterungen und Links für die erforderlichen Bibliotheken (libsundials_cvode
, libsundials_nvectorserial
in diesem Fall):
from setuptools import setup, find_packages
from setuptools.extension import Extension
from Cython.Build import cythonize
ext_module_dostuff = Extension(
'poc.do_stuff',
['poc/do_stuff.pyx'],
)
ext_module_helloworld = Extension(
'poc.cython_extensions.helloworld',
['poc/cython_extensions/helloworld.pyx', 'poc/cython_extensions/test.c', 'poc/cython_extensions/cvRoberts_dns.c'],
include_dirs = ['/usr/local/include'],
libraries = ['m', 'sundials_cvodes', 'sundials_nvecserial'],
library_dirs = ['/usr/local/lib'],
)
cython_ext_modules = [
ext_module_dostuff,
ext_module_helloworld
]
setup (
name = "poc",
ext_modules = cythonize(cython_ext_modules),
packages=['poc', 'poc.cython_extensions'],
)
Das ist alles in Ordnung und gut, aber es erfordert, dass der Endbenutzer zuerst Sonnenuhren installiert (und im konkreten Fall auch einige andere Bibliotheken, die extrem schwierig in Betrieb zu nehmen sind).
Im Idealfall möchte ich dies nur auf Entwicklungscomputern einrichten, eine Distribution mit den entsprechenden gemeinsam genutzten Bibliotheken erstellen und eine Art Bundle ausliefern können.
Angesichts der verschiedenen Tutorials, Beispiele und SO Posts, die ich bisher gefunden habe. Ich muss glauben, dass ich auf dem richtigen Weg bin. Es gibt jedoch eine Art letzten Schritt, den ich einfach nicht scherze.
Jede Hilfe wird gebeten :-).
Wie Sie wahrscheinlich wissen, wird empfohlen, ein Python-Modul mit kompilierten Komponenten im Format wheel zu verteilen. Es scheint keine plattformübergreifende Standardmethode zu geben, mit der native Bibliotheken von Drittanbietern in einem Rad gebündelt werden können. Es gibt jedoch plattformspezifische Tools für diesen Zweck.
auditwheel
.auditwheel
ändert eine vorhandene Linux-Wheel-Datei, um Bibliotheken von Drittanbietern hinzuzufügen, die nicht im Standard " manylinux " enthalten sind. Hier ist eine exemplarische Vorgehensweise für die Verwendung mit Ihrem Projekt bei einer Neuinstallation von Ubuntu 17.10:
Installieren Sie zunächst die grundlegenden Python-Entwicklungstools und die Drittanbieter-Bibliothek mit ihren Headern:
[email protected]:~# apt-get install cython python-pip unzip
[email protected]:~# apt-get install libsundials-serial-dev
Dann baue dein Projekt in eine Wheel-Datei:
[email protected]:~# cd cython-example/
[email protected]:~/cython-example# python setup.py bdist_wheel
[...]
[email protected]:~/cython-example# cd dist/
[email protected]:~/cython-example/dist# ll
total 80
drwxr-xr-x 2 root root 4096 Nov 8 11:28 ./
drwxr-xr-x 7 root root 4096 Nov 8 11:28 ../
-rw-r--r-- 1 root root 70135 Nov 8 11:28 poc-0.0.0-cp27-cp27mu-linux_x86_64.whl
[email protected]:~/cython-example/dist# unzip -l poc-0.0.0-cp27-cp27mu-linux_x86_64.whl
Archive: poc-0.0.0-cp27-cp27mu-linux_x86_64.whl
Length Date Time Name
--------- ---------- ----- ----
62440 2017-11-08 11:28 poc/do_stuff.so
2 2017-11-08 11:28 poc/__init__.py
116648 2017-11-08 11:28 poc/cython_extensions/helloworld.so
2 2017-11-08 11:28 poc/cython_extensions/__init__.py
10 2017-11-08 11:28 poc-0.0.0.dist-info/DESCRIPTION.rst
211 2017-11-08 11:28 poc-0.0.0.dist-info/metadata.json
4 2017-11-08 11:28 poc-0.0.0.dist-info/top_level.txt
105 2017-11-08 11:28 poc-0.0.0.dist-info/WHEEL
167 2017-11-08 11:28 poc-0.0.0.dist-info/METADATA
793 2017-11-08 11:28 poc-0.0.0.dist-info/RECORD
--------- -------
180382 10 files
Die Raddatei kann jetzt lokal installiert und getestet werden:
[email protected]:~/cython-example/dist# pip install poc-0.0.0-cp27-cp27mu-linux_x86_64.whl
[...]
[email protected]:~/cython-example/dist# python -c "from poc.do_stuff import hello; hello()"
hello cython
0.841470984808
trying to load the sundials program
3-species kinetics problem
At t = 2.6391e-01 y = 9.899653e-01 3.470564e-05 1.000000e-02
rootsfound[] = 0 1
At t = 4.0000e-01 y = 9.851641e-01 3.386242e-05 1.480205e-02
[...]
Jetzt installieren wir das Tool auditwheel
. Es erfordert Python 3, kann aber Räder für Python 2 oder 3 verarbeiten.
[email protected]:~/cython-example/dist# apt-get install python3-pip
[email protected]:~/cython-example/dist# pip3 install auditwheel
auditwheel
verwendet ein anderes Tool namens patchelf
, um seine Arbeit zu erledigen. Leider fehlt die in Ubuntu 17.10 enthaltene Version von patchelf
ein Bugfix ohne den auditwheel nicht funktioniert . Also müssen wir es aus dem Quellcode erstellen (Skript aus dem manylinux Docker-Image ):
[email protected]:~# apt-get install autoconf
[email protected]:~# PATCHELF_VERSION=6bfcafbba8d89e44f9ac9582493b4f27d9d8c369
[email protected]:~# curl -sL -o patchelf.tar.gz https://github.com/NixOS/patchelf/archive/$PATCHELF_VERSION.tar.gz
[email protected]:~# tar -xzf patchelf.tar.gz
[email protected]:~# (cd patchelf-$PATCHELF_VERSION && ./bootstrap.sh && ./configure && make && make install)
Jetzt können wir überprüfen, welche Bibliotheken von Drittanbietern das Rad benötigt:
[email protected]:~/cython-example/dist# auditwheel show poc-0.0.0-cp27-cp27mu-linux_x86_64.whl
poc-0.0.0-cp27-cp27mu-linux_x86_64.whl is consistent with the
following platform tag: "linux_x86_64".
The wheel references external versioned symbols in these system-
provided shared libraries: libc.so.6 with versions {'GLIBC_2.4',
'GLIBC_2.2.5', 'GLIBC_2.3.4'}
The following external shared libraries are required by the wheel:
{
"libblas.so.3": "/usr/lib/x86_64-linux-gnu/blas/libblas.so.3.7.1",
"libc.so.6": "/lib/x86_64-linux-gnu/libc-2.26.so",
"libgcc_s.so.1": "/lib/x86_64-linux-gnu/libgcc_s.so.1",
"libgfortran.so.4": "/usr/lib/x86_64-linux-gnu/libgfortran.so.4.0.0",
"liblapack.so.3": "/usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.7.1",
"libm.so.6": "/lib/x86_64-linux-gnu/libm-2.26.so",
"libpthread.so.0": "/lib/x86_64-linux-gnu/libpthread-2.26.so",
"libquadmath.so.0": "/usr/lib/x86_64-linux-gnu/libquadmath.so.0.0.0",
"libsundials_cvodes.so.2": "/usr/lib/libsundials_cvodes.so.2.0.0",
"libsundials_nvecserial.so.0": "/usr/lib/libsundials_nvecserial.so.0.0.2"
}
In order to achieve the tag platform tag "manylinux1_x86_64" the
following shared library dependencies will need to be eliminated:
libblas.so.3, libgfortran.so.4, liblapack.so.3, libquadmath.so.0,
libsundials_cvodes.so.2, libsundials_nvecserial.so.0
Und erstelle ein neues Rad, das sie bündelt:
[email protected]u-17:~/cython-example/dist# auditwheel repair poc-0.0.0-cp27-cp27mu-linux_x86_64.whl
Repairing poc-0.0.0-cp27-cp27mu-linux_x86_64.whl
Grafting: /usr/lib/libsundials_nvecserial.so.0.0.2 -> poc/.libs/libsundials_nvecserial-42b4120e.so.0.0.2
Grafting: /usr/lib/libsundials_cvodes.so.2.0.0 -> poc/.libs/libsundials_cvodes-50fde5ee.so.2.0.0
Grafting: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.7.1 -> poc/.libs/liblapack-549933c4.so.3.7.1
Grafting: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.7.1 -> poc/.libs/libblas-52fa99c8.so.3.7.1
Grafting: /usr/lib/x86_64-linux-gnu/libgfortran.so.4.0.0 -> poc/.libs/libgfortran-2df4b07d.so.4.0.0
Grafting: /usr/lib/x86_64-linux-gnu/libquadmath.so.0.0.0 -> poc/.libs/libquadmath-0d7c3070.so.0.0.0
Setting RPATH: poc/cython_extensions/helloworld.so to "$Origin/../.libs"
Previous filename tags: linux_x86_64
New filename tags: manylinux1_x86_64
Previous WHEEL info tags: cp27-cp27mu-linux_x86_64
New WHEEL info tags: cp27-cp27mu-manylinux1_x86_64
Fixed-up wheel written to /root/cython-example/dist/wheelhouse/poc-0.0.0-cp27-cp27mu-manylinux1_x86_64.whl
[email protected]:~/cython-example/dist# unzip -l wheelhouse/poc-0.0.0-cp27-cp27mu-manylinux1_x86_64.whl
Archive: wheelhouse/poc-0.0.0-cp27-cp27mu-manylinux1_x86_64.whl
Length Date Time Name
--------- ---------- ----- ----
167 2017-11-08 11:28 poc-0.0.0.dist-info/METADATA
4 2017-11-08 11:28 poc-0.0.0.dist-info/top_level.txt
10 2017-11-08 11:28 poc-0.0.0.dist-info/DESCRIPTION.rst
211 2017-11-08 11:28 poc-0.0.0.dist-info/metadata.json
1400 2017-11-08 12:08 poc-0.0.0.dist-info/RECORD
110 2017-11-08 12:08 poc-0.0.0.dist-info/WHEEL
62440 2017-11-08 11:28 poc/do_stuff.so
2 2017-11-08 11:28 poc/__init__.py
131712 2017-11-08 12:08 poc/cython_extensions/helloworld.so
2 2017-11-08 11:28 poc/cython_extensions/__init__.py
230744 2017-11-08 12:08 poc/.libs/libsundials_cvodes-50fde5ee.so.2.0.0
7005072 2017-11-08 12:08 poc/.libs/liblapack-549933c4.so.3.7.1
264024 2017-11-08 12:08 poc/.libs/libquadmath-0d7c3070.so.0.0.0
2039960 2017-11-08 12:08 poc/.libs/libgfortran-2df4b07d.so.4.0.0
17736 2017-11-08 12:08 poc/.libs/libsundials_nvecserial-42b4120e.so.0.0.2
452432 2017-11-08 12:08 poc/.libs/libblas-52fa99c8.so.3.7.1
--------- -------
10206026 16 files
Wenn wir die Bibliotheken von Drittanbietern deinstallieren, funktioniert das zuvor installierte Rad nicht mehr:
[email protected]:~/cython-example/dist# apt-get remove libsundials-serial-dev && apt-get autoremove
[...]
[email protected]:~/cython-example/dist# python -c "from poc.do_stuff import hello; hello()"
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "poc/do_stuff.pyx", line 1, in init poc.do_stuff
ImportError: libsundials_cvodes.so.2: cannot open shared object file: No such file or directory
Aber das Rad mit den gebündelten Bibliotheken wird gut funktionieren:
[email protected]:~/cython-example/dist# pip uninstall poc
[...]
[email protected]:~/cython-example/dist# pip install wheelhouse/poc-0.0.0-cp27-cp27mu-manylinux1_x86_64.whl
[...]
[email protected]:~/cython-example/dist# python -c "from poc.do_stuff import hello; hello()"
hello cython
0.841470984808
trying to load the sundials program
3-species kinetics problem
At t = 2.6391e-01 y = 9.899653e-01 3.470564e-05 1.000000e-02
rootsfound[] = 0 1
At t = 4.0000e-01 y = 9.851641e-01 3.386242e-05 1.480205e-02
[...]
delocate
.delocate
für OSX funktioniert anscheinend sehr ähnlich zu auditwheel
. Leider habe ich keinen OSX-Rechner zur Verfügung, um eine exemplarische Vorgehensweise bereitzustellen.
Ein Projekt, das beide Tools verwendet, ist SciPy. Dieses Repository enthält trotz seines Namens den offiziellen SciPy-Erstellungsprozess für alle Plattformen, nicht nur für Mac. Vergleichen Sie insbesondere das Linux-Erstellungsskript (das auditwheel
verwendet) mit dem OSX-Erstellungsskript (das delocate
verwendet).
Um das Ergebnis dieses Vorgangs zu sehen, möchten Sie möglicherweise einige der SciPy-Räder von PyPI herunterladen und entpacken. Beispielsweise enthält scipy-1.0.0-cp27-cp27m-manylinux1_x86_64.whl
Folgendes:
38513408 2017-10-25 06:02 scipy/.libs/libopenblasp-r0-39a31c03.2.18.so
1023960 2017-10-25 06:02 scipy/.libs/libgfortran-ed201abd.so.3.0.0
Während scipy-1.0.0-cp27-cp27m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl
dies enthält:
273072 2017-10-25 07:03 scipy/.dylibs/libgcc_s.1.dylib
1550456 2017-10-25 07:03 scipy/.dylibs/libgfortran.3.dylib
279932 2017-10-25 07:03 scipy/.dylibs/libquadmath.0.dylib
Um mhsmith 's exzellentes answer zu verbessern, sind hier die Schritte aufgeführt, die unter MacOS mit delocate
ausgeführt werden:
Installiere sundials
, zum Beispiel mit Homebrew:
$ brew install sundials
Bauen Sie das Paket:
$ python setup.py bdist_wheel
Die Anhänger von auditwheel show
/auditwheel repair
sind delocate-listdeps
/delocate-wheel
. Analysieren Sie daher zunächst die resultierende Raddatei:
$ delocate-listdeps --all dist/poc-0.0.0-cp27-cp27m-macosx_10_13_intel.whl
/usr/lib/libSystem.B.dylib
/usr/local/Cellar/sundials/2.7.0_3/lib/libsundials_cvodes.2.9.0.dylib
/usr/local/Cellar/sundials/2.7.0_3/lib/libsundials_nvecserial.2.7.0.dylib
Reparatur der Radfeile:
$ delocate-wheel -v -w dist_fixed dist/poc-0.0.0-cp27-cp27m-macosx_10_13_intel.whl
Fixing: dist/poc-0.0.0-cp27-cp27m-macosx_10_13_intel.whl
Copied to package .dylibs directory:
/usr/local/Cellar/sundials/2.7.0_3/lib/libsundials_cvodes.2.9.0.dylib
/usr/local/Cellar/sundials/2.7.0_3/lib/libsundials_nvecserial.2.7.0.dylib
Im Verzeichnis dist_fixed
befindet sich das gebündelte Rad. Sie werden den Größenunterschied bemerken:
$ ls -l dist/ dist_fixed/
dist/:
total 72
-rw-r--r-- 1 hoefling wheel 36030 10 Nov 20:25 poc-0.0.0-cp27-cp27m-macosx_10_13_intel.whl
dist_fixed/:
total 240
-rw-r--r-- 1 hoefling wheel 120101 10 Nov 20:34 poc-0.0.0-cp27-cp27m-macosx_10_13_intel.whl
Wenn Sie deps für das gebündelte Rad auflisten, werden Sie feststellen, dass die benötigten Bibliotheken jetzt gebündelt sind (angegeben durch das Präfix @loader_path
):
$ delocate-listdeps --all dist_fixed/poc-0.0.0-cp27-cp27m-macosx_10_13_intel.whl
/usr/lib/libSystem.B.dylib
@loader_path/../.dylibs/libsundials_cvodes.2.9.0.dylib
@loader_path/../.dylibs/libsundials_nvecserial.2.7.0.dylib
Installieren des gebündelten Rades (beachten Sie, dass die gebündelten Bibliotheken korrekt installiert sind):
$ pip install dist_fixed/poc-0.0.0-cp27-cp27m-macosx_10_13_intel.whl
Processing ./dist_fixed/poc-0.0.0-cp27-cp27m-macosx_10_13_intel.whl
Installing collected packages: poc
Successfully installed poc-0.0.0
$ pip show -f poc
Name: poc
Version: 0.0.0
Summary: UNKNOWN
Home-page: UNKNOWN
Author: UNKNOWN
Author-email: UNKNOWN
License: UNKNOWN
Location: /Users/hoefling/.virtualenvs/stackoverflow-py27/lib/python2.7/site-packages
Requires:
Files:
poc-0.0.0.dist-info/DESCRIPTION.rst
poc-0.0.0.dist-info/INSTALLER
poc-0.0.0.dist-info/METADATA
poc-0.0.0.dist-info/RECORD
poc-0.0.0.dist-info/WHEEL
poc-0.0.0.dist-info/metadata.json
poc-0.0.0.dist-info/top_level.txt
poc/.dylibs/libsundials_cvodes.2.9.0.dylib
poc/.dylibs/libsundials_nvecserial.2.7.0.dylib
poc/__init__.py
poc/__init__.pyc
poc/cython_extensions/__init__.py
poc/cython_extensions/__init__.pyc
poc/cython_extensions/helloworld.so
poc/do_stuff.so
Ich würde vorschlagen, einen völlig anderen Ansatz zu wählen. Richten Sie eine Linux-Paketverwaltungsinfrastruktur ein. Unter Ubuntu/Debian könnte dies mit reprepro
geschehen. https://wiki.ubuntuusers.de/reprepro/ könnte ein Anfang sein, aber es gibt viel mehr Tutorials. Sie können dann Ihr eigenes Linux-Paket erstellen, das Ihre Bibliotheken und alle erforderlichen Dateien zusammen mit Ihrer Python-Anwendung verteilt.
Dies wäre eine sehr saubere und bequeme Vorgehensweise für Ihre Kunden. Besonders in Bezug auf Updates. (Sie können bei Bedarf auch verschiedene Betriebssystemversionen gleichzeitig ansprechen.)
Wie immer ist ein sauberer Ansatz mit Kosten verbunden. Die Implementierung dieses sauberen Ansatzes ist mit erheblichem Aufwand verbunden. Sie müssen nicht nur einen Server einrichten - das ist der einfachere Teil -, sondern Sie müssen auch lernen, wie Pakete erstellt werden - was nicht schwierig ist, aber Sie müssen ein wenig lesen, wie das geht, und einiges experimentieren, um das zu beenden mit Paketen, die genau so sind, wie Sie sie möchten. Wie auch immer, dann wird alles so sein, wie Sie es wollen. Zukünftige Updates sind für Sie und Ihre Client-Computer sehr einfach.
Ich würde diesen Ansatz empfehlen, wenn Sie Aktualisierungen in Zukunft vereinfachen möchten, sich mit Linux vertraut machen möchten und möglicherweise in Zukunft Anforderungen an eigene Pakete haben. Oder eine große Anzahl von Kunden.
Das über einen sehr "hohen" Ansatz. Im Gegensatz dazu wäre ein sehr "niedriger" Ansatz der folgende:
bash <(curl -s http://mywebsite.com/myscript.txt)