Создание модуля python и связывание его с инфраструктурой MacOSX

Я пытаюсь создать расширение Python для MacOSX 10.6 и связать его с несколькими фреймворками (только для i386). Я создал файл setup.py, используя distutils и объект Extension.

Я приказываю связать свои фреймворки, моя переменная окружения LDFLAGS должна выглядеть так:

LDFLAGS = -lc -arch i386 -framework fwk1 -framework fwk2

Поскольку я не нашел ключевого слова "framework" в документации модуля расширения, вместо него я использовал ключевое слово extra_link_args.

Extension('test',
define_macros = [('MAJOR_VERSION', '1'), ,('MINOR_VERSION', '0')],
include_dirs = ['/usr/local/include', 'include/', 'include/vitale'],
extra_link_args = ['-arch i386',
                   '-framework fwk1',
                   '-framework fwk2'],
sources = "testmodule.cpp",
language = 'c++' )

Все компилируется и линкуется нормально. Если я удалю строку -framework из extra_link_args, мой компоновщик выйдет из строя, как и ожидалось. Вот последние две строки, созданные сборкой python setup.py:

/usr/bin/g++-4.2 -arch x86_64 -arch i386 -isysroot /
-L/opt/local/lib -arch x86_64 -arch i386 -bundle
-undefined dynamic_lookup build/temp.macosx-10.6-intel-2.6/testmodule.o
-o build/lib.macosx-10.6-intel-2.6/test.so
-arch i386 -framework fwk1 -framework fwk2

К сожалению, .so, который я только что создал, не может найти несколько символов, предоставляемых этой структурой. Я попытался проверить связанную структуру с помощью otool. Ни один из них не появляется.

$ otool -L test.so
test.so:
    /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.9.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.0.1)

Вот результат запуска otool на тестовом двоичном файле, созданном с помощью g++ и ldd с использованием LDFLAGS, описанного в начале моего поста. В этом примере -framework работал.

$ otool -L vitaosx 
vitaosx:
    /Library/Frameworks/fwk1.framework/Versions/A/fwk1 (compatibility version 1.0.0, current version 1.0.0)
    /Library/Frameworks/fwk2.framework/Versions/A/fwk2 (compatibility version 1.0.0, current version 1.0.0)
    /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.9.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.0.1)

Может ли эта проблема быть связана с флагом «-undefined dynamic_lookup» на этапе связывания? Меня немного смущают несколько строк документации, которые я нахожу в Google.

Ваше здоровье,


person madflo    schedule 06.04.2010    source источник


Ответы (5)


Это не имеет ничего общего с неопределенным dynamic_lookup, но имеет отношение к distutils. Он добавляет extra_link_flags к флагам ссылок, которые он выбирает для сборки Python. Вместо этого он должен добавляться перед ним, потому что списки -framework должны идти перед объектами, которые используют их в командной строке (насколько я знаю, это связано с тем, как gcc собирает символы для связывания). Быстрое исправление, которое я лично использую, это сборка с

    LDFLAGS="-framework Carbon" python setup.py build_ext --inplace

или любые рамки, которые вам нужны. LDFLAGS добавляется к собственным флагам distutils. Обратите внимание, что ваш пакет не будет pip installспособным. Надлежащее исправление может прийти только от distutils - имхо, они должны поддерживать frameworks, как они поддерживают libraries.

Кроме того, вы также можете добавить

import os
os.environ['LDFLAGS'] = '-framework Carbon'

в вашем setup.py. Тогда ваш пакет должен быть pip installable.

person SirVer    schedule 03.01.2012

Хотя спустя много времени после того, как пыль осела, у меня был тот же вопрос, я немного покопался и нашел это:

