都说BUCK很牛逼,mobile项目使用monorepo的首选,目前项目中也在考虑使用它,周末就简单调研一下。网上的资料真的是太少,官方文档又很不详细,给出的大多是概念性的东西,不上手总是没有感觉的。参考一下Airbnb的Demo,先写个HelloWorld让它run起来。
下图是使用BUCK编译过程的依赖关系图,这个有别于我们使用Xcode进行的编译(CMD + B,编译基于工程文件.xcodeproj
/.xcworkspace
以及官方的xcodebuild
命令行工具),但图中的每个节点都对应一个Xcode build过程的一个产物

从上到下来进行看一遍:
apple_package
:编译打包最终生成的.ipa文件;
apple_bundle
:可以直接拖拽进simulator进行安装的.app文件
apple_binary
:编译生成的二进制文件,及相应的资源文件,如图片,xib,资源文件。当这个apple_binary
是一个library依赖时,可以把它理解为iOS中的Framework。
apple_resource
:资源文件
apple_asset_catalog
:Assets目录下的内容。
apple_binary
同时可以依赖其他的apple_binary
,类似于在Xcode项目中依赖于系统Framework或自己构建的动态库/静态库。
如果构建的是一个简单的,没有其他依赖的App,那么这些内容就已经足够。当然要编译代码,还需要编译工具,BUCK使用的是Facebook自家的工具xctool,在使用xctool进行编译时需要的那些编译参数,BUCK也提供了导入方式,但具体的参数就需要自己来写了,好在Airbnb的sample中给出了很好的示例,那些繁琐的编译配置项可以直接拿来使用。
BUCK不使用.xcodeproj
/.xcworkspace
,而是使用了自己定义的配置文件,文件名就是BUCK,这里给出一个最简单的实例,使用BUCK构建一个最简单的Hello World应用,它不包含任何的依赖(Pods/Carthage)只有一个AppDelegate和一个ViewController,在之后的文章中会一步一步添加依赖,包括Cocoapods,系统Framework以及以及构建完成的Library(比如之前文章中的mars)。我们先来下这个项目的目录结构:

第一个感受就是它没有
.xcodeproj
,取而代之的是BUCK
文件和在根目录下的一个隐藏文件:.buckconfig
,这两个文件中描述了,需要编译的文件都在哪,依赖关系是怎么样的(比如第一张图中的apple_bundle
声明自己的依赖是apple_binary
)以及编译参数是什么?
先来看一下BUCK文件:
load("//Config:configs.bzl", "app_binary_configs", "info_plist_substitutions")
apple_asset_catalog(
name = "Assets",
visibility = ["//App:"],
app_icon = "AppIcon",
dirs = ["Assets.xcassets"],
)
apple_resource(
name = "Resource",
visibility = ["//App:"],
files = glob(["**/*.storyboard"]),
)
apple_binary(
name = "MyBuckSampleAppBinary",
visibility = [
"//App:",
"//App/...",
],
swift_version = "5",
srcs = [
"AppDelegate.swift",
"ViewController.swift",
],
configs = app_binary_configs("MyBuckSampleApp"),
deps = [
":Resource",
":Assets",
]
)
apple_bundle(
name = "MyBuckSampleApp",
visibility = [
"//App:",
],
extension = "app",
binary = ":MyBuckSampleAppBinary",
product_name = "MyBuckSampleApp",
info_plist = "Info.plist",
info_plist_substitutions = info_plist_substitutions("MyBuckSampleApp"),
)
apple_package(
name = "MyBuckSampleAppPackage",
bundle = ":MyBuckSampleApp"
)
xcode_workspace_config(
name = "workspace",
workspace_name = "MyBuckSampleApp",
src_target = ":MyBuckSampleApp",
additional_scheme_actions = {
'Build': {
'PRE_SCHEME_ACTIONS': ["echo 'Started'"],
'POST_SCHEME_ACTIONS': ["echo 'Finished'"],
},
}
)
其实看名字就已经能知道他们都是什么。
srcs
/files
/dirs
:声明需要编译的文件位置,资源文件的位置、目录;
deps
:声明依赖,可以看到依赖的字符串前有个:
,冒号后的字符串就是相应编译单元(官方称之为Target)的名字,比如apple_binary的deps中的:Resource
,Resource
就是apple_resource
的name
;
load(...)
:用于导入外部的函数,这些函数文件名为.bzl,其实就是python函数,用于生产一些配置,文件的第一行是从根目录下的Config目录下的configs.bzl中导入两个函数"app_binary_configs", "info_plist_substitutions"。
另一个配置文件是根目录下的.buckconfig
,其中大多都是编译参数:
[cxx]
default_platform = iphonesimulator-x86_64
cflags = -g -fmodules -fobjc-arc -D BUCK -w $(config custom.other_cflags)
cxxflags = -fobjc-arc -std=c++14 -D DEBUG -g $(config custom.other_cxxflags)
combined_preprocess_and_compile = true
pch_enabled = false
ldflags = -Xlinker -objc_abi_version -Xlinker 2 -fobjc-arc -fobjc-link-runtime $(config custom.other_cxxflags)
[swift]
version = 5.0
compiler_flags = -DBUCK -whole-module-optimization $(config custom.optimization) $(config custom.config_swift_compiler_flags) $(config custom.other_swift_compiler_flags)
use_filelist = true
[apple]
use_swift_delegate = false
use_header_maps_in_xcode = false
generate_missing_umbrella_headers = true
iphonesimulator_target_sdk_version = 11.0
iphoneos_target_sdk_version = 11.0
provisioning_profile_read_command = security cms -Di
xctool_default_destination_specifier = platform=iOS Simulator,OS=latest
[parser]
polyglot_parsing_enabled = true
default_build_file_syntax = SKYLARK
[project]
ide_force_kill = always
project_schemes = true
ide = xcode
allow_symlinks = forbid
ignore = tools, \
.git, \
[build]
threads = 4
[custom]
config = debug
optimization = -Onone
config_swift_compiler_flags = -DDEBUG -enable-testing -g
code_coverage_cflags = -fprofile-instr-generate -fcoverage-mapping
code_coverage_cxxflags = -fprofile-instr-generate -fcoverage-mapping
code_coverage_ldflags = -fprofile-instr-generate
code_coverage_swift_compiler_flags = -profile-generate -profile-coverage-mapping
之后通过BUCK的命令行工具就可以工作了,此处使用了一个Makefile
build:
buck build //App:MyBuckSampleAppBinary
debug:
buck install //App:MyBuckSampleApp --run --simulator-name 'iPhone 11'
targets:
buck targets //...
clean:
rm -rf **/*.xcworkspace
rm -rf **/*.xcodeproj
rm -rf buck-out
rm -rf .buckd
kill_xcode:
killall Xcode || true
killall Simulator || true
project: clean
buck project //App:workspace
open App/MyBuckSampleApp.xcworkspace
build:就是编译一下
debug:将编译好的app装在simulator上
tagets:列出当前项目中的所有编译单元
clean:删除缓存
project:生成工程文件,方便通过Xcode打开进行debug或是写代码。
第一次尝试,总算是OK了,但是对于一个项目来说,这远远不够的,后面会继续更新,例如:添加依赖,Swift、OC混合编程的项目配置,不同环境的构建,签名,如何配合fastlane进行自动化。
网友评论