GMTC 全球大前端技术大会(北京站)门票 9 折特惠中,点击立减 ¥480 了解详情
写点什么

构建 iOS 持续集成平台(一)——自动化构建和依赖管理

2013 年 9 月 10 日

2000 年 Matin Fowler 发表文章 Continuous Integration【1】;2007 年,Paul Duvall, Steve Matyas 和 Andrew Glover 合著的《Continuous Integration:Improving Software Quality and Reducing Risk》 【2】出版发行,该书获得了 2008 年的图灵大奖。持续集成理念经过 10 多年的发展,已经成为了业界的标准。在 Java, Ruby 的世界已经诞生了非常成熟的持续集成工具和实践,而对于 iOS 领域来说,因为技术本身相对比较年轻和苹果与生俱来的封闭思想,在持续集成方面的发展相对滞后一些,但是,随着越来越多的 iOS 开发者的涌入,以及各个互联网巨头加大对 iOS 开发的投入,诞生了一大批非常好用的持续集成工具和服务,本文的目的就是介绍一下如何有效的利用这些类库,服务快速构建一个 iOS 开发环境下的持续集成平台。

自动化构建

在 MartinFowler 的文章 [1] 中关于自动化的构建定义如下:

复制代码
Anyone should be able to bring in a virgin machine, check the sources
out of the repository, issue a single command, and have a running
system on their machine.

因此,自动化构建的的首要前提是有一个支持自动化构建的命令行工具,可以让开发人员可以通过一个简单的命令运行当前项目。

命令行工具

自动化构建的命令行工具比持续集成的概念要诞生得早很多,几十年前,Unix 世界就已经有了 Make,而 Java 世界有 Ant,Maven,以及当前最流行的 Gradle,.Net 世界则有 Nant 和 MSBuild。作为以 GUI 和命令行操作结合的完美性著称的苹果公司来说,当然也不会忘记为自己的封闭的 iOS 系统提供开发环境下命令行编译工具:xcodebuild【3】

xcodebuild

在介绍 xcodebuild 之前,需要先弄清楚一些在 XCode 环境下的一些概念【4】:

  • Workspace:简单来说,Workspace 就是一个容器,在该容器中可以存放多个你创建的 Xcode Project, 以及其他的项目中需要使用到的文件。使用 Workspace 的好处有,1), 扩展项目的可视域,即可以在多个项目之间跳转,重构,一个项目可以使用另一个项目的输出。Workspace 会负责各个 Project 之间提供各种相互依赖的关系 ;2), 多个项目之间共享 Build 目录。
  • Project:指一个项目,该项目会负责管理生成一个或者多个软件产品的全部文件和配置,一个 Project 可以包含多个 Target。
  • Target:一个 Target 是指在一个 Project 中构建的一个产品,它包含了构建该产品的所有文件,以及如何构建该产品的配置。
  • Scheme:一个定义好构建过程的 Target 成为一个 Scheme。可在 Scheme 中定义的 Target 的构建过程有:Build/Run/Test/Profile/Analyze/Archive
  • BuildSetting:配置产品的 Build 设置,比方说,使用哪个 Architectures?使用哪个版本的 SDK?。在 Xcode Project 中,有 Project 级别的 Build Setting,也有 Target 级别的 Build Setting。Build 一个产品时一定是针对某个 Target 的,因此,XCode 中总是优先选择 Target 的 Build Setting,如果 Target 没有配置,则会使用 Project 的 Build Setting。

弄清楚上面的这些概念之后,xcodebuild 就很好理解了,官网上对其作用的描述如下:

复制代码
xcodebuild builds one or more targets contained in an Xcode
project, or builds a scheme contained in an Xcode workspace or
Xcode project.

xcodebuild 就是用了构建产品的命令行工具,其用法可以归结为 3 个部分:

  • 可构建的对象
  • 构建行为
  • 一些其他的辅助命令

可以构建的对象有,默认情况下会运行 project 下的第一个 target:

  • workspace:必须和“-scheme”一起使用,构建该 workspace 下的一个 scheme。
  • project:当根目录下有多个 Project 的时候,必须使用“-project”指定 project,然后会运行
  • target:构建某个 Target
  • scheme:和“-workspace”一起使用,指定构建的 scheme。
  • ……

构建行为包括:

  • clean: 清除 build 目录下的
  • build: 构建
  • test: 测试某个 scheme,必须和"-scheme"一起使用
  • archive: 打包,必须和“-scheme”一起使用
  • ……

辅助命令包括:

  • -sdk:指定构建使用的 SDK
  • -list:列出当前项目下所有的 Target 和 scheme。
  • -version:版本信息
  • ……

关于 xcodebuild 更多详细的命令行请参见: https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/xcodebuild.1.html

下图是使用 XcodeBuild 运行一个 scheme 的 build 的结果:

了解了 xcodebuild 的用法之后,接下来分析一下 xcodebuild 的主要缺陷:

  • 从上图直接可以得到的感觉,其脚本输出的可读性极差,
  • 只能要么完整的运行一个 target 或者 scheme,要么全部不运行。不能指定运行 Target 中特定的测试。
  • 最令人发指的是,XCode 4 中的 xcodebuild 居然不支持 iOSUnitTest 的 Target【5】,当我尝试运行一个 iOS App 的测试 target 时,得到如下的错误:

对于上面提到的缺陷,Facebook 给出了他们的解决方案:xctool【6】

xctool

xctool 在 其主页直接表明了其目的:

<i>xctool is a replacement for Apple's xcodebuild that makes it easier to build and<br></br> test iOS and Mac products. It's especially helpful for continuous integration.</i>其作用是替代 xcodebuild,目的是让构建和测试更加容易,更好的支持持续集成。从个人感受来看,它的确成功取代了 xcodebuild。但是 xctool 说到底只是对 xcodebuild 的一个封装,只是提供了更加丰富的 build 指令,因此,使用 xctool 的前提是 xcodebuild 已经存在,且能正常工作。

安装

xctool 的安装非常简单,只需要 clone xctool 的 repository 到项目根目录就可以使用, 如果你的机器上安装有 Homebrew,可以通过“brew install xctool”命令直接安装。(注意:使用 xctool 前一定要首先确认 xcodebuild 已安装且能正确工作)。

用法

关于 xctool 的用法就更加人性化了,几乎可以重用所有的 xcodebuild 的指令,配置。只需要注意一下几点:

  • xctool 不支持 target 构建,只能使用 scheme 构建。
  • 支持“-only”指令运行指定的测试。
  • 支持多种格式的 build 报告。

例子:

复制代码
path/to/xctool.sh
-workspaceYourWorkspace.xcworkspace
-schemeYourScheme
test -only SomeTestTarget:SomeTestClass/testSomeMethod

下图是我使用 xctool 运行 test 的效果:

常见问题:

No architectures to compile for (ONLY_ACTIVE_ARCH=YES, active arch=x86_64, VALID_ARCHS=armv7 armv7s).

解决方法:到 Project Setting 中,把"Build Active Architecture Only"设置为 NO

Code Sign error: A valid provisioning profile matching the application’s Identifier ‘dk.muncken.MyApp’ could not be found

解决方法:通过“-sdkiphonesimulator”指定 SDK,从而能够使用符合 iOS 约定的 application Identifier。

依赖管理

选定了命令行工具之后, 接下来可以考虑下依赖管理的问题了。我到现在还记得几年前,刚从 Ant 转到使用 Maven 的那种爽快的感觉。后来,进入 Ruby 的世界,其与生俱来的 Gem 管理系统,也让其依赖管理变得极其简单。 对于 iOS 平台来说,在做项目时,经常需要使用到各种各样的第三方 Framework,这同样需要一个爽快的依赖管理系统,不然的话,各位可以想象一下重复的下载 Framework 文件,拖入各个 Target 的 Build Phase 的 Link Binary With Libraries 中的场景。这种重复的劳动对于“懒惰”的程序员来说,是很难接受的,于是,活跃的社区开发者们提供了这样的一个工具:Cocoapods【7】

Cocoapods 开始于 2011 年 8 月 12 日,经过 2 年多的发展,现在已经超过 2500 次提交,并且持续保持活跃更新,目前已成为 iOS 领域最流行的第三方依赖管理工具。从技术层面来说,其是一个 Ruby Gem,从功能层面来说,其是一个 iOS 平台下的依赖管理工具,为 iOS 项目开发提供了类似于在 Ruby 世界使用 Gem 的依赖管理体验。

安装

前面提到 cocoapods 本质上是一个 Ruby Gem,因此,其使用前提首先是 Ruby 开发环境。庆幸的是,Mac 下都自带 Ruby。这样,只需要简单的 2 条命令,就可以把 cocoapods 安装好:

复制代码
$ [sudo] gem install cocoapods
$ pod setup

用法

cocoapods 的使用方式和使用 Ruby Gem 非常相似,首先需要在项目根目录下创建文件 Podfile, 在 Podfile 中,开发人员只需要按照规则配置好如下内容就好:

  • 项目支持的平台,版本(iOS/OSX)
  • 每个 target 的第三方依赖

例子:

复制代码
platform :ios, '6.0'
inhibit_all_warnings!
xcodeproj `MyProject`
pod 'ObjectiveSugar', '~> 0.5'
target :test do
pod 'OCMock', '~> 2.0.1'
end
post_install do |installer|
installer.project.targets.each do |target|
puts "#{target.name}"
end
end

修改好配置文件之后,只需要简单的使用“pod install”即可安装好所有的依赖,执行该命令之后,在项目跟目录下会出现“.xcworkspace”和“Pods”两个目录:

接下来,开发者需要使用 xcworkspace 打开项目而不是使用 xcodeproject,打开项目之后,在项目目录下除了自己的 project 以外,还可以看到一个叫做 Pods 的项目,该项目会为每一个依赖创建一个 target:

在 Podfile 中,还可以指定依赖专属于某个 Target,

复制代码
target :CocoaPodsTest do
pod 'OCMock', '~> 2.0.1'
pod 'OCHamcrest'
end

如果你记不清楚某个依赖库的名称,可以使用“pod search ”模糊搜索依赖库中的相似库, 另外,如果你想使用的库在 cocoapods 的中央库中找不到,那么,你可以考虑为开源社区做做贡献,把你觉得好用的库添加到中央库中,Cocoapods 的官网上有具体的步骤【8】

原理

CocoaPods 的原理思想基本上来自于 Jonah Williams 的博客“Using Open Source Static Libraries in Xcode 4”【9】, 当使用“pod install”安装文件时,cocoapods 做了如下这些事:

  • 创建或者更新当前的 workspace
  • 创建一个新的项目来存放静态库
  • 把静态库会编译生成的 libpods.a 文件配置到 target 的 build phase 的 link with libraries 中
  • 在依赖项目中创建 *.xcconfig 文件, 指定在编译时的一些参数和依赖
  • 添加一个新的名为“Copy Pods Resource”的 Build Phase,该 build phase 会使用"${SRCROOT}/Pods/Pods-CocoaPodsTest-resources.sh"把 Pods 下的资源文件拷贝到 app bundle 下。

注意事项

当使用 xctool 作为命令行工具构建项目时,使用 cocoapods 管理依赖时,需要做一些额外的配置:

  • 编辑 Scheme,把 pods 静态库项目作为显式的依赖添加到项目的 build 中,
  • 把 pods 依赖项目拖动到本来的项目之上,表示先编译 pods 静态库项目,再编译自己的项目。


感谢张凯峰对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。

2013 年 9 月 10 日 00:1317891

评论

发布
暂无评论
发现更多内容

Flink处理函数-6-4

小知识点

scala 大数据 flink

阿里云服务器搭建

时间是一个人最好的证明

阿里云 服务器 域名

Java 中的反射是什么

Rayjun

Java 反射

架构师训练营第一期-第四周课后作业

卖猪肉的大叔

极客大学架构师训练营

第四周学习代码系统架构总结

三板斧

如何设计一个牛逼的API接口

Java旅途

Spring Boot API

架构师训练营第四周作业

Shunyi

极客大学架构师训练营

第四周作业

icydolphin

极客大学架构师训练营

架构师训练营第三周课后练习

薛凯

区块链将这样影响你的生产生活

CECBC区块链专委会

区块链 信息发展

架构师训练营 1 期 - 第四周 - 系统架构

三板斧

极客大学架构师训练营

5张表的sql整懵阿里p7:你们能看明白自己写的啥吗?

小Q

MySQL 数据库 学习 调优 mycat

架构师训练营第三周总结

薛凯

架构师训练营第四周总结

薛凯

有符号类型引发的奇怪现象

jiangling500

ARTS Week13

丽子

架构师训练营第四周课后练习

薛凯

架构师训练营第一周课后练习

薛凯

架构师训练营第一周总结

薛凯

数据库技术丨GaussDB(DWS)数据同步状态查看方法

华为云开发者社区

数据库 高可用

架构师训练营第一期-第四周学习总结

卖猪肉的大叔

极客大学架构师训练营

京东区块链之供应链应用篇:溯源应用结合区块链能碰撞出什么火花?

京东科技开发者

区块链 供应链

2020.10.05-2020.10.11 学习总结

icydolphin

极客大学架构师训练营

spring-boot-route(十七)使用aop记录操作日志

Java旅途

Spring Boot aop

通证、通证经济与区块链

CECBC区块链专委会

区块链 通证经济

全面到哭!BAT内部Java求职面试宝典,必须人手一份!

Java成神之路

Java 阿里巴巴 程序员 面试 编程语言

Go 语言内存管理三部曲(二)解密栈内存管理

网管

go 堆栈 内存管理 内存布局

《Linux学习笔记》从常用命令、常用操作到网络管理、性能优化

Java架构之路

Java Linux 程序员 面试 编程语言

从格力直播看品牌商的渠道变革

boshi

数字化转型 品牌 直播带货 优化业务

LeetCode题解:22. 括号生成,递归先生成再过滤,JavaScript,详细注释

Lee Chen

LeetCode 前端进阶训练营

研发和测试在多国陆续展开 全球央行数字货币研发驶入快车道

CECBC区块链专委会

数字货币 金融

DIY 的 Kubernetes 集群的稳定性保障实践

DIY 的 Kubernetes 集群的稳定性保障实践

构建iOS持续集成平台(一)——自动化构建和依赖管理-InfoQ