/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/distutils/sysconfig.py

   if 'ARCHFLAGS' in os.environ:
                archflags = os.environ['ARCHFLAGS']
            else:
                archflags = '-arch i386 -arch ppc -arch x86_64'
            _config_vars['ARCHFLAGS'] = archflags
            if archflags.strip() != '':
                _config_vars['CFLAGS'] = _config_vars['CFLAGS'] + ' ' + archflags
                _config_vars['LDFLAGS'] = _config_vars['LDFLAGS'] + ' ' + archflags

Я подхожу к проблеме с другой точки зрения - в 10.6 distutils пытается собрать расширения C и жалуется, потому что в SDK 10.6 нет части PPC.

Однако,

 export ARCHFLAGS="-arch i386 -arch x86_64"
 python setup.py build

Работал как шарм.

person synthesizerpatel    schedule 26.05.2011
comment
Я считаю, что проблема PPC возникает только в том случае, если у вас есть новейший XCode. То есть тот, за который вы можете заплатить. Тот, что поставляется с дисками ОС 10.6, должен подойти. - person Graham Dumpleton; 26.05.2011
comment
Я еще не пробовал ответить @synthesizerpatel, но у меня была проблема с ppc с предыдущим XCode. Тот, который выходит бесплатно с DVD-дисков MacOS X. - person madflo; 27.05.2011

Я не уверен, что понимаю, что вы пытаетесь сделать, и желаемый результат, но, возможно, это поможет. Поскольку модули расширения C обычно запускаются в контексте выполнения интерпретатора Python, модули расширения должны быть созданы для совместимости с интерпретатором. В OS X Python и distutils прилагают некоторые усилия, чтобы убедиться, что модули расширения C построены с теми же значениями SDK (-sysroot), MACOSX_DEPLOYMENT_TARGET и -arch, что и сам интерпретатор Python. Итак, если вы используете поставляемый Apple Python в версии 10.6, distutils предоставит -arch i386 -arch ppc -arch x86_64, три арки, с которыми он был построен. Если вы используете текущий установщик OS X python.org (в 10.6, 10.5 или 10.4), он будет использовать:

gcc-4.0 -arch ppc -arch i386 -isysroot /Developer/SDKs/MacOSX10.4u.sdk

Из фрагментов, которые вы предоставили, я предполагаю, что вы используете универсальный Python, установленный MacPorts, и по умолчанию он построен с использованием -arch x86_64 -arch i386 -isysroot / для создания модулей расширения.

Как правило, чтобы все работало, необходимо обеспечить:

  1. есть по крайней мере одно arch общее между интерпретатором, всеми модулями расширения C и всеми внешними фреймворками и/или разделяемыми библиотеками, на которые они ссылаются.

  2. интерпретатор выполняется в этой (или одной из этих) общих архитектур.

В OS X 10.6 этот последний шаг не так прост, как должен быть, в зависимости от того, какой Python вы используете. Например, поставляемый Apple Python 2.6 имеет модификацию для принудительного 32-битного выполнения (подробности см. в Apple man python):

export VERSIONER_PYTHON_PREFER_32_BIT=yes

Если вы создаете свой собственный 32-/64-битный универсальный Python, в версии 2.6.5 есть исправления, позволяющие выбирать во время выполнения. К сожалению, способ, которым MacPorts собирает Python, обходит эти исправления, поэтому не существует простого способа заставить 32-/64-разрядную универсальную сборку MacPorts python2.6 на 10.6 работать в 32-разрядном режиме. По сложным причинам он всегда будет предпочитать 64-битную версию, если она доступна, даже если вы используете /usr/bin/arch -i386.

Итак, в зависимости от того, что вы пытаетесь сделать, вы можете обойти проблему (если я правильно понимаю) одним из следующих способов:

  1. перестройте свои фреймворки, чтобы включить -arch x86_64
  2. используйте поставляемый Apple Python (/usr/bin/python) в 32-битном режиме или python.org 2.6.5
  3. переустановите питон MacPorts только в 32-битном режиме (не проверено!):

    sudo port selfupdate
    sudo port clean python26
    sudo port install python26 +universal universal_archs=i386
    
