如何将Perl模块"通用"(x86_64, arm64)安装和更新?

5

是否可以安装和更新具有通用(x86_64、arm64)架构支持的 Perl(CPAN)模块?如果可以,那么如何操作?

背景

在基于 arm 的 macOS 计算机上,可以按照以下方式为一个指定架构安装 Perl CPAN 模块:

sudo cpan -i Encode
### equivalent since `-arm64` is the native processor in this situation:
sudo arch -arm64 cpan -i Encode

file /Library/Perl/5.30/darwin-thread-multi-2level/auto/Encode/Encode.bundle
# /Library/Perl/5.30/darwin-thread-multi-2level/auto/Encode/Encode.bundle:
#     Mach-O 64-bit bundle arm64

sudo arch -x86_64 cpan -i Encode

file /Library/Perl/5.30/darwin-thread-multi-2level/auto/Encode/Encode.bundle
# /Library/Perl/5.30/darwin-thread-multi-2level/auto/Encode/Encode.bundle:
#     Mach-O 64-bit bundle x86_64

不过,请注意,苹果公司的perl本身就是一个“通用二进制文件”:

file /usr/bin/perl

# /usr/bin/perl: Mach-O universal binary with 2 architectures: 
#    [x86_64:Mach-O 64-bit executable x86_64] 
#    [arm64e:Mach-O 64-bit executable arm64e]
# /usr/bin/perl (for architecture x86_64):  
#     Mach-O 64-bit executable x86_64
# /usr/bin/perl (for architecture arm64e):  
#     Mach-O 64-bit executable arm64e

当本地和非本地应用程序共享相同的Perl依赖项时,任何一种架构的XOR都会产生冲突。例如,GnuCash Finance::Quote 不能在Arm上本地运行,而MacTeX LaTeX Live Update可以在Intel或Arm处理器上本地运行。这两个应用程序都使用Pearl Encode module

如果找不到所需的架构版本,则应用程序日志错误消息将是以下之一:

'/Library/Perl/5.30/darwin-thread-multi-2level/auto/Encode/Encode.bundle'(mach-o文件,但是是不兼容的架构(有'arm64',需要'x86_64'))

'/Library/Perl/5.30/darwin-thread-multi-2level/auto/Encode/Encode.bundle'(mach-o文件,但是是不兼容的架构(有'x86_64',需要'arm64'))


注意:运行这些应用程序的一种解决方法是安装 x86_64 架构的常见 Perl 模块依赖项,然后在 Rosetta2(x86_64)模式下运行通用应用程序。

进一步的发现

cc选项'-bundle'

cc -bundle在保存的安装日志中被发现。

rm -f blib/arch/auto/Encode/Encode.bundle
cc  -bundle -undefined dynamic_lookup  Encode.o def_t.o encengine.o  -o blib/arch/auto/Encode/Encode.bundle 
chmod 755 blib/arch/auto/Encode/Encode.bundle
…
Manifying 18 pod documents
Files found in blib/arch: installing files in blib/lib into architecture dependent library tree
Installing /Library/Perl/5.30/darwin-thread-multi-2level/auto/Encode/Encode.bundle

然而,man cccc --help没有提供有关cc clang LLVM编译器的-bundle选项的任何开发人员信息。因此,不清楚-bundle实际上是在做什么,或者Perl新手如何使用这个信息。

lipo

似乎“构建通用二进制文件的最安全方法是分别编译模块,然后使用lipo合并生成的.bundle文件。”参见:meta::cpan Config_u.pm

苹果文章"Building a Universal macOS Binary"提供了一个多步骤的示例:

以下示例显示了一个makefile,它将单个源文件编译两次-每个架构一次。然后,它使用lipo工具将生成的可执行文件合并为通用二进制文件。

x86_app: main.c
    $(CC) main.c -o x86_app -target x86_64-apple-macos10.12
arm_app: main.c
    $(CC) main.c -o arm_app -target arm64-apple-macos11
universal_app: x86_app arm_app
    lipo -create -output universal_app x86_app arm_app

lipo需要将各个架构文件作为输入来-create通用文件。

文件Encode.bundle

搜索和查看所有Encode.bundle文件,发现了混合的通用和非通用二进制文件。

find / -name "Encode.bundle"

file /Applications/FreeCAD_0.20.app/Contents/Resources/lib/perl5/5.32/core_perl/auto/Encode/Encode.bundle
file /Library/Perl/5.30/darwin-thread-multi-2level/auto/Encode/Encode.bundle
file /System/Library/Perl/5.30/darwin-thread-multi-2level/auto/Encode/Encode.bundle
file /System/Library/Perl/5.34/darwin-thread-multi-2level/auto/Encode/Encode.bundle
file /Users/USERNAME/.cpan/build/Encode-3.19-0/blib/arch/auto/Encode/Encode.bundle

