构建 python 模块并将其链接到 MacOSX 框架

发布于 2024-08-28 04:58:15 字数 1977 浏览 11 评论 0原文

我正在尝试在 MacOSX 10.6 上构建 Python 扩展并将其链接到多个框架(仅限 i386)。我使用 distutils 和 Extension 对象创建了一个 setup.py 文件。

我为了链接到我的框架,我的 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++' )

一切都编译和链接良好。如果我从 extra_link_args 中删除 -framework 行,我的链接器将按预期失败。这是 python setup.py build 生成的最后两行:

/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)

此问题是否可以链接到链接步骤上的“-undefineddynamic_lookup”标志?我对在 Google 上找到的几行文档感到有点困惑。

干杯,

I'm trying to build a Python extension on MacOSX 10.6 and to link it against several frameworks (i386 only). I made a setup.py file, using distutils and the Extension object.

I order to link against my frameworks, my LDFLAGS env var should look like :

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

As I did not find any 'framework' keyword in the Extension module documentation, I used the extra_link_args keyword instead.

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++' )

Everything is compiling and linking fine. If I remove the -framework line from the extra_link_args, my linker fails, as expected. Here is the last two lines produced by a python setup.py build :

/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

Unfortunately, the .so that I just produced is unable to find several symbols provided by this framework. I tried to check the linked framework with otool. None of them is appearing.

$ 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)

There is the output of otool run on a test binary, made with g++ and ldd using the LDFLAGS described at the top of my post. On this example, the -framework did work.

$ 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)

May this issue be linked to the "-undefined dynamic_lookup" flag on the linking step ? I'm a little bit confused by the few lines of documentation that I'm finding on Google.

Cheers,

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

云雾 2024-09-04 04:58:15

这与未定义的dynamic_lookup无关,但与distutils有关。它将 extra_link_flags 附加到它为 python 构建选择的链接标志。相反,它应该在前面加上它,因为 -framework 列表必须位于在 cmdline 上使用它们的对象之前(据我所知,这是由于 gcc 收集符号进行链接的方式所致)。我个人使用的一个快速修复方法是使用

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

您需要的任何框架进行构建。 LDFLAGS 被添加到 distutils 自己的标志前面。请注意,您的软件包将无法pip install。正确的修复只能来自 distutils - 恕我直言,他们应该像支持库一样支持框架。

或者,您也可以添加

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

到 setup.py 中。然后你的包应该可以pip install了。

This has nothing to do with the undefined dynamic_lookup but all with distutils. It appends the extra_link_flags to the link flags it chooses for python building. Instead it should prepend it because the -framework listings must come before the objects that use them on the cmdline (AFAIK this is due how gcc gathers symbols for linking). A quick fix that I personally use is building with

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

or whatever frameworks you need. LDFLAGS is prepended to distutils own flags. Note that your package will not be pip installable. A proper fix can only come from distutils - imho they should support frameworks like they support libraries.

Alternatively, you can also add

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

in your setup.py. Your package should then be pip installable.

━╋う一瞬間旳綻放 2024-09-04 04:58:15

尽管尘埃落定很久之后,我自己也有同样的问题,我挖了一下并发现了这个:

/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 扩展,并且抱怨因为 10.6 SDK 中没有 PPC 部分。

然而,

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

它的作用就像一个魅力。

Although long after the dust has settled, having the same question myself I dug around a little bit and found this:

/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

I'm coming at the problem from a different angle - on 10.6 distutils is trying to build C extensions and as complains because there's no PPC portion in the 10.6 SDK.

However,

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

Worked like a charm.

执着的年纪 2024-09-04 04:58:15

我不确定我是否理解您想要做什么以及您想要的结果,但这也许会有所帮助。由于 C 扩展模块通常在 Python 解释器的执行上下文中运行,因此必须构建扩展模块以与解释器兼容。在 OS X 上,Python 和 distutils 会遇到一些麻烦,以确保使用相同的 SDK (-sysroot)、MACOSX_DEPLOYMENT_TARGET 值和 - 构建 C 扩展模块。 arch 值是 Python 解释器本身最初构建的。因此,如果您在 10.6 上使用 Apple 提供的 Python,distutils 将提供 -arch i386 -arch ppc -arch x86_64,这是构建它所用的三个 arch。如果您使用当前的 python.org OS X 安装程序(在 10.6、10.5 或 10.4 上),它将使用:

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

