졸업 프로젝트 진행하며 얻은 Pyinstaller 사용 팁을 공유하고자, 졸프 기술 블로그 문서에서 분리해 왔다.
ㅤ
ㅤ
Pyinstaller 매뉴얼: https://pyinstaller.org/en/stable/
매뉴얼에 다 나와 있다. 하지만 읽어도 와닿지 않아서 좀 헤맸다. 사실 검색했을 때도 헤맸다. 간단한 파일을 .exe로 묶어 본 적은 있는데, 이런 대용량의 라이브러리를 요구하는 파일은 처음 압축해 봐서 정말 많이 헤맸다. 이젠 저 문서가 친숙하다.
ㅤ
ㅤ
pip install pyinstaller
먼저 pyinstaler 라이브러리를 설치해야 한다. 나는 가상환경을 만들어서 pyinstaller를 설치했는데, 간혹 pyinstaller에 어떤 라이브러리가 포함되어 있으므로 어떤 거 uninstall 하라는 요구를 하기도 했다. 그럼 그냥 uninstall 하면 된다.
ㅤ
ㅤ



cd D:\Dropbox\Dropbox\[대학]\졸업 프로젝트\캡스톤 그로스\실행 파일
pyinstaller -F main.py -n POINT-CHECKER -i pointchecker.ico
배포하고자 하는 파일이 있는 경로로 이동해 pyinstaller로 파일을 묶는다.
처음에는 "assets" 폴더, "pointchecker.ico", "main.py"가 있었다. 실행 후 "build" 폴더, "dist" 폴더, "POINT-CHECKER.spec"가 생겼다. "dist" 폴더에 들어가면, "POINT-CHECKER.exe"가 있는 것을 확인할 수 있다. "POINT-CHECKER.exe"를 실행할 때에는 반드시 해당 .exe 파일이 있는 경로에 "assets" 폴더와 "pointchecker.ico"가 있어야 한다.
코드에 대한 간단한 설명을 하자면, pyinstaller는 기본적으로 python으로 실행하고자 하는 .py 파일을 필요한 라이브러리가 포함된 .exe 파일로 압축을 해 준다. 압축 시 몇 가지 옵션을 지정할 수 있다. -F의 경우 한 .exe 파일로 묶으라고 지정하는 것이다. -n은 결과물인 .exe 파일의 이름을 지정하는 것이고, -i는 아이콘 파일을 지정하는 것이다.
아래 좀 더 복잡한 코드를 묶으며 매뉴얼을 훑어 보고 알게 된 건데, -i로 아이콘 파일 지정할 때 '-i ./assets/pointchekcer.ico'처럼 상대경로로 아이콘 지정도 가능하다.
그리고 --distpath라는 옵션으로 최종 결과물의 저장 폴더를 지정할 수 있다. 디폴트는 .\dist 밑인데, 나는 dist 폴더가 아닌, 현재 폴더에 저장해 봤다.
ㅤ