# /Applications/FreeCAD_0.20.app/…/core_perl/auto/Encode/Encode.bundle: 
#     Mach-O 64-bit bundle x86_64

# /Library/Perl/5.30/darwin-thread-multi-2level/auto/Encode/Encode.bundle: 
#     Mach-O 64-bit bundle x86_64

# /System/Library/Perl/5.30/darwin-thread-multi-2level/auto/Encode/Encode.bundle: 
#     Mach-O universal binary with 2 architectures: 
#         [x86_64:Mach-O 64-bit bundle x86_64] 
#         [arm64e:Mach-O 64-bit bundle arm64e]

# /System/Library/Perl/5.34/darwin-thread-multi-2level/auto/Encode/Encode.bundle:
#     Mach-O universal binary with 2 architectures: 
#         [x86_64:Mach-O 64-bit bundle x86_64]
#         [arm64e:Mach-O 64-bit bundle arm64e]

# /Users/USERNAME/.cpan/build/Encode-3.19-0/blib/arch/auto/Encode/Encode.bundle: 
#     Mach-O 64-bit bundle x86_64

观察:

  1. file /System/Library/Perl/…Encode.bundle 显示 Pearl 确实存在通用二进制使用。
  2. file /Library/Perl/…Encode.bundle 表明用户安装和/或更新可能会掩盖共享应用程序使用的通用 /System/Library/Perl/…Encode.bundle

目标

理想情况下,一个总体解决方案应该:

  1. 足够普遍,不需要修改每个添加的模块。
  2. 适用于初始模块安装和任何后续更新。
  3. 不会在 Perl 安装中创建依赖冲突。

可能的方法

无论是隐式还是明确调用,似乎都需要使用 lipo 来创建通用二进制文件。

只是在思考一些方法方向:

  • 修改Perl的make文件?(如何安全地进行此操作?这是一种实用的方法吗?)

  • 创建Config_u的更新版本?

perl -MConfig_u Makefile.PL
  • 有并行的 /Perl/arm64/…/Perl/x86_64/… 目录,然后通过脚本使用 lipo 合并成一些 /Perl/some_universal_version/…

  • 这是否可以像 sudo arch -x86_64 -arm64 -arm64e cpan -i Encode 这样简单?