从您提供的代码片段来看,我猜您正在使用 MacPorts 安装的通用 Python,并且默认情况下,它使用 -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 的方式绕过了这些修复,因此似乎没有任何简单的方法可以强制 MacPorts python2.6 32-/64-位通用构建在 10.6 上以 32 位模式运行。由于复杂的原因,它总是更喜欢 64 位(如果可用),即使您使用 /usr/bin/arch -i386

因此,根据您想要执行的操作,您可以通过以下任一方法解决该问题(如果我理解正确的话):

  1. 重建您的框架以包含 -arch x86_64
  2. 使用 Apple 提供的32 位模式下的 Python (/usr/bin/python) 或 python.org 2.6.5
  3. 以仅 32 位模式重新安装 MacPorts python(未经测试!):

    sudo 端口 selfupdate
    sudo 端口清理 python26
    sudo port install python26 +通用universal_archs=i386
    

I am not sure I understand what you are trying to do and your desired outcome but perhaps this will help. Because C extension modules are normally run within the execution context of the Python interpreter, extension modules have to be built to be compatible with the interpreter. On OS X, Python and distutils go to some trouble to ensure that C extension modules are built with the same SDK (-sysroot), MACOSX_DEPLOYMENT_TARGET value, and -arch values as the Python interpreter itself was originally built. So, if you are using the Apple-supplied Python on 10.6, distutils will supply -arch i386 -arch ppc -arch x86_64, the three archs that it was built with. If you use a current python.org OS X installer (on 10.6, 10.5, or 10.4), it will use:

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

From the snippets you supply, I'm guessing you are using a MacPorts-installed universal Python and, by default, it is built with and uses -arch x86_64 -arch i386 -isysroot / for building extension modules.

Generally, to make everything work you need to ensure:

  1. there is at least one arch in
    common among the interpreter, all C
    extension modules, and all external
    frameworks and/or shared libraries
    that they link to

  2. the interpreter is executing in that (or one of those) common architecture(s).

On OS X 10.6, that last step is not as easy as it should be depending on which Python you are using. For instance, the Apple-supplied Python 2.6 has a modification to force 32-bit execution (see Apple's man python for details):

export VERSIONER_PYTHON_PREFER_32_BIT=yes

If you build your own 32-/64-bit universal Python, there are fixes in 2.6.5 to allow selection at run-time. Unfortunately, the way MacPorts builds Python bypasses those fixes so there does not appear to be any simple way to force a MacPorts python2.6 32-/64-bit universal build on 10.6 to run in 32-bit mode. For complicated reasons, it will always prefer 64-bit, if available, even if you use /usr/bin/arch -i386.

So, depending on what you are trying to do, you may be able to work around the issue (if I understand it correctly) by either:

  1. rebuild your frameworks to include -arch x86_64
  2. use the Apple-supplied Python (/usr/bin/python) in 32-bit mode or the python.org 2.6.5
  3. reinstall the MacPorts python in 32-bit-only mode (untested!):

    sudo port selfupdate
    sudo port clean python26
    sudo port install python26 +universal universal_archs=i386
    
ま柒月 2024-09-04 04:58:15

看来我的框架是为 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)

如果有人知道如何强制在编译和链接时使用 Python 的 distutils 使用 -arch...请分享您的建议。

It seems that my framework is compiled for ppc and i386 but not 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

I removed the -arch x86_64 flag from my linking line. My library is linked against my frameworks :

$ 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)

If someone know how to force the -arch to be used at compile and link time with Python's distutils... please share your advice.

梦巷 2024-09-04 04:58:15

我自己也遇到过这个。我必须绕过 distutils,因为它们似乎硬编码了 -undefineddynamic_lookup。这是我用来模拟 distutils 的 Makefile:

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) 
lt; -c -o $@

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

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

我确信有一种方法可以让 distutils 停止发出 -undefined 参数,但上面的方法在 10.7 上对我有用

I just ran into this myself. I had to bypass distutils, because they appear to hard-code the -undefined dynamic_lookup. Here is the Makefile I'm using to emulate 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) 
lt; -c -o $@

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

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

I'm sure there is a way to get distutils to stop emitting that -undefined argument, but the above worked for me on 10.7

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文