@author ASCE1885的 Github 微博 CSDN
本文由于潜在的商业目的,不开放全文转载许可,谢谢!
使用Facebook Buck对已有的Android项目进行改造,首先需要理解Buck的一些基本概念,在这个的基础上,我们才能既快又好的进行改造工作。本文就先来介绍4个主要的概念,它们对于编写Buck构建脚本至关重要:
- 构建规则(Build Rule)
- 构建目标(Build Target)
- 构建文件(Build File)
- 构建目标模式(Build Target Pattern)
构建规则(Build Rule)
构建规则是从一组输入文件中生成输出文件的过程。Buck已经内建了一系列构建规则,定义了构建Android代码时会用到的公共操作,例如
- 编译基于Android SDK编写的Java代码,可以使用: android_library
- 生成APK文件,可以使用:android_binary
构建规则是一个泛称,在构建文件中使用内建Python函数定义的规则以及创建的用于执行这个操作的Java对象,都称之为构建规则。每个构建规则至少需要具备如下三个参数:
-
name
:构建规则的名字,在一个构建文件中必须保持唯一性 -
deps
:构建规则的依赖项,以一个构建目标的列表的形式表示。 -
visibility
:可见性,表明其他构建规则是否可以依赖该构建规则,以一个构建目标模式的列表形式表示。
在Buck中,每一个构建规则可以产出零个或者一个输出文件,其他构建规则可以声明依赖这些输出文件。例如:
- 构建规则
android_library
的输出是一个JAR文件,因此,构建规则android_binary
可以声明依赖于规则android_library
,这样就可以在最终生成的APK文件中包含android_library
输出的JAR包。 - 构建规则
android_library
B依赖于另一个规则android_library
A,那么在B编译时,会将A生成的JAR文件包含进来。
讲了这么多,我们还是来看一下构建规则的示例,这个示例是一个android_binary
规则,它依赖于android_resource
和android_library
,分别表示生成APK所需的资源和Java代码文件:
android_resource(
name = 'res',
res = 'res',
assets = 'assets',
)
android_library(
name = 'src',
srcs = glob(['src/**/*.java']),
deps = [
':res',
],
)
# 构建这个规则将会生成一个名为 messenger.apk的文件
android_binary(
name = 'messenger',
manifest = 'AndroidManifest.xml',
keystore = '//keystores:prod',
package_type = 'release',
proguard_config = 'proguard.cfg',
deps = [
':res',
':src',
],
)
有一点需要明确,构建规则依赖的规则,会先于构建规则本身进行构建。构建规则和它的依赖之间是一个有向图,Buck要求这个图是无环的,也就是最终形成一个有向无环图。这样Buck就可以对独立的子图进行并行构建,从而提高构建效率。
构建目标(Build Target)
构建目标是一个字符串,用于标识工程中的某个构建规则,一个完整的构建目标示例:
//java/com/facebook/share:ui
主要有三个组成部分:
-
//
前缀表示路径是相对于工程根目录 -
java/com/facebook/share
表示构建文件BUCK位于java/com/facebook/share
目录中 - 冒号后面的
ui
表示构建文件里面定义的构建规则名字,在一个构建文件中,构建规则的名字是唯一的。
在同一个构建文件中,可以使用构建目标的相对路径来引用它。构建目标的相对路径以冒号(:)开始,紧跟着三大组成部分的第三部分:构建规则的名字。例如在构建文件java/com/facebook/share/BUCK
中,:ui
可以用来引用//java/com/facebook/share:ui
。
# 这是java/com/facebook/share/BUCK文件的内容
java_binary(
name = 'ui_jar',
deps = [
# 跟使用 '//java/com/facebook/share:ui' 效果一样
':ui',
],
)
构建目标经常作为构建规则的参数,并在Buck的命令行界面中使用。完整的情况下,使用命令行构建buck时,我们需要输入完整的构建目标如下:
buck build //java/com/facebook/share:share
幸运的是,Buck在解析命令行的构建目标时很宽松(解析构建文件中的构建目标时很严格),所以可以把前缀//
去掉,一样可以正常构建:
buck build java/com/facebook/share:share
如果在冒号前面多了一个/
,同样也会被忽略,因此我们可以这么写:
buck build java/com/facebook/share/:share
可以注意到,其中的java/com/facebook/share/
可以通过tab键帮我们自动补齐,不用手动一个字母一个字母的输入了。更进一步,如果冒号后面的构建规则名字和最后一个路径相同,也可以忽略:
# 相当于 //java/com/facebook/share:share.
buck build java/com/facebook/share/
构建文件(Build File)
构建文件用于定义一个或者多个构建规则,统一命名为BUCK。工程中的Java源文件只能被离它最近的BUCK文件引用,这里的“最近“指的是文件目录树中离该Java源文件所在目录最近的。例如,如果我们的工程中有如下所示的BUCK文件:
java/com/facebook/base/BUCK
java/com/facebook/common/BUCK
java/com/facebook/common/collect/BUCK
那么这些BUCK文件中定义的构建规则有如下限制:
-
java/com/facebook/base/BUCK
文件中的构建规则只能引用java/com/facebook/base/
目录中的源文件 -
java/com/facebook/common/BUCK
文件中的构建规则可以引用该目录中除了子目录java/com/facebook/common/collect/
之外的所有Java源文件 -
java/com/facebook/common/collect/
目录中的Java源文件只能被java/com/facebook/common/collect/BUCK
这个文件中的构建规则所引用。
构建文件所能引用到的源文件范围我们称之为构建包(build package),想要引用到其他构建包的源文件,需要在构建规则中使用deps
引用这些文件。回到前面的例子,位于java/com/facebook/common/concurrent/
的代码需要依赖java/com/facebook/common/collect/
包下面的代码,假设java/com/facebook/common/collect/BUCK
文件中有一个构建规则如下:
java_library(
name = 'collect',
srcs = glob(['*.java']),
deps = [
'//java/com/facebook/base:base',
],
)
那么java/com/facebook/common/BUCK
文件应该定义规则如下:
java_library(
name = 'concurrent',
srcs = glob(['concurrent/*.java']),
deps = [
'//java/com/facebook/base:base',
'//java/com/facebook/common/collect:collect',
],
)
错误的做法我们也说明一下,直接在srcs中引用concurrent包中的源文件是无效的:
java_library(
name = 'concurrent',
srcs = glob(['collect/*.java', 'concurrent/*.java']),
deps = [
'//java/com/facebook/base:base',
],
)
构建目标模式(Build Target Pattern)
构建目标模式是一个用来匹配一个或者多个构建目标的字符串,一般作为参数传递给构建规则中的visibility
属性。一个构建目标其实也是一个构建目标模式,只是它匹配的是它自己:
# 匹配到 '//apps/myapp:app'.
'//apps/myapp:app'
以冒号:
结尾的构建目标模式可以匹配到同一目录中的构建目标:
# 匹配到 '//apps/myapp:app_debug' 和'//apps/myapp:app_release'.
'//apps/myapp:'
以/...
结尾的构建目标模式可以匹配到该目录及其子目录中的构建目标:
# 匹配到 '//apps:common' and '//apps/myapp:app'.
'//apps/...'
欢迎关注我的微信公众号
