iOS | 静态库(.Framework&.a )制作流程&自动
前言
最近公司项目要开发一款SDK
使用,研究了一下.Framework
和.a
的制作过程以及自动脚本打包,这里记录一下,关于静态库的概念部分这里不做说明,网上很多文章都有介绍,本文主要介绍静态库的制作过程以及自动打包.
.Framework 打包流程
创建项目
1.新建工程选择iOS —> Cocoa Touch Framework
image.png2.进入项目,导入自己需要打包SDK的源代码,别忘记并勾选"Copy item needed"选项
image.png3.进入 TARGETS —> Build Phases,设置需要对外暴露的都文件,从Project拖到Public中即可,不需要暴露的放在Project中
image.pngXcode相关设置
1.TARGETS —> Build Settings 中设置相关项
设置 Build Active Architecture Only选项为NO, NO选项意思:是当前打包的.framework支持所有的设备.否则打包时只能用当前选择版本的模拟器或真机运行.
image.png2.设置 Mach-O Type 为 StaticLibrary(静态库)
image.png3. 设置framework最低支持的版本
image.png4. 如果静态库需要支持 armv7s,需要在 Architectures 中手动添加armv7s, 默认打包的真机只有 armv7和 arm64
image.png编译Framework 文件
1. 不同编译环境下编译会生成不同的静态库,debug模拟器,debug真机,release模拟器,release真机;
我们这里设置是Release, 选择Edit Scheme--> Buid Configuration设置为release
image.png2. Command+B 编译,编译后我们可以看到 framework 文件由红色变为了黑色,通过 show in Finder 即可查看编译后的文件
image.png3. 我们的静态库如果要支持模拟器和真机,就需要在真机和模拟器下各自编译一次,打开文件所在位置,我们发现真机和模拟器的静态库都在这里了.
image.png合并文件
1.查看静态库所支持的架构
-
终端->lipo -info 库, 这里需要查看的 framework 内部的库文件,不是 .framework 本体
lipo -info /Users/liuchuan/Library/Developer/Xcode/DerivedData/TestSDK-hkydpbtrsnfiegdptvmaviikkiuf/Build/Products/Release-iphoneos/TestSDK.framework/TestSDK
输出结果如下图:
image.png上图可以看到当前库只能支持真机运行,如果需要支持真机和模拟器都可以运行,需要将2个库文件进行合并
2.文件合并
- lipo -create 真机文件 模拟器文件 -output 目标文件
lipo -create /Users/liuchuan/Library/Developer/Xcode/DerivedData/TestSDK-hkydpbtrsnfiegdptvmaviikkiuf/Build/Products/Release-iphonesimulator/TestSDK.framework/TestSDK /Users/liuchuan/Library/Developer/Xcode/DerivedData/TestSDK-hkydpbtrsnfiegdptvmaviikkiuf/Build/Products/Release-iphoneos/TestSDK.framework/TestSDK -output TestSDK
合并后查看:
image.png
此时文件可以支持模拟器和真机了;
最后我们将合并后的文件替换掉原有.framework 中的目标文件即可; 然后拖入需要使用的工程即可;
image.png
脚本自动打包
上面我们大致知道怎么通过手动合并来进行打包,实际开发中,如果使用手动打包,每次修改了源代码,需要手动编译真机和模拟器,然后合并,浪费很多时间.接下来我们看下如果使用自动脚本来进行编译合并打包的
建立一个Aggregate target
1. 添加一个target
image.png2. 选择Aggregate,并添加工程名字,随便写即可
image.png3. 新建运行脚本
image.png4. 添加脚本语言
image.png具体脚本如下:
#
if [ "${ACTION}" = "build" ]
then
#要build的target名
target_Name=${PROJECT_NAME}
#build之后的文件夹路径
build_DIR=${SRCROOT}/build
#真机build生成的framework文件路径
DEVICE_DIR_Framework=${build_DIR}/Release-iphoneos/${PROJECT_NAME}.framework
#模拟器build生成的framework文件路径
SIMULATOR_DIR_Framework=${build_DIR}/Release-iphonesimulator/${PROJECT_NAME}.framework
#目标文件夹路径
INSTALL_DIR=${SRCROOT}/Products/${PROJECT_NAME}
#判断build文件夹是否存在,存在则删除
if [ -d "${build_DIR}" ]
then
rm -rf "${build_DIR}"
fi
#判断目标文件夹是否存在,存在则删除该文件夹
if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi
#创建目标文件夹
mkdir -p "${INSTALL_DIR}"
#build之前clean一下
xcodebuild -target ${target_Name} clean
#真机build
xcodebuild -target ${target_Name} -configuration Release -sdk iphoneos
#模拟器build
xcodebuild -target ${target_Name} -configuration Release -sdk iphonesimulator
#复制头文件到目标文件夹
cp -R "${DEVICE_DIR_Framework}" "${INSTALL_DIR}"
#合成模拟器和真机包
lipo -create "${DEVICE_DIR_Framework}/${PROJECT_NAME}" "${SIMULATOR_DIR_Framework}/${PROJECT_NAME}" -output "${INSTALL_DIR}/${PROJECT_NAME}.framework/${PROJECT_NAME}"
#打开目标文件夹
open "${INSTALL_DIR}"
fi
5. 运行脚本自动打包
- 选中我们刚才创建的TestSDK_Script这个target,然后运行
- 运行完毕会自动在根目录创建合并后的.framework 文件,并且会自动打开文件夹所在位置
通过上面的脚本,我们可以很方便的实现一键打包功能了,再也不用挨个编译然后合并了;
报错相关:
使用了打包好的静态库, 在项目打包.IPA文件上传到App Store的, 可能会遇到如下错误 ,存在支持bitcode的问题
image.pngld: bitcode bundle could not be generated because '/Users/liuchuan/Desktop/medeng/Demo/ZYAuthSDKDemo/ZYAuthSDKDemo/ZYAuthSDK/ZYAuthSDK.framework/ZYAuthSDK(ZYAuthSDK.o)' was built without full bitcode. All object files and libraries for bitcode must be generated from Xcode Archive or Install build for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)
解决办法,在我们打包静态库的时候添加以下内容
在target->build Setting-> Add User-Defined Setting 添加选项 BITCODE_GENERATION_MODE -> bitcode
image.png.a 打包流程
.a 静态库打包方式基本和.framework 类似,下来主要对一些不同之处进行说明
创建项目
- 新建工程选择iOS —> Cocoa Touch Static Library
- 需要公开的头文件放在此处
- .a 自动打包脚本
if [ "${ACTION}" = "build" ]
then
#要build的target名
target_Name=${PROJECT_NAME}
#build之后的文件夹路径
build_DIR=${SRCROOT}/build
#真机build生成的头文件的文件夹路径
DEVICE_DIR_INCLUDE=${build_DIR}/Release-iphoneos/include/${PROJECT_NAME}
#真机build生成的.a文件路径
DEVICE_DIR_A=${build_DIR}/Release-iphoneos/lib${PROJECT_NAME}.a
#模拟器build生成的.a文件路径
SIMULATOR_DIR_A=${build_DIR}/Release-iphonesimulator/lib${PROJECT_NAME}.a
#目标文件夹路径
INSTALL_DIR=${SRCROOT}/Products/${PROJECT_NAME}
#目标头文件文件夹路径
INSTALL_DIR_Headers=${SRCROOT}/Products/${PROJECT_NAME}/Headers
#目标.a路径
INSTALL_DIR_A=${SRCROOT}/Products/${PROJECT_NAME}/lib${PROJECT_NAME}.a
#判断build文件夹是否存在,存在则删除
if [ -d "${build_DIR}" ]
then
rm -rf "${build_DIR}"
fi
#判断目标文件夹是否存在,存在则删除该文件夹
if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi
#创建目标文件夹
mkdir -p "${INSTALL_DIR}"
#build之前clean一下
xcodebuild -target ${target_Name} clean
#模拟器build
xcodebuild -target ${target_Name} -configuration Release -sdk iphonesimulator
#真机build
xcodebuild -target ${target_Name} -configuration Release -sdk iphoneos
#复制头文件到目标文件夹
cp -R "${DEVICE_DIR_INCLUDE}" "${INSTALL_DIR_Headers}"
#合成模拟器和真机.a包
lipo -create "${DEVICE_DIR_A}" "${SIMULATOR_DIR_A}" -output "${INSTALL_DIR_A}"
#打开目标文件夹
open "${INSTALL_DIR}"
fi