草庐IT

iOS M1 芯片 编译爬坑

哥哥乘风 2023-03-28 原文

主要是两个错误,引起混淆。导致爬了挺久的坑。

1、 In xxxx/proj.ios_mac/xxxx.framework/xxxx(xxxx.a-arm64-master.o), building for iOS Simulator, but linking in object file built for iOS, for architecture arm64.
或者
ld: warning: ignoring file YoupPth/Build/Products/Debug-iphonesimulator/xxxx/xxxx.framework/xxxx, 
building for iOS Simulator-x86_64 but attempting to link with file built for iOS Simulator-arm64
Undefined symbols for architecture x86_64:
  "_OBJC_CLASS_$_xxxx", referenced from:
      objc-class-ref in xxxx.o
ld: symbol(s) not found for architecture x86_64
    
    
2、 In xxxx/proj.ios_mac/xxxx.xcodeproj The linked framework 'Pods_xxx_iOS.framework' is missing one or more architectures required by this target: x86_64.

看起来好像是差不多,都是由于 项目中的依赖库 提供的指令集 不完整导致的。

第一个 是说  xxxx.framework  指令集为 iOS 真机 这个库, 不能 链接 为 iOS 模拟器 的 目标文件。 实际上这个问题 应该是一个兼容问题,只出现在 M1 芯片的 Mac 上。 因为 M1 芯片的 Mac 本身就是 arm64 架构,它的模拟器也是 arm64 架构的。

首先有一点要明确:

我们在 intel 机型上 创建一个静态库文件,编译后会生成两个版本。一个是模拟器版, x86_64 架构(如果M1 机型上创建 那么是 arm64 架构),一个是真机版, arm64 架构。

如果 intel 机型上 生成的静态库 需要 给 intel 机型上的 其他工程引入,一般操作是通过 lipo 工具合并成一个 模拟器和真机 通用的 静态库,以方便模拟器或者真机调试。

链接真机的时候 会把真机的指令 链接进 目标文件,链接模拟器 就把模拟器的指令链接进 目标文件

当链接的时候: 针对 一般第三方库, 以前普遍提供的是 包含真机 arm64 指令集 和 模拟器 x86_64 指令集的 通用版本。

在 intel 机型上也就是你的Mac 打包机器为 intel 芯片,  为真机链接 arm64 架构 指令,为模拟器链接 x86_64 架构 指令没有什么问题。

但是 你的Mac 打包机器为 M1 芯片的时候,   为真机链接 arm64 架构指令,为模拟器 还是链接 x86_64 指令就会有问题。因为 此时 M1 机型 的模拟器架构是 arm64! 只能跑 arm64 指令。

这时候 Xcode 就会报以上类型的错误,而不会去为 模拟器 链接 该依赖库 真机的 arm64 指令!!!

验证很简单:

你首先针对 真机 生成一个静态库,在M1 机型上 链接到 模拟器的可执行文件,依然不行。即使他们 都是在 arm64 架构下。

 

那么如何解决这个问题? 有以下两种方式。

1、使用 Rosetta 模式 运行Xcode, 重新编译。

2005年, 苹果从PowerPC 芯片切换到 因特尔,Rosetta 最初是为 PowerPC 应用转换到 x86 上,能让大多数 PowerPC 应用在 x86 Mac 上运行,但是会损失部分性能。 

Rosetta 模式 会使 Xcode 能够 在链接 x86静态库 前 将其转换为 arm64 指令。下图 倒数第二行 使用 Rosetta 打开! (注意 只有 M1 机型上才有)

通过 Rosetta 模式运行 Xcode, Xcode 是以 x86架构的方式运行(正常是 Apple 也就是 arm64), 静态库是 x86 的也能正常链接跑。  

在 M1 的 Mac 机型,模拟器 正常是以 arm64 运行的,模拟器 启动 app 也是以 arm64 的方式运行。

而PC 上 Rosetta 模式启动 Xcode 让 App 链接依赖库, 链接器会链接 x86(也就是当前Xcode 运行模式)的指令,即使依赖库既有 arm64 也有 x86 指令集。如果 依赖库本身只有 arm64 指令集,那么会报 未定义 符号,无法链接。

