20 сентября 2010 в 14:53

Облегчаем использование pyinstaller для создания exe

Недавно начал свое знакомство с языком Python с целью применения его для написания в короткие сроки приложений, выполняющих необходимую задачу здесь и сейчас. Так как планируемые приложения могли быть запущены не только на ос, в которых установлен сам Python, было решено собирать exe. Почитав пару топиков на хабре и комментарии к ним, пришел к выводу, что для этих целей прекрасно подойдет pyinstaller. Он довольно прост в использовании, но все же некоторые повторяющиеся моменты можно сократить.

Для начала опишу используемый для этого софт:
  1. CPython 2.6.5. Спросите почему не 2.7 или 3.x? Описанная ниже версия pywin, используемая для pyinstaller, дружит только с версиями до 2.6 включительно
  2. pyinstaller 1.4 r854 (на момент начала использования это была последняя ревизия)
  3. pywin32-214.win32-py2.6 — последняя доступная на sourceforge версия. Используется pyinstaller для определения зависимостей (вообще им надо сруливать с него, т.к. последнее обновление датируется 09 годом)
  4. UPX 3.0.7 — используем для сжатия файлов


Для того, чтобы произвести сборку exe в соответствии с мануалом необходимо:
  1. Запустить Configure.py, который проверит наличие всех необходимых для сборки компонентов (выполнять при смене конфигураций, будь-то добавление/удаление UPX, изменение библиотек и т.д.)
  2. С помощью MakeSpec.py из папки pyinstaller в соответсвии с необходимыми параметрами создать spec файл
  3. С помощью Build.py из той же папки, указав путь к spec файлу, произвести сборку exe


В связи с тем, что pyinstaller у меня хранится в папке python (путь по умолчанию), а исходники на другом диске, то процедуру шаманства с путями в консоли и постоянным дописыванием одинаковых параметров я решил сократить используя bat файл, разбор которого сейчас и будет произведен.

Для начала, определил цели:
  1. Отвязка от необходимости постоянно задавать путь к MakeSpec и Build файлам, а так же spec файлу
  2. Получение готового exe в той же директории, что и исходный код


Реализация самого bat-файла (комментарии по ходу действия, готовый батник с комментариями лежит ниже):
1. Стандартные для bat строчки — отмена вывода команд в консоль, очистка последней:
@echo off
cls

2. Указываем путь к pyinstaller и путь к папке, в которой будут хранится spec и exe файлы:
set pyinstPath=c:\python26\pyinstaller
set buildPath=%pyinstPath%\bin

3. Задание значений переменной проверки перемещения готового exe и проверки, введено ли имя исходного файла (по сути — флаги):
set moveBin=0
set fileParam=0

4. Начинаем проверку введенных параметров. Для обработки вызываем метку с введеным ключом (3-ая строчка). После обработки каждого параметра возвращаемся на метку :ParamChk. В случае же если параметры закончились, переходим на метку сборки бинарника (2-ая строчка):
:ParamChk
if "%1%"=="" goto binBuild
goto p%1

5. Собственно сами метки ключей/параметров. Вариации ввода разнообразные, суть одна и та же — в случае, если такой ключ существует, производим необходимые манипуляции, такие как получение имени исходного файла, пути по которому будет хранится spec файл, вывод помощи и с помощью команды shift производится сдвиг параметров на 1 влево (был параметр %2, стал %1 и т.д.):
:p-F
:p/F
:p/file
:p--file
if not "%2"=="" (
set pyFile=%2
if not %fileParam%==1 set fileParam=1
)
shift & shift & goto ParamChk
 
:p-O
:p/O
:p/out
:p--out
if not "%2"=="" set buildPath=%2
shift & shift & goto ParamChk
 
:p-M
:p/M
:p/move
:p--move
set moveBin=1
shift & goto ParamChk
 
:p-H
:p/H
:p/help
:p--help
echo.
echo Use: %~n0 [/F [path]filename] [/O dir] [/M]
echo.
echo /F, -F, /file, --file - Path to .py file 
echo /O, -O, /out, --out - Output directory. Default directory - %buildPath%
echo /M, -M, /move, --move - Move created binary to .py file directory
goto :EOF

6. После проверки и обработки всех параметров переходим к метке :binBuild и проверяем, было ли введено имя исходного файла. В случае если нет — выводим сообщение о необходимости наличия имени файла, наличии справки и завершаем работу:
:binBuild
if %fileParam%==0 echo No file to work with. Use /for help & goto :EOF

7. Задаем параметры, в соответствии с которыми будет производится сборка exe. Синтаксис берем в мануале к pyinstaller. В данном случае указаны следующие параметры — сборка в один файл, сжатие с помощью UPX + смена папки для готового exe:
set specParams=---%buildPath%

