Hotaru's Notebook

GNU Make: Build Xcode Project

Preface

Xcode 很垃圾, 但还好有很多开源工具可以帮我尽可能的避开 Xcode 带来的不便。 除了可以在 Xcode IDE 内构建工程外, 还可以使用 xcodebuild 来构建, 这就意味着我可以用 GNU Make 来构建工程, 这样就能更好的与 CI/CD 整合了。

Makefile 入门

基本语法

之所以选择 Makefile 而不是其它构建工具或 plain-old shell script 是因为:

  1. Makefile 比 shell script 多了目标依赖和其它一些 Makefile 的便利语法;
  2. 其它构建工具或多或少有较高的学习曲线;
  3. 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 版的软件。

#Coding #GNU #Makefile #Xcode #Apple