Xcode 构建规则
本文翻译自:Xcode Build Rules
当我把默认的CSS样式规则独立到一个文件中时,我面临着一个困境:我如何才能把这个文件嵌入进静态库中,同时又很容易地向里面添加内容。我之前讲解了如何把任意的文件转换为 C 字节码数组。但是仍然需要手动操作。
我做了一些搜索,发现在常规的Linux系统上,人们似乎有一个名叫 objcopy 的工具,可以把文件复制到一个对象文件(.o)里面,从而最终通过链接器把这些对象文件链接起来。但遗憾的是这个工具并没有在Xcode中出现。所以这是不可能的,因为我希望所有人都可以构建DTCoreText。
Xcode 的构建规则拯救了我们。它可以自动化任何类型的操作,似乎这是这个问题的一种解决方案。
Xcode知道如何处理三种基本的源文件(非常简单):
-
c, c++ or Objective-C 源码,或者任何可以被编译的代码
-
头文件
-
其它类型文件
如果你把 “其它类型文件” 添加到一个构建目标中(target),会被当作资源,仅仅是复制到你的应用包中。
现在我将教你如何把任何类型文件变成可编译的。例如你想在构建你的游戏时将一些纹理图片压缩,或者想把某些资源文件嵌入你的二进制文件中。
创建一个构建规则
当Xcode被要求编译一个文件时,它会有一系列的规则来约定如何去做。现在我们将要做的就是添加我们自己的规则,来把一个CSS文件转换成一个 .c 文件。而对于.c 文件,Xcode有一个内建的构建规则。你可以使用两个或者多个链式规则来组合处理每个规则返回的结果。
有一点不是很方便的就是,你不能定义全局的规则。你必须对每个构建目标重复定义你的构建规则。
为了添加你的构建规则,你可以去到项目信息里面来查看你的构建目标,如下:
这个规则对所有.css后缀格式的文件起作用,然后会执行一个自定义的脚本:
cd "$INPUT_FILE_DIR" # move into file dir, otherwise xxd takes the full path for the symbol
/usr/bin/xxd -i "$INPUT_FILE_NAME" "$DERIVED_SOURCES_DIR/$INPUT_FILE_BASE.css.c" # builds a c file with a hex array
首先进入到输入文件所在的目录,因为 xxd 命令总是会将完整的路径转换为c数组的名称。上面的形式就会被命名为 default_c ,长度将会是 default_c_len。
上面的规则将会告诉Xcode如何把一个 .css文件 转换为一个 .c 文件。下一步就是确保我们的default.css文件会被编译,而不是被复制。
编译、不复制
你可以在Build Pases栏目下看到default.css 放到了 Compile Sources下面。
如果你编译这个项目,你可以看到所有的编译过程日志信息。在构建日志的上面,你可以看到文件被预处理,再下面一点你可以看到编译出来的c文件。
再下面,这个库工具把所有的对象文件放到了库归档里面。如果你不相信我,你可以试试 nm 工具来检查最终产品的对象符号。
nm DemoApp | grep default_c
00045474 D _default_css
00045fe0 D _default_css_len
在default.css.c 文件中有两个符号。下面我将告诉你如何访问到这些符号。
访问对象化的文件
上面我讲过, xxd 命令会用传过来的文件名字来创建两个变量,一个是c类型的数组,一个是长度。唯一改变的就是必须要把文件名转换成合法的字符,所以 点 符号转为下划线。
在 DTCSSStylesheet.m 文件里,我向下面那样访问这个数组。我声明两个外部变量,提示链接器插入正确的地址给他们。
+ (DTCSSStylesheet *)defaultStyleSheet
// external symbols generated via custom build rule and xxd
extern unsigned char default_css[];
extern unsigned int default_css_len;
}
那么从它们里面拿到字符就很容易了:
+ (DTCSSStylesheet *)defaultStyleSheet
{
// get the data from the external symbol
NSData *data = [NSData dataWithBytes:default_css length:default_css_len];
NSString *cssString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
return [[DTCSSStylesheet alloc] initWithStyleBlock:cssString];
}
这样可以正常执行,因为我知道所有我的资源都是UTF8 编码的。例如,一个字符占用一个字节。变量default_css_len 整数可以很方便地告诉我们数组的长度,因为在C语言中,一个数组只是指向了数组的第一个字节。
总结
现在,有了自定义构建规则,你不用每次在构建前执行而外的外部脚本,现在这些都集成到构建过程了。你能通过这个规则做什么取决于你的想象力和你的脚本能力。
使用自定义构建规则比使用外部程序有一个优点,就是它们本身是项目的一部分。所以如果它们使用标准的命令,那么就可以无缝地移植到其他开发人员的机器上,这对于开源项目非常有用。