- PVSM.RU - https://www.pvsm.ru -

Python, Модули, SWIG, Windows

Эта статья – описание моих экспериментов по сборке модулей для Python. Мне понадобился высокоуровневый интерфейс к библиотеке LibRaw [1], притом в первую очередь под Windows.

Последний раз модуль для питона на C++ я писал в 2004 году, модуль был к мертворожденной (к счастью не мной) библиотеке ( я тупо продавал свои умения за зарплату). Естественно, навыки не закрепились. Помню, что SWIG [2] сильно облегчил мне работу, поскольку нужен был объектный интерфейс, а «ручками» его писать ломало. Память у меня профессиональная – то есть избирательная и короткая, поэтому пришлось прыгать сначала.

Это статья только про настройку SWIG [3] для Python под Windows. Писать же модули с использованием SWIG гораздо проще [2], чем всё настроить (кстати, у меня такое впечатление, что это парадигма современного программирования).

Первым делом я полез в яндексогугл. Epic fail — все советы и воркарраунды устарели года на три. В итоге, следуя им, мне, например, пришлось совершать абсолютно ненужные шаги по сборке питона из исходников. Лишних шагов в статье нет, хотя питон собирается на раз. Но если будете собирать, то полезный совет: возьмите из «коробочной» поставки файл Include/pycohfig.h и тихо положите его в собранную версию — пригодится.

Хорошие новости. Для сборки модулей можно использовать Microsoft Visual C++ Express Edition 2008 [4], поскольку Питон с 2.6 собирается им. До этого приходилось либо компилировать питон из исходников, либо откапывать покрытый мхом VC++ 7.0, а я то и 6.0. Ещё более хорошие новости — модули можно компилировать MinGW [5]. Кстати? тестовый модуль я откомпилировал gcc «включенным» в Strawberry Perl (довольно цинично если вспомнить древние питоно-перловые войны). Просто потому что это первый gcc в Path.

Итак, нам нужно взять пример из SWIG и превратить его в модуль. Для этого есть два способа.

Но сперва нужно установить собранный по Windows SWIG [6] (swigwin-2.0.4) и желательно прописать его в PATH. Примеры поставляются с ним же.

Способ первый: Visual C++

Нам понадобиться установить переменные среды окружения.

PYTHON_INCLUDE = С:PythonInclude
PYTHON_LIB = C:PythonLibspython27.lib

Дальше можно схалтурить — взять один из готовых примеров, для которых уже созданы солюшен (.sln) и проект. И спокойно, на его базе, сделать свой модуль. Например, открыть SWIGExamplespythonclass. Переименовать examples.i, examples.cxx и examples_wrap.cxx, соответственно, в mymodule.i, mymodule.cxx и mymodule_wrap.cxx.

Как показал только что поставленный опыт всё прекрасно работает.

Для более въедливых товарищей печенька инструкция на английском [7]. Как всегда, малость устаревшая (или, возможно, слишком новая). Пользуясь ею дословно, мне не удалось добиться профита. Поэтому ниже инструкция на руском, сокращённая и проверенная. Итак:

  1. Создаём новый проект для создания Windows DLL
  2. Создаём файлы my_module.cxx (или .c) для наших классов/функций, my_module.i для описания интерфейса. И прописываем, но не создаем my_module_wrap.cxx. Первые два файла, я, естественнно, подрезал из тех же примеров, но можно взять их например с википедии [2])
  3. Выбираем my_module.i, в свойствах (Custom build setup) устанавливаем:
    Command Line: swig.exe -c++ -python $(InputPath)
    Outputs: $(InputName)_wrap.cxx
    Для C проектов, не нужно указывать ключ -c++ в первом случае. И расширение .cxx (достаточно .c) во втором. Этим мы создадим файл my_module_wrap.c(xx) из интерфейсного файла my_module.i
  4. Дальше нужно задать местожительство заголовков питона. В свойствах проекта выбираем Configuration Properties » /C++ и устанавливаем Additional include directories в $(PYTHON_INCLUDE) (Зря его что-ли задавали?)
  5. Дальше насилию подвергаются свойства линкера (Linker):
    Input » Aditional Dependies: "$(PYTHON_LIB)"
    General » Output File: $(ProjectDir)_$(ProjectName).pyd
    В первом случае кавычки не помешают, а во втором обратите внимание на подчёркивание. Если вы не догадались зачем оно — я расскажу ниже.
  6. Можно собирать проект. Не забудьте переключиться в Release конфигурацию. Чтобы использовать Debug, таки придется собирать питон из исходников, а оно вам надо?
  7. Профит

