聊聊Universal framework原理

使用universal framework进行编译。在使用xcode7编译时发现,由于系统生成的目录有变化,导致无法生成原始脚本依赖的相关文件目录结构。

XCode7编译错误总结

  • 错误A:用python脚本的universal framework。
/Users/fangying/Documents/project/XXX/DerivedData/XXX/Build/Products/Debug-iphoneos/XXX.framework/Versions/A
  • 错误B:用shell脚本的universal framework。虽然不会编译错误,但是编译输入的framework是错误的。具体请看下图:

  • 错误C:用shell脚本的universal framework。出现下面的错误。

iPhoneOS.platform/Developer/usr/bin/libtool: No such file or directory

iPhone平台Framework简介

xxx.framework包是iPhone平台对于资源、二进制包、头文件集合。通过这个集合包可以方便提供给使用者提供业务bundle或者SDK的功能。

对于系统提供两种framework:

  • 静态framework
  • 动态framework,比如系统Fundation.framework、AudioToolBox.framework等等。该framework类似与系统动态库。而对于开发者的动态framework,需要把工程设置target to 8.0以上才能支持。

对于framework,iOS系统有严格规定:

# MyFramework.framework
# |-- MyFramework -> Versions/Current/MyFramework
# |-- Headers -> Versions/Current/Headers
# |-- Resources -> Versions/Current/Resources
# `-- Versions
# |-- A
# | |-- MyFramework
# | |-- Headers
# | | `-- MyFramework.h
# | `-- Resources
# | |-- Info.plist
# | |-- MyViewController.nib
# | `-- en.lproj
# | `-- InfoPlist.strings
# `-- Current -> A

注意 对于Mac OS每个App都是一个bundle,对于每个bundle都有严格文件目录格式,以及严格文件名要求,这些严格要求就像App加载协议一样,系统通过该基于文件目录格式和文件名,加载、启动App。个人认为这个有利于app管理和动态部署。Mac OS重装系统,不用想Windows重装系统一样,重装系统后,Mac OS之前应用都可以正常使用,Window就完蛋了,需要重新安装,配环境变量。想想都心碎。

Universal Framework介绍

Universal Framework主要功能编译出在iOS上运行的静态framework包。

主要功能:

  • 编译静态framework包
  • 编译基于多架构二进制的framework包(armv7,arm64,i386,x86_64),其中只有编译achive包 or 编译命令有有 UFW_ACTION=action时,会编译4个架构的framework包
  • 支持资源编译到framework中。

Universal Framework原理

其实Universal Framework原理比较简单,具体分为两点:

  • 修改工程配置,编译基于静态库的framework
  • 加入python脚本,把xcodebuild生成的原生framework结构,修改为iphone平台framework的文件格式
  • 分别打各个架构包,利用libtool -static XXX XXX XXX XXX -o 二进制文件

比如对于相关framework集成多架构包命令如下

'/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/libtool',
    '-static',
    '/Users/fangying/Documents/project/tbguideman/DerivedData/TBGuideMan/Build/Intermediates/ArchiveIntermediates/XXX/IntermediateBuildFilesPath/XXX.build/Release-iphoneos/XXX.build/Objects-normal/armv7/XXXX.ufwbuild',
    '/Users/fangying/Documents/project/tbguideman/DerivedData/TBGuideMan/Build/Intermediates/ArchiveIntermediates/XXXX/IntermediateBuildFilesPath/XXXX.build/Release-iphoneos/XXXX.build/Objects-normal/arm64/XXXX.ufwbuild',
    u'/Users/fangying/Documents/project/tbguideman/DerivedData/TBGuideMan/Build/Intermediates/ArchiveIntermediates/XXXX/IntermediateBuildFilesPath/XXXX.build/Release-iphonesimulator/XXX.build/Objects-normal/i386/XXXX.ufwbuild',
    u'/Users/fangying/Documents/project/tbguideman/DerivedData/TBGuideMan/Build/Intermediates/ArchiveIntermediates/XXXX/IntermediateBuildFilesPath/XXXX.build/Release-iphonesimulator/XXX.build/Objects-normal/x86_64/XX.ufwbuild',
    '-o',
    '/Users/fangying/Documents/project/tbguideman/DerivedData/TBGuideMan/Build/Intermediates/ArchiveIntermediates/XXX/IntermediateBuildFilesPath/UninstalledProducts/XXX.framework/XXX'

Xcode编译过程

背景知识,对于xcode打包过程,是依赖于xcode ide中build phases。如图
聊聊Universal framework原理_第1张图片

如上图:该工程的编译顺序逻辑为:

  1. 编译依赖的target
  2. 运行该脚本
  3. 执行文件编译为.o
  4. 对于外部引用库进行link
  5. 拷贝public文件到指定目录
  6. 拷贝资源文件到指定目录
  7. 运行该脚本

关于该理解、可以参考编译log日志进行理解,按照编译时间顺序

  • Process Info.plist