8. Далее производим создание spec файла и создание exe на его основе, используя заданные выше пути к MakeSpec, Build и параметры для первого. Присутствует один жуткий костыль — учитывая, что я не спец по batch файлам, то способ выделить из пути к исходному файлу только его имя я нашел только с помощью цикла (2-ая строчка):
"%pyinstPath%\MakeSpec.py" %specParams% "%pyFile%"
for %%in (%pyFile%) do (set fileName=%%~ni)
%pyinstPath%\Build.py "%buildPath%\%fileName%.spec"

9. В случае если был задан параметр на перемещение бинарника в папку с исходным кодом, для ее получения используем костыль из пункта 8-м. А затем просто перемещаем файл с заменой без подтверждения:
if %moveBin%==1 (
for %%in (%pyFile%) do (set outDir=%%~pi)
move /"%buildPath%\dist\%fileName%.exe" %outDir%
)


Готовый bat файл с комментариями лежит тут.
Для использования достаточно отредактировать строки по пунктам 2 и 7 в соответствии с вашим расположением папки pyinstaller и требуемым параметрам.

Disclaimer:
Вот и все. Хотелось бы отметить, что в комментариях как здесь, так и в самом bat файле я не описывал особенности синтаксиса команд, а просто описал последовательность выполненных действий. Для справки по команде в консоли достаточно выполнить <имя_команды> /? и вам напишут больше, чем я могу тут описать и знаю. Если вы знаете, как избавится от некоторых костылей или облегчить/упростить выполнение некоторых пунктов, с радостью выслушаю. Если вы считаете, что данный топик не должен находится в блоге Python, а в каком-то другом, просьба указать в каком именно.
Алексей Хомченко @gagoman
карма
11,0
рейтинг 0,0
Самое читаемое Разработка

