学会看懂构建日志
选择报告导航栏下的Build选项可以查看构建日志。
工程之间有一些依赖关系,例如Pods又例如子工程。
举个栗子
拿objcio中栗子,首先处理的是SSZipArchive任务。Build内容如下:
(1) ProcessPCH /.../Pods-SSZipArchive-prefix.pch.pch Pods-SSZipArchive-prefix.pch normal armv7 objective-c com.apple.compilers.llvm.clang.1_0.compiler
(2) cd /.../Dev/objcio/Pods
setenv LANG en_US.US-ASCII
setenv PATH "..."
(3) /.../Xcode.app/.../clang
(4) -x objective-c-header
(5) -arch armv7
... configuration and warning flags ...
(6) -DDEBUG=1 -DCOCOAPODS=1
... include paths and more ...
(7) -c
(8) /.../Pods-SSZipArchive-prefix.pch
(9) -o /.../Pods-SSZipArchive-prefix.pch.pch
根据流程来大致描述:
- 新起一行的文字块描述一个任务
- 路径跳转以及环境变量设置
- 预编译文件处理展示了所有引用到得参数
- -x 代表了使用语言的种类
- 目标CPU架构指定为armv7
- 隐藏定义被添加
- -c 告诉前端编译器要运行预处理器,语法分析器,类型检查,LLVM生成与优化以及指定代码生成。最后调用汇编器生成a.o文件
- 输入文件
- 输出文件
再具体来看看如何处理的,对这个工程任务来说,一共两个任务来处理objc预编译头文件:
ProcessPCH /.../Pods-SSZipArchive-prefix.pch.pch Pods-SSZipArchive-prefix.pch normal armv7 objective-c ...
ProcessPCH /.../Pods-SSZipArchive-prefix.pch.pch Pods-SSZipArchive-prefix.pch normal armv7s objective-c ...
工程任务生成两种类型架构代码-v7与v7s各一次。
紧接着处理预编译过的头文件,我们找到SSZipArchive任务:
CompileC ...
Libtool ...
CreateUniversalBinary ...
上面已经足够自解释了:首先编译.m与.c文件,然后创建目标文件,最后将两者的.a文件合并成一个通用的二进制文件同时能在v7/v7s上运行。
紧接着是其他所依赖的工程类似的操作步骤。
当所有需要依赖项目准备好的时候,开始构建我们的APP。在日志中我们还可以看到其他有意思的任务如下:
PhaseScriptExecution ...
DataModelVersionCompile ...
Ld ...
GenerateDSYMFile ...
CopyStringsFile ...
CpResource ...
CopyPNGFile ...
CompileAssetCatalog ...
ProcessInfoPlistFile ...
ProcessProductPackaging /.../some-hash.mobileprovision ...
ProcessProductPackaging objcio/objcio.entitlements ...
CodeSign ...
唯一晦涩的是Ld(链接器名字),其与libtool很像,libtool短名是ld与lipo.ld与用来生成可执行文件,libtool用来成库。
现在我们要从另外一个问题来看研究这些任务:Xcode怎么知道这些任务必须被执行。
控制构建流程
下图应该很熟悉了。。。
其中后三个选项与构建关系最为紧密。
构建阶段/Build Phases
构建阶段为你展示可执行的二级制代码,并让你从高级层面进行定制。
首先,先建立任务得依赖关系。CocoaPods详细的脚本与Compile Sources
可以指定哪些文件需要被编译,即确定编译源。(源文件处理是根据规则跟设置来进行处理的,与build phases无关)
当编译完成我们接下来则要进行链接,我们在Link Binary with Libraries
选项中根据库把对应的二进制文件链接起来。
链接结束,最后的编译阶段是拷贝静态资源,例如图片,字体到APP BUNDLE。PNG图片还额外根据构建设置进行优化处理。
而还要进行的Code Signing不属于构建阶段属于另外得构建步骤--打包。
自定义构建阶段
当然你可以自定义构建阶段。比如你现在构建阶段运行你自己写的脚本,正如CocoaPods经常做的工作。你甚至可以添加额外的构建阶段来拷贝资源。当你遇到你需要将特定的资源拷贝到指定目录的时候这是非常获益的。
自定义构建阶段另外一个非常棒的用途是可以水印你的APP ICON带上版本号以及提交哈希值。你可以添加Run Script
构建阶段以此来枷锁你的版本号和版本哈希值:
version=`/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${INFOPLIST_FILE}"`
commit=`git rev-parse --short HEAD`
甚至你可以强迫自己或者你同事来遵守代码规范,比如一个文件代码不能超过200行。。。(尼玛。。这么变态)
find "${SRCROOT}" \( -name "*.h" -or -name "*.m" \) -print0 | xargs -0 wc -l | awk '$1 > 200 && $2 != "total" { print $2 ":1: warning: file more than 200 lines" }'