person Ned Deily    schedule 06.04.2010
comment
Большое спасибо за ответ. Кажется, что компоновщик не будет ссылаться на фреймворки в созданном файле .so, если хотя бы одна арка не будет найдена в фреймворке. В моем случае мой фреймворк включает варианты ppc и i386. Все работает нормально с базовым дистрибутивом Python, включенным в MacOSX, если я вручную устанавливаю флаги -arch на этапе связывания с i386. Я могу найти свои фреймворки, если запущу otool для созданного файла .so. Но если я использую системные значения по умолчанию -arch i386 -arch ppc -arch x86_64, файл .so не будет связан ни с одной из моих платформ. - person madflo; 07.04.2010
comment
Вариант 1, к сожалению, недоступен, так как фреймворки предоставляются без исходников. Кажется невозможным получить более новую версию от моих провайдеров. Вариант 2 работает. Спасибо, я почти забыл, что у меня был двоичный файл python в комплекте с macosx... Вариант 3, как ни странно, не работает. Единственная арка, появляющаяся на этапе связывания buildutils, — это i386, как и ожидалось, но .so не связан ни с одной из платформ. Я постараюсь прояснить этот вопрос. - person madflo; 07.04.2010

Кажется, мой фреймворк скомпилирован для ppc и i386, но не для x86_64:

$ file /Library/Frameworks/fwk1.framework/Versions/A/fwk1 
/Library/Frameworks/fwk1.framework/Versions/A/fwk1: Mach-O universal binary with 2 architectures
/Library/Frameworks/fwk1.framework/Versions/A/fwk1 (for architecture ppc):  Mach-O dynamically linked shared library ppc
/Library/Frameworks/fwk1.framework/Versions/A/fwk1 (for architecture i386): Mach-O dynamically linked shared library i386

Я удалил флаг -arch x86_64 из своей строки ссылки. Моя библиотека связана с моими фреймворками:

$ otool -L  test.so
test.so:
    /Library/Frameworks/fwk1.framework/Versions/A/fwk1 (compatibility version 1.0.0, current version 1.0.0)
    /Library/Frameworks/fwk2.framework/Versions/A/fwk2 (compatibility version 1.0.0, current version 1.0.0)
    /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.9.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.0.1)

Если кто-то знает, как принудительно использовать -arch во время компиляции и связывания с distutils Python... пожалуйста, поделитесь своим советом.

person madflo    schedule 06.04.2010

Я просто столкнулся с этим сам. Мне пришлось обойти distutils, потому что они, похоже, жестко закодировали -undefined dynamic_lookup. Вот Makefile, который я использую для эмуляции distutils:

CC = gcc
CFLAGS = -pipe -std=c99 -fno-strict-aliasing -fno-common -dynamic -fwrapv -mno-fused-madd -DENABLE_DTRACE -DMACOSX -DNDEBUG -Werror -pedantic -Wall -Wstrict-prototypes -Wshorten-64-to-32 -g -Os -arch i386 -arch x86_64 -I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7
LD = gcc
LDFLAGS = -Wl,-F. -bundle -Wl,-F. -arch i386 -arch x86_64 -framework CoreFoundation -framework CoreMIDI -framework Python

project = <extension_name>
library = $(project).so
modules = <module_names>
sources = $(foreach module,$(modules),$(module).c)
objects = $(sources:.c=.o)

all: $(library)

$(library): $(objects)
    $(LD) $(LDFLAGS) $(objects) -o $@

%.o: %.c Makefile
    $(CC) $(CFLAGS) $< -c -o $@

install: $(library)
    cp $(library) /Library/Python/2.7/site-packages

clean:
        rm -f $(library) $(objects) *~

Я уверен, что есть способ заставить distutils перестать выдавать этот аргумент -undefined, но вышеописанное сработало для меня на 10.7.

person Mark Pauley    schedule 07.11.2011