Комментарии (16)

  • +2
    Вот бы ещё осветили создание exe со скриптом из ОС Linux (=
    • +2
      К сожалению я ленив, и все никак не дойдут руки заставить себя больше сидеть в linux с целю самообучения. Пока что я просто пользователь xubuntu на виртуальной машине, изучающий основы.
    • 0
      По крайней мере с помощью PyInstaller это невозможно. Как они пишут, «теоретически это возможно, но вне наших приоритетов». Т.е. под Linux получится выполняемый файл, специфичный для того же Linux (ELF).
    • 0
      Под wine у меня получалось собирать.
  • +1
    Не могли бы вы вкратце рассказать о преимуществах pyinstaller перед py2exe? В чем разница?
    • +1
      1) Кроссплатформенный
      2) Меньше проблем при переносе собранного экзешника между различными версиями винды. В py2exe без специального хака распространены следующие проблемы: www.google.ru/search?&q=py2exe+ImportError:+DLL+load+failed

      Но мне в целом больше нравится py2exe — он ничего не распаковывает во временную директорию и возможностей кастомизации у него (насколько я понял) больше.
      • 0
        > 1) Кроссплатформенный
        Вот с этого места можно поподробней…
        .
        >В py2exe без специального хака распространены следующие проблемы:
        Если ложить pythonXX.dll рядом с exe'шником, то проблем быть не должно…

        >py2exe — он ничего не распаковывает во временную
        Эм… Еще больше не понимаю.
        Грубо говоря, на выходе py2exe создается папка, с exe запускалкой, и со всем необходимым для переноса приложения (dll ки, pydы, library.zip), и все это «слинковано». А на выходе pyinstaller что? И что в linuxе получается?
        Я не призываю использовать py2exe, просто хочу выяснить как он работает. Не хотели бы вы написать более подробную статью?
        • +2
          > Вот с этого места можно поподробней…
          Подробнее — на официальном сайте. Вкратце: с помощью pyinstaller'a можно собирать самораспаковывающиеся инсталляторы (я бы их так назвал) для питоновских скриптов\проектов для нескольких различных ОС (Win, MacOS, Linux, Irix), в то время как py2exe умеет «мастерить» только экзешники.

          > Если ложить pythonXX.dll рядом с exe'шником, то проблем быть не должно…
          Дело не в питоновской либе (без неё вообще ничего не заработает), а в скомпиленных сишных модулях, которые вы можете использовать в питоновском коде. Например у вас сложный проект и вы используете PyWin32 и wxPython, которые содержат достаточного много .pyd файлов. Собрав свое приложение под Windows 7 и попробовав запустить его на Windows XP, вы с 90% вероятностью получите что-то типа этого: stackoverflow.com/questions/1979486/py2exe-win32api-pyc-importerror-dll-load-failed. А всё потому, что при сборке py2exe подхватывает по зависимости dll-ки из 7-ки и на Windows XP пытается импортировать оттуда необходимые функции, поэтому, собственно, и обламывается. Я в своё время писал что-то типа «билдовой системы» для сборки питон-проектов в экзешники, и там пришлось изучить все известные баги и ворэраунды для py2exe. А уж сколько там напильником пришлось допиливать для того чтобы добавить поддержку питона 2.6 — это я даже пересказывать не решусь.

          > Грубо говоря, на выходе py2exe создается папка, с exe запускалкой, и со всем необходимым для переноса приложения (dll ки, pydы, library.zip), и все это «слинковано». А на выходе pyinstaller что? И что в linuxе получается?
          Напишу как работает вкратце:

          Pyinstaller: собирает все используемые в проекте модули и зависящие от них модули в одну папку, туда же идут pywintypes.dll и pythonXX.dll в одну директорию и запаковывает её в архив, этот архив присоединяется к bootloader'у — это маленький бинарничек, который выполняет всю полезную работу, а именно распаковывает архив в темповую директорию и запускает оттуда нужный скрипт.

          Py2exe: алгоритм почти такой-же за исключением того, что 1) бутлоадер не распаковывает архив, а извлекает необходимые из него модули на лету, необходимость определяется специальным хуком на импорт модулей (находится в файле zipextimporter.py), 2) если правильно настроите параметр bundle_files, то можно включить pythonXX.dll в сам бутлоадер и он будет загружать её из самого экзешника (это может привезти к ошибкам загрузки некоторых сторонних модулей)
          • 0
            >этот архив присоединяется к bootloader'у — это маленький бинарничек, который выполняет всю полезную работу, а именно распаковывает архив в темповую директорию и запускает оттуда нужный скрипт.
            Вот именно это нужно было узнать. Спасибо! То есть отдавать конечное приложение можно в виде одного исполняемого файла, который не требует установки.
            И последний вопрос: есть ли возможность «на лету» менять содержимое этого архива? То есть «запихивать» обратно измененный файл с настройками программы?
            • +1
              > И последний вопрос: есть ли возможность «на лету» менять содержимое этого архива? То есть «запихивать» обратно измененный файл с настройками программы?

              Ну теоретически такая возможность есть (я имею ввиду метод add в файле archive.py), но она влечет за собой несколько сложных махинаций с экзешником, поэтому я настоятельно советую ей не пользоваться.
            • 0
              Полиморфный вирус на питоне? xD
              • 0
                Будете смеяться, из вики:
                Некоторые антивирусы распознают Frets on Fire для Win32 как вирус. Это происходит из‑за того, что игра скомпилирована в исполняемый файл с помощью утилиты py2exe(англ.), которая так же использовалась для создания кейлоггера Backdoor.Rajump. При создании вирусного описания этого кейлоггера была допущена ошибка, что и является причиной ложной идентификации игры как вируса
  • 0
    >> Так как планируемые приложения могли быть запущены не только на ос, в которых установлен сам Python, было решено собирать exe

    У меня была похожая задача, мне нужно было сваять пару утилит для клиента. Мне по душе Java/C#/Ruby но решил что эти языки не очень подходят для быстрых клиентских утилит.

    Случайно нашел для себя язык Vala live.gnome.org/Vala/Tutorial

    Тот же C# но создает *очень* быстрый код. Быстрее/сравнимый с С++, но программы куда более читабельные чем С/С++ code.google.com/p/vala-benchmarks/wiki/BenchResults
    • 0
      Одно дело «быстрая» утилита, другое дело «быстро написать» утилиту. У автора поста второй случай, у вас первый.

      И да — хорошо, когда клиент на Linux, а если под Windows? Vala уже позволяет писать Win-приложения?
      • 0
        >> Одно дело «быстрая» утилита, другое дело «быстро написать» утилиту. У автора поста второй случай, у вас первый.

        У меня и первый (нужно нативное приложение) и второй случай одновременно :) Плюс я пытался выучить что-нибудь новое. Вначале выбирал между Go и D в результате написал на Vala. Мне понравилось что синтакс очень близок к Java/C#

        > И да — хорошо, когда клиент на Linux, а если под Windows?
        На Linux/MacOSX работает отлично. К сожалению на windows не пробывал. Официально GLIB/GTK имеются под эту операционку www.gtk.org/download-windows.html но подозреваю что использование Vala под Windows не лишено подводных камней.
  • +2
    мне больше cx_Freeze подошел (http://cx-freeze.sourceforge.net/)

    тоже кросс платформенный, и работает сходу. более того python 3.1 поддерживает, а py2exe и pyinstaller

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.