1
请注意,ExtUtils::MakeMaker 用于编译 Encode bundle 的命令是 cc -mmacosx-version-min=12.0 -bundle -undefined dynamic_lookup -fstack-protector-strong Encode.o def_t.o encengine.o -o blib/arch/auto/Encode/Encode.bundle。如果可能创建通用的 .bundle 文件(参见例如 https://ss64.com/osx/lipo.html),那么修改 Makefile.PL 或者可能需要更改 ExtUtils::MakeMaker 才能使其正常工作。 - Håkon Hægland
@HåkonHægland -bundle 很有趣。然而,在系统本身上没有关于 -bundle 选项的额外开发者信息。无论是隐式调用还是直接调用,lipo 都似乎是所需的工具。根据您提到的细节,我们在问题中添加了其他发现。 - l --marc l
有人问能否更新 ExtUtils::MakeMaker 以生成带有“通用二进制”(例如 arm64、x86_64)指令的 Makefile #445。 - l --marc l
2个回答

4
env ARCHFLAGS='-arch arm64 -arch arm64e -arch x86_64' perl Makefile.PL

make

arch -x86_64 make test
arch -arm64  make test
arch -arm64e make test

(sudo) make install

观察:如果只安装了arm64arm64e中的一个,我会遇到运行时问题。到目前为止,包括x86_64在内的arm64arm64e都取得了成功。

检查:

file Encode.bundle
# Encode.bundle: Mach-O universal binary with 3 architectures: 
#     [x86_64:Mach-O 64-bit bundle x86_64] 
#     [arm64e:Mach-O 64-bit bundle arm64]
#     [arm64e:Mach-O 64-bit bundle arm64e]

在历史的man perlmacosx页面中发现了这种方法:

Related to this support is the new environment variable ARCHFLAGS, which provides a way to build extensions for different machine and 32/64-bit architectures. The default architecture to build extensions before 10.5 was the (single) architecture of the building machine. In 10.6 and beyond, the default architectures were changed to building 32-bit for both PowerPC and Intel, and 64-bit only for Intel. With ARCHFLAGS, this can be changed to whatever architectures the user wants to build. For example:

% env ARCHFLAGS='-arch i386 -arch x86_64' perl Makefile.PL
% make
% make install

will build only 2-way universal.


env ARCHFLAGS='…' 变量也适用于 cpan。但是,要将单架构二进制文件替换为相同版本号的通用二进制文件,则需要使用 -f force 选项。 cpan 版本比较不支持通用二进制文件。

# ok for initial install or version update
(sudo) env ARCHFLAGS='-arch arm64 -arch arm64e -arch x86_64' cpan -i Encode

# -f force is needed to "reinstall|replace" an existing version
(sudo) env ARCHFLAGS='-arch arm64 -arch arm64e -arch x86_64' cpan -f -i Encode

注意:使用-f force选项无法提供潜在有用的测试结果信息。


方法:App::cpanminus

使用cpanm干净安装GnuCash Finance::Quote的示例:

sudo env ARCHFLAGS='-arch arm64 -arch arm64e -arch x86_64' cpan App::cpanminus
sudo env ARCHFLAGS='-arch arm64 -arch arm64e -arch x86_64' cpanm Test2
sudo env ARCHFLAGS='-arch arm64 -arch arm64e -arch x86_64' cpanm Finance::Quote
sudo env ARCHFLAGS='-arch arm64 -arch arm64e -arch x86_64' cpanm JSON::Parse

注意:这适用于全新安装。现有的安装可能需要卸载-重新安装或强制安装过程来替换非通用二进制文件。

使用findfile命令确定哪些Perl的*.bundle模块需要通过卸载-重新安装或强制安装的方法进行修复。

find /Library/Perl -name "*.bundle" | xargs file
# /Library/Perl/5.30/…/Readonly/XS/XS.bundle:
#     Mach-O universal binary with 3 architectures: 
#         [x86_64:Mach-O 64-bit bundle x86_64] 
#         [arm64:Mach-O 64-bit bundle arm64] 
#         [arm64e:Mach-O 64-bit bundle arm64e]
# /Library/Perl/5.30/…/Test/LeakTrace/LeakTrace.bundle: 
#     Mach-O universal binary with 3 architectures: 
# …
#
# /Library/Perl/5.30/…/Date/Simple/Simple.bundle: 
#     Mach-O 64-bit bundle x86_64
# /Library/Perl/5.30/…/Sereal/Encoder/Encoder.bundle:
#     Mach-O 64-bit bundle x86_64

相关内容


1
是否可以安装和更新具有通用(x86_64,arm64)架构支持的Perl(CPAN)模块?
我相信您可以通过在Makefile.PL中为相关架构添加-arch标志来更新.bundle文件。我使用Encode进行了测试,手动编译Encode.bundle而不是像这样使用ExtUtils::MakeMaker(macOS M1,ventura 13.1,homebrew perl版本5.34):
#! /bin/bash
arch_opt="-arch x86_64 -arch arm64"
opt1="-I./Encode -fno-common -DPERL_DARWIN -fno-strict-aliasing"
opt2="-mmacosx-version-min=12.0 -fstack-protector-strong"
opt3="-pipe -DPERL_USE_SAFE_PUTENV -Wno-error=implicit-function-declaration -O3"
opt4=-DVERSION=\"3.19\"
opt5=-DXS_VERSION=\"3.19\"
opt6="-I/opt/homebrew/Cellar/perl/5.34.0/lib/perl5/5.34.0/darwin-thread-multi-2level/CORE"
ccopts="$arch_opt $opt1 $opt2 $opt3 $opt4 $opt5 $opt6"
ldopts="$arch_opt $opt2"
cc -c  $ccopts Encode.c
cc -c $ccopts def_t.c
cc -c $ccopts encengine.c
cc -bundle -undefined dynamic_lookup $ldopts \
   Encode.o def_t.o encengine.o -o blib/arch/auto/Encode/Encode.bundle
file blib/arch/auto/Encode/Encode.bundle

上一个命令的输出现在是:
blib/arch/auto/Encode/Encode.bundle: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit bundle x86_64] [arm64:Mach-O 64-bit bundle arm64]
blib/arch/auto/Encode/Encode.bundle (for architecture x86_64):  Mach-O 64-bit bundle x86_64
blib/arch/auto/Encode/Encode.bundle (for architecture arm64):   Mach-O 64-bit bundle arm64

这表明 Encode.bundle 已经更新为具有 x86_64arm64 架构的通用二进制文件。


谢谢。我会尝试这种方法。另外,根据您之前对问题的评论,我刚刚添加了一些额外的信息到问题中。 - l --marc l

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接