cd D:\GitHub\Point-Checker
pyinstaller -F main.py -n Point-Checker -i .\assets\pointchecker.ico --distpath . --add-data "D:\GitHub\Point-Checker\assets;.\assets" --hidden-import=easyocr --hidden-import=gdown --add-data "D:\venvs\pyinstaller_venv\Lib\site-packages\ultralytics\cfg\default.yaml;.\ultralytics\cfg" --collect-all easyocr --add-data "D:\venvs\pyinstaller_venv\Lib\site-packages\easyocr\character\*.txt;.\easyocr\character" --add-data "D:\GitHub\Point-Checker\tamil_ocr;.\tamil_ocr" --add-data "D:\venvs\pyinstaller_venv\Lib\site-packages\pytorch_lightning\version.info;.\pytorch_lightning" --add-data "D:\venvs\pyinstaller_venv\Lib\site-packages\lightning_fabric\version.info;.\lightning_fabric" --hidden-import=timm --hidden-import=timm.models.helpers
ㅤ
cpu에서만 실행되는 버전을 만들어서 배포하려고 묶었는데, 정말... 힘들었다. 일단 서버에 있었던 각종 객체인식과 OCR 모델을 .exe에 포함하는 과정에 많이 복잡했다. 그래서 사실 중간에 그만둘까 생각도 많이 했다. (어떻게 끝까지 근성으로 밀어붙여서 완성을 해냈으니 그 점은 조금 뿌듯하다.)
일단 Pyinstaller 매뉴얼 '--add-data' 설명을 보면 -add-data="옮길 파일 or 폴더 경로;도착 폴더 경로" 이렇게 쓰라고 되어 있는데 '도착 폴더'가 뭔지 감이 안 잡혀서 헤맸다. 결론적으로 도착 폴더는 .exe 내부에 생성되는 라이브러리 폴더를 말하는 것 같은데, 그냥 '.\라이브러리\모듈' 형식으로 쓰면 됐다. 그러니까 현재 경로(.) 밑에 기존 폴더 구조 형식을 똑같이 베껴 쓰면 되는 거였는데... 처음엔 이해가 안 가서 --add-data 사용이 많이 힘들었다.
ㅤ
일단 말썽을 일으킨 라이브러리가 크게 세 가지 있었는데, 전부 객체인식과 OCR에 관련된 라이브러리다.
1) ultralytics
이 라이브러리는 ultralytics\cfg\default.yaml이 빠져 있어서 오류를 일으켰다.
2) easyocr
이 라이브러리는 easyocr\character*.txt가 빠져 있어서 오류를 일으켰다.
3) tamil_ocr
이 라이브러리는 pytorch_lightning\version.info, lightning_fabric\version.info, timm, timm.models.helpers가 빠져 있어서 오류를 일으켰다.
3번이 제일 말썽이었다. 아무래도 tamil_ocr의 경우 pip로 install하는 라이브러리가 아니라, github에서 끌어다 쓰는 형식이라 이래저래 엉켰던 것 같다. (사실 .py 내에서 쓸 때에도 자잘한 설정을 해 줘야 하는 라이브러리여서 복잡함이 가중된 것 같다.)
ㅤ
오류 해결하는 방법은 간단한데, 단순하게 빠진 것들을 --add-data와 --hidden-import를 사용해서 넣어 주면 된다. .exe 내에 라이브러리를 만들어 저장하는 구조이기 때문에, 가상환경 혹은 로컬 환경에 설치된 Lib 구조를 그대로 복사해오는 느낌이다.
--add-data "D:\venvs\pyinstaller_venv\Lib\site-packages\ultralytics\cfg\default.yaml;.\ultralytics\cfg"
위와 같이 cfg 밑에 빠진 파일이라면, destination을 '.\ultralytics\cfg'로 지정해 주면 된다. 빠진 게 여러 파일이면 *.txt 처럼 지정해 주면 되고, 폴더 전체면 폴더만 쓰면 된다. 근데 폴더를 복사 대상으로 쓰니까 복사가 잘 안 되는 문제를 초반에 겪었기 때문에... 웬만하면 파일 복사용으로만 사용하자.
--hidden-import=timm.models.helpers
라이브러리 import 하라고 명시하는 옵션이다. 원래는 가상환경에서 실행할 때 가상환경 내의 모든 라이브러리를 hook로 알아서 묶어서 가져오는데, 그래도 빠지는 게 생길 때 이 옵션을 사용하면 된다.
No Module Import 에러가 났을 때 그냥 없다는 모듈 이름 그대로 저렇게 붙여 넣으면 알아서 포함이 된다. 이 프로젝트 묶을 때 timm을 먼저 추가했는데도 timm 하위의 모듈(timm.models.helpers)이 없다는 오류가 떴었다... 반신반의하면서 추가했는데 오류가 고쳐진 것으로 보아 pyinstaller가 묶을 때 간혹 실수하는 모양이다.
ㅤ
ㅤ
pyinstaller 쓰기 참... 쉽지 않은데 또 파이썬을 .exe로 변환하는 게 이거 말고는 없는 것 같다. 다음에 파이썬 파일 배포해서 이 라이브러리를 또 쓰게 된다면, 그때는 spec을 쓰거나 코드를 사용해 보고 싶다. 사실상 커맨드가 길어지면 길어질수록 이럴 거면 spec 파일에 적을 걸 그랬다는 생각도 했다.
또 많은 사람이 그렇듯 .exe 파일에 -F 옵션을 사용해서 모든 파일을 한 번에 묶도록 했는데, EasyOCR/Tamil-OCR/ultralytics가 들어가면서 파일이 매우 무거워졌다. 그래서 묶는 데에도 시간이 걸리고 실행해서 오류 확인할 때까지도 창 열리는 데에 시간이 많이 걸렸다. 배포에 .exe만한 게 없지만... 창 여는 데에 시간이 너무 걸리면 사용성이 많이 떨어지므로 .py 배포를 우선적으로 진행하는 선택을 했다. 조금 더 나은 배포 방법은 없는지, 추후 고안해 보고 싶다.