注意:Rosetta 的方式 虽然 足以支持大多数主流生产力应用程序,但无法兼容那些需要与操作系统、硬件或图形硬件进行直接交互的软件。

 

2、编译链接 app 工程的时候  排除 arm64 架构。 具体 在 Build Settings 里 设置 Excluded Architectures 选项,在 Debug 模式下 添加 arm64 。真机不会出现这个问题,所以 Release 不需要。

所以先 检查工程以及子工程 编译设置 排除 arm64 架构的编译产物。 

检查 Build Settings 里面的 Excluded Architectures 添加  arm64。  clean 重编。 

最终跑起来  会发现  排除了 arm64 指令集后 模拟器也是以 x86 的方式启动 App 了。 

查看 活动监视器 里面  进程的 种类 就可以清晰的知道。 

模拟器与 模拟器启动的 App 运行指令集不同,通过 XPC 方式进行通信,理论上没有啥问题,但是 XPC 通信 相对同步时间比较长, 对于 时间敏感的逻辑可能会出问题。

比如 滑动手势 或者 列表的惯性滚动。 

 

对于通过 cocoapods 维护依赖库的方式来说, 如果手动更改后,重新  pod update 会导致又要重新修改,所以excluded Architectures  需要写进 Podfile

post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = "arm64"
    end
  end
end

 

但是如果需要 编译依赖库文件 提供给第三方 使用 那么你的生成产物又不能排除 arm64.  你可能就需要针对不同 的架构打不同的版本。有更好的解决办法。

为了解决 M1 芯片机型 封装库文件的问题, 2019年 的 WWDC 大会  Apple 提供了 XCFrameworks 的方式,这就可以理解成 新的 xcodebuild 命令替换以前  lipo 命令,将不同指令集 的库文件合并成 通用二进制文件。

只不过 XCFrameworks 还可以包含其他第三方的库。 这里就不展开说 XCFrameworks 了。

 

文章开头说的第二个错误 是说 链接 Pods_xxxx_iOS.framework 这个framework 中 缺失了 x86_64 架构 的某个库。

解决:检查 编译 生成 Pods_xxxx_iOS.framework 的 所有依赖库的工程 或者 prebuilt 的库,是否存在 x86_64 架构。

1、针对子工程,如果 Xcode 13, 检查工程的 User-Defined 里面的  VALID_ARCHS 值 是否存在  x86_64,没有则需要添加。 如果是 Xcode 12 则在  Architectures 里面检查 是否存在 x86_64。其他架构也是同样

2、针对 预编译库, 可以使用 lipo 工具查看包含一些什么 指令集。是否缺失 x86_64。如果缺失了,需要获取带有 x86_64 指令的依赖库。

例如:lipo -i xxx.a

主要是 Xcode12 默认不再对 x86_64 架构 进行支持了,打通用包的时候  需要手动添加。

 

lipo 工具 可以对  库文件的架构进行修改

nm 工具 用来现实一个 程序包的符号表

strip 用来删除一个 程序包里的符号表

ar 工具 可以获取 库文件 链接前的  .o 文件  

libtool -static -o  xxx.a  *.o   又可以合并 *.o 文件 为 xxx.a。 或者 直接用 ld。

 

参考:

https://juejin.cn/post/7037037120158269448

