1. pyinstaller 란?
python으로 작성된 프로그램을 Package 단위로 배포의 편의성을 위해서 사용하는 것 같다.
아래의 사이트에서 글을 읽어보면 목적을 호환성으로 두고있다.
Python은 C Library로 사용이 가능하고, Python 의 내부 Library 가능하기때문에 Python으로만 개발을 진행을 했을 때, 이것을 다른곳에 배포할때 쉽지 않다.
이를 해결해주는 것이 아래의 Tool 이고 실행파일을 만들어준다.
https://www.pyinstaller.org/
1.1 설치방법
$ pip install pyinstaller
$ pip list | grep PyInstaller
PyInstaller (3.4)
$ pyinstaller --help
or
$ ~/.local/bin/pyinstaller --help
usage: pyinstaller [-h] [-v] [-D] [-F] [--specpath DIR] [-n NAME]
[--add-data ]
[--add-binary ] [-p DIR]
[--hidden-import MODULENAME]
[--additional-hooks-dir HOOKSPATH]
[--runtime-hook RUNTIME_HOOKS] [--exclude-module EXCLUDES]
[--key KEY] [-d [{all,imports,bootloader,noarchive}]] [-s]
[--noupx] [-c] [-w]
[-i ]
[--version-file FILE] [-m ] [-r RESOURCE]
[--uac-admin] [--uac-uiaccess] [--win-private-assemblies]
[--win-no-prefer-redirects]
[--osx-bundle-identifier BUNDLE_IDENTIFIER]
[--runtime-tmpdir PATH] [--bootloader-ignore-signals]
[--distpath DIR] [--workpath WORKPATH] [-y]
[--upx-dir UPX_DIR] [-a] [--clean] [--log-level LEVEL]
scriptname [scriptname ...]
$ pyinstaller --noconfirm --log-level=WARN \
--onefile --nowindow \
--add-data="README:." \
--add-data="image1.png:img" \
--add-binary="libfoo.so:lib" \ //library 를 추가시 세부 옵션은 아래의 사이트에서 확인 , 사용시, *.spec파일도 변경
--hidden-import=secret1 \
--hidden-import=secret2 \
--upx-dir=/usr/local/share/ \
myscript.spec
설치방법
https://pypi.org/project/PyInstaller/
사용방법- 자세히 읽어보자
https://pyinstaller.readthedocs.io/en/stable/usage.html
1.2 기본테스트
간단하게 실행가능한 python을 작성해보고, 이와 관련된 부분들을 Package를 만드는 작업을 해보자.
$ pip install procfs
$ vi test.py
import io
from procfs import Proc
def JHLEE():
print "Jeonghun LEE"
if __name__ == "__main__":
JHLEE()
proc = Proc()
print proc.loadavg
$ python test.py
Jeonghun LEE
{'average': {1: 0.08, 5: 0.09, 15: 0.07},
'entities': {'current': 1, 'total': 423},
'last_pid': 3833}
$ pyinstaller test.py
or
$ ~/.local/bin/pyinstaller test.py // 실행할 python
64 INFO: PyInstaller: 3.4
65 INFO: Python: 2.7.12
65 INFO: Platform: Linux-4.4.38-tegra-aarch64-with-Ubuntu-16.04-xenial
66 INFO: wrote /home/nvidia/jhlee/pyinstall/test.spec
90 INFO: UPX is not available.
94 INFO: Extending PYTHONPATH with paths
['/home/nvidia/jhlee/pyinstall', '/home/nvidia/jhlee/pyinstall']
94 INFO: checking Analysis
106 INFO: checking PYZ
112 INFO: checking PKG
113 INFO: Bootloader /home/nvidia/.local/lib/python2.7/site-packages/PyInstaller/bootloader/Linux-64bit-aarch/run
113 INFO: checking EXE
114 INFO: checking COLLECT
WARNING: The output directory "/home/nvidia/jhlee/pyinstall/dist/test" and ALL ITS CONTENTS will be REMOVED! Continue? (y/n)y
5285 INFO: Removing dir /home/nvidia/jhlee/pyinstall/dist/test
5312 INFO: Building COLLECT COLLECT-00.toc
5357 INFO: Building COLLECT COLLECT-00.toc completed successfully.
$ cat test.spec //pyinstaller 상위 실행으로 생성
# -*- mode: python -*-
block_cipher = None
a = Analysis(['test.py'],
pathex=['/home/nvidia/jhlee/pyinstall'],
binaries=[], //binaries=[('foo.so', 'lib')], 직접 추가해도 됨
datas=[],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='test',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name='test')
$ ls dist/test/
bz2.aarch64-linux-gnu.so _codecs_tw.aarch64-linux-gnu.so libtinfo.so.5
_codecs_cn.aarch64-linux-gnu.so _hashlib.aarch64-linux-gnu.so libz.so.1
_codecs_hk.aarch64-linux-gnu.so libbz2.so.1.0 _multibytecodec.aarch64-linux-gnu.so
_codecs_iso2022.aarch64-linux-gnu.so libcrypto.so.1.0.0 readline.aarch64-linux-gnu.so
_codecs_jp.aarch64-linux-gnu.so libpython2.7.so.1.0 resource.aarch64-linux-gnu.so
_codecs_kr.aarch64-linux-gnu.so libreadline.so.6 test
$ dist/test/test //ELF 실행 완료
Jeonghun LEE
'1.10 1.06 1.03 1/554 11741\n'
위에서 Package 만드는것을 다시 반복한다면, 아래와 같이 spec파일로만 다시 실행
$ pyinstaller test.py
or
$ ~/.local/bin/pyinstaller test.spec
64 INFO: PyInstaller: 3.4
65 INFO: Python: 2.7.12
65 INFO: Platform: Linux-4.4.38-tegra-aarch64-with-Ubuntu-16.04-xenial
88 INFO: UPX is not available.
93 INFO: Extending PYTHONPATH with paths
['/home/nvidia/jhlee/pyinstall', '/home/nvidia/jhlee/pyinstall']
93 INFO: checking Analysis
100 INFO: Building because inputs changed
101 INFO: Initializing module dependency graph...
105 INFO: Initializing module graph hooks...
265 INFO: running Analysis Analysis-00.toc
369 INFO: Caching module hooks...
403 INFO: Analyzing test.py
3816 INFO: Loading module hooks...
3818 INFO: Loading module hook "hook-encodings.py"...
5360 INFO: Looking for ctypes DLLs
5360 INFO: Analyzing run-time hooks ...
5371 INFO: Looking for dynamic libraries
6439 INFO: Looking for eggs
6440 INFO: Python library not in binary dependencies. Doing additional searching...
6724 INFO: Using Python library /usr/lib/aarch64-linux-gnu/libpython2.7.so.1.0
6741 INFO: Warnings written to /home/nvidia/jhlee/pyinstall/build/test/warn-test.txt
6799 INFO: Graph cross-reference written to /home/nvidia/jhlee/pyinstall/build/test/xref-test.html
6908 INFO: checking PYZ
6918 INFO: checking PKG
6919 INFO: Bootloader /home/nvidia/.local/lib/python2.7/site-packages/PyInstaller/bootloader/Linux-64bit-aarch/run
6919 INFO: checking EXE
6921 INFO: checking COLLECT
WARNING: The output directory "/home/nvidia/jhlee/pyinstall/dist/test" and ALL ITS CONTENTS will be REMOVED! Continue? (y/n)y
12143 INFO: Removing dir /home/nvidia/jhlee/pyinstall/dist/test
12167 INFO: Building COLLECT COLLECT-00.toc
12214 INFO: Building COLLECT COLLECT-00.toc completed successfully.
1.3 Spec 파일 수정 및 옵션
상위에서 처럼 실행하면 spec파일은 자동으로 생성이 되며, 사용되는 옵션에따라 spec파일도 변경이 된다.
제대로 생성이 안되거나 동작이 안되면, WARNING 과 build/warn-xxxx 파일을 파악하여 분석하여 상위 옵션을 변경하거나, spec을 직접 수정하자.
PYTHONPATH (-p)
pyinstaller setup.spec
https://pythonhosted.org/PyInstaller/spec-files.html
2. pyinstaller 사용후 결론
회사동료가 추천을 해주어 Python Project를 이 프로그램을 이용하여 쉽게 Package를 만들고, 이것이 ELF 포맷으로 동작되는 것을 보고,
Python의 Package가 완전 Compile되어 동작되는 줄 잠시 착각했다.
하지만 동작방식을 세부적으로 분석을 해 보면, Python을 Compile되는 방식이 아니라 ELF 포맷으로 맞춰주고 안에 구성을 Python library을 넣어
나머지 의 기본틀로 동작시켜 구동하는 구조이다.
(확인 방법은 간단하며, ELF Format을 간단히 분석을 해보면 된다 ldd 와 readelf )
결론적으로 Python의 성능상의 개선은 있겠지만, 다만 관련 Library를 한곳에 넣어 배포의 편이성 및 호환성에 맞추어 야 할 것 같다.
나중에는 완전히 Library 형태로 Python의 종속을 벗어난 형태로 ELF 포맷으로 변경이 될지도 모르니
그때 다시 한번테스트를 한번해봐야 할 것 같다.