GNU Make: Build Xcode Project
Preface
Xcode 很垃圾, 但还好有很多开源工具可以帮我尽可能的避开 Xcode 带来的不便。
除了可以在 Xcode IDE 内构建工程外, 还可以使用 xcodebuild 来构建, 这就意味着我可以用 GNU
Make 来构建工程, 这样就能更好的与 CI/CD 整合了。
Makefile 入门
基本语法
之所以选择 Makefile 而不是其它构建工具或 plain-old shell script 是因为:
- Makefile 比 shell script 多了目标依赖和其它一些 Makefile 的便利语法;
 - 其它构建工具或多或少有较高的学习曲线;
 - macOS 自带一个 GNU Make 而其它构建工具还需要自行安装。
 
Makefile 的语法还是很简单的, 具体可以去 makefiletutorial.com 快速入门, 下面只做一个简单的例子:
# Comment starts with number sign(#).
# Run commands in bash shell.
SHELL=/bin/bash
# Define a variable.
my_variable:=my value, and spaces are ok.
target: dependency1 dependency2
	# Add at sign(@) before the command to stop it from being printed.
	@echo "Last"
dependency1: dependency2
	@echo "Second"
dependency2:
	@echo "First"
# Build `target` every time the user asked for.
.PHONY: target
复制上面的代码然后粘贴到一个文本文件内并将其命名为 Makefile, 然后在 Makefile 文件所在
目录运行命令 make, 运行完成后命令行的输出应该是:
First
Second
Last
需要注意一点的是: Makefile 用 tab 缩进而不是 spaces, 如果用 spaces 作为缩进的话会出现这样 的错误:
Makefile:<line number>: *** missing separator.  Stop.
在 make 和 shell 之间传递变量
一个 Target 内逐个运行多个命令的时候每个命令都是在新的进程里运行, 也就意味着前一次运行的命令里 有设置变量或 export 变量的话在运行下一个命令时这些变量就不存在了, 因此需要一个方法在 shell 和 make 的环境之间交换数据。
default:
	# Passing variable from shell to make
	$(eval from_shell:=$(shell echo "from_shell"))
	# Passing variable from make to shell
	echo $(from_shell)
使用命令构建 Xcode 工程
xcodebuild 大致的用法是这样的:
xcodebuild -project ./<project>.xcodeproj \
	-target <target> \
	-destination platform=<iOS, macOS or something else> \
	-configuration <Debug | Release> build
比如构建一个 debug 版的 hello world 工程运行在 iOS 上, 则使用如下命令:
xcodebuild -project ./HelloWorld.xcodeproj \
	-target app \
	-destination platform=iOS \
	-configuration Debug build
整合 Makefile 和 xcodebuild
使用 GNU Make 的一个原因就是可以用不同条件构建工程, 比如提供一个参数来构建 Release 版软件:
SHELL=/bin/bash
is_release=false
.PHONY: build
build:
	$(eval xcodebuild_conf:=Debug)
ifeq ($(is_release), true)
    $(eval xcodebuild_conf:=Release)
endif
    @xcodebuild -project ./HelloWorld.xcodeproj \
        -target app \
        -destination platform=iOS \
        -configuration $(xcodebuild_conf) build
如果我使用命令:
make is_release=true build
来构建工程的话, 提供给 xcodebuild 的 -configuration 参数的值就会是 Release, 也就
是要求 xcodebuild 构建 release 版的软件。