ProcessInfoPlistFile DerivedData/XXX/Build/Products/Debug-iphoneos/XXX.framework/Info.plist XXXX/Info.plist
    cd /Users/fangying/Documents/project/XXXXr_testing
    export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
    builtin-infoPlistUtility /Users/fangying/Documents/project/XXXXX_testing/XXXX/Info.plist -expandbuildsettings -format binary -platform iphoneos -o /Users/fangying/Documents/project/XXXX_testing/DerivedData/XXXXX/Build/Products/Debug-iphoneos/XXX.framework/Info.plist
  • Run custom shell script ‘Run Script’
PhaseScriptExecution Run\ Script DerivedData/XXX/Build/Intermediates/XXX.build/Debug-iphoneos/XXX.build/Script-D0914C4B1BAFBD01003C4371.sh
    cd /Users/fangying/Documents/project/XXX
    export ACTION=build
    export AD_HOC_CODE_SIGNING_ALLOWED=NO

...
...
...
    export variant=normal
    /bin/sh -c /Users/fangying/Documents/project/XXXX/DerivedData/XXXX/Build/Intermediates/XXXX.build/Debug-iphoneos/XXXXXX.build/Script-D0914C4B1BAFBD01003C4371.sh

从这段运行log,可以了解系统会把编译时中间文件以及编译的脚本都放到DerivedData/XXX/Build/Intermediates/XXX.build/Debug-iphoneos/XXX.build中

  • complie .m .cpp .c .mm等文件

  • Create static library + Create universal binary
    聊聊Universal framework原理_第2张图片

Libtool DerivedData/XXX/Build/Intermediates/XXX.build/Debug-iphoneos/XXX.build/Objects-normal/armv7/XXX normal armv7
    cd /Users/fangying/Documents/project/XXX
    export IPHONEOS_DEPLOYMENT_TARGET=6.0
    export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/libtool -static -arch_only armv7 -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.0.sdk -L/Users/fangying/Documents/project/XXX/DerivedData/XXX/Build/Products/Debug-iphoneos -L/Users/fangying/Documents/project/XXX/Pods/OpenSSL/lib -filelist /Users/fangying/Documents/project/XXX/DerivedData/XXX/Build/Intermediates/XXX.build/Debug-iphoneos/XXX.build/Objects-normal/armv7/XXX.LinkFileList -ObjC -framework Foundation -o /Users/fangying/Documents/project/XXX/DerivedData/XXX/Build/Intermediates/XXX.build/Debug-iphoneos/XXX.build/Objects-normal/armv7/XXX
CreateUniversalBinary DerivedData/XXX/Build/Products/Debug-iphoneos/XXX.framework/XXX normal armv7\ arm64
    cd /Users/fangying/Documents/project/XXX
    export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/libtool -static /Users/fangying/Documents/project/XXX/DerivedData/XXX/Build/Intermediates/XXX.build/Debug-iphoneos/XXX.build/Objects-normal/armv7/XXX /Users/fangying/Documents/project/XXX/DerivedData/XXX/Build/Intermediates/XXX.build/Debug-iphoneos/XXX.build/Objects-normal/arm64/XXX -o /Users/fangying/Documents/project/XXX/DerivedData/XXX/Build/Products/Debug-iphoneos/XXX.framework/XXX
  • Copy 资源头文件和资源文件

聊聊Universal framework原理_第3张图片

CopyPlistFile DerivedData/XXX/Build/Products/Debug-iphoneos/XXX.framework/XXX-Info.plist XXX/XXX-Info.plist
    cd /Users/fangying/Documents/project/XXX
    export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
    builtin-copyPlist --convert binary1 --outdir /Users/fangying/Documents/project/XXX/DerivedData/XXX/Build/Products/Debug-iphoneos/XXX.framework -- XXX/XXX-Info.plist

Universal Framework python编译过程

聊聊Universal framework原理_第4张图片

Universal python逻辑是使用两个进程分别编译iphoneos和iphonesimulator两个版本的。
结合上图,其中主要几点(红色表示子进程,先进行黑色流程,然后两个并行执行)

  • master表示是iphoneos
  • 非master表示是iphonesimulator
  • achive表示是基于xcode achive打包,可以使用xcodebuild命令输入UFW_ACTION=action指明该次xcode打包是achive打包(该achive非xcode achive,只是使得逻辑进入is achive的python脚本运行逻辑)
  • iphoneos编译是编译armv7,arm64 archs, iphonesimulator是编译x86_64, i386 archs

结合上图,分解几步说明下相关的流程:
1. project类是python类,描述当前打包环境、获取打包命令、执行打包命令等功能
起具体的值是通过加载
2. 保存iphone os的打包环境变量到ufw_build_state.json文件中,提供给编译simulator子进程获取环境变量使用
3. 重新获取iphoneos编程的环境变量,保证后续编译命令都是编译iphoneos包
4. 从ufw_build_state.json获得基础环境变量,然后,设定iphonesimulator相关的环境变量
5. 编译各个架构的二进制包
6. 如果是iphonesimulator的进程,执行这里,这里绑定i386+x86_64
7. 如果是iphoneos进程,执行这里,这里绑定4个架构的二进制包

你可能感兴趣的:(框架,脚本,xcode)