Pythonã§ãå®è¡å¯èœãªãã€ããªãäœæããã«ã¯ã©ããããããã®ããªïŒãšæã£ãŠèª¿ã¹ãŠã¿ãã®ã§ãããPyInstallerãšãããã®ã
䜿ãã°ããããã§ãã
PyInstaller Manual — PyInstaller 6.4.0 documentation
GitHub - pyinstaller/pyinstaller: Freeze (package) Python programs into stand-alone executables
Pythonスクリプトを単一実行ファイルにする方法 #Python - Qiita
python3 - Python のプログラムを実行可能バイナリにコンパイルするには? - スタック・オーバーフロー
PyInstallerãšã¯
Pythonã§æžãããã¢ããªã±ãŒã·ã§ã³ããã¹ã¿ã³ãã¢ãã³ã§å®è¡å¯èœãªåœ¢åŒã«ãŸãšããããšãã§ããããŒã«ã§ãã
Python 2.7ã3.4ã3.7ã§åäœãããã«ããã©ãããã©ãŒã ã§åäœããŸãããPyInstallerèªäœãå®è¡ãããã©ãããã©ãŒã ãšã¯
ç°ãªããã©ãããã©ãŒã åãã®ãã€ããªã¯äœæããããšãã§ããŸãããã¯ãã¹ãã©ãããã©ãŒã åãã«ãã€ããªãäœæããããšã¯
ã§ããªãã®ã§ãäŸãã°ææã¡ãWindowsç°å¢ã§ãLinuxç°å¢åãã®ãã€ããªãäœæããããšæã£ãããLinuxãVMãªã©ã§çšæãã
å¿
èŠããããšããããšã§ãã
å眮ãã¯ãããããã«ããŠããã£ããè©ŠããŠã¿ãŸãããã
ç°å¢
ä»åã®ç°å¢ã¯ããã¡ãã
$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 18.04.2 LTS Release: 18.04 Codename: bionic $ python3 -V Python 3.6.7
ä»®æ³ç°å¢ãå©çšããããšã«ããŸãã
$ python3 -m venv venv $ . venv/bin/activate
ãé¡ãšãµã³ãã«ããã°ã©ã ãå®è¡å¯èœãã€ããªã®äœæ
Pythonã§ãèµ·åã¹ã¯ãªãããšã¯å¥ã®ã¹ã¯ãªãããå€éšã©ã€ãã©ãªã䜿çšããç°¡åãªã¢ããªã±ãŒã·ã§ã³ãäœæããŠãããã
PyInstallerã§å®è¡å¯èœãªãã€ããªã«ãŸãšããŠã¿ãŸãã
ãŸãã¯ãPyInstallerã®ã€ã³ã¹ããŒã«ã
$ pip3 install pyinstaller
ããŒãžã§ã³ã
$ pip3 freeze ... PyInstaller==3.4
å€éšã©ã€ãã©ãªãšããŠã¯ãrequestsãšBeatiful Soupã䜿ãããšã«ããŸãã
$ pip3 install requests beautifulsoup4 $ pip3 freeze ... beautifulsoup4==4.7.1 ... requests==2.21.0 ...
ãããã®ã©ã€ãã©ãªã䜿ã£ãŠããã®ããã°ã®ãããããŒãžãååŸããŠãã¿ã€ãã«ã ããè¿ããããªé¢æ°ãäœæããŸãã
func.py
from bs4 import BeautifulSoup import requests def get_title(url): response = requests.get(url) soup = BeautifulSoup(response.text, 'html.parser') return soup.title.text
äœæããé¢æ°ãå©çšãããèµ·åã¹ã¯ãªããã
app.py
from func import get_title title = get_title('https://kazuhira-r.hatenablog.com/') print("https://kazuhira-r.hatenablog.com/'s title = {}".format(title))
確èªã
$ python3 app.py https://kazuhira-r.hatenablog.com/'s title = CLOVERð
ããã§ãæºåã¯å®äºã§ãã
ã§ã¯ãPyInstallerã§ãã€ããªãäœæããŸãããpyinstallerãã³ãã³ãã«ãèµ·åã¹ã¯ãªãããåŒæ°ã«æž¡ããŠå®è¡ã
$ pyinstaller app.py
ãdistããšãããã£ã¬ã¯ããªãã§ããã®ã§ããã®äžèº«ãèŠããšå®è¡å¯èœãã¡ã€ã«ãSOãã¡ã€ã«ã䞊ãã§ããŸãã
$ ll -h dist/app åèš 12M drwxrwxr-x 3 xxxxx xxxxx 4.0K 4æ 24 23:39 ./ drwxrwxr-x 3 xxxxx xxxxx 4.0K 4æ 24 23:39 ../ -rwxr-xr-x 1 xxxxx xxxxx 22K 10æ 22 2018 _bz2.cpython-36m-x86_64-linux-gnu.so* -rwxr-xr-x 1 xxxxx xxxxx 147K 10æ 22 2018 _codecs_cn.cpython-36m-x86_64-linux-gnu.so* -rwxr-xr-x 1 xxxxx xxxxx 155K 10æ 22 2018 _codecs_hk.cpython-36m-x86_64-linux-gnu.so* -rwxr-xr-x 1 xxxxx xxxxx 27K 10æ 22 2018 _codecs_iso2022.cpython-36m-x86_64-linux-gnu.so* -rwxr-xr-x 1 xxxxx xxxxx 267K 10æ 22 2018 _codecs_jp.cpython-36m-x86_64-linux-gnu.so* -rwxr-xr-x 1 xxxxx xxxxx 135K 10æ 22 2018 _codecs_kr.cpython-36m-x86_64-linux-gnu.so* -rwxr-xr-x 1 xxxxx xxxxx 111K 10æ 22 2018 _codecs_tw.cpython-36m-x86_64-linux-gnu.so* -rwxr-xr-x 1 xxxxx xxxxx 30K 10æ 22 2018 _hashlib.cpython-36m-x86_64-linux-gnu.so* -rwxr-xr-x 1 xxxxx xxxxx 73K 10æ 22 2018 _json.cpython-36m-x86_64-linux-gnu.so* -rwxr-xr-x 1 xxxxx xxxxx 33K 10æ 22 2018 _lzma.cpython-36m-x86_64-linux-gnu.so* -rwxr-xr-x 1 xxxxx xxxxx 56K 10æ 22 2018 _multibytecodec.cpython-36m-x86_64-linux-gnu.so* -rwxr-xr-x 1 xxxxx xxxxx 6.2K 10æ 22 2018 _opcode.cpython-36m-x86_64-linux-gnu.so* -rwxr-xr-x 1 xxxxx xxxxx 118K 10æ 22 2018 _ssl.cpython-36m-x86_64-linux-gnu.so* -rwxr-xr-x 1 xxxxx xxxxx 1.7M 4æ 24 23:39 app* -rw-rw-r-- 1 xxxxx xxxxx 754K 4æ 24 23:39 base_library.zip drwxrwxr-x 2 xxxxx xxxxx 4.0K 4æ 24 23:39 certifi/ -rwxr-xr-x 1 xxxxx xxxxx 66K 1æ 30 2017 libbz2.so.1.0* -rwxr-xr-x 1 xxxxx xxxxx 2.5M 12æ 6 00:59 libcrypto.so.1.1* -rwxr-xr-x 1 xxxxx xxxxx 199K 12æ 20 2017 libexpat.so.1* -rwxr-xr-x 1 xxxxx xxxxx 151K 6æ 29 2017 liblzma.so.5* -rwxr-xr-x 1 xxxxx xxxxx 4.5M 10æ 22 2018 libpython3.6m.so.1.0* -rwxr-xr-x 1 xxxxx xxxxx 288K 5æ 16 2017 libreadline.so.7* -rwxr-xr-x 1 xxxxx xxxxx 424K 12æ 6 00:59 libssl.so.1.1* -rwxr-xr-x 1 xxxxx xxxxx 167K 5æ 23 2018 libtinfo.so.5* -rwxr-xr-x 1 xxxxx xxxxx 115K 5æ 23 2017 libz.so.1* -rwxr-xr-x 1 xxxxx xxxxx 32K 10æ 22 2018 readline.cpython-36m-x86_64-linux-gnu.so* -rwxr-xr-x 1 xxxxx xxxxx 16K 10æ 22 2018 resource.cpython-36m-x86_64-linux-gnu.so* -rwxr-xr-x 1 xxxxx xxxxx 25K 10æ 22 2018 termios.cpython-36m-x86_64-linux-gnu.so*
ãŸããã«ã¬ã³ããã£ã¬ã¯ããªãèŠããšããdistããã£ã¬ã¯ããªä»¥å€ã«ã¯èµ·åã¹ã¯ãªããå.specããšãããã¡ã€ã«ãšãbuildããã£ã¬ã¯ããªã
äœæãããŠããŸãã
$ ll åèš 36 drwxrwxr-x 6 xxxxx xxxxx 4096 4æ 24 23:39 ./ drwxrwxr-x 14 xxxxx xxxxx 4096 4æ 24 23:13 ../ drwxrwxr-x 2 xxxxx xxxxx 4096 4æ 24 23:39 __pycache__/ -rw-rw-r-- 1 xxxxx xxxxx 156 4æ 24 23:38 app.py -rw-rw-r-- 1 xxxxx xxxxx 950 4æ 24 23:39 app.spec drwxrwxr-x 3 xxxxx xxxxx 4096 4æ 24 23:39 build/ drwxrwxr-x 3 xxxxx xxxxx 4096 4æ 24 23:39 dist/ -rw-rw-r-- 1 xxxxx xxxxx 183 4æ 24 23:39 func.py drwxrwxr-x 6 xxxxx xxxxx 4096 4æ 24 23:13 venv/
å®è¡ããŠã¿ãŸãããã
$ dist/app/app https://kazuhira-r.hatenablog.com/'s title = CLOVERð
åããŸããããä»ã®ã¹ã¯ãªãããå€éšã©ã€ãã©ãªã®æ å ±ãŸã§èŠãŠããã«ãããŠãããããã§ãã
ãã ããã®æ¹æ³ã§ã®ãã«ãã§ã¯ããdistããã£ã¬ã¯ããªé
äžã®ãã¡ã€ã«ããŸãããšäœ¿ãå¿
èŠããããäŸãã°ãšã³ããªãšãªã
å®è¡å¯èœãã¡ã€ã«ã ããæã¡åºããŠããã©ã€ãã©ãªã足ããã«ãšã©ãŒã«ãªããŸãã
$ ./app [12] Error loading Python lib '/tmp/libpython3.6m.so.1.0': dlopen: /tmp/libpython3.6m.so.1.0: cannot open shared object file: No such file or directory
ãããé¿ããã«ã¯ãå®å šã«åäžã®ãã€ããªãšããŠãã«ãããå¿ èŠããããŸãã
What PyInstaller Does and How It Does It — PyInstaller 6.4.0 documentation
åäžã®ãã€ããªãäœæããã«ã¯ãã--onefileããªãã·ã§ã³ãæå®ããŸãã
$ pyinstaller --onefile app.py
ä»åºŠã¯ããdistããã£ã¬ã¯ããªå ã«äœæããããã¡ã€ã«ãã²ãšã€ã«ãªããŸãã
$ ll dist åèš 6072 drwxrwxr-x 2 xxxxx xxxxx 4096 4æ 24 23:48 ./ drwxrwxr-x 6 xxxxx xxxxx 4096 4æ 24 23:48 ../ -rwxr-xr-x 1 xxxxx xxxxx 6207248 4æ 24 23:48 app*
ãã®çææ¹æ³ã ãšãåäžã®ãã¡ã€ã«ãé åžããã ãã§å®è¡å¯èœã«ãªããŸãã
$ ./app https://kazuhira-r.hatenablog.com/'s title = CLOVERð
ãªãããã®ãããªãã«ãæã®æå®ã¯specæ¡åŒµåã®ãã¡ã€ã«ã«ä¿åãããŠããã®ã§
app.spec
# -*- mode: python -*- block_cipher = None a = Analysis(['app.py'], pathex=['/path/to/application-directory'], binaries=[], 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, a.binaries, a.zipfiles, a.datas, [], name='app', debug=False, bootloader_ignore_signals=False, strip=False, upx=True, runtime_tmpdir=None, console=False )
ä»®ã«distãã£ã¬ã¯ããªãªã©ããªãããŠããspecãã¡ã€ã«ããåããªãã·ã§ã³ã§åäœæããããšãã§ããŸãã
$ pyinstaller app.spec
ãããªæãã§ã