有关iOS M1 芯片 编译爬坑的更多相关文章

  1. ruby - Sinatra set cache_control to static files in public folder编译错误 - 2

    我不知道为什么,但是当我设置这个设置时它无法编译设置:static_cache_control,[:public,:max_age=>300]这是我得到的syntaxerror,unexpectedtASSOC,expecting']'(SyntaxError)set:static_cache_control,[:public,:max_age=>300]^我只想将“过期”header设置为css、javaascript和图像文件。谢谢。 最佳答案 我猜您使用的是Ruby1.8.7。Sinatra文档中显示的语法似乎是在Ruby1.

  2. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  3. .net - 是否有 Ruby .NET 编译器? - 2

    是否有适用于Ruby语言的.NETFramework编译器?我听说过DLR(动态语言运行时),这是否将使Ruby能够用于.NET开发? 最佳答案 IronRuby是Microsoft支持的项目,建立在动态语言运行时之上。 关于.net-是否有Ruby.NET编译器?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/199638/

  4. python - 使用 Python、Ruby 和 Perl 重新编译 MacPort 版本的 MacVim - 2

    关闭。这个问题是off-topic.它目前不接受答案。想改进这个问题吗?Updatethequestion所以它是on-topic用于堆栈溢出。关闭10年前。ImprovethisquestionLinux专家正在转向Mac(10.8)。因为我懒...我使用MacPorts安装MacVim。它似乎安装没有错误。我只需要mvim中的python、ruby和perl支持。$/opt/local/bin/mvim--version|egrep'patches|python|ruby|perl'Includedpatches:1-244,246-646+multi_lang-mzscheme+

  5. ruby - 为什么 `middleman serve` 有效,但是 `middleman build` 编译这个 Sass 失败? - 2

    当我刚刚运行middleman时服务,all.css编译得很好,只包含对+box-shadow(none)的调用:/*line1,/home/yang/asdf/source/stylesheets/content.css.sass*/div{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}但是当我构建网站时,我得到了这个Sass/Compass错误:$middlemanbuildSlim::EmbeddedEngineisdeprecated,itiscalledSlim::EmbeddedinSlim2.0

  6. ruby - 有没有办法在 Ruby 中执行编译时类型检查? - 2

    我知道Ruby是动态和强类型的,但据我所知,由于每个参数缺少显式类型表示法(或契约),当前语法不允许在编译时检查参数类型。如果我想执行编译时类型检查,我有哪些(实际成熟的)选项?更新我的意思是类型检查类似于典型的静态类型语言。比如C。例如,C函数表示每个参数的类型,编译器检查传入的参数是否正确。voidfunc1(structAAAaaa){structBBBbbb;func1(bbb);//Wrongtype.Compiletimeerror.}作为另一个例子,Objective-C通过放置显式类型信息来做到这一点。-(id)method1:(AAA*)aaa{BBB*bbb=[[A

  7. ruby - Ruby 将来可以编译并更快吗? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭7年前。ImprovethisquestionC、Java、C#和Python都是从头编译的。感谢Facebook,PHP现在也可以编译并可以在HHVM上运行,从而提高程序的性能。Ruby不可编译并且比上述语言慢。Ruby有没有可能在未来被编译(就像PHP和HHVM一样)?或者可能有一些原因不能做到?

  8. ruby - 有没有办法让文件夹包含在生产构建中但不让 jekyll 编译它? - 2

    我认为最好的例子是images/文件夹或node_modules/用于将图像和依赖项包含在最终构建中,而无需花费很长时间编译。编辑:抱歉没有具体说明,但我很清楚keep_files和exclude两者都对我的情况没有帮助。exclude从编译和站点构建中排除文件夹和文件,并且每次都需要额外的流水线工具来手动移动它们。keep_files要求文件首先存在于最终构建中,这对于某些生产环境(GitHub的gh-pages等)是不可能的 最佳答案 你可能想看看这个:ExcludingadirectoryfromJekyllwatchP.S.

  9. ruby - 我如何将 Ripper 的 AST 重新编译回 Ruby 代码? - 2

    Ripper是Ruby1.9附带的解析库。它将Ruby代码转换为AST,如下所示:ppRipper.sexp("deffoo;yield:a;return1end")#=>[:program,[[:def,[:@ident,"foo",[1,4]],[:params,nil,nil,nil,nil,nil],[:bodystmt,[[:yield,[:args_add_block,[[:symbol_literal,[:symbol,[:@ident,"a",[1,16]]]]],false]],[:return,[:args_add_block,[[:@int,"1",[1,26]]

  10. ruby - 编译 Ruby 内联 C 代码 - 解决错误 - 2

    我正在尝试获取此Ruby内联C代码http://pastie.org/2825882上类。该代码在vanillaC中运行,但在这里我收到错误和警告。是什么导致了这个错误?./backtrack_inline.rb:67:error:lvaluerequiredasunary'&'operand另外,为什么会出现以下错误?./backtrack_inline.rb:73:error:toofewargumentstofunction'backtrack'检查生成的C代码(http://pastie.org/2826036)我没有发现参数有任何问题。但我也收到以下警告:./backtrac

随机推荐