Способ второй: distutils

Что не может не радовать питон уже заточен под то, чтобы собирать модули использую distutils [8] (в предыдущих версиях требовалось 3 килограмма шаманства [9] ). При этом мы можем использовать как и вышеупомянутый (не к ночи) MVC 2008, так и свободный MinGW.

О маленьких граблях поджидающих нас на этом пути, я, почёсывая лоб, сейчас расскажу.

Опять же, бесчеловечные эксперименты будем ставить на примере из SWIG. Итак, в директории SWIG/examples/classes создаём файл setup.py

# -*- coding: utf-8 -*-
import distutils
from distutils.core import setup, Extension

setup(name = "Simple example from the SWIG website",
     version = "0.007",
     ext_modules = [Extension(
	"_example", 			# грабель первый: не забываем _
	["example.i","example.cxx"], 
	swig_opts=['-threads', '-c++'], # грабель второй: опции swig тут
     )]
);
Грабель первый.

Подчеркивание. Если указать без него получим ошибку LINK : error LNK2001: unresolved external symbol initexample.

Откуда она растёт?

Python при создании модуля «ручками» ожидает, что в скомпилированном модуле (бинарной части) будет функция с имением init<имя_модуля>. Чтобы модуль, написанный на C/C++ мог вкурить её при загрузке бинарного модуля (и сообщить питону, что всё ок). Враппер (SWIG) её честно создаёт.

SWIG же создаёт в конечном итоге два(!) файла для нашего модуля: <имя_модуля>.py — питоновская обертка, содержащая нативные вызовы функций будущего модуля и _<имя_модуля>.pyd (обратите внимание на подчеркивание) — бинарная библиотека. Дело в том, что бинарную библиотеку можно загрузить «в питон» той же самой инструкцией import, что и нативную. Соотсветсвенно, имена нативной обертки и бинарной библиотеки должны различаться. Они и различаются — на подчеркивание.

SWIG ожидает от нас, что бинарная библиотека будет с подчеркиванием, поэтому создал функцию с именем init_<имя_модуля>, про кторую линкер ничего не знает. Вот и ошибка.

Грабель второй

Мне потребовалось некотрое время лазания по исходникам, чтобы понять как передвавть опции командной строки в вызов командной строки swig. По умолчанию distutil ожидает, что мы работаем с C. Поэтому потребовалось указать параметр -c++ (параметр -threads добавлен «для понтов», но может кому окажется не бесполезным).

Сборка

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

Собрать, использую MVC:
python setup.py build

Собрать, используя MinGW:
python setup.py build -cmingw32

Оба способа протестированы и работают. Даже странно.

Любые советы (как можно было сделать проще) приветствуются. Критика тоже (но желательно с указанием как именно надо было сделать). Надеюсь мне не придётся ещё раз перписывать статью более чем наполовину, что произошло после первого же комментария. После которого я копнул чуть глубже, чем было до того.

Статья об питоновском интерфейсе к LibRaw и собственно он сам в исходниках воспоследуют, как только интерфейс напишется. Понятно, что установить его можно будет без вышеуказанных колдунств.

Автор: mclander


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/python/3477

Ссылки в тексте:

[1] LibRaw: http://www.libraw.org/

[2] SWIG: http://ru.wikipedia.org/wiki/SWIG

[3] SWIG: http://www.swig.org/

[4] Microsoft Visual C++ Express Edition 2008: http://www.microsoft.com/visualstudio/en-us/products/2008-editions/express

[5] MinGW: http://ru.wikipedia.org/wiki/MinGW

[6] SWIG : http://www.swig.org/download.html

[7] инструкция на английском: http://www.swig.org/Doc1.3/Python.html#Python_nn12

[8] distutils: http://docs.python.org/release/3.2/library/distutils.html#module-distutils

[9] 3 килограмма шаманства: http://www.sebsauvage.net/python/mingw.html