本文已参与「新人创作礼」活动,一起开启掘金创作之路。
优势与隐忧
优势
- 允许多功能选择构建工具
- 启用CI的构建/测试避免(不构建不受影响的内容)超出构建系统的能力
- 通过验证和构建分离简化仓库布局和验证.
- 通过符号分析"Strict" deps (只声明使用的)能够被极大地简化
- 对图谱的完全了解有助于实现更好的自动化
- 可以通过符号分析和模板化来管理资源的排序
- 简化engBuild所做的变量"trimming"
- 通过多构建系统和途径启用低成本实验.
隐忧
- 从开发者中抽象构建系统
- 对某些构建问题采取分类途径(TBD - 分类插件说明)
- IDE的初始有限支持
- 比如Android Studio理解gradle, 但Spice并不.
- 基于维护的需要引入新的工具
现有技术
对已经尝试过的类似尝试和系统的讨论.
Dropbox
- Android构建系统现代化
- 注意(WIP):
- 在没有看到drop box实现的情况下, 我们只能基于可怜的数据推测问题是啥.
- “BMBF采用非常特殊的文件和目录结构.”
- “ BMBF不兼容于Gradle的增量构建因为build.gradle文件在每一次开发者构建应用的时候会被重新创建”
- 构建文件应该只能在Workspace刷新或者从master或者模块增量中拉取时被构建.
- 选项1: 后台线程只能用于理解Workspace中的变更并且只能按需生成gradle文件.
- 选项2: 构建gradle插件, 它会读取spice文件并且配置图谱.
- “对于在公司超过6个月的工程师, 如果依然不知道如何使用BMBF创建新的模块, 这是很不常见的. “
- 应该基于工具而非定制添加新的模块.
- 创建模块文件和生成构建系统配置的简单脚本会有很大帮助.
- 应该基于工具而非定制添加新的模块.
- “BMBF很固执己见, 它使得向未构建进BMBF的Gradle脚本中添加函数很困难.”
- 在疏忽仓库维护者的情况下开发者添加定制构建脚本是不理想的, 因为它增加了大规模的支持负担和成本.
- 定制配置例子: git.sqcorp.co/projects/AN…
- 使元数据开放并尽可能地简单
- 编码扩展点 (比如代码生成扩展, 像Anvil或者proto, 分析扩展等.)
- 需要考虑的用例:
- Anvil
- 自定义源定位, 比如SQLDelight
- 额外的元数据, 比如Android
- 需要考虑的用例:
- 在疏忽仓库维护者的情况下开发者添加定制构建脚本是不理想的, 因为它增加了大规模的支持负担和成本.
Cocoapods
- 它是如何关联的?
- 稳定的元数据规范定义了依赖图谱和模块语义
- Square的iOS bazel迁移成功的很大一部分是cocopods提供了这么一个稳定的迁移工具基础.
- 我们只用Cocoapods可以吗?
- iOS特定的
- 元数据语言用运行时提供了条件语句和一些命令特性.
- 用声明式内容混合了构建和打包相关的元数据.
Maven
- 它是如何关联的?
- 尽管构建系统就其本身而言, 它既是构建系统, 也是元数据系统.
- Maven对于构建元数据确实有正确的想法.
- 理论上POM可以作为构建元数据结构来服务, 但的确有一些问题:
- xml是人类不可读的
- POM有可扩展的混合, 包括纯构建元数据和额外的构建插件配置
- 由于Maven元数据的流行, 向maven依赖的直接映射将会十分重要, 尤其是在JVM语言构建空间(比如gradle和bazel)对于外部(非项目)依赖
- TBD细节: 我们喜欢什么, 我们如何必须区分, 等等.
例子
Spice yml 工作空间例子
tools:
- name: "dagger"
any: ["kotlin", "java"]
- name: "anvil"
all: ["kotlin", "dagger"]
- name: "android"
all: ["kotlin", "java"]
- name: "kotlin"
- name: "sqldelight"
all: ["kotlin"]
not: ["sqldelight_legacy"]
- name: "test"
external:
- name: "maven",
urls: [...repo urls..],
artifacts: { com.foo.thing : "0.4.1" }
Spice yml 模块例子
shared:
srcs: ["src/main"]
deps: ["/other:module", "/maven/com.foo.thing"]
tools: ["android", "hephaestus"]
variant:
- name: "debug"
srcs: ["src/debug"]
deps: ["/debug/module"]
- name: "v2"
overlay: ["debug"]
deps: ["/other:module"]
- name: "v1"
overlay: ["debug"]
Gradle Kotlin 模版例子
注意: 这是一个比较早期的例子, 源于设计的早期版本. 用作伪代码进行解释.
pluginNames = mapOf(
"anvil" to "com.squareup.anvil"
)
Cabinet(".").render { spice ->
spice.workspace.apply {
Gradle.Workspace {
block("plugins") {
tools.forEach { p ->
call("invoke", pluginNames[p.name], call("version", p.version)
}
}
block("repositories") {
external.urls.forEach{ u ->
block("maven") { call("url", u.url) }
}
}
}
}
spice.modules.forEach { module ->
Gradle.Build(module.path) {
module.tools.forEach { p ->
id pluginNames[p.name]
}
block("android") {
module.variants.forEach { v ->
block(v.name) { ... variant specific attributes ... }
}
}
block("dependencies") {
module.shared.deps { d ->
when {
d.external -> "main" implementation d.coordinates
else -> "main" implementation call("project", d.coordinates)
}
}
module.variants.forEach { v ->
when {
// TODO: fix this
d.external -> v.name (implementation) (d.coordinates)
else -> v.name (implementation) call("project", d.coordinates)
}
}
}
}
存储格式选项
- Protocol buffers
- 用起来有点古怪, 但有成熟的工具支持
- 优势: 支持定义类型, 有ide的支持
- 隐忧: 不容易验证
- 验证逻辑可以通过custom_options来完成
- Yaml
- 通常且已经用于配置
- 优势: 支持定义类型, 有ide的支持
- 隐忧: 不容易验证
- Json
- 众所周知
- 优势: 成熟的工具支持
- 隐忧: 不容易验证
- Xml
- 哈哈哈. 哦等一下你是认真的. 让我笑得更厉害点.
- 隐忧: 冗余
- Custom
- 语法不熟悉
- 子集Gradle
- 隐忧: 太松散了, 期待Kotlin或者Groovy.
- Kotlin DSL
- 编译类型安全, 语言众所周知
- 优势: 能够将验证构建进DSL
- 隐忧: 需要禁止条件语句和其它的语言特性, 比如变量, 函数定义, 类, 等等.
- Starlark子集
- 隐忧: 可能执行得比较慢.
- 隐忧: 通过添加函数和条件语句增加了元数据的复杂性
术语
- Project (又名 node) - 配置单元, 通过依赖关于到其它的Project, 并应用到该Project的工具.
- 比如: gradle project, bazel target
- Dependency (又名 edge) - Project之间的关系, 形成了有向无环图
- Tool - 标签, 引用了一些工具, 这些工具将会应用到不同场景下的node.
- Tool在元数据中进行抽象, 并恰当地应用于任何上下文.
- 比如“java”在构建系统中意味着java编译, 但可能在检索上下文中被忽略.
- 另一个看待Tools的方式是作为配置信号, 基础传输单位, 因为通常情况下这引用了要么是工具链要么是专门配置的工具链, Tool就是这个术语.
进化中的设计决策
暂定的设计决策可能会发生改变, 但应该被记录, 所以我们能够持续追踪.
- 依赖排除和deps门诊
- Bazel风格的workspace层次的规范
- 使用maven/gradle声明站点需要是导入程序的一项功能
- 工作空间需要添加/删除/替换语法勾子来进行deps门诊
- 声明站点的deps门诊需要使用封装了“reformed”节点的新项目来完成.
- Bazel风格